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

如何调试LD_PRELOAD库中的__attribute__((__constructor__))构造函数?

调试LD_PRELOAD共享库的__attribute__((constructor))函数

你遇到的问题是典型的:LD_PRELOAD加载的共享库构造函数会在GDB完成断点设置前就执行,哪怕用starti也拦不住——因为动态链接器在进程主程序启动前,就会加载共享库并调用其构造函数,而starti的暂停点是主程序的第一条指令,这时候构造函数早就跑完了。

下面是几种可行的调试方案:

方法一:利用GDB的共享库加载事件暂停

GDB提供了stop-on-solib-events选项,开启后会在共享库加载/卸载时自动暂停进程。我们可以利用这个特性,在构造函数执行前(也就是共享库刚加载完成时)设置断点:

具体操作:

  1. 启动GDB时加入相关配置:
gdb /bin/true -ex 'set environment LD_PRELOAD ./preflight.so' \
              -ex 'set stop-on-solib-events 1' \
              -ex 'starti'
  1. 当GDB输出Stopped due to shared library event时,说明preflight.so已经加载完成,但构造函数还没执行。此时设置断点:
(gdb) break preflight
(gdb) continue

这时候进程就会在preflight函数的入口处暂停,你可以正常调试了。

方法二:在构造函数中主动触发调试陷阱

直接修改共享库代码,在构造函数开头加入触发GDB暂停的代码,比如发送SIGTRAP信号:

修改后的preflight.c:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

__attribute__((__constructor__))
void preflight(void) {
    raise(SIGTRAP); // 触发GDB自动暂停
    puts("Exiting from preflight");
    exit(42);
}

重新编译库后,用GDB启动目标程序:

gdb /bin/true -ex 'set environment LD_PRELOAD ./preflight.so' -ex 'run'

进程会在raise(SIGTRAP)处自动暂停,之后你可以继续调试后续代码。

方法三:直接指定构造函数的内存地址断点

如果上述方法不适用,你还可以先找到构造函数的入口地址,再设置硬件断点。不过这种方法需要提前知道地址,不如前两种方便:

  1. 先不加载库,用gdb加载目标程序,然后手动加载共享库:
(gdb) file /bin/true
(gdb) set environment LD_PRELOAD ./preflight.so
(gdb) sharedlibrary ./preflight.so
  1. 查看preflight函数的地址:
(gdb) info functions preflight
  1. 根据输出的地址设置断点:
(gdb) break *0x12345678  # 替换成实际的地址
(gdb) run

为什么原来的方法失效?

starti的作用是让进程在主程序的第一条用户指令处暂停,但动态链接器的初始化(包括加载LD_PRELOAD的库、执行构造函数)是在主程序启动前完成的。当GDB执行starti时,动态链接器已经完成了构造函数的调用,所以断点根本没机会触发。

内容的提问来源于stack exchange,提问作者Joseph Sible-Reinstate Monica

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 19:22:51