温馨提示:本文翻译自stackoverflow.com,查看原文请点击:assembly - Absolute addressing using PC-relative addressing in a relocatable program. What would the modificati
assembly linker relocation

assembly - 在可重定位程序中使用PC相对寻址进行绝对寻址。

发布于 2020-04-10 10:27:05

我正在尝试解决系统软件第2.2节中的以下练习 Leland L. Beck 撰写的3 / e系统编程简介(用于VTU)

对于仅具有程序计数器相对地址的机器,存在一个汇编程序。如果要汇编一条指令,其操作数是内存中的绝对地址(例如LDA 100),则要从内存中的地址(十六进制)100加载寄存器A。如何在可重定位的程序中汇编这样的指令?

需要进行哪些搬迁操作?

对于使用PC相对寻址如何完成绝对寻址,我有些困惑。我相信汇编程序需要为加载程序生成一条命令,指示加载程序存储程序的开始地址(例如BEGIN),并100在遇到错误时LDA 100通过减少PCby 来跳转到该地址PC - 100,从而跳转到100内存中的绝对地址但是,我不确定修改记录的样子。

有人可以对此提供一些澄清吗?

查看更多

提问者
S.D.
被浏览
128
Peter Cordes 2020-02-01 16:54

我认为您想要一个提供绝对地址的重定位记录,然后将其留给动态链接器/ runtime-fixup-applier来计算正确的相对位移,以从该重定位所应用的位置到达该绝对地址。

可能不是那么简单。例如,x86-64 RIP相对寻址是相对于指令的末尾而言的,但例如mov [RIP+rel32], imm32是用寻址模式中rel32部分之后的立即编码。但是,如果没有立即执行,通常是在指令末尾。因此,寻址模式相对的点可能不是固定位置。您必须应用该职位。

但是我们可以把它留给汇编器,让它增加一些偏移量以说明基数的不同,因此重新定位将以正确的绝对地址为目标。

这使重定位记录保持紧凑,大小与“普通”记录相同:只是要应用重定位的位置,类型,32位绝对地址或计算机使用的任何宽度。(如果绝对地址足够宽,您甚至可以将绝对地址编码到PC相对偏移所在的位置。)

或相对于某个基数的正确相对偏移量,以达到所需的绝对地址,例如0。这就是GNU / Linux ELF .o文件所使用的,PIE可执行文件也是如此。这也解决了偏向重定位的问题,以考虑到它的存储位置和相对位置之间的任何可变距离。

因此,例如要将整个图像从重新定位00x10000,您只需0x10000从每个绝对目标相对重新定位中减去


顺便说一句,您可以在带有GAS的GNU / Linux上针对i386相对调用说明进行实际操作。x86上的Near调用始终使用call rel32编码,但是在支持必要重定位的平台上,好的汇编程序使您可以编写绝对目标,并为链接器提供正确的重定位。在x86机器代码中调用绝对指针

# foo.s
.globl _start
_start:
nop                 # some padding so the base of the call address doesn't start at 0
nop
call 0x123456       # relative call to that absolute address

用编译,用gcc -m32 -c foo.s拆卸objdump -drwC -Mintel

foo.o:     file format elf32-i386

Disassembly of section .text:

00000000 <_start>:
   0:   90                      nop
   1:   90                      nop
   2:   e8 52 34 12 00          call   123459 <_start+0x123459> 3: R_386_PC32   *ABS*

readelf -a foo.o 显示重定位部分,如下所示:

Relocation section '.rel.text' at offset 0x94 contains 1 entry:
 Offset     Info    Type            Sym.Value  Sym. Name
00000003  00000002 R_386_PC32       

目标地址不属于此重定位记录;它被编码为现有的机器代码。这适用于i386,但可能不适用于x86-64。没有建筑就能-m32给我们带来:

Relocation section '.rela.text' at offset 0xb0 contains 1 entry:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000003  000000000002 R_X86_64_PC32                        123452

无论哪种方式,请注意,“偏移量”是应用偏移量的位置(2个NOP字节加上“调用”的操作码字节意味着rel32从该3字节的基数0 开始到该节中的字节。)0x123452x86-64中的重定位是实际目标... 6减去rel32的长度(4个字节)。

“名称+加数”列标题适用于通过定位具有偏移量的符号名称而获得的重定位。例如mov eax, [global_array + 12],无论链接器将起始位置放在哪里,都需要从12个字节开始加载global_array

另请注意,我们正在查看的是未链接的.o文件,而不是链接的可执行文件。x86-64 ELF共享对象不允许对32位绝对目标进行运行时修复。整个对象可能随机位于+ -2GiB以上。(这就是我使用的原因-m32)。

似乎32位PIE可执行文件也不正确支持此功能。可能是因为名称中的位置无关。:P Building with gcc -m32 -pie -nostdlib foo.s为我们提供了一种编码,e8 4f 24 12 00编码适用于的图像库0x1000(即使在设置断点并启动PIE可执行文件以允许重定位之后,也可以从GDB内部开始。)

但是,如果我们使用gcc -m32 -shared -nostdlib foo.s进行构建以创建共享库,则仍然允许文本重定位:

$ gcc -m32 -shared -nostdlib foo.s && objdump -drwC -Mintel a.out

a.out:     file format elf32-i386


Disassembly of section .text:

00001000 <_start>:
    1000:       90                      nop
    1001:       90                      nop
    1002:       e8 4f 24 12 00          call   123456 <_DYNAMIC+0x1204ae>

请注意,反汇编使用重定位信息来计算正确的最终通话目标。

但是我认为这实际上是无效的,因为readelf输出不会显示任何重定位。执行它仍然失败(甚至跳转到正确的地址);我们得到0xf7ffc002 <+2>: e8 4f 24 12 00 call 0xf811e456

无论如何,我认为GNU / Linux上的运行时重定位失败是因为我滥用文本重定位。.o目标文件的重定位记录完全起作用。