313游戏网 手游攻略 手游评测 C/C++ 动态链接和地址无关代码(PIC)

C/C++ 动态链接和地址无关代码(PIC)

时间:2024-08-28 18:28:11 来源:互联网 浏览:0

其实C/C++ 动态链接和地址无关代码(PIC)的问题并不复杂,但是又很多的朋友都不太了解,因此呢,今天小编就来为大家分享C/C++ 动态链接和地址无关代码(PIC)的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!

静态库由许多.o文件打包而成,在链接阶段会被链接到程序中。链接的应用程序可以直接运行,无需依赖任何其他运行时元素。但如果有多个应用程序需要使用这个库,每个应用程序都需要将静态库打包到自己的应用程序中,这样就浪费了磁盘空间。同时,如果多个进程同时运行,每个进程都有“几乎相同的代码”则浪费内存;动态库和应用程序是分开的。因此,当动态库的代码升级时,只要库的接口不变,应用程序仍然可以正常运行。但对于静态库,应用程序必须重新编译才能使用最新的库代码;如果当前系统中的多个进程使用同一个动态库,则只需要将一个动态库加载到整个内存中并与所有调用它的人共享。过程。

3.什么是Position independent code?

位置无关代码(PIC) 是代码或数据不使用硬编码地址的代码。通过使用相对寻址

PIC不使用绝对地址来寻址数据或fun,而是使用一些相对寻址方法。 PIC一般是针对共享库的。

4.如何做到地址无关的代码

对于共享库,以代码中的add.c为例,它提供了外部

全局变量,符号,即数据;功能、符号; PIC的目的是让我们的代码段对于所有用户来说都是一样的(代码段是不能修改的,比如使用上一篇文章介绍的符号Relocation方法,这个方法实际上修改的是最终的代码段),它的具体实现是更巧妙的是,核心思想是增加一个间接层,即GOT(全局偏移表)和PLT(过程链接表),来完成数据和函数的间接寻址。

PIC的一个核心思想就是在数据段和代码段之间使用一定的偏移量。因为对于链接器来说,在合并许多目标文件时,它清楚地知道所有段的大小以及它们之间的偏移量。如上图所示,代码段紧随数据段之后,因此任何代码段中的指令到数据段开头的偏移量可以通过大小减去指令与代码段之间的距离轻松计算出来的代码段。距线段起点的偏移量。假设距离代码段起始点偏移0x80的指令想要获取数据段中的数据。此时,链接器知道相对偏移量(0xEF80),可以使用相对偏移量进行相对寻址。

5.GOT

如上图所示,GOT是一个存储各种地址的数组。前三个元素是动态链接器的一些信息,后面的元素是一些符号的地址,即变量和函数的地址。

GOT存储在数据段中。数据段可以修改。这非常重要。如果代码段中有获取变量地址的指令,则代码段中的指令并不直接获取绝对地址,而是指向GOT中的某个位置,动态链接器可以找到变量的地址或函数然后修改对应的地址值。这样就保证了代码段的地址无关,保证了代码段的稳定性。

GOT 在ELF 文件中有两个对应的部分,即.got 和.got.plt。它们其实是一个部分,只是根据功能不同分为两部分:

.got 和.got.plt 彼此相邻。一般来说,在汇编代码中,当前.text中的某条指令+固定的偏移量将定位.got.plt节的起始点;got 用于变量重定位。定位服务,got.plt服务功能重定位;因此,在.got.plt段的起始地址加上正值对应于.got.plt段,加上负值对应于.got段。

6.GOT在获取变量地址时的例子

C/C++ 动态链接和地址无关代码(PIC)

下面我们来分析一下,

lyf@liuyifei:~/test$ objdump -d -Mintel ./libadd.so ./libadd.so: 文件格式elf32-i386 . 0000116d add: 116d: 55 Push ebp 116e: 89 e5 mov ebp,esp 1170: 53推送EBX 1171: 83 ec 04 sub esp,0x4 1174: e8 e7 fe ff ff 调用1060 __x86.get_pc_thunk.bx 1179: 81 c3 87 2e 00 00 添加ebx,0x2e87 117f: e8 bc fe ff致电1040 foo@plt 1184: 8b 83 f8 ff ff ff mov eax,DWORD PTR [ebx-0x8] 118a: 8b 10 mov edx,DWORD PTR [eax] 118c: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 118f: 01 c2 添加edx,eax 1191: 8b 4 5 0c 移动eax, DWORD PTR [ebp+0xc] 1194: 01 d0 添加eax,edx 1196: 8b 5d fc mov ebx,DWORD PTR [ebp-0x4] 1199: c9 离开119a: c3 ret 0000119b __x86.get_pc_thunk.ax333 60 119b: 8b 04 24 mov eax,DWORD PTR [esp ] 119e: c3 retcall 116e __x86.get_pc_thunk.ax 用于获取下一条指令的地址(32位专用,64位有特殊寄存器),即0x1179。

