Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 如何诊断GNU ld链接器行为随时间变化的差异?_Assembly_X86 64_Ld_Memory Alignment - Fatal编程技术网

Assembly 如何诊断GNU ld链接器行为随时间变化的差异?

Assembly 如何诊断GNU ld链接器行为随时间变化的差异?,assembly,x86-64,ld,memory-alignment,Assembly,X86 64,Ld,Memory Alignment,我在2018年编译并链接了一个小型x86-64汇编程序。我现在正试图复制构建,但在链接的时候,我在最终的二进制文件中得到了不同的结果 使用以下命令组合和链接了两个文件: $ nasm -f elf64 prng.asm; ld -s -o prng prng.o 我在2018年创建的原始ELF名为prng。我今天创建的版本名为prng2。我已经验证了中间对象文件prng.o是相同的,因此我排除了源代码或nasm是我看到的差异的原因。下面我展示了新旧ELF上的objdump输出: 原件: $

我在2018年编译并链接了一个小型x86-64汇编程序。我现在正试图复制构建,但在链接的时候,我在最终的二进制文件中得到了不同的结果

使用以下命令组合和链接了两个文件:

$ nasm -f elf64 prng.asm; ld -s -o prng prng.o 
我在2018年创建的原始ELF名为
prng
。我今天创建的版本名为
prng2
。我已经验证了中间对象文件
prng.o
是相同的,因此我排除了源代码或nasm是我看到的差异的原因。下面我展示了新旧ELF上的
objdump
输出:

原件:

$ objdump -x prng

prng:     file format elf64-x86-64
prng
architecture: i386:x86-64, flags 0x00000102:
EXEC_P, D_PAGED
start address 0x00000000004000b0

Program Header:
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
         filesz 0x0000000000000150 memsz 0x0000000000000150 flags r-x
    LOAD off    0x0000000000000150 vaddr 0x0000000000600150 paddr 0x0000000000600150 align 2**21
         filesz 0x0000000000000008 memsz 0x0000000000000008 flags rw-

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         000000a0  00000000004000b0  00000000004000b0  000000b0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000008  0000000000600150  0000000000600150  00000150  2**2
                  CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
no symbols
最新:

$ objdump -x prng2

prng2:     file format elf64-x86-64
prng2
architecture: i386:x86-64, flags 0x00000102:
EXEC_P, D_PAGED
start address 0x0000000000401000

Program Header:
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**12
         filesz 0x00000000000000e8 memsz 0x00000000000000e8 flags r--
    LOAD off    0x0000000000001000 vaddr 0x0000000000401000 paddr 0x0000000000401000 align 2**12
         filesz 0x00000000000000a0 memsz 0x00000000000000a0 flags r-x
    LOAD off    0x0000000000002000 vaddr 0x0000000000402000 paddr 0x0000000000402000 align 2**12
         filesz 0x0000000000000008 memsz 0x0000000000000008 flags rw-

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         000000a0  0000000000401000  0000000000401000  00001000  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000008  0000000000402000  0000000000402000  00002000  2**2
                  CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
no symbols
我可以看出,差异似乎是由于不同的路线。但是,我无法确定是什么导致使用不同的路线

  • 我今天使用的是Ubuntu 20.04.1,而2018年我使用的是Ubuntu 16.04
  • 今天我使用的是AMD Ryzen 3700X CPU,而2018年我使用的是Intel Core i7-860
我相信在Ubuntu的两个版本之间,
ld
的版本会有所改变。在此期间,ld的对齐行为是否可能发生变化,例如使用不同的默认链接器脚本

或者CPU会影响对齐值的选择吗


为什么程序标题现在有三个部分,而以前只有两个?

现代
ld
.rodata
部分放在一个单独的读取页面中,不带exec。这需要将其放入一个单独的ELF段(程序头条目,由加载程序读取)。术语:ELF节是在程序头列表之后的节列表中列出的内容

较旧的
ld
.rodata
放入与
.text
相同的段中,使用exec只读。这在过去几年内确实发生了变化,比如2018年?(大约从2017年开始,我就一直在使用Arch GNU/Linux,这是一个滚动发行版,主要使用未经修改的上游源代码,并且在IIRC前后发生了一些变化。)

较旧的
ld
.text
开头的同一磁盘页中也有ELF头和
.data
的初始值设定项。(对于.data和.text总计小于4k的小文件)。此磁盘页以两种不同的方式进行映射:文本段的Read+Exec,位于用于代码和只读数据的虚拟地址;数据段的Read+Write,用于
.data

注意入口点地址
0x00000000004000b0
(在ELF头+数据之后,从页面开始的一些小偏移量)与新可执行文件中对齐的
0x0000000000401000
页面通过对齐磁盘上的数据,可以映射到虚拟内存,而无需将任何内容重叠到不需要执行的可执行段。这样做的自然结果是页面对齐内存地址,但这是一个副作用,而不是目标

您的可执行文件没有
.rodata
部分(您的输入也没有),但ELF头本身仍然映射在具有LOAD属性的段中(映射到内存)

顺便说一句,最好使用
readelf
,而不是
objdump
来检查ELF头


此更改有助于防止ROP和幽灵攻击,因为它不会将固定数据作为“小工具”提供给用户。(现在大多数程序通过确保W^X使代码注入变得不可能,更复杂的攻击必须寻找现有的可执行字节序列。因此,强化的下一步是尽可能减少不需要的可执行页面。)

它与您运行的CPU或构建的CPU无关。正如@old_timer所指出的,您不应该期望来自不同版本的工具链的相同二进制文件。出于这个或其他原因,甚至对于将工具版本签名嵌入元数据中的某个位置的工具来说,更改这样的默认值当然是可能的。(像GCC这样的编译器可以做到这一点,可能是NASM和
ld
没有做到。)



您可以从源代码构建GNUBINUTILS的旧版本,或者从二进制软件包获取旧的ld


或者编写自己的链接器脚本,将
.rodata
放在与
.text
相同的程序段中。(我认为,
ld
的工作原理是有一个默认链接器脚本;如果您可以在旧的ld源中找到默认链接器脚本,您可能可以将其与当前安装的ld一起使用。)

出于安全原因,这是一个更改,因为保护标志的页面粒度。@Jester-谢谢,你能指出到底发生了什么变化(希望能给出一个如何扭转这种影响的提示)吗?你可以从源代码构建一个旧版本的GNU binutils,或者从二进制软件包中获取
ld
。或者编写自己的链接器脚本,将
.rodata
放在与
.text
相同的程序段中,而不是单独读取而不执行页面。(这就是Jester ware所说的变化。)处理器不应该有任何影响,但工具正在不断开发中,没有理由期望同一代码从一个工具到另一个工具具有相同的输出,无论是不同的工具链还是同一工具的不同版本。如果你在新电脑上使用旧的工具,你会得到同样的结果吗?如果您只是使用发行版中的C库,那么链接器脚本或引导程序很可能已经改变了对齐方式,等等。这类问题可以从一段关于这一点的文章中受益,因此我们可以解决导致此问题的具体问题。这里没有
.rodata
输入部分。只读无执行程序段仅存在于ELF头中。原始可执行文件放入ELF头、
.text
段和