1

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

2
  • 1
    Have you tried section .text align=1 in the NASM source? Does that make any difference? Commented Nov 26, 2024 at 12:02
  • @ecm: Wow, that did it!!! NICE! Add as an answer so you have the full credit! Commented Nov 26, 2024 at 12:35

1 Answer 1

2

As documented in the NASM manual the default attributes for the ELF section .text are section .text progbits alloc exec nowrite align=16.

Apparently gas has a lower alignment, below-or-equal 8, so that's how you observed a difference. The solution is to specify an align=1 attribute in the section directive, like so:

section .text align=1 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.