珠海响应式网站制作个人备案网站可以做产品推广

当前位置: 首页 > news >正文

珠海响应式网站制作,个人备案网站可以做产品推广,个人网站备案 费用,网站一直不收录阅读之前的说明 先说明#xff0c;本篇很长#xff0c;也很枯燥#xff0c;若不是绝对的技术偏执狂是看不下去的.将通过一段简单代码去跟踪编译成ELF格式后的内容.看看ELF究竟长了怎样的一副花花肠子#xff0c;用readelf命令去窥视ELF的全貌#xff0c;最后用objdump命令…阅读之前的说明 先说明本篇很长也很枯燥若不是绝对的技术偏执狂是看不下去的.将通过一段简单代码去跟踪编译成ELF格式后的内容.看看ELF究竟长了怎样的一副花花肠子用readelf命令去窥视ELF的全貌最后用objdump命令反汇编ELF.找到了大家熟悉main函数. 开始之前先说结论: ELF 分四块其中三块是描述信息(也叫头信息)另一块是内容放的是所有段/区的内容. ELF头定义全局性信息 Segment(段)头内容描述段的名字开始位置类型偏移大小及每段由哪些区组成. 内容区ELF有两个重要概念 Segment(段) 和 Section(区)段比区大二者之间关系如下: 每个Segment可以包含多个Section每个Section可以属于多个SegmentSegment之间可以有重合的部分拿大家熟知的.text.data.bss举例它们都叫区但它们又属于LOAD段. Section(区)头内容描述区的名字开始位置类型偏移大小等信息 ELF一体两面面对不同的场景扮演不同的角色这是理解ELF的关键链接器只关注13(区)4 的内容加载器只关注123(段)的内容鸿蒙对EFL的定义在 kernel\extended\dynload\include\los_ld_elf_pri.h文件中 示例代码 在windows目录E:\harmony\docker\case_code_100下创建 main.c文件如下: #include stdio.h void say_hello(char *who) {printf(hello %s!\n who); } char my_name harmony os;int main() {say_hello(my_name);return 0; }
做好了环境映射所以文件会同时出现在docker中.编译生成ELF-运行-readelf -h查看app头部信息. root5e3abe332c5a:/home/docker/case_code_100# ls main.c root5e3abe332c5a:/home/docker/case_code_100# gcc -o app main.c root5e3abe332c5a:/home/docker/case_code_100# ls app main.c root5e3abe332c5a:/home/docker/case_code_100# ./app hello harmony os!名正才言顺 一下是关于ELF的所有中英名词对照.建议先仔细看一篇再看系列篇部分. 可执行可连接格式 : ELF(Executable and Linking Format) ELF文件头:ELF header 基地址:base address 动态连接器: dynamic linker 动态连接: dynamic linking 全局偏移量表: got(global offset table) 进程链接表: plt(Procedure Linkage Table) 哈希表: hash table 初始化函数 : initialization function 连接编辑器 : link editor 目标文件 : object file 函数连接表 : procedure linkage table 程序头: program header 程序头表 : program header table 程序解析器 : program interpreter 重定位: relocation 共享目标 : shared object 区(节): section 区(节)头 : section header 区(节)表: section header table 段 : segment 字符串表 : string table 符号表: symbol table 终止函数 : termination functionELF历史 ELF(Executable and Linking Format)即可执行可连接格式最初由UNIX系统实验室(UNIX System Laboratories – USL)做为应用程序二进制接口(Application Binary Interface - ABI)的一部分而制定和发布.是鸿蒙的主要可执行文件格式. ELF的最大特点在于它有比较广泛的适用性通用的二进制接口定义使之可以平滑地移植到多种不同的操作环境上.这样不需要为每一种操作系统都定义一套不同的接口因此减少了软件的重复编码与编译加强了软件的可移植性.
ELF整体布局 ELF规范中把ELF文件宽泛地称为目标文件 (object file)这与我们平时的理解不同.一般地我们把经过编译但没有连接的文件(比如Unix/Linux上的.o文件)称为目标文件而ELF文件仅指连接好的可执行文件在ELF规范中所有符合ELF格式规范的都称为ELF文件也称为目标文件这两个名字是相同的而经过编译但没有连接的文件则称为可重定位文件 (relocatable file)“或待重定位文件 (relocatable file)”.本文采用与此规范相同的命名方式所以当提到可重定位文件时一般可以理解为惯常所说的目标文件而提到目标文件时即指各种类型的ELF文件. ELF格式可以表达四种类型的二进制对象文件(object files): 可重定位文件(relocatable file)用于与其它目标文件进行连接以构建可执行文件或动态链接库.可重定位文件就是常说的目标文件由源文件编译而成但还没有连接成可执行文件.在UNIX系统下一般有扩展名.o.之所以称其为可重定位是因为在这些文件中如果引用到其它目标文件或库文件中定义的符号变量或者函数的话只是给出一个名字这里还并不知道这个符号在哪里其具体的地址是什么.需要在连接的过程中把对这些外部符号的引用重新定位到其真正定义的位置上所以称目标文件为可重定位或者待重定位的.可执行文件(executable file)包含代码和数据是可以直接运行的程序.其代码和数据都有固定的地址 或相对于基地址的偏移系统可根据这些地址信息把程序加载到内存执行.共享目标文件(shared object file)即动态连接库文件.它在以下两种情况下被使用:第一在连接过程中与其它动态链接库或可重定位文件一起构建新的目标文件第二在可执行文件被加载的过程中被动态链接到新的进程中成为运行代码的一部分.包含了代码和数据这些数据是在链接时被链接器ld和运行时动态链接器ld.so.l、libc.so.l、ld-linux.so.l使用的.核心转储文件(core dump file就是core dump文件) 可重定位文件用在编译和链接阶段. 可执行文件用在程序运行阶段. 共享库则同时用在编译链接和运行阶段本篇 app 就是个 DYN可直接运行.Type: DYN (Shared object file)在不同阶段我们可以用不同视角来理解ELF文件整体布局如下图所示: 从上图可见ELF格式文件整体可分为四大部分: ELF Header: 在文件的开始描述整个文件的组织.即readelf -h app看到的内容Program Header Table: 告诉系统如何创建进程映像.用来构造进程映像的目标文件必须具有程序头部表可重定位文件可以不需要这个表.表描述所有段(Segment)信息即readelf -l app看到的前半部分内容.Segments:段(Segment)由若干区(Section)组成.是从加载器角度来描述 ELF 文件.加载器只关心 ELF header Program header table 和 Segment 这三部分内容。 在加载阶段可以忽略 section header table 来处理程序所以很多加固手段删除了section header tableSections: 是从链接器角度来描述 ELF 文件. 链接器只关心 ELF headerSections 以及 Section header table 这三部分内容。在链接阶段可以忽略 program header table 来处理文件.Section Header Table:描述区(Section)信息的数组每个元素对应一个区通常包含在可重定位文件中可执行文件中为可选(通常包含) 即readelf -S app看到的内容从图中可以看出 Segment:Section(M:N)是多对多的包含关系.Segment是由多个Section组成Section也能属于多个段. ELF头信息 ELF头部信息对应鸿蒙源码结构体为 LDElf32Ehdr 各字段含义已一一注解很容易理解. //kernel\extended\dynload\include\los_ld_elf_pri.h /
Elf header / #define LD_EI_NIDENT 16 typedef struct {UINT8 elfIdent[LD_EI_NIDENT]; / Magic number and other info ///含前16个字节又可细分成class、data、version等字段具体含义不用太关心只需知道前4个字节点包含ELF关键字这样可以判断当前文件是否是ELF格式UINT16 elfType; / Object file type ///表示具体ELF类型可重定位文件/可执行文件/共享库文件UINT16 elfMachine; / Architecture ///表示cpu架构UINT32 elfVersion; / Object file version ///表示文件版本号UINT32 elfEntry; / Entry point virtual address ///对应Entry point address程序入口函数地址通过进程虚拟地址空间地址表达UINT32 elfPhoff; / Program header table file offset ///对应Start of program headers表示program header table在文件内的偏移位置UINT32 elfShoff; / Section header table file offset ///对应Start of section headers表示section header table在文件内的偏移位置UINT32 elfFlags; / Processor-specific flags ///表示与CPU处理器架构相关的信息UINT16 elfHeadSize; / ELF header size in bytes ///对应Size of this header表示本ELF header自身的长度UINT16 elfPhEntSize; / Program header table entry size ///对应Size of program headers表示program header table中每个元素的大小UINT16 elfPhNum; / Program header table entry count ///对应Number of program headers表示program header table中元素个数UINT16 elfShEntSize; / Section header table entry size ///对应Size of section headers表示section header table中每个元素的大小UINT16 elfShNum; / Section header table entry count ///对应Number of section headers表示section header table中元素的个数UINT16 elfShStrIndex; / Section header string table index ///对应Section header string table index表示描述各section字符名称的string table在section header table中的下标 } LDElf32Ehdr; root5e3abe332c5a:/home/docker/case_code_100# readelf -h app ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00Class: ELF64Data: 2s complement little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: DYN (Shared object file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x1060Start of program headers: 64 (bytes into file)Start of section headers: 14784 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 13Size of section headers: 64 (bytes)Number of section headers: 31Section header string table index: 30 解读 显示的信息就是 ELF header 中描述的所有内容了。这个内容与结构体 LDElf32Ehdr 中的成员变量是一一对应的 Size of this header: 64 (bytes)也就是说ELF header 部分的内容一共是 64 个字节。64个字节码长啥样可以用命令od -Ax -t x1 -N 64 app看并对照结构体LDElf32Ehdr来理解. root5e3abe332c5a:/home/docker/case_code_10051# od -Ax -t x1 -N 64 app 000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 000010 03 00 3e 00 01 00 00 00 60 10 00 00 00 00 00 00 000020 40 00 00 00 00 00 00 00 c0 39 00 00 00 00 00 00 000030 00 00 00 00 40 00 38 00 0d 00 40 00 1f 00 1e 00 000040 简单解释一下命令的几个选项 -Ax: 显示地址的时候用十六进制来表示。如果使用 -Ad意思就是用十进制来显示地址; -t -x1: 显示字节码内容的时候使用十六进制(x)每次显示一个字节(1); -N 64只需要读取64个字节; 这里留意这几个内容下面会说明先记住. Entry point address: 0x1060 //代码区 .text 起始位置即程序运行开始位置 Size of program headers: 56 (bytes)//每个段头大小 Number of program headers: 13 //段数量 Size of section headers: 64 (bytes)//每个区头大小 Number of section headers: 31 //区数量 Section header string table index: 30 //字符串数组索引该区记录所有区名称 段(Segment)头信息 段(Segment)信息对应鸿蒙源码结构体为 LDElf32Phdr //kernel\extended\dynload\include\los_ld_elf_pri.h / Program Header / typedef struct {UINT32 type; / Segment type / //段类型UINT32 offset; / Segment file offset / //此数据成员给出本段内容在文件中的位置即段内容的开始位置相对于文件开头的偏移量.UINT32 vAddr; / Segment virtual address / //此数据成员给出本段内容的开始位置在进程空间中的虚拟地址.UINT32 phyAddr; / Segment physical address / //此数据成员给出本段内容的开始位置在进程空间中的物理地址.对于目前大多数现代操作系统而言应用程序中段的物理地址事先是不可知的所以目前这个成员多数情况下保留不用或者被操作系统改作它用.UINT32 fileSize; / Segment size in file / //此数据成员给出本段内容在文件中的大小单位是字节可以是0.UINT32 memSize; / Segment size in memory / //此数据成员给出本段内容在内容镜像中的大小单位是字节可以是0.UINT32 flags; / Segment flags / //此数据成员给出了本段内容的属性.UINT32 align; / Segment alignment / //对于可装载的段来说其p_vaddr和p_offset的值至少要向内存页面大小对齐. } LDElf32Phdr; 解读 用readelf -l查看app段头部表内容先看命令返回的前半部分: root5e3abe332c5a:/home/docker/case_code_100# readelf -l app Elf file type is DYN (Shared object file) Entry point 0x1060 There are 13 program headers starting at offset 64 Program Headers:Type Offset VirtAddr PhysAddrFileSiz MemSiz Flags AlignPHDR 0x0000000000000040 0x0000000000000040 0x00000000000000400x00000000000002d8 0x00000000000002d8 R 0x8INTERP 0x0000000000000318 0x0000000000000318 0x00000000000003180x000000000000001c 0x000000000000001c R 0x1[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]LOAD 0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000618 0x0000000000000618 R 0x1000LOAD 0x0000000000001000 0x0000000000001000 0x00000000000010000x0000000000000225 0x0000000000000225 R E 0x1000LOAD 0x0000000000002000 0x0000000000002000 0x00000000000020000x0000000000000190 0x0000000000000190 R 0x1000LOAD 0x0000000000002db8 0x0000000000003db8 0x0000000000003db80x0000000000000260 0x0000000000000268 RW 0x1000DYNAMIC 0x0000000000002dc8 0x0000000000003dc8 0x0000000000003dc80x00000000000001f0 0x00000000000001f0 RW 0x8NOTE 0x0000000000000338 0x0000000000000338 0x00000000000003380x0000000000000020 0x0000000000000020 R 0x8NOTE 0x0000000000000358 0x0000000000000358 0x00000000000003580x0000000000000044 0x0000000000000044 R 0x4GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x00000000000003380x0000000000000020 0x0000000000000020 R 0x8GNU_EH_FRAME 0x000000000000201c 0x000000000000201c 0x000000000000201c0x000000000000004c 0x000000000000004c R 0x4GNU_STACK 0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 RW 0x10GNU_RELRO 0x0000000000002db8 0x0000000000003db8 0x0000000000003db80x0000000000000248 0x0000000000000248 R 0x1 数一下一共13个段其实在ELF头信息也告诉了我们共13个段 Size of program headers: 56 (bytes)//每个段头大小 Number of program headers: 13 //段数量 仔细看下这些段的开始地址和大小发现有些段是重叠的.那是因为一个区可以被多个段所拥有.例如:0x2db8 对应的 .init_array区就被第四LOAD 和 GNU_RELRO两段所共有. PHDR此类型header元素描述了program header table自身的信息.从这里的内容看出示例程序的program header table在文件中的偏移(Offset)为0x40即64号字节处.该段映射到进程空间的虚拟地址(VirtAddr)为0x40.PhysAddr暂时不用其保持和VirtAddr一致.该段占用的文件大小FileSiz为0x2d8.运行时占用进程空间内存大小MemSiz也为0x2d8.Flags标记表示该段的读写权限这里R表示只读Align对齐为8表明本段按8字节对齐. INTERP此类型header元素描述了一个特殊内存段该段内存记录了动态加载解析器的访问路径字符串.示例程序中该段内存位于文件偏移0x318处即紧跟program header table.映射的进程虚拟地址空间地址为0x318.文件长度和内存映射长度均为0x1c即28个字符具体内容为/lib64/ld-linux-x86-64.so.2.段属性为只读并按字节对齐. LOAD此类型header元素描述了可加载到进程空间的代码区或数据区: 其第二段包含了代码区文件内偏移为0x1000文件大小为0x225映射到进程地址0x001000处属性为只读可执行(RE)段地址按0x1000(4K)边界对齐.其第四段包含了数据区文件内偏移为0x2db8文件大小为0x260映射到进程地址0x003db8处属性为可读可写(RW)段地址也按0x1000(4K)边界对齐. DYNAMIC此类型header元素描述了动态加载段其内部通常包含了一个名为.dynamic的动态加载区.这也是一个数组每个元素描述了与动态加载相关的各方面信息将在系列篇(动态加载篇)中介绍.该段是从文件偏移0x2dc8处开始长度为0x1f0并映射到进程的0x3dc8.可见该段和上一个段LOAD4 0x2db8是有重叠的. GNU_STACK可执行栈即栈区在加载段的过程中当发现存在PT_GNU_STACK也就是GNU_STACK segment 的存在如果存在这个这个段的话看这个段的 flags 是否有可执行权限来设置对应的值.必须为RW方式. 再看命令返回内容的后半部分-段区映射关系 Section to Segment mapping:Segment Sections…0001 .interp02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt03 .init .plt .plt.got .plt.sec .text .fini04 .rodata .eh_frame_hdr .eh_frame05 .init_array .fini_array .dynamic .got .data .bss06 .dynamic07 .note.gnu.property08 .note.gnu.build-id .note.ABI-tag09 .note.gnu.property10 .eh_frame_hdr1112 .init_array .fini_array .dynamic .got 13个段和31个区的映射关系右边其实不止31个区是因为一个区可以共属于多个段例如 .dynamic .interp.got Segment:Section(M:N)是多对多的包含关系.Segment是由多个Section组成Section也能属于多个段.这个很重要说第二遍了. INTERP段只包含了.interp区LOAD2段包含.interp、.plt、.text等区.text代码区位于这个段. 这个段是 RE’属性只读可执行的.LOAD4包含.dynamic、.data、.bss等区 数据区位于这个段.这个段是 RW’属性可读可写. .data、.bss都是数据区有何区别呢?.data(ZI data)它用来存放初始化了的(initailized)全局变量(global)和初始化了的静态变量(static)..bss(RW data )它用来存放未初始化的(uninitailized)全局变量(global)和未初始化的静态变量.DYNAMIC段包含.dynamic区. 区表 区(section)头表信息对应鸿蒙源码结构体为 LDElf32Shdr //kernel\extended\dynload\include\los_ld_elf_pri.h / Section header / typedef struct {UINT32 shName; / Section name (string tbl index) ///表示每个区的名字UINT32 shType; / Section type ///表示每个区的功能UINT32 shFlags; / Section flags ///表示每个区的属性UINT32 shAddr; / Section virtual addr at execution ///表示每个区的进程映射地址UINT32 shOffset; / Section file offset ///表示文件内偏移UINT32 shSize; / Section size in bytes ///表示区的大小UINT32 shLink; / Link to another section ///Link和Info记录不同类型区的相关信息UINT32 shInfo; / Additional section information ///Link和Info记录不同类型区的相关信息UINT32 shAddrAlign; / Section alignment ///表示区的对齐单位UINT32 shEntSize; / Entry size if section holds table ///表示区中每个元素的大小(如果该区为一个数组的话否则该值为0) } LDElf32Shdr; 示例程序共生成31个区.其实在头文件中也已经告诉我们了 Size of section headers: 64 (bytes)//每个区头大小 Number of section headers: 31 //区数量 通过readelf -S命令看看示例程序中 section header table的内容如下所示. root5e3abe332c5a:/home/docker/case_code_100# readelf -S app There are 31 section headers starting at offset 0x39c0:Section Headers:[Nr] Name Type Address OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .interp PROGBITS 0000000000000318 00000318000000000000001c 0000000000000000 A 0 0 1[ 2] .note.gnu.propert NOTE 0000000000000338 000003380000000000000020 0000000000000000 A 0 0 8[ 3] .note.gnu.build-i NOTE 0000000000000358 000003580000000000000024 0000000000000000 A 0 0 4[ 4] .note.ABI-tag NOTE 000000000000037c 0000037c0000000000000020 0000000000000000 A 0 0 4[ 5] .gnu.hash GNU_HASH 00000000000003a0 000003a00000000000000024 0000000000000000 A 6 0 8[ 6] .dynsym DYNSYM 00000000000003c8 000003c800000000000000a8 0000000000000018 A 7 1 8[ 7] .dynstr STRTAB 0000000000000470 000004700000000000000084 0000000000000000 A 0 0 1[ 8] .gnu.version VERSYM 00000000000004f4 000004f4000000000000000e 0000000000000002 A 6 0 2[ 9] .gnu.version_r VERNEED 0000000000000508 000005080000000000000020 0000000000000000 A 7 1 8[10] .rela.dyn RELA 0000000000000528 0000052800000000000000d8 0000000000000018 A 6 0 8[11] .rela.plt RELA 0000000000000600 000006000000000000000018 0000000000000018 AI 6 24 8[12] .init PROGBITS 0000000000001000 00001000000000000000001b 0000000000000000 AX 0 0 4[13] .plt PROGBITS 0000000000001020 000010200000000000000020 0000000000000010 AX 0 0 16[14] .plt.got PROGBITS 0000000000001040 000010400000000000000010 0000000000000010 AX 0 0 16[15] .plt.sec PROGBITS 0000000000001050 000010500000000000000010 0000000000000010 AX 0 0 16[16] .text PROGBITS 0000000000001060 0000106000000000000001b5 0000000000000000 AX 0 0 16[17] .fini PROGBITS 0000000000001218 00001218000000000000000d 0000000000000000 AX 0 0 4[18] .rodata PROGBITS 0000000000002000 00002000000000000000001b 0000000000000000 A 0 0 4[19] .eh_frame_hdr PROGBITS 000000000000201c 0000201c000000000000004c 0000000000000000 A 0 0 4[20] .eh_frame PROGBITS 0000000000002068 000020680000000000000128 0000000000000000 A 0 0 8[21] .init_array INIT_ARRAY 0000000000003db8 00002db80000000000000008 0000000000000008 WA 0 0 8[22] .fini_array FINI_ARRAY 0000000000003dc0 00002dc00000000000000008 0000000000000008 WA 0 0 8[23] .dynamic DYNAMIC 0000000000003dc8 00002dc800000000000001f0 0000000000000010 WA 7 0 8[24] .got PROGBITS 0000000000003fb8 00002fb80000000000000048 0000000000000008 WA 0 0 8[25] .data PROGBITS 0000000000004000 000030000000000000000018 0000000000000000 WA 0 0 8[26] .bss NOBITS 0000000000004018 000030180000000000000008 0000000000000000 WA 0 0 1[27] .comment PROGBITS 0000000000000000 00003018000000000000002a 0000000000000001 MS 0 0 1[28] .symtab SYMTAB 0000000000000000 000030480000000000000648 0000000000000018 29 46 8[29] .strtab STRTAB 0000000000000000 000036900000000000000216 0000000000000000 0 0 1[30] .shstrtab STRTAB 0000000000000000 000038a6000000000000011a 0000000000000000 0 0 1 Key to Flags:W (write) A (alloc) X (execute) M (merge) S (strings) I (info)L (link order) O (extra OS processing required) G (group) T (TLS)C (compressed) x (unknown) o (OS specific) E (exclude)l (large) p (processor specific) String Table 在 ELF header 的最后 2 个字节是 0x1e 0x00即30. 它对应结构体中的成员 elfShStrIndex意思是这个 ELF 文件中字符串表是一个普通的 Section在这个 Section 中存储了 ELF 文件中使用到的所有的字符串。 我们使用readelf -x读出下标30区的数据: root5e3abe332c5a:/home/docker/case_code_100# readelf -x 30 app Hex dump of section .shstrtab:0x00000000 002e7379 6d746162 002e7374 72746162 ..symtab..strtab0x00000010 002e7368 73747274 6162002e 696e7465 ..shstrtab..inte0x00000020 7270002e 6e6f7465 2e676e75 2e70726f rp..note.gnu.pro0x00000030 70657274 79002e6e 6f74652e 676e752e perty..note.gnu.0x00000040 6275696c 642d6964 002e6e6f 74652e41 build-id..note.A0x00000050 42492d74 6167002e 676e752e 68617368 BI-tag..gnu.hash0x00000060 002e6479 6e73796d 002e6479 6e737472 ..dynsym..dynstr0x00000070 002e676e 752e7665 7273696f 6e002e67 ..gnu.version..g0x00000080 6e752e76 65727369 6f6e5f72 002e7265 nu.version_r..re0x00000090 6c612e64 796e002e 72656c61 2e706c74 la.dyn..rela.plt0x000000a0 002e696e 6974002e 706c742e 676f7400 ..init..plt.got.0x000000b0 2e706c74 2e736563 002e7465 7874002e .plt.sec..text..0x000000c0 66696e69 002e726f 64617461 002e6568 fini..rodata..eh0x000000d0 5f667261 6d655f68 6472002e 65685f66 _frame_hdr..eh_f0x000000e0 72616d65 002e696e 69745f61 72726179 rame..init_array0x000000f0 002e6669 6e695f61 72726179 002e6479 ..fini_array..dy0x00000100 6e616d69 63002e64 61746100 2e627373 namic..data..bss0x00000110 002e636f 6d6d656e 7400 ..comment. 可以发现这里其实是一堆字符串这些字符串对应的就是各个区的名字.因此section header table中每个元素的Name字段其实是这个string table的索引.为节省空间而做的设计再回头看看ELF header中的 elfShStrIndex Section header string table index: 30 //字符串数组索引该区记录所有区名称 它的值正好就是30指向了当前的string table. 符号表 Symbol Table Section Header Table中还有一类SYMTAB(DYNSYM)区该区叫符号表.符号表中的每个元素对应一个符号记录了每个符号对应的实际数值信息通常用在重定位过程中或问题定位过程中进程执行阶段并不加载符号表.符号表对应鸿蒙源码结构体为 LDElf32Sym. //kernel\extended\dynload\include\los_ld_elf_pri.h / Symbol table / typedef struct {UINT32 stName; / Symbol table name (string tbl index) ///表示符号对应的源码字符串为对应String Table中的索引UINT32 stValue; / Symbol table value ///表示符号对应的数值UINT32 stSize; / Symbol table size ///表示符号对应数值的空间占用大小UINT8 stInfo; / Symbol table type and binding ///表示符号的相关信息 如符号类型(变量符号、函数符号)UINT8 stOther; / Symbol table visibility /UINT16 stShndx; / Section table index *///表示与该符号相关的区的索引例如函数符号与对应的代码区相关 } LDElf32Sym; 用readelf -s读出示例程序中的符号表如下所示 root5e3abe332c5a:/home/docker/case_code_100# readelf -s appSymbol table .dynsym contains 7 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printfGLIBC_2.2.5 (2)3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_mainGLIBC_2.2.5 (2)4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable6: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalizeGLIBC_2.2.5 (2)Symbol table .symtab contains 67 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 0000000000000318 0 SECTION LOCAL DEFAULT 12: 0000000000000338 0 SECTION LOCAL DEFAULT 23: 0000000000000358 0 SECTION LOCAL DEFAULT 34: 000000000000037c 0 SECTION LOCAL DEFAULT 45: 00000000000003a0 0 SECTION LOCAL DEFAULT 56: 00000000000003c8 0 SECTION LOCAL DEFAULT 67: 0000000000000470 0 SECTION LOCAL DEFAULT 78: 00000000000004f4 0 SECTION LOCAL DEFAULT 89: 0000000000000508 0 SECTION LOCAL DEFAULT 910: 0000000000000528 0 SECTION LOCAL DEFAULT 1011: 0000000000000600 0 SECTION LOCAL DEFAULT 1112: 0000000000001000 0 SECTION LOCAL DEFAULT 1213: 0000000000001020 0 SECTION LOCAL DEFAULT 1314: 0000000000001040 0 SECTION LOCAL DEFAULT 1415: 0000000000001050 0 SECTION LOCAL DEFAULT 1516: 0000000000001060 0 SECTION LOCAL DEFAULT 1617: 0000000000001218 0 SECTION LOCAL DEFAULT 1718: 0000000000002000 0 SECTION LOCAL DEFAULT 1819: 000000000000201c 0 SECTION LOCAL DEFAULT 1920: 0000000000002068 0 SECTION LOCAL DEFAULT 2021: 0000000000003db8 0 SECTION LOCAL DEFAULT 2122: 0000000000003dc0 0 SECTION LOCAL DEFAULT 2223: 0000000000003dc8 0 SECTION LOCAL DEFAULT 2324: 0000000000003fb8 0 SECTION LOCAL DEFAULT 2425: 0000000000004000 0 SECTION LOCAL DEFAULT 2526: 0000000000004018 0 SECTION LOCAL DEFAULT 2627: 0000000000000000 0 SECTION LOCAL DEFAULT 2728: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c29: 0000000000001090 0 FUNC LOCAL DEFAULT 16 deregister_tm_clones30: 00000000000010c0 0 FUNC LOCAL DEFAULT 16 register_tm_clones31: 0000000000001100 0 FUNC LOCAL DEFAULT 16 __do_global_dtors_aux32: 0000000000004018 1 OBJECT LOCAL DEFAULT 26 completed.806033: 0000000000003dc0 0 OBJECT LOCAL DEFAULT 22 __do_global_dtors_aux_fin34: 0000000000001140 0 FUNC LOCAL DEFAULT 16 frame_dummy35: 0000000000003db8 0 OBJECT LOCAL DEFAULT 21 __frame_dummy_init_array_36: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c37: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c38: 000000000000218c 0 OBJECT LOCAL DEFAULT 20 FRAME_END39: 0000000000000000 0 FILE LOCAL DEFAULT ABS40: 0000000000003dc0 0 NOTYPE LOCAL DEFAULT 21 __init_array_end41: 0000000000003dc8 0 OBJECT LOCAL DEFAULT 23 _DYNAMIC42: 0000000000003db8 0 NOTYPE LOCAL DEFAULT 21 __init_array_start43: 000000000000201c 0 NOTYPE LOCAL DEFAULT 19 __GNU_EH_FRAME_HDR44: 0000000000003fb8 0 OBJECT LOCAL DEFAULT 24 _GLOBAL_OFFSET_TABLE_45: 0000000000001000 0 FUNC LOCAL DEFAULT 12 _init46: 0000000000001210 5 FUNC GLOBAL DEFAULT 16 __libc_csu_fini47: 0000000000004010 8 OBJECT GLOBAL DEFAULT 25 my_name48: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab49: 0000000000004000 0 NOTYPE WEAK DEFAULT 25 data_start50: 0000000000004018 0 NOTYPE GLOBAL DEFAULT 25 _edata51: 0000000000001218 0 FUNC GLOBAL HIDDEN 17 _fini52: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printfGLIBC_2.2.553: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_mainGLIBC_54: 0000000000004000 0 NOTYPE GLOBAL DEFAULT 25 __data_start55: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start56: 0000000000004008 0 OBJECT GLOBAL HIDDEN 25 __dso_handle57: 0000000000002000 4 OBJECT GLOBAL DEFAULT 18 _IO_stdin_used58: 00000000000011a0 101 FUNC GLOBAL DEFAULT 16 __libc_csu_init59: 0000000000004020 0 NOTYPE GLOBAL DEFAULT 26 _end60: 0000000000001060 47 FUNC GLOBAL DEFAULT 16 _start61: 0000000000004018 0 NOTYPE GLOBAL DEFAULT 26 __bss_start62: 0000000000001174 30 FUNC GLOBAL DEFAULT 16 main63: 0000000000001149 43 FUNC GLOBAL DEFAULT 16 say_hello64: 0000000000004018 0 OBJECT GLOBAL HIDDEN 25 TMC_END65: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable66: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalizeGLIBC_2.2 在最后位置找到了亲切的老朋友 main和say_hello 62: 0000000000001174 30 FUNC GLOBAL DEFAULT 16 main63: 0000000000001149 43 FUNC GLOBAL DEFAULT 16 say_hello main函数符号对应的数值为0x1174其类型为FUNC大小为30字节对应的代码区索引为16. say_hello函数符号对应数值为0x1149其类型为FUNC大小为43字节对应的代码区索引同为16. Section Header Table: [16] .text PROGBITS 0000000000001060 0000106000000000000001b5 0000000000000000 AX 0 0 16 反汇编代码区 在理解了String Table和Symbol Table的作用后通过objdump反汇编来理解一下.text代码区: root5e3abe332c5a:/home/docker/case_code_100# objdump -j .text -l -C -S app0000000000001149 say_hello: say_hello():1149: f3 0f 1e fa endbr64114d: 55 push %rbp114e: 48 89 e5 mov %rsp%rbp1151: 48 83 ec 10 sub \(0x10%rsp1155: 48 89 7d f8 mov %rdi-0x8(%rbp)1159: 48 8b 45 f8 mov -0x8(%rbp)%rax115d: 48 89 c6 mov %rax%rsi1160: 48 8d 3d 9d 0e 00 00 lea 0xe9d(%rip)%rdi # 2004 _IO_stdin_used0x41167: b8 00 00 00 00 mov \)0x0%eax116c: e8 df fe ff ff callq 1050 printfplt1171: 90 nop1172: c9 leaveq1173: c3 retq0000000000001174 main: main():1174: f3 0f 1e fa endbr641178: 55 push %rbp1179: 48 89 e5 mov %rsp%rbp117c: 48 8b 05 8d 2e 00 00 mov 0x2e8d(%rip)%rax # 4010 my_name1183: 48 89 c7 mov %rax%rdi1186: e8 be ff ff ff callq 1149 say_hello118b: b8 00 00 00 00 mov \(0x0%eax1190: 5d pop %rbp1191: c3 retq1192: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax%rax1)1199: 00 00 00119c: 0f 1f 40 00 nopl 0x0(%rax) 0x1149 0x1174正是say_hellomain函数的入口地址.并看到了激动人心的指令 1186: e8 be ff ff ff callq 1149 say_hello 很佩服你还能看到这里牛逼牛逼! 看了这么久还记得开头的C代码的样子吗? 再看一遍 : ) #include stdio.h void say_hello(char *who) {printf(hello %s!\n who); } char *my_name harmony os; int main() {say_hello(my_name);return 0; } root5e3abe332c5a:/home/docker/case_code_100# ./app hello harmony os! 但是!!! 晕怎么还有but西卡西…上面请大家记住的还有一个地方没说到 Entry point address: 0x1060 //代码区 .text 起始位置即程序运行开始位置 它的地址并不是main函数位置0x1174是0x1060!而且代码区的开始位置是0x1060没错的. [16] .text PROGBITS 0000000000001060 0000106000000000000001b5 0000000000000000 AX 0 0 16 难度main不是入口地址? 那0x1060上放的是何方神圣再查符号表发现是 60: 0000000000001060 47 FUNC GLOBAL DEFAULT 16 _start 从反汇编堆中找到 _start 0000000000001060 _start: _start():1060: f3 0f 1e fa endbr641064: 31 ed xor %ebp%ebp1066: 49 89 d1 mov %rdx%r91069: 5e pop %rsi106a: 48 89 e2 mov %rsp%rdx106d: 48 83 e4 f0 and \)0xfffffffffffffff0%rsp1071: 50 push %rax1072: 54 push %rsp1073: 4c 8d 05 96 01 00 00 lea 0x196(%rip)%r8 # 1210 __libc_csu_fini107a: 48 8d 0d 1f 01 00 00 lea 0x11f(%rip)%rcx # 11a0 __libc_csu_init1081: 48 8d 3d ec 00 00 00 lea 0xec(%rip)%rdi # 1174 main1088: ff 15 52 2f 00 00 callq *0x2f52(%rip) # 3fe0 __libc_start_mainGLIBC_2.2.5108e: f4 hlt108f: 90 nop 这才看到了0x1174的main函数.所以真正的说法是: 从内核动态加载的视角看程序运行首个函数并不是main而是_start.但从应用程序开发者视角看main就是启动函数. 经常有很多小伙伴抱怨说不知道学习鸿蒙开发哪些技术不知道需要重点掌握哪些鸿蒙应用开发知识点 为了能够帮助到大家能够有规划的学习这里特别整理了一套纯血版鸿蒙HarmonyOS Next全栈开发技术的学习路线包含了鸿蒙开发必掌握的核心知识要点内容有ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等鸿蒙HarmonyOS NEXT技术知识点。 《鸿蒙 (Harmony OS)开发学习手册》共计892页:https://gitcode.com/HarmonyOS_MN 如何快速入门 1.基本概念 2.构建第一个ArkTS应用 3.…… 开发基础知识: 1.应用基础知识 2.配置文件 3.应用数据管理 4.应用安全管理 5.应用隐私保护 6.三方应用调用管控机制 7.资源分类与访问 8.学习ArkTS语言 9.…… 基于ArkTS 开发 1.Ability开发 2.UI开发 3.公共事件与通知 4.窗口管理 5.媒体 6.安全 7.网络与链接 8.电话服务 9.数据管理 10.后台任务(Background Task)管理 11.设备管理 12.设备使用信息统计 13.DFX 14.国际化开发 15.折叠屏系列 16.…… 鸿蒙开发面试真题含参考答案:https://gitcode.com/HarmonyOS_MN OpenHarmony 开发环境搭建 《OpenHarmony源码解析》:https://gitcode.com/HarmonyOS_MN 搭建开发环境Windows 开发环境的搭建Ubuntu 开发环境搭建Linux 与 Windows 之间的文件共享……系统架构分析构建子系统启动流程子系统分布式任务调度子系统分布式通信子系统驱动子系统…… OpenHarmony 设备开发学习手册:https://gitcode.com/HarmonyOS_MN 写在最后 如果你觉得这篇内容对你还蛮有帮助我想邀请你帮我三个小忙 点赞转发有你们的 『点赞和评论』才是我创造的动力。关注小编同时可以期待后续文章ing不定期分享原创知识。想要获取更多完整鸿蒙最新学习资源请移步前往