
1.ELF格式ELF(Excutable and Linkable Format可执行和可链接格式).o库文件(.so和.a)以及可执行程序都是FLE格式也就是说可执行程序内的二进制代码不是杂乱无章存放有自己的结构和格式什么信息放在什么位置是井然有序的因为.o和.so都是ELF格式因此.o可以动态链接形成动态库.so也可以形成可执行程序主要是把相同区域内容进行合并(.text.data.rodata等)ELF格式是Unix和类Unix系统下特有的而Windows下也有类似功能的文件格式大概就是编译时预处理生成.i主要做头文件展开宏替换等编译生成汇编代码生成.s汇编生成.o如果打包成静态库就是按一定顺序把各个.o文件按序排好而动态库是把相同section合并在形成可执行程序的时候静态链接就是拷贝静态库中用到的.o文件和动态链接合并相同section类似静态库的打包用的是相对地址从0开始但是拷贝到可执行程序就是绝对地址而动态链接只记录用到哪些动态库在运行的时候找动态库运行时找和编译时找可以不是一个路径因为链接只记录库名不记录库地址链接就是看路径下有没有要用的有就过ELF Header是用来划分区域的可以用来定位Program Header Table和各Section以及Section Header Table和文件系统中的GDT比较像Section Header Table表明有几个Section每个Section的详细信息Program Header Table列举有效的段和属性进程地址空间段进程地址空间来自可执行程序每个区放什么区的大小有多少个区通过size命令可以查看可执行程序的节text是代码data是已初始化全局变量bss是未初始化block started by symbolreadelf -h 可执行程序名查看可执行程序的1.1 FLE HeaderELF文件本质也是文件而文件是4KB为单位放在磁盘上可以看作一维数组使用线性地址进行访问访问文件的读写指针相当于访问线性一维数组的下标[guchenVM-0-9-centos test_5_23]$ readelf-Sa.out There are30section headers, starting at offset 0x2d58: Section Headers:[Nr]Name Type Address Offset Size EntSize Flags Link Info Align[0]NULL 0000000000000000 00000000 0000000000000000 0000000000000000000[1].interp PROGBITS 0000000000400238 00000238 000000000000001c 0000000000000000 A001[2].note.ABI-tag NOTE 0000000000400254 00000254 0000000000000020 0000000000000000 A004[3].note.gnu.build-i NOTE 0000000000400274 00000274 0000000000000024 0000000000000000 A004[4].gnu.hash GNU_HASH 0000000000400298 00000298 0000000000000044 0000000000000000 A508[5].dynsym DYNSYM 00000000004002e0 000002e0 00000000000002e8 0000000000000018 A618[6].dynstr STRTAB 00000000004005c8 000005c8 0000000000000130 0000000000000000 A001[7].gnu.version VERSYM 00000000004006f8 000006f8 000000000000003e 0000000000000002 A502[8].gnu.version_r VERNEED 0000000000400738 00000738 0000000000000040 0000000000000000 A628[9].rela.dyn RELA 0000000000400778 00000778 0000000000000060 0000000000000018 A508[10].rela.plt RELA 00000000004007d8 000007d8 0000000000000210 0000000000000018 AI5238[11].init PROGBITS 00000000004009e8 000009e8 000000000000001a 0000000000000000 AX004[12].plt PROGBITS 0000000000400a10 00000a10 0000000000000170 0000000000000010 AX0016[13].text PROGBITS 0000000000400b80 00000b80 0000000000000d92 0000000000000000 AX0016[14].fini PROGBITS 0000000000401914 00001914 0000000000000009 0000000000000000 AX004[15].rodata PROGBITS 0000000000401920 00001920 00000000000002d0 0000000000000000 A0016[16].eh_frame_hdr PROGBITS 0000000000401bf0 00001bf0 0000000000000054 0000000000000000 A004[17].eh_frame PROGBITS 0000000000401c48 00001c48 0000000000000174 0000000000000000 A008[18].init_array INIT_ARRAY 0000000000601de0 00001de0 0000000000000008 0000000000000008 WA008[19].fini_array FINI_ARRAY 0000000000601de8 00001de8 0000000000000008 0000000000000008 WA008[20].jcr PROGBITS 0000000000601df0 00001df0 0000000000000008 0000000000000000 WA008[21].dynamic DYNAMIC 0000000000601df8 00001df8 0000000000000200 0000000000000010 WA608[22].got PROGBITS 0000000000601ff8 00001ff8 0000000000000008 0000000000000008 WA008[23].got.plt PROGBITS 0000000000602000 00002000 00000000000000c8 0000000000000008 WA008[24].data PROGBITS 00000000006020c8 000020c8 0000000000000004 0000000000000000 WA001[25].bss NOBITS 00000000006020e0 000020cc 0000000000000020 0000000000000000 WA0032[26].comment PROGBITS 0000000000000000 000020cc 000000000000002d 0000000000000001 MS001[27].symtab SYMTAB 0000000000000000 00002100 0000000000000858 000000000000001828468[28].strtab STRTAB 0000000000000000 00002958 00000000000002f5 0000000000000000001[29].shstrtab STRTAB 0000000000000000 00002c4d 0000000000000108 0000000000000000001Key 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)1.2 Section header tableOffset会表明各个Section在a.out中的偏移量用于定位各个节查看符号表[guchenVM-0-9-centos test_5_23]$ readelf-s a.out Symbol table.dynsymcontains31entries:Num:Value Size Type Bind Vis Ndx Name0:00000000000000000NOTYPE LOCAL DEFAULT UND1:00000000000000000FUNC GLOBAL DEFAULT UND wattr_on2:00000000000000000FUNC GLOBAL DEFAULT UND has_colors3:00000000000000000FUNC GLOBAL DEFAULT UND waddch4:00000000000000000FUNC GLOBAL DEFAULT UND cbreak5:00000000000000000FUNC GLOBAL DEFAULT UND init_pair6:00000000000000000FUNC GLOBAL DEFAULT UND wrefresh7:00000000000000000FUNC GLOBAL DEFAULT UND keypad8:00000000000000000FUNC GLOBAL DEFAULT UND initscr9:00000000000000000FUNC GLOBAL DEFAULT UND start_color10:00000000000000000FUNC GLOBAL DEFAULT UND powGLIBC_2.2.5(2)11:00000000000000000FUNC GLOBAL DEFAULT UND wattr_off12:00000000000000000FUNC GLOBAL DEFAULT UND cosGLIBC_2.2.5(2)13:00000000000000000FUNC GLOBAL DEFAULT UND __libc_start_mainGLIBC_2.2.5(3)14:00000000000000000NOTYPE WEAK DEFAULT UND __gmon_start__15:00000000000000000FUNC GLOBAL DEFAULT UND endwin16:00000000000000000FUNC GLOBAL DEFAULT UND wgetch17:00000000000000000FUNC GLOBAL DEFAULT UND wclear18:00000000000000000FUNC GLOBAL DEFAULT UND noecho19:00000000000000000FUNC GLOBAL DEFAULT UND sinGLIBC_2.2.5(2)20:00000000000000000FUNC GLOBAL DEFAULT UND wmove21:00000000000000000FUNC GLOBAL DEFAULT UND curs_set22:00000000000000000FUNC GLOBAL DEFAULT UND mvprintw23:00000000006020cc0NOTYPE GLOBAL DEFAULT24_edata24:00000000006021000NOTYPE GLOBAL DEFAULT25_end25:00000000006020e04OBJECT GLOBAL DEFAULT25COLS26:00000000006020f08OBJECT GLOBAL DEFAULT25stdscr27:00000000006020cc0NOTYPE GLOBAL DEFAULT25__bss_start28:00000000006020f84OBJECT GLOBAL DEFAULT25LINES29:00000000004009e80FUNC GLOBAL DEFAULT11_init30:00000000004019140FUNC GLOBAL DEFAULT14_fini Symbol table.symtabcontains89entries:Num:Value Size Type Bind Vis Ndx Name0:00000000000000000NOTYPE LOCAL DEFAULT UND1:00000000004002380SECTION LOCAL DEFAULT12:00000000004002540SECTION LOCAL DEFAULT23:00000000004002740SECTION LOCAL DEFAULT34:00000000004002980SECTION LOCAL DEFAULT45:00000000004002e00SECTION LOCAL DEFAULT56:00000000004005c80SECTION LOCAL DEFAULT67:00000000004006f80SECTION LOCAL DEFAULT78:00000000004007380SECTION LOCAL DEFAULT89:00000000004007780SECTION LOCAL DEFAULT910:00000000004007d80SECTION LOCAL DEFAULT1011:00000000004009e80SECTION LOCAL DEFAULT1112:0000000000400a100SECTION LOCAL DEFAULT1213:0000000000400b800SECTION LOCAL DEFAULT1314:00000000004019140SECTION LOCAL DEFAULT1415:00000000004019200SECTION LOCAL DEFAULT1516:0000000000401bf00SECTION LOCAL DEFAULT1617:0000000000401c480SECTION LOCAL DEFAULT1718:0000000000601de00SECTION LOCAL DEFAULT1819:0000000000601de80SECTION LOCAL DEFAULT1920:0000000000601df00SECTION LOCAL DEFAULT2021:0000000000601df80SECTION LOCAL DEFAULT2122:0000000000601ff80SECTION LOCAL DEFAULT2223:00000000006020000SECTION LOCAL DEFAULT2324:00000000006020c80SECTION LOCAL DEFAULT2425:00000000006020e00SECTION LOCAL DEFAULT2526:00000000000000000SECTION LOCAL DEFAULT2627:00000000000000000FILE LOCAL DEFAULT ABS crtstuff.c28:0000000000601df00OBJECT LOCAL DEFAULT20__JCR_LIST__29:0000000000400bb00FUNC LOCAL DEFAULT13deregister_tm_clones30:0000000000400be00FUNC LOCAL DEFAULT13register_tm_clones31:0000000000400c200FUNC LOCAL DEFAULT13__do_global_dtors_aux32:00000000006020fc1OBJECT LOCAL DEFAULT25completed.635533:0000000000601de80OBJECT LOCAL DEFAULT19__do_global_dtors_aux_fin34:0000000000400c400FUNC LOCAL DEFAULT13frame_dummy35:0000000000601de00OBJECT LOCAL DEFAULT18__frame_dummy_init_array_36:00000000000000000FILE LOCAL DEFAULT ABS test.c37:00000000000000000FILE LOCAL DEFAULT ABS crtstuff.c38:0000000000401db80OBJECT LOCAL DEFAULT17__FRAME_END__39:0000000000601df00OBJECT LOCAL DEFAULT20__JCR_END__40:00000000000000000FILE LOCAL DEFAULT ABS41:0000000000601de80NOTYPE LOCAL DEFAULT18__init_array_end42:0000000000601df80OBJECT LOCAL DEFAULT21_DYNAMIC43:0000000000601de00NOTYPE LOCAL DEFAULT18__init_array_start44:0000000000401bf00NOTYPE LOCAL DEFAULT16__GNU_EH_FRAME_HDR45:00000000006020000OBJECT LOCAL DEFAULT23_GLOBAL_OFFSET_TABLE_46:00000000004019102FUNC GLOBAL DEFAULT13__libc_csu_fini47:00000000006020c80NOTYPE WEAK DEFAULT24data_start48:00000000000000000FUNC GLOBAL DEFAULT UND wattr_on49:00000000000000000FUNC GLOBAL DEFAULT UND has_colors50:00000000000000000FUNC GLOBAL DEFAULT UND waddch51:00000000000000000FUNC GLOBAL DEFAULT UND cbreak52:00000000006020cc0NOTYPE GLOBAL DEFAULT24_edata53:00000000004019140FUNC GLOBAL DEFAULT14_fini54:00000000000000000FUNC GLOBAL DEFAULT UND init_pair55:00000000000000000FUNC GLOBAL DEFAULT UND wrefresh56:00000000000000000FUNC GLOBAL DEFAULT UND keypad57:00000000000000000FUNC GLOBAL DEFAULT UND initscr58:00000000000000000FUNC GLOBAL DEFAULT UND start_color59:00000000000000000FUNC GLOBAL DEFAULT UND powGLIBC_2.2.560:00000000000000000FUNC GLOBAL DEFAULT UND wattr_off61:00000000000000000FUNC GLOBAL DEFAULT UND cosGLIBC_2.2.562:00000000000000000FUNC GLOBAL DEFAULT UND __libc_start_mainGLIBC_63:00000000006020c80NOTYPE GLOBAL DEFAULT24__data_start64:0000000000401123525FUNC GLOBAL DEFAULT13draw_heart_outline_hollow65:0000000000401330619FUNC GLOBAL DEFAULT13draw_heart_outline_simple66:0000000000400d72945FUNC GLOBAL DEFAULT13draw_heart_outline_parame67:00000000000000000NOTYPE WEAK DEFAULT UND __gmon_start__68:00000000004019280OBJECT GLOBAL HIDDEN15__dso_handle69:00000000006020e04OBJECT GLOBAL DEFAULT25COLS70:00000000004019204OBJECT GLOBAL DEFAULT15_IO_stdin_used71:00000000004018a0101FUNC GLOBAL DEFAULT13__libc_csu_init72:00000000006020f08OBJECT GLOBAL DEFAULT25stdscr73:00000000006021000NOTYPE GLOBAL DEFAULT25_end74:0000000000400b800FUNC GLOBAL DEFAULT13_start75:00000000006020cc0NOTYPE GLOBAL DEFAULT25__bss_start76:000000000040159b759FUNC GLOBAL DEFAULT13main77:00000000000000000FUNC GLOBAL DEFAULT UND endwin78:00000000000000000FUNC GLOBAL DEFAULT UND wgetch79:00000000006020f84OBJECT GLOBAL DEFAULT25LINES80:00000000000000000FUNC GLOBAL DEFAULT UND wclear81:00000000000000000FUNC GLOBAL DEFAULT UND noecho82:00000000000000000FUNC GLOBAL DEFAULT UND sinGLIBC_2.2.583:00000000006020d00OBJECT GLOBAL HIDDEN24__TMC_END__84:00000000000000000FUNC GLOBAL DEFAULT UND wmove85:0000000000400c6d261FUNC GLOBAL DEFAULT13draw_heart_outline_ascii86:00000000000000000FUNC GLOBAL DEFAULT UND curs_set87:00000000004009e80FUNC GLOBAL DEFAULT11_init88:00000000000000000FUNC GLOBAL DEFAULT UND mvprintw就是.dynsym是需要上.so找实现方法也就是跳转到.so执行而.symtab除此之外还有包括main、自己写的函数等不需要.so的Value列是地址执行a.outOS ld读取ELF header找到程序入口点_startVS下是CRTStartup跳转到_start执行_start调用_libc_start_main_libc_start_main需要知道main函数的地址.systab里Name为main对应Value存储的是main函数的地址链接器把main函数的地址以立即数的形式写入到_start函数的机器码中运行时CPU执行_start直接把写死的地址传给_libc_start_main即可包括_start把argcargcenv等参数传给_libc_start_main也就是说用户级的入口是main编译器的入口是_start那么符号表我们要的就是Name到Value也就是地址的转换而引用是多个变量名映射到同一个地址相当于多个key哈希到一个地址但引用的底层是指针实现的全局变量的引用在编译的时候放到符号表定义的字符常量放在.rodata而mainprintf等放在.strtab里1.3 Program Header Table-l选项l取自loader也就是与加载器有关因为不同的节大小不一上面查到的节是30个节ELF文件在磁盘上存放没有段只有节而加载到内存OS读磁盘文件以4KB为单位加载到内存中如果30个节分别以4KB为单位进行加载浪费内存因此在加载的时候由加载器将相同权限的节合并成段segment比如.data.bss都是可读可写的节因此本来要加载30个4KB现在只需要9个加载器先找到ELF Header确定Program Header Table的地址访问Program Header Table找到节和段的映射关系接着根据ELF Header找到各个节的起始地址找到各个节合并为段将段所在文件块加载到内存这个段也就是进程虚拟地址空间的正文代码段等包括权限也是用来进行页表写入标明LOAD的段是要加载到内存用来初始化进程的虚拟地址空间也就是mm_struct中的vm_area_struct而下面只有两个也就是代码段和数据段初始化vm_area_struct因为LOAD段在编译期间其实已经生成了逻辑地址也就是未来虚拟地址空间中的虚拟地址用LOAD段虚拟地址的起始地址和结束地址来初始化vm_area_struct的vm_start和vm_end那如何查各个节呢没有命令可以直接看到各个节但是可以把a.out转为反汇编来观察我们看到每行都有地址每行代码的编址其实和指令集有关当前指令地址当前指令长度下一条指令地址在CPU执行指令的时候不需要每一条代码都有明确的地址有一个起始地址就可以指令构成包括指令长度、操作码、数据而操作码和数据也可能省略所以有起始地址接着执行完该条指令当前指令地址当前指令长度就是下一条指令的地址2.程序加载先创建进程相关的数据结构包括PCB虚拟地址空间页表打开/继承stdoutstderrstdin为其创建struct file将结构体对象的地址填在struct files_struct* filestask_struct内部中的文件描述符表再加载/替换可执行程序的ELF二进制文件甚至可以懒加载/延迟加载使用缺页中断等方式实现在需要的时候进行加载这里的加载是建立映射关系清空旧的虚拟地址空间内存布局-根据Program Header Table建立新映射告诉内核将来某个虚拟地址范围对应文件哪一段-设置mm-start_code记录新的代码段、堆、栈的虚拟地址范围-设置页表入口页表先写着“未映射”访问时再处理缺页中断填充程序在编译之后在磁盘上也有“地址”是虚拟地址32位系统下为0x00000000-0x11111111比如VS调试反汇编第一列就是地址以及刚才反汇编的最左侧也是地址包括call跳转调用函数也是使用地址可执行程序平坦模式虚拟地址空间编址但是一般可执行程序的地址不是0一般约定为0x400000而且可执行程序内部每一行代码都有地址但节内部连续节之间可以不连续那么OS和可执行程序都有虚拟地址可以理解为一种技术标准OS和编译器都遵守方便多个模块进行拼接程序的虚拟地址更准确的是逻辑地址很多问题的答案是从历史来的不了解历史就很难理解其中缘由早期没有虚拟地址空间概念CPU内部有很多段寄存器cs(code segment)ds(data segmtn)ss等早期CPU如8086内部有段寄存器和偏移量的寄存器以代码段为例代码段加载到内存代码段的起始地址填到cs访问代码段的时候cs偏移地址比如偏移地址为1的代码要跳转到偏移地址为3的代码还是要拿着cs3来访问那么倒逼着编译器在编译程序的时候从0开始编址也就是相对地址也就是说编译器编译程序的方式受到程序运行方式的影响编译器本身不是独立的个体受OS如何使用程序的影响因为编译出的程序就是被OS使用的现在CPU内部也有段地址寄存器但不是主流方式段地址偏移量逻辑地址而虚拟地址空间出现之后每个进程独享虚拟地址空间代码段和数据段不再单独从0开始编址而是比如代码段从0x40000开始数据段从0x601ed0开始大家各自占程序虚拟地址空间的一块区域每个段的起始地址一般称为基址也就是从程序虚拟地址空间0开始的偏移量而虚拟地址是OS和内存的概念磁盘上可执行程序文件中的地址其实是逻辑地址但是Linux下逻辑地址在数值上和虚拟地址相同针对非PIE得exec内核数据结构创建完之后要进行初始化怎么填代码区数据区从什么地方开始来自哪里mm_struct中的代码段起始和结束位置均来自二进制可执行程序而代码加载到内存有虚拟地址和物理地址可以用来填充页表通过二进制程序的Program Header TableLOAD的区域都是要加载到内存的RE也就是可读可执行一般是代码段RW可读可写一般是数据段包括virtaddr虚拟地址和MemSiz也就是段大小那么CPU是怎么执行的呢Entel X86的CPU内部有EIP(Extended Instruction Pointer)或者PC指针执行下一条要执行代码的指针把ELF Header中的Entry Point Address也就是程序入口地址填到CPU读取代码执行函数内部相互调用的是虚拟地址可执行程序的时候就写好了还没加载到内存肯定不是物理地址也可能是偏移量存在库的概念CPU拿到的地址都是虚拟地址CPU中CR3(Control Register 3当前进程的上下文之一)存储当前进程页表的物理地址CPU内部集成的MMU实现虚拟地址到物理地址的转化拿到虚拟地址查CR3获取页表地址查对应的物理地址地址转化过程是在CPU内部完成的进来的是虚拟地址出去的是物理地址不需要硬件工程师做什么CPU和内存之间有物理线也就是说CPU和硬件走线的时候不需要考虑虚拟地址的问题整个过程是由CPU软件自动完成的出去的是物理地址直接访问内存也就是说虚拟地址空间不仅需要OS参与也需要编译器参与体现在ELF格式那么windows也是一个逻辑用的是Entel/AMD的CPU有MMUCR3PC指针虚拟地址空间等只要支持虚拟地址空间都要有这些