当前位置: 首页 > news >正文

linux x_86_64动态链接,gdb理解link_map参数

接着前面的动态链接文章继续聊哈。源程序ex1.c没变(参见前一次的文章)

在前面的文章中,对rela_arg还是有了些了解,但对_dl_runtime_resolve的link_map参数还没搞清楚,查了好多资料,终于对下面几个概念有了了解

1、link_map在main函数执行前就已经确定还是没确定。

答:执行前就已经确定了。

2、link_map的第一个节点是啥,第二个节点又是啥,第三个节点再是啥。。。

答:就我目前的了解,第一个节点是elf文件本身的起始地址。

第二个节点是“linux-vsdo.so.1”,虽然还不知道这个有啥作用。

第三个节点是“/usr/lib/x86)64-linux-gnu/libc.so.6“,这是共享库

第四个节点应该是/lib64/ld-linux-x86-64.so.2。这是动态链接器

上面2,3,4个节点你用ldd ex1,就清楚的显示出来了,后面我用gdb方式来理解。

你去查,问,几乎没有回答准确的,只有gdb方式一步一步调试,就明白了。

3、link_map的结构大家知道一下(可能每个glibc的版本不一样link_map的结构不一样,我的是glibc2.42)

struct link_map

{

ElfW(Addr) l_addr;//是指向加载模块的起始地址,加载libc.so.6就是libc.so.6的起始地址,加载linux-vsdo.so.1,就是这个模块的地址。

char *l_name; /*这里放着指向模块的绝对路径的字符串的地址,比较绕,这是一个地址,指向模块的名字*/

ElfW(Dyn) *l_ld; /*这是指向动态节的地址.*/

struct link_map *l_next, *l_prev; /*这是指向下一个模块的结构体和前一个模块的结构体初始地址,切记是地址*/

};

============================================

正式开始:gdb调试(我的环境是mac x86_64下使用模拟器的kali-linux6.5,属于debian-linux,下面具体的地址可能随着各位的系统不一样,具体显示的地址不一样,但思路是一样的。我也在mac m系列机器上下载了kali-linux,反汇编显示的代码和这里的也大不一样,大家仅借鉴思路,如不同,欢迎大家一起探讨)

gdb ex1 开始调试

b main //设置断点到main函数

run //运行到main处

si //进入main函数 rip=0x0000555555555154

si //rip = 0x0000555555555157 ,汇编代码call 0x555555555030<puts@plt>

si //rip= 0x0000555555555030,汇编代码jmp *0x2fca(%rip)#0x555555558000<puts@got.plt>这个地址以后存放真实的puts地址,目前是下一条指令地址

si //rip=。。。5036(前面一样,后四位写一下),汇编push $0x0, 就是rela_arg压栈

si //rip= 5036 汇编jmp 。。。5020,跳转到5020处,执行_dl_runtime_resolve定位函数,找到puts的真实地址,写到0x555555558000处。

si //rip = 5020,汇编push 0x2fca(%rip) #0x555555557ff0,将这个地址的值压栈,就是link_map的地址。

si //rip = 5026,汇编jmp *0x2fcc(%rip) #0x555555557ff8,跳转到这个地址存储的地址,执行_dl_runtime_resolve。

x/a 0x555555557ff0 // 我们先看一下,0x555555557ff0处的值是0x7ffff7ffe2f0,这就是link_map的起始地址。

x/a 0x7ffff7ffe2f0//显示的0x555555554000,这就是载入内存后地址重载的elf进程的起始地址。所以link_map的第一个节点就是elf文件载入进程后的起始地址。

si //再走一步,就会执行/lib64/ld-lijux-x86-64.so.2的_dl_runtime_resolve函数了

x/16x 0x7ffff7ffe2f0 //会显示如下

55554000 00005555f7ffe8c8 00007fff

55557de0 00005555f7ffe8d0 00007fff

.....(后面有许多)

我们看红色的,结合上面的link_map结构,红色的是l_addr,是0000555555554000,要反过来看,就是elf载入进程的起始地址。

蓝颜色的就是指向l_name的地址。这里elf进程是没有名字的,是“ ”,可以用x/s 00007ffff7ffe8c8去看一下。

紫色的是指向dynamic的地址,我们可以看到是000055555557de0,我们在elf的节头表中可以看到偏移地址是3de0,在进程中加上前面的进程起始地址555555554000,就是555555557de0,验证正确。

关键是第四个黑色的00007ffff7ffe8d0,这个就是link_map结构中的下一个模块的结构体起始地址。

x/16x 0x7ffff7ffe8d0

又出现了类似上面的一些数据

00007ffff7fc500000007ffff7fc5371

00007ffff7fc53e000007ffff7fbf170

其中7ffff7fc5371(蓝色)就是下一个模块的名字指向的地址

x/s 0x00007ffff7fc5371,见证奇迹的时候到了,显示“linux-vdso.so.1”

