一、GUN处理目标文件的工具
AR 创建静态库,插入、删除、列出和提取成员; SRING 列出目标文件中的字符串; SIRIP 从目标文件中删除符号表信息; NM 列出目标文件符号表中定义的符号;READELF 显示一个目标文件的完整结构 SIZE 列出目标文件中节的名字和大小; OBJDUMP 所有二进制工具之母,可显示一个目标文件中所有的信息。LDD 列出一个可执行文件在运行时所需要的动态库
首先对一个c文件进行编译生成.o目标文件
然后可以通过nm 和readelf来查看目标文件的符号表
nm------->
00000000 t $d 00000014 t $d 00000000 t $d 00000010 t $d U heap_init 00000000 T init_all 00000000 T main U printf 00000000 t $t 00000000 t $t U uart_init
其中
B --- 全局非初始化数据段(BBS段)的符号,其值表示该符号在bss段中的偏移,如g1
b --- 全局static的符号,如g3
r --- const型只读的变量(readonly)
N --- debug用的符号
T --- 位于代码区的符号,比如本文件里的函数main foo
t --- 位于代码区的符号,一般是static函数
U --- 位于本文件外的调用函数或变量符号,比如系统的printf()函数
readelf -s xxx.o
Symbol table ‘.symtab‘ contains 31 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 FILE LOCAL DEFAULT ABS main.c 2: 00000000 0 SECTION LOCAL DEFAULT 1 3: 00000000 0 SECTION LOCAL DEFAULT 2 4: 00000000 0 SECTION LOCAL DEFAULT 3 5: 00000000 0 SECTION LOCAL DEFAULT 4 6: 00000000 0 SECTION LOCAL DEFAULT 5 7: 00000000 0 SECTION LOCAL DEFAULT 7 8: 00000000 0 SECTION LOCAL DEFAULT 9 9: 00000000 0 NOTYPE LOCAL DEFAULT 9 $d 10: 00000000 0 NOTYPE LOCAL DEFAULT 9 $t 11: 00000014 0 NOTYPE LOCAL DEFAULT 9 $d 12: 00000000 0 SECTION LOCAL DEFAULT 11 13: 00000000 0 NOTYPE LOCAL DEFAULT 11 $d 14: 00000000 0 NOTYPE LOCAL DEFAULT 11 $t 15: 00000010 0 NOTYPE LOCAL DEFAULT 11 $d 16: 00000000 0 SECTION LOCAL DEFAULT 13 17: 00000000 0 SECTION LOCAL DEFAULT 14 18: 00000000 0 SECTION LOCAL DEFAULT 16 19: 00000000 0 SECTION LOCAL DEFAULT 18 20: 00000000 0 SECTION LOCAL DEFAULT 20 21: 00000000 0 SECTION LOCAL DEFAULT 22 22: 00000000 0 SECTION LOCAL DEFAULT 24 23: 00000000 0 SECTION LOCAL DEFAULT 28 24: 00000000 0 SECTION LOCAL DEFAULT 25 25: 00000000 0 SECTION LOCAL DEFAULT 26 26: 00000000 24 FUNC GLOBAL DEFAULT 9 init_all 27: 00000000 0 NOTYPE GLOBAL DEFAULT UND heap_init 28: 00000000 0 NOTYPE GLOBAL DEFAULT UND uart_init 29: 00000000 20 FUNC GLOBAL DEFAULT 11 main 30: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf
二、链接
通过ld进行链接
链接可以把包括 目标文件,静态库,动态库,编译链接生成完全链接的可执行目标文件
链接器主要完成以下操作
1)符号解析:将符号引用和符号定义联系起来
2)重定位:从0地址开始生成代码数据节
目标文件.o (可重定位目标文件):
静态库.a (通过ar命令生成) :合并静态库和目标文件使未定义符号能引用到唯一一个定义生成可执行文件
动态库.so(gcc -shared -fPIC -o xxx.so x1.c x2.c): 在链接时加载时进行动态链接(-fpic生成位置无关代码)
将动态库的文本数据重定位到某个存储器段
在链接时会在elf的text段生成PLT过程链接表 在data段开始的地方生成GOT全局偏移表
三、项目的地址规划
1)elf目标文件包含的各个节段
①text段:代码段(codesegment/textsegment)
通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定。
②rodata段:只读数据
存放C中的字符串和#define定义的常量
③data段:数据段(datasegment
通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
④bss段: BSS段(bsssegment)
通常是指用来存放程序中未初始化的全局变量的一块内存区域。不占任何实际的磁盘空间。
⑤其他
symtab段:符号表
rel.text段
rel.data段
debug段
line段
strtab段
⑥stack栈:
是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
⑦heap堆:
堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
写一个链接脚本
ENTRY( __reset); MEMORY { dram : ORIGIN = 0x00080000, LENGTH = 0x10000 } SECTIONS { .text : { _start_text_ = .; /*start.o(.text*)*/ *(.text*) *(.rodata*) . = ALIGN(4); _end_text = .; } > dram .data : { _start_data = .; *(.data*) . = ALIGN(4); _end_data = .; } > dram .bss : { _start_bss_ = .; *(.bss) *(COMMON) . = ALIGN(4); _end_bss_ = .; } > dram }
规划了各个节段从0x00080000开始,大小0x10000
各个段顺序是text < rodata < data < bss,4字节对齐
通过 ld ldflags -T link.ld *.o *.a -o x.elf
.S文件为汇编代码,一般为系统刚跑起来的时候运行的代码
包括了stage1 stage2的代码