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/x8664-linux-gnu/libc.so.6“这是共享库第四个节点应该是/lib64/ld-linux-x86-64.so.2。这是动态链接器上面234个节点你用ldd ex1就清楚的显示出来了后面我用gdb方式来理解。你去查问几乎没有回答准确的只有gdb方式一步一步调试就明白了。3、link_map的结构大家知道一下可能每个glibc的版本不一样link_map的结构不一样我的是glibc2.42struct 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函数 rip0x0000555555555154si //rip 0x0000555555555157 汇编代码call 0x555555555030putspltsi //rip 0x0000555555555030汇编代码jmp *0x2fca%rip#0x555555558000putsgot.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 00007fff55557de0 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又出现了类似上面的一些数据00007ffff7fc500000007ffff7fc537100007ffff7fc53e000007ffff7fbf170其中7ffff7fc5371(蓝色就是下一个模块的名字指向的地址x/s 0x00007ffff7fc5371见证奇迹的时候到了显示“linux-vdso.so.1”这就是第二个模块或叫共享库的名字和ldd ex1显示的对上了。接着就好办了粉色的00007ffff7fbf170就是接下来一个模块的地址了link_map结构体是一个链表模式。x/16x 0x00007ffff7fbf170又出现了一堆数据00007ffff7daf00000007ffff7fbf14000007ffff7f95940 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链接时根据每个节产生的数据数量一步步产生datadynamicgot.plt等等等等可以看节头表当一步步累加字节到.rela.plt节时正好是4000估计每个elf根据代码大小符号多少会有不同那就在.rela.plt的第一个条目puts处的offset就是4000第二个条目如exit函数就是4008以上是看了资料后自己猜或理解的没有通过学习编译链接原理实践得知的