这就是第二个模块(或叫共享库)的名字,和ldd ex1显示的对上了。

接着就好办了,粉色的00007ffff7fbf170就是接下来一个模块的地址了,link_map结构体是一个链表模式。

x/16x 0x00007ffff7fbf170

又出现了一堆数据,

00007ffff7daf00000007ffff7fbf140

00007ffff7f95940 00007ffff7ffdda0

我们不看别的,就看红色的数据。这是第三个模块的名字的地址。

x/s 0x00007ffff7fbf140 “/usr/lib/x86_64-linux-gnu/libc.so.6"再次印证了ldd,完全正确。

==================================================

明白了l_name和l_next,其他也基本明白了,这些无聊的地址变换最终要找到属于我们的数据,在其中_dl_runtime_resolve 通过这两个参数,找到puts字符串,找到libc.so.6的完全路径,再通过dysym函数定位到puts函数真正的执行地址,就能显示了,有了真正地址,写入got.plt所在的地址,下次再调用就不用去_dl_runtime_resolve了。这就是动态链接。

这两天又问自己0x555555558000这个地址是哪里来的呢,汇编代码是

jmp *0x2fca(%rip),是当前地址的下一条地址+0x2fca,为啥呢?

其实在Elf文件的.rela.plt里,puts函数的offset是4000,载入进程后,加上其实地址0x5555555555554000,就是0x5555555555558000了,要获取puts的真实地址的地址早已确定了(有点绕哈),那么要从8000这个地址取到puts的真实地址,必须要当前地址的下一条加上某个值,才能到8000处,那就只能加0x2fca了。

那么又问自己.rela.plt里的4000又是哪里来的呢?

是gcc编译过程中产生的,其实应该是ld链接器产生的(gcc只是一个壳而已,调用了预处理,编译,汇编,链接等过程,还没学会),ld链接时根据每个节产生的数据数量,一步步产生data,dynamic,got.plt等等等等,可以看节头表,当一步步累加字节到.rela.plt节时,正好是4000(估计每个elf根据代码大小,符号多少会有不同),那就在.rela.plt的第一个条目puts处的offset,就是4000,第二个条目如exit函数,就是4008(以上是看了资料后自己猜或理解的,没有通过学习编译链接原理实践得知的)

http://www.jsqmd.com/news/1128583/

相关文章:

  • 内向者和别人聊天缺少共同话题的庖丁解牛
  • 数学艺术图案画-曼陀罗(39)
  • YouTube AI 助手存在提示注入风险,点击链接或致创作者私人视频标题泄露!
  • Dify 本地化部署指南(全平台)
  • 终极精简指南:使用PowerShell脚本让Windows 11瘦身50%
  • 『物流翻译+支付说明多语言』跨境国际化再升级 | VortMall微服务商城系统v1.3.8版本正式发布
  • Claude Code Auto mode 的成本与延迟,别只看模型价格,还要看每一次动作背后的安全往返
  • Audacity终极指南:3小时从零到精通的免费音频编辑完整教程
  • AI让我们啥时候失业。
  • 一站式Android固件解包工具:20+厂商格式的终极解决方案
  • 建站工具测评:BBWEYY/比文云/Framer/Make/Brevo(2026年7月更新)含零代码SAAS、AI编程、源码定制交付
  • Claude Code 会话管理,把一次对话沉淀成可恢复、可分叉、可审计的工程现场
  • Java依赖注入:为何@注解成技术隐患?官方推荐方案揭秘
  • 【学习记录】Week11(三):House of Botcake 与 House of Pig——现代 CTF 堆利用的双子星
  • TC78H653FTG与MK24FN1M0VDC12的直流有刷电机驱动方案
  • 小样本学习实战:数据增强与模型优化策略
  • 从0到1跑通AI录播:小鹿播演播厅基础配置与避坑流程
  • 【关于接口幂等】
  • Python三元表达式:原理、陷阱与高效工程实践
  • Windows平台终极ADB驱动安装指南:3步完成Android开发环境配置
  • c++数据结构竞赛 -常见排序(没有归并和快速排序)
  • Python3面向对象001
  • 2026-07-04:找到第一个唯一偶数。用go语言,在数组 nums 中寻找这样的数:它是偶数(能被 2 整除),并且在 nums 里只出现一次。请返回满足条件的那个偶数的值,并且以其在数组中的首次
  • DIFY-01:创建你的第一个对话助手
  • 国产模型顶配组合,OpenClaw 集成 DeepSeek V4 2026 实操指南
  • MC6470与STM32F423RH在6DOF运动控制中的优化实践
  • 终极指南:构建Apple Music级动态歌词体验的完整解决方案
  • Android图片解码器libjpeg-turbo vs Skia最佳实践
  • Windows 本地 AI 部署避坑指南,OpenClaw 2.7.9 完整落地操作记录
  • 使用SVN+CruiseControl+ANT实现持续集成之一