While doing some experiments inspired by many interesting articles on tiny ELF executables, I've noticed GNU's ld generates a different executable when fed with a nasm-generated .o object file or with a (GNU)as-generated one, both using (what I presume to be¹) an equivalent assembly source.
¹: Their object files differ only on non-code sections, and are bitwise identical if I strip --strip-section-headers *.o, so I believe that's a fair assumption.
NASM (v2.16.1):
; tiny-nasm.asm SECTION .text GLOBAL _start _start: mov eax, 60 ; Select the _exit syscall (60 in Linux ABI) mov edi, 42 ; Set the exit code argument for _exit syscall ; Perform the selected syscall GAS (v2.42):
# tiny-gas.S .SECTION .text .GLOBL _start _start: mov $60, %eax # Select the _exit syscall (60 in Linux ABI) mov $42, %edi # Set the exit code argument for _exit syscall # Perform the selected syscall nasm -f elf64 tiny-nasm.asm && ld -no-pie -z noseparate-code tiny-nasm.o -o tiny-nasm.bin as tiny-gas.S -o tiny-gas.o && ld -no-pie -z noseparate-code tiny-gas.o -o tiny-gas.bin strip --strip-section-headers *.bin wc -c *.bin diff -u <(readelf -Wa tiny-nasm.bin) <(readelf -Wa tiny-gas.bin) 132 tiny-gas.bin 140 tiny-nasm.bin 272 total --- /dev/fd/63 2024-11-26 02:56:40.248293325 -0300 +++ /dev/fd/62 2024-11-26 02:56:40.248293325 -0300 @@ -8,7 +8,7 @@ Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 - Entry point address: 0x400080 + Entry point address: 0x400078 Start of program headers: 64 (bytes into file) Start of section headers: 0 (bytes into file) Flags: 0x0 @@ -25,7 +25,7 @@ Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align - LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x00008c 0x00008c R E 0x1000 + LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000084 0x000084 R E 0x1000 There is no dynamic section in this file. Both binaries work identically and as expected, and their only difference is the entry point address offset chosen by ld, which accounts for the file size, 8 bytes smaller for as.
- Why this discrepancy in
ld?
As the arguments were identical, I assume the explanation is in the (stripped) sections, which I've omitted here for brevity but can include if needed. But what in those sections could trigger the different entry point address chosen by ld?
If relevant: Ubuntu 24.04, Linux desktop 6.8.0-48 x86_64, binutils 2.42, AMD Ryzen 5 5700G
section .text align=1in the NASM source? Does that make any difference?