如何在C/汇编中使用相对位置?

5

据说位置无关代码只使用相对位置而不是绝对位置,那么在c和汇编中分别如何实现呢?

char test[] = "string";为例,如何通过相对地址引用它?

3个回答

2
在x86架构上,位置无关代码基本上是这样的:
        call 1f
1:      popl %ebx

紧接着使用ebx作为基指针,偏移量等于要访问的数据与popl指令地址之间的距离。

实际上,这通常更加复杂,通常会使用一个小型thunk函数来加载PIC寄存器,例如:

load_ebx:
        movl 4(%esp),%ebx
        addl $some_offset,%ebx
        ret

偏移量的选择是这样的,当thunk返回时,ebx包含指向程序/库中指定特殊点(通常是全局偏移表的开头)的指针,然后所有随后的ebx相关访问可以简单地使用所需数据与指定特殊点之间的距离作为偏移量。

在其他架构上,原则上一切都类似,但可能有更简单的方法来加载程序计数器。许多架构只允许您将pcip寄存器用作相对寻址模式下的普通寄存器。


这里的 call 1f 是什么意思? - mysql_go
2
在正向方向上,下一个标签为 1: - R.. GitHub STOP HELPING ICE

2
在C语言中,位置无关代码是编译器实现的一个细节。请查看您的编译器手册以确定是否支持,并了解如何操作。
在汇编语言中,位置无关代码是指令集架构的一个细节。请查看您的CPU手册以了解如何读取PC(程序计数器)寄存器,它的效率如何,以及将代码地址转换为数据地址的推荐最佳实践。
随着大多数现代操作系统将代码和数据分离到不同的页面中,位置相关数据已经不太流行了。这是实现自包含可执行模块的好方法,但现在最常见的这种东西是病毒。

@mysql:我指的是数据,而不是目标代码。子程序调用通常是PC相对的,就像所有跳转一样。(调用DLL的情况可能是例外。)PIC是指对全局变量的访问,请参阅GCC手册或您的编译器手册。 - Potatoswatter
@Potatoswatter,我还是不明白,也许一个详细的问题会让事情变得清晰...为什么这个答案说问题中的原始解决方案使用了绝对地址“str”?https://dev59.com/LG445IYBdhLWcg3wAFcC#5095573 - mysql_go
@mysql_go:指令movl $str, %ecx,特别是操作数$str,试图从绝对地址加载。然而,链接器无法确定代码段中的地址,因此无法汇编该指令。该问题的解决方案实现了PIC作为一种hack。问题本身指定了字符串如何变成位置无关的,这是你的问题缺失的部分。char test[] = "string";本身将按平台默认方式分配,这可能与PIC没有任何关系。 - Potatoswatter
@Potatoswatter,如果 $str 不能使用,它如何引用该字符串? - mysql_go
@mysql:在这个问题中?通过安排$str紧随call指令,使其作为返回地址推送到堆栈上。请注意,标签str实际上根本没有被使用。 - Potatoswatter

0

伪代码如下:

lea str1(pc), r0 ; load address of string relative to the pc (assuming constant strings, maybe)
st r0, test  ; save the address in test (test could also be PIC, in which case it could be relative
             ; to some register)

很多情况取决于您的编译器和CPU架构,正如之前的答案所述。一种找出方法是使用适当的标志(对于gcc,为-PIC -S)进行编译,并查看您获得的汇编语言。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接