调用call时,会将下一条指令的地址压入堆栈; __x86.get_pc_thunk.ax函数中,mov eax, DWORD PTR [esp]将栈顶元素,即下一条指令的地址,保存在eax寄存器中;函数调用完成后,下一条指令的地址存储在eax寄存器中。 eax加上0x2e87就是0x4000,那么这个地址对应的是什么呢?这个0x2e87是链接器在链接时知道的从当前代码地址到GOT的OFFSET。

lyf@liuyifei:~/test$ readelf -S ./libadd.so 共有31 个节头,从偏移量0x3710: 开始节头: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .note.gnu.bu[.] 注意00000154 000154 000024 00 A 0 4 [ 2] .gnu.hash GNU_HASH 00000178 000178 00002c 04 A 3 0 4 [ 3] .dynsym DYNSYM 000001a4 0 001a4 000080 10 安4 1 4 [ 4] .dynstr STRTAB 00000224 000224 00006f 00 A 0 0 1 [ 5] .rel.dyn REL 00000294 000294 000040 08 A 3 0 4 [ 6] .rel.plt REL 000002d4 0002 d4 000008 08 AI 3 18 4 [ 7] .init PROGBITS 00001000 001000 000024 00 AX 0 0 4 [ 8] .plt PROGBITS 00001030 001030 000020 04 AX 0 0 16 [ 9] .plt.got PROGBITS 00001050 001050 000008 08 AX 0 0 8 [10] .text 程序00001060 001060 00013f 00 AX 0 0 16 [11] .fini PROGBITS 000011a0 0011a0 000018 00 AX 0 0 4 [12] .eh_frame_hdr PROGBITS 00002000 002000 000034 0 0 A 0 0 4 [13] .eh_frame PROGBITS 00002034 002034 0000ac 00 A 0 0 4 [14] .init_array INIT_ARRAY 00003f24 002f24 000004 04 WA 0 0 4 [15] .fini_array FINI_ARRAY 00003f28 002f28 000004 04 WA 0 0 4 [16] .dynamic 动态00003f2c 0 02f2c 0000c0 08 WA 4 0 4 [17] .got PROGBITS 00003fec 002fec 000014 04 WA 0 0 4 [18] .got.plt PROGBITS 00004000 003000 000010 04 WA 0 0 4 [19] .data PROGBITS 00004010 003010 000008 00 WA 0 0 4 [20] . bss 诺比特00 004018 003018 000004 00 WA 0 0 1 [21] .comment PROGBITS 00000000 003018 00002b 01 MS 0 0 1 [22] .debug_aranges PROGBITS 00000000 003043 000020 00 0 0 1 [23] .debug_info PROGBITS 00000000 00 3063 000085 00 0 0 1 [24] .debug_abbrev PROGBITS 00000000 0030e8 000079 00 0 0 1 [25] .debug_line PROGBITS 00000000 003161 000054 00 0 0 1 [26] .debug_str PROGBITS 00000000 0031b5 000099 01 MS 0 0 1 [27] .debug_line_str PROGB ITS 0000000 0 00324e 000015 01 MS 0 0 1 [28] .symtab SYMTAB 00000000 003264 0001e0 10 29 23 4 [29] .strtab STRTAB 00000000 003444 0001b0 00 0 0 1 [30] .shstrtab STRTAB 00000000 0035f4 00011b 00 0 0 1 Flags: 的关键W(写入)、A(分配)、X (执行)、M(合并)、S(字符串)、I(信息)、L(链接顺序)、O(需要额外的操作系统处理)、G(组)、T(TLS)、C(压缩)、x(未知)、o(操作系统特定)、E(排除)、D(mbind)、p(处理器特定) 从libadd.so的段表中,我们可以看到0x4000对应于段.got.plt。

