【问题标题】:Instructions appended to end of assembly装配结束时附加的说明
【发布时间】:2017-01-20 05:41:56
【问题描述】:

我正在尝试按照 this 教程创建二进制文件,但链接器似乎在程序集末尾附加了额外的指令。我认为这是操作系统的拆卸过程。


本教程尝试在 Linux 上编译一个简单的 32 位 C 程序:

int main() {
}

使用这些命令:

gcc -c test.c
ld -o test -Ttext 0x0 -e main test.o
objcopy -R .note -R .comment -S -O binary test test.bin
ndisasm -b 32 test.bin

我运行的是 64 位 Linux,因此修改了编译步骤如下:

gcc -m32 -c test.c 
ld -m elf_i386 -o test -Ttext 0x0 -e main test.o
objcopy -R .note -R .comment -S -O binary test test.bin
ndisasm -b 32 test.bin

预期的输出是:

00000000 55            push ebp
00000001 89E5          mov ebp,esp
00000003 C9            leave
00000004 C3            ret

我的输出如下:

;; START expected output
00000000  55                push bp
00000001  89E5              mov bp,sp
00000003  5D                pop bp
00000004  C3                ret
;; END expected output
00000005  0000              add [eax],al
00000007  001400            add [eax+eax],dl
0000000A  0000              add [eax],al
0000000C  0000              add [eax],al
0000000E  0000              add [eax],al
00000010  017A52            add [edx+0x52],edi
00000013  0001              add [ecx],al
00000015  7C08              jl 0x1f
00000017  011B              add [ebx],ebx
00000019  0C04              or al,0x4
0000001B  0488              add al,0x88
0000001D  0100              add [eax],eax
0000001F  001C00            add [eax+eax],bl
00000022  0000              add [eax],al
00000024  1C00              sbb al,0x0
00000026  0000              add [eax],al
00000028  D8FF              fdivr st7
0000002A  FF                db 0xff
0000002B  FF0500000000      inc dword [dword 0x0]
00000031  41                inc ecx
00000032  0E                push cs
00000033  088502420D05      or [ebp+0x50d4202],al
00000039  41                inc ecx
0000003A  C50C04            lds ecx,[esp+eax]
0000003D  0400              add al,0x0
0000003F  00                db 0x00

附加指令的目的是什么,如何从目标文件和二进制文件中删除它们?

编辑:

  • objcopy args 中的错字(commet -> 评论)。更新了反汇编输出。

【问题讨论】:

  • @IgnacioVazquez-Abrams 我正在尝试查找有关.comment 部分的信息,但发现的并不多。它是什么,为什么你认为它是那个部分?
  • 如果您使用objdump -x test.o 来转储所有精灵标题/部分,您可能会发现您有一个.eh_frame 部分。由于您不排除它会占用文件中的空间。如果您不打算使用异常处理,请将其添加到您的 GCC 命令行 -fno-exceptions -fno-asynchronous-unwind-tables 。所以gcc -m32 -c test.c -fno-exceptions -fno-asynchronous-unwind-tables 应该可以解决你的问题。
  • 它们实际上不是指令,它们是异常帧数据,在转换为二进制平面文件时直接放在代码之后。 ndisasm 恰好将数据解码为指令,因为它不知道其中的区别。
  • @MichaelPetch 谢谢,它成功了。您想让您的评论成为答案吗?

标签: gcc assembly x86 nasm ld


【解决方案1】:

通常,当您在输出文件中看到其他数据/说明时,问题的根源可能是出现在您预期代码之后的部分。解决这个问题的一种方法是查询 ELF 可执行文件以查看它定义了哪些部分。可以只查询带有-x 参数的部分到OBJDUMP。使用此命令:

objdump -x test

应该在大多数现代版本的 GCC 中使用默认参数产生与此类似(不完全)的输出:

test:     file format elf32-i386
test
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000000

Program Header:
    LOAD off    0x00001000 vaddr 0x00000000 paddr 0x00000000 align 2**12
         filesz 0x00000040 memsz 0x00000040 flags r-x
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4
         filesz 0x00000000 memsz 0x00000000 flags rw-

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00000005  00000000  00000000  00001000  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .eh_frame     00000038  00000008  00000008  00001008  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .comment      0000001d  00000000  00000000  00001040  2**0
                  CONTENTS, READONLY
SYMBOL TABLE:
00000000 l    d  .text  00000000 .text
00000008 l    d  .eh_frame      00000000 .eh_frame
00000000 l    d  .comment       00000000 .comment
00000000 l    df *ABS*  00000000 test.c
00001040 g       .eh_frame      00000000 __bss_start
00000000 g     F .text  00000005 main
00001040 g       .eh_frame      00000000 _edata
00001040 g       .eh_frame      00000000 _end

首先应该寻找意想不到的部分。在输出到文件test.bin 之前,您的OBJCOPY 命令使用-RELF 对象中删除部分。你做到了:

objcopy -R .note -R .comment -S -O binary test test.bin

如果我们排除 .note.comment 部分,则上面 OBJDUMP 输出中剩下的明显部分是 .eh_frame.eh_frame.text 部分之后被放入您的文件test.bin。这包含异常展开信息。这不是实际的指示。 NDISASM 将非代码作为指令转储,因为二进制文件不区分代码和数据。 NDISASM 盲目地将所有数据转换为指令。

有几种方法可以解决这个问题。您可以像处理其他两个一样排除 .eh_frame 部分。你可以使用:

objcopy -R .note -R .comment -R .eh_frame -S -O binary test test.bin

您也可以告诉 GCC 不要在代码中生成异步异常展开表。这可以通过 GCC 选项来完成:

gcc -m32 -c test.c -fno-asynchronous-unwind-tables  

这与我的评论有点不同,因为我建议禁用所有异常。您只需要禁用异步展开表即可取消 .eh_frame 部分。本节的有用性(或缺乏)在此Stackoverflow answer 中进行了讨论。 GCC 的手册页 (man gcc) 讨论了选项 -fasynchronous-unwind-tables

   -fasynchronous-unwind-tables
       Generate unwind table in DWARF 2 format, if supported by target machine.  The table is exact at each
       instruction boundary, so it can be used for stack unwinding from asynchronous events (such as debugger or
       garbage collector).

这是当今大多数 GCC 的默认设置。使用 -fno-asynchronous-unwind-tables 会关闭此功能。


您链接到的tutorial 是在 2000 年生产的。GCC 及其选项(以及发行版使用的默认值)多年来发生了变化。很可能在创建该教程时,异步展开表还不存在。这可以解释为什么您观察到的输出与教程不同。

【讨论】:

  • 您对本教程的看法是正确的。我必须对标志进行许多修改,才能表现出与示例中显示的相同的一般行为。我知道__start 部分,但在遇到教程之前不知道注入到目标文件中的所有其他部分。
猜你喜欢
  • 2023-04-11
  • 2017-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多