为什么我的结果与微型 asm 示例不同?

Why do my results different following along the tiny asm example?

我正在阅读此页https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html

这是示例之一

; tiny.asm
BITS 32
GLOBAL _start
SECTION .text
_start:
                mov     eax, 1
                mov     ebx, 42  
                int     0x80

Here we go:

$ nasm -f elf tiny.asm
$ gcc -Wall -s -nostdlib tiny.o
$ ./a.out ; echo $?
42

Ta-da! And the size?

$ wc -c a.out
    372 a.out

但是我没有得到相同的结果。我尝试了 nasm -f elf64,然后在 gcc 上尝试了 -m32(然后又在 clang 上)。无论我尝试什么,我都无法让它变得很小。我在拱门 linux

$ cat tiny.asm 
; tiny.asm
BITS 32
GLOBAL _start
SECTION .text
_start:
                mov     eax, 1
                mov     ebx, 42  
                int     0x80

[eric@eric test]$ gcc -Wall -s -nostdlib -m32 tiny.o
[eric@eric test]$ stat ./a.out 
File: ./a.out
Size: 12780         Blocks: 32         IO Block: 4096   regular file
Device: 2eh/46d Inode: 1279        Links: 1
Access: (0755/-rwxr-xr-x)  Uid: ( 1000/     eric)   Gid: ( 1000/     eric)
Access: 2020-12-26 17:19:19.216294869 -0500
Modify: 2020-12-26 17:19:19.216294869 -0500
Change: 2020-12-26 17:19:19.216294869 -0500
Birth: -

-static 不是默认值,即使使用 -nostdlib,当 GCC 默认配置为制作 PIE 时。 使用gcc -m32 -static -nostdlib获取历史行为。 (-static 表示 -no-pie)。有关更多信息,请参阅

此外,您可能需要禁用其他部分与 gcc -Wl,--nmagic 的对齐或使用自定义 linker 脚本,并且可能禁用 GCC 添加的额外元数据部分。 Minimal executable size now 10x larger after linking than 2 years ago, for tiny programs?

如果您不link编译任何编译器生成的(来自 C).o 文件,您可能没有 .eh_frame 部分。但如果你是,你可以用 gcc -fno-asynchronous-unwind-tables 禁用它。 (另请参阅 了解旨在查看编译器的 asm 文本输出的一般提示,而不是可执行文件的大小。)

另请参阅 (ndisasm 根本不处理元数据,仅处理平面二进制文件,因此它“反汇编”元数据。因此那里的答案包含有关如何避免其他部分的信息。)

GCC -Wl,--build-id=none 将避免在可执行文件中包含 .note.gnu.build-id 部分。

$ nasm -felf32 foo.asm
$ gcc -m32 -static -nostdlib -Wl,--build-id=none -Wl,--nmagic foo.o
$ ll a.out 
-rwxr-xr-x 1 peter peter 488 Dec 26 18:47 a.out
$ strip a.out 
$ ll a.out 
-rwxr-xr-x 1 peter peter 248 Dec 26 18:47 a.out

(在 x86-64 Arch GNU/Linux、NASM 2.15.05、gcc 10.2、来自 GNU Binutils 2.35.1 的 ld 上测试。)


您可以使用 readelf -a a.out 检查可执行文件中的部分(或使用更具体的选项仅获取 readelf 的一部分输出。)例如剥离之前,

$ readelf -S unstripped_a.out
...
 Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08048060 000060 00000c 00  AX  0   0 16
  [ 2] .symtab           SYMTAB          00000000 00006c 000070 10      3   3  4
  [ 3] .strtab           STRTAB          00000000 0000dc 000021 00      0   0  1
  [ 4] .shstrtab         STRTAB          00000000 0000fd 000021 00      0   0  1

顺便说一句,你肯定想在使用BITS 32的文件上使用nasm -felf64,除非你正在编写内核或其他东西从 64 位长模式切换到 32 位兼容模式。将 32 位机器代码放入 64 位目标文件中没有帮助。只有当你想要原始二进制模式工作时才使用 BITS (稍后在那个微型 ELF 教程中)。当你从 .o 到 link 时,这只会让你搬起石头砸自己的脚;不要这样做。 (尽管如果您正确使用与 BITS 指令匹配的 nasm -felf32 并无害处。)