1184: 8b 83 f8 ff ff ff mov eax,DWORD PTR [ebx-0x8] 找到.got.plt后,取[ebx-0x8]位置的值(数据,加上负值)放入eax中。其实这里取的是global_extern_int的值,然后执行几个数字([ebp+0x8]和[ebp+0xc])的相加,输入参数,压入栈,取对应的值来自堆栈)。0xf7fba179 与.got.plt 的偏移量是0x2e87。加法后为0xf7fbd000,ebx-0x8=0xF7FBCFF8;打印0xF7FBCFF8对应的值,应该是global_extern_int的地址。我们发现这与实际情况相符。

7.PIC中的函数地址的获取

上面介绍了PIC中变量地址的获取。对于函数调用,我们可以使用同样的方法来实现,即将动态库中加载的函数的真实地址放入GOT中的内存中,然后使用间接引用。获得它的方法。但这里有一个问题。目前,许多C/C++库中都包含大量函数,只有解析动态链接库才能获得这些库函数的真实地址。如果采用上述GOT的方式,在加载动态库时,首先解析出所有函数的地址,然后将其填入GOT表中。你可以想象一下GOT表有多大。另外,我们在调用动态库时,有时只调用了一个printf函数,而根本不调用其他函数。因此,编译系统采用了一种称为Lazy Binding的技术。只有当函数被实际调用时,才会解析其内存中的真实地址。核心原理仍然是通过增加一个间接层,即PLT(Procedure Linkage Table)来完成函数的间接寻址和延迟绑定。

PLT位于代码段中,是与GOT类似的数组结构,如下图所示。

当代码段中调用printf函数时,编译器将其转换为对printf@plt的调用,即PLT的第N个元素。除了PLT中的第一个元素PLT[0]之外,其他每个元素都表示有关要寻址的功能的信息。 PLT[0]是动态链接器的一些程序,用于做真正的Runtime地址解析;每个PLT元素包含以下三个部分。首先,每个PLT项对应一个GOT项。 GOT是数据段,是可写的,所以真正的运行时地址一定是存储在对应的GOT中的吗?调用解析器时准备参数;当调用解析器时,它是PLT[0],一个固定的地方。如果这个地方不固定,这个方案就无法实施;如上图4所示,当函数的地址尚未解析时,GOT[n]中存储的是PLT[n]中第二条指令的地址。当第一次调用printf 函数时,

去调用PLT[n],然后跳转到*GOT[n],如上图3所示;因为GOT[n]当前指向PLT[n]中第二条指令的地址,解引用,跳转到*GOT[n],然后跳转到PLT[n]的第二条指令,如上面的4所示数字;然后调用动态链接器解析器,如上图5所示;解析器负责解析内存中加载的printf的真实地址,然后将解析后的地址放入GOT[n]中,如上图6所示,然后调用printf函数。当这个过程中再次调用printf函数时,

此时GOT[n]已经保存了printf函数的地址,jmp跳转到printf函数,控制权将转移到printf函数,因此不会再次调用解析器。通过上面的步骤我们可以看到

通过PLT+GOT,共享库的代码段(.text)真正做到了地址无关,因为解析后地址保存在GOT中,而在数据段(.data)中。通过延迟绑定,动态链接器只有在函数实际被调用时才会解析出函数的真实地址。

8.PLT+GOT在获取函数地址时的例子

主链接PIC的动态库libadd.so,检查其包含的段,

用户评论

发呆

C/C++ 动态链接和地址无关代码(PIC) 这篇文章非常棒!终于弄懂了动态链接和 PIC 的关系,之前一直没搞明白,现在终于可以安心地去写代码了!

    有20位网友表示赞同!

暮光薄凉

太棒了!这篇文章让我对动态链接和 PIC 的理解更深入了,特别是关于地址无关代码的解释,简直是醍醐灌顶!

    有5位网友表示赞同!

太易動情也是罪名

作者的讲解很清晰,深入浅出,让我对动态链接和 PIC 的概念有了更直观的认识。推荐给所有学习 C/C++ 的朋友。

    有12位网友表示赞同!

余温散尽ぺ

文章内容很好,但个人感觉有些地方过于理论化,希望作者可以结合实际案例进行说明,这样更容易理解。

    有11位网友表示赞同!

疲倦了

