代码重定位后GDB添加符号及断点地址计算疑问
我从开机阶段就开始单步调试U-Boot执行流程。U-Boot初始启动时会完成自重重定位,本次调试中重定位地址(relocaddr)为0x3bf53000。我通过以下操作实现了重定位后的单步调试:
>>> p /x ((struct global_data)*0x3bb4eda0)->relocaddr $1 = 0x3bf53000 >>> add-symbol-file u-boot 0x3bf53000 add symbol table from file "u-boot" at .text_addr = 0x3bf53000 (y or n) y Reading symbols from u-boot... >>> b board_init_r Breakpoint 4 at 0x9ab54: board_init_r. (2 locations) >>> info b Num Type Disp Enb Address What 1 breakpoint keep n 0x00000000000825f8 <relocate_code+20> breakpoint already hit 1 time 2 breakpoint keep y <MULTIPLE> 2.1 y 0x000000000009ab58 <board_init_r+4> 2.2 y 0x000000003bf6db54 in board_init_r at common/board_r.c:797
查看二进制文件时,符号地址如下:
naveen@workstation:~/.repos/src/arm64/u-boot$ readelf -s u-boot | grep board_init_r 5162: 000000000009ab54 132 FUNC GLOBAL DEFAULT 3 board_init_r
我疑惑为何不能直接用 0x3bf53000(relocaddr) + 0x9ab54(偏移量) = 0x3BFEDB54 设置断点?而使用add-symbol-file u-boot 0x3bf53000命令后,GDB将断点设置在0x3bf6db54,两者相差0x80000。该值恰好是U-Boot的原始加载地址,因此我猜测0x9ab54是绝对加载地址,需减去0x80000后再加relocaddr才能得到正确的断点地址。我的理解是否正确?若正确,能否提供readelf或类似命令来验证该逻辑?
另外,我执行以下命令后发现同一地址0x80000有4个条目,而非预期仅_start:
naveen@workstation:~/.repos/src/arm64/u-boot$ readelf -s u-boot | grep 0000000000080000 1: 0000000000080000 0 SECTION LOCAL DEFAULT 1 26: 0000000000080000 0 NOTYPE LOCAL DEFAULT 1 $x 5419: 0000000000080000 0 NOTYPE GLOBAL DEFAULT 1 _start 6138: 0000000000080000 0 OBJECT GLOBAL DEFAULT 1 __image_copy_start
你的理解完全正确
U-Boot编译时的链接地址(原始加载地址)是0x80000,readelf输出的符号地址是基于这个链接地址的绝对地址,而非镜像起始的偏移量。正确的重定位后地址计算公式为:重定位后地址 = relocaddr + (符号链接地址 - 链接基地址)
对应到你的场景就是:0x3bf53000 + (0x9ab54 - 0x80000) = 0x3bf53000 + 0x1ab54 = 0x3bf6db54
这和GDB通过add-symbol-file设置的断点地址完全一致。
验证方法
可以通过以下命令确认链接基地址和符号偏移:
查看U-Boot的链接基地址
使用readelf查看镜像程序头,确认加载基地址:readelf -l u-boot | grep -A5 LOAD输出中
VirtAddr列的第一个值就是链接时的基地址(此处应为0x80000)。计算符号相对于链接基地址的偏移
直接用符号地址减去链接基地址,比如board_init_r的偏移为0x9ab54 - 0x80000 = 0x1ab54,加上relocaddr即可得到正确的重定位后地址。
关于0x80000多个条目的说明
地址0x80000对应多个条目是正常现象:
SECTION条目:表示第1个段(通常是.text段)的起始地址为0x80000。$x:链接器生成的局部符号,标记代码段起始位置。_start:U-Boot的入口点,定义在代码起始处。__image_copy_start:U-Boot镜像复制的起始标记,由链接脚本定义,和入口点地址一致。
这些符号都指向镜像的起始地址,因此地址相同属于正常情况。
内容的提问来源于stack exchange,提问作者InsaneCoder

