You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GDB如何改变动态链接行为?LD_PRELOAD注入段错误排查

问题解答

一、GDB如何改变动态链接行为?

你遇到的核心差异来自地址空间布局随机化(ASLR) 和程序加载阶段的重定位时机:

  • 未启动程序时,objdump或GDB里看到的0x1b65是ELF文件中的相对虚拟地址(RVA),即相对于程序默认加载基址的偏移,并非实际运行时的虚拟地址。
  • GDB默认会禁用ASLR(部分系统需手动执行set disable-randomization on),因此程序每次启动都会加载到固定基址(x86_64系统下通常为0x555555554000)。启动程序后,动态链接器完成重定位,符号实际地址就是「基址 + RVA」,也就是0x555555554000 + 0x1b65 = 0x555555555b65。
  • 而GDB外默认开启ASLR,程序每次启动的加载基址随机变化,直接使用固定的0x1b65或GDB里的固定地址,必然会访问错误内存触发段错误。

此外,GDB在启动程序时会接管动态链接器的加载流程,确保程序完成重定位后再允许查看符号地址,这也是你能在GDB中看到实际运行地址的原因。

二、如何在GDB外获取重定位后的符号地址?

以下是几种实用方法:

1. 临时禁用ASLR,固定加载基址

通过setarch命令关闭当前进程的ASLR,让程序每次启动都加载到固定基址,再用「基址 + RVA」计算实际地址:

# x86_64系统
setarch x86_64 -R ./myapp &
# 获取进程PID
PID=$!
# 查看程序加载基址
grep myapp /proc/$PID/maps | head -1

输出第一行的起始地址即为基址(例如0x555555554000),加上objdump得到的0x1b65就是符号实际运行地址。

2. 用非交互式GDB直接获取地址

无需手动进入GDB交互界面,直接让GDB启动程序并打印目标符号地址:

gdb --batch --ex 'start' --ex 'p &your_symbol_name' --ex 'quit' ./myapp

输出会直接给出重定位后的地址,格式类似$1 = (void (*)()) 0x555555555b65。

3. 运行时通过/proc文件系统计算

程序启动后,通过/proc文件系统获取加载基址,再结合RVA计算实际地址:

# 后台启动程序
./myapp &
PID=$!
# 获取可执行文件的加载基址
BASE_ADDR=$(grep -o '^[0-9a-f]*' /proc/$PID/maps | head -1)
# 转换进制并计算实际地址
ACTUAL_ADDR=$(printf "0x%x" $((16#$BASE_ADDR + 16#1b65)))
echo "实际符号地址: $ACTUAL_ADDR"

4. 在程序中内置打印逻辑

如果可以修改myapp代码,直接在程序启动时打印目标符号的地址:

#include <stdio.h>

// 假设目标符号为foo
void foo() {}

int main() {
    printf("foo的运行地址: %p\n", &foo);
    // 其他业务逻辑
    return 0;
}

编译运行后即可直接获取实际地址。


内容的提问来源于stack exchange,提问作者stumbling.fool

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 05:46:11