我之前一直对动态链接和 PIC 的区别感到困惑,看完这篇文章后,终于豁然开朗!谢谢作者的精彩分享!

    有10位网友表示赞同!

苍白的笑〃

这篇文章真的很实用,对理解动态链接和 PIC 很有帮助。不过,我感觉作者可以多加一些代码示例,这样更易于理解。

    有10位网友表示赞同!

太难

作者对动态链接和 PIC 的解释非常详细,但是我还是有些地方不太明白,希望作者能够提供更多更深入的讲解。

    有5位网友表示赞同!

冷青裳

看了这篇文章,我终于明白了动态链接和 PIC 的作用,真是受益匪浅!推荐给所有想要学习 C/C++ 的人。

    有15位网友表示赞同!

坠入深海i

作者的文章写得很好,但是我个人觉得排版有点乱,希望作者能够改进一下,这样看起来会更舒服。

    有8位网友表示赞同!

放血

这篇文章让我对动态链接和 PIC 有了全新的认识,之前一直以为它们是两个独立的概念,现在才明白它们之间的联系。感谢作者的分享!

    有6位网友表示赞同!

隔壁阿不都

我个人感觉这篇文章有些过于专业,不太适合初学者阅读。希望作者能够提供一些更基础的讲解,方便更多的人理解。

    有14位网友表示赞同!

屌国女农

作者对动态链接和 PIC 的解释很详细,但是我感觉有些地方不够直观,希望作者能够加入一些图示或者动画,这样更容易理解。

    有20位网友表示赞同!

有一种中毒叫上瘾成咆哮i

这篇文章非常实用,对理解动态链接和 PIC 有很大帮助。不过,我感觉作者可以多加一些实际应用的案例,这样更能体现出动态链接和 PIC 的价值。

    有14位网友表示赞同!

龙卷风卷走爱情

作者的文章写得很好,但是我个人觉得有些地方过于冗长,希望作者能够精简一些内容,这样更易于阅读。

    有5位网友表示赞同!

歆久

我之前一直对动态链接和 PIC 的区别感到困惑,看完这篇文章后,终于弄明白了!感谢作者的精彩分享!

    有8位网友表示赞同!

独角戏°

这篇文章让我对动态链接和 PIC 的理解更深入了,特别是关于地址无关代码的解释,简直是醍醐灌顶!

    有6位网友表示赞同!

浅笑√倾城

作者的讲解很清晰,深入浅出,让我对动态链接和 PIC 的概念有了更直观的认识。推荐给所有学习 C/C++ 的朋友。

    有5位网友表示赞同!

一纸愁肠。

文章内容很好,但个人感觉有些地方过于理论化,希望作者可以结合实际案例进行说明,这样更容易理解。

    有20位网友表示赞同!

又落空

我之前一直对动态链接和 PIC 的区别感到困惑,看完这篇文章后,终于豁然开朗!谢谢作者的精彩分享!

    有5位网友表示赞同!

西瓜贩子

这篇文章真的很实用,对理解动态链接和 PIC 很有帮助。不过,我感觉作者可以多加一些代码示例,这样更易于理解。

    有17位网友表示赞同!

标题:C/C++ 动态链接和地址无关代码(PIC)
链接:https://www.313yx.com//news/sypc/178654.html
版权:文章转载自网络,如有侵权,请联系删除!
资讯推荐
更多
亚丝娜礼包|DNF神秘礼包SAO套装详解,微笑的棺材就是他

这次礼包内容丰富,包含时装、光环、称号、宠物、宝珠、亚丝娜的篮子以及各种消耗品,一起来看看完整的内容。女

2024-08-28
金铲铲之战皮肤原画,金铲铲之战游戏原画

金铲铲之战画之灵强势阵容推荐金铲铲之战画之灵强势阵容怎么搭配?金铲铲之战S11斗射卡莎阵容怎么搭配?金铲铲

2024-08-28
崩坏星穹铁道什么时候上线,崩坏星穹铁道bilibili

崩坏星穹铁道2.2卡池角色及上线时间崩坏星穹铁道2.2卡池角色有哪些?崩坏星穹铁道2.2卡池上线时间是什么时候?

2024-08-28
金铲铲之战s1阵容t0,金铲铲之战s1最佳阵容

金铲铲之战s11低费阵容搭配建议金铲铲之战s11低费阵容有哪些?金铲铲之战s11低费阵容怎么搭配?金铲铲之战s11低

2024-08-28