Ivan Enderlin
Ivan Enderlin

Reputation: 112

Why an Elf file with a single small segment produces an error?

I'm generating an Elf executable file. I am encountering a problem I don't understand. I've been able to reproduce the problem with a small nasm file:

bits 64

va equ 0x1000

elf_header:
    db  0x7F, "ELF", 2, 1, 1, 0 ; e_ident
    times 8 db 0                ; unused padding
    dw  2                       ; e_type
    dw  62                      ; e_machine
    dd  1                       ; e_version
    dq  va + 0x40 + 0x38        ; e_entry
    dq  0x40                    ; e_phoff
    dq  0                       ; e_shoff
    dd  0                       ; e_flags
    dw  0x40                    ; e_ehsize
    dw  0x38                    ; e_phentsize
    dw  1                       ; e_phnum
    dw  0x40                    ; e_shentsize
    dw  0                       ; e_shnum
    dw  0                       ; e_shstrndx

program_header:
    dd  1                       ; p_type
    dd  7                       ; p_flags
    dq  _start                  ; p_offset
    dq  va + _start             ; p_vaddr
    dq  va + _start             ; p_paddr
    dq  _start_end - _start     ; p_filesz
    dq  _start_end - _start     ; p_memsz
    dq  0x1000                  ; p_align

_start:
    mov edi, 42
    mov eax, 60
    syscall

_start_end:

As you can see, there is a single program header pointing to a single tiny segment _start. Reading this file, it seems okay:

$ nasm -f bin -o test test.asm
$ objdump --all-headers test

debug:  file format elf64-x86-64
architecture: x86_64
start address: 0x0000000000001078

Program Header:
    LOAD off    0x0000000000000078 vaddr 0x0000000000001078 paddr 0x0000000000001078 align 2**12
         filesz 0x000000000000000c memsz 0x000000000000000c flags rwx

Dynamic Section:

Sections:
Idx Name          Size     VMA              Type
  0 PT_LOAD#0     0000000c 0000000000001078 TEXT

SYMBOL TABLE:

The rule offset % alignment == vaddr % aligment is respected as far as I understand it.

However, when I try to run this program, I get the following error:

$ ./test
assertion failed [segment_file_size >= size_of_elf_and_program_headers]: first load segment does not span the elf header size

I see in multiple places on Internet that it is NOT mandatory for the first load segment to include the elf header, along with the program header, but this failing assertion seems to tell the opposite.

However, I can trick this by changing _start to add empty instructions (nop) in order to increase the segment size:

_start:
    times 150 nop
    mov edi, 42
    mov eax, 60
    syscall

_start_end:

This time, it executes nicely:

$ nasm -f bin -o test test.asm
$ objdump --all-headers test

debug:  file format elf64-x86-64
architecture: x86_64
start address: 0x0000000000001078

Program Header:
    LOAD off    0x0000000000000078 vaddr 0x0000000000001078 paddr 0x0000000000001078 align 2**12
         filesz 0x00000000000000a2 memsz 0x00000000000000a2 flags rwx

Dynamic Section:

Sections:
Idx Name          Size     VMA              Type
  0 PT_LOAD#0     000000a2 0000000000001078 TEXT

SYMBOL TABLE:


$ ./test
$ echo $?
42

Another alternative would effectively to use set offset = 0, and move the offset onto vaddr, like so (diff based on the first version with the initial _start):

--- test.asm    2024-11-12 10:03:40
+++ offset_eq_0.asm 2024-11-12 10:03:48
@@ -22,11 +22,11 @@
 program_header:
     dd  1                       ; p_type
     dd  7                       ; p_flags
-    dq  _start                  ; p_offset
-    dq  va + _start             ; p_vaddr
-    dq  va + _start             ; p_paddr
-    dq  _start_end - _start     ; p_filesz
-    dq  _start_end - _start     ; p_memsz
+    dq  0                       ; p_offset
+    dq  va                      ; p_vaddr
+    dq  va                      ; p_paddr
+    dq  _start_end - _start + 0x40 + 0x38 ; p_filesz
+    dq  _start_end - _start + 0x40 + 0x38 ; p_memsz
     dq  0x1000                  ; p_align
 
 _start:

The entry point is still set at va + 0x40 + 0x38, which is valid. And this time, it works:


$ ./offset_eq_0
$ echo $?
42

In this version, the p_filesz and p_memsz are larger than the size of the Elf header too.

I'm generating this file from aarch64 macOS, but I'm running on a x86_64 Debian machine (Linux debian 6.11.5-orbstack-00280-g96d99c92a42b #51 SMP Sun Nov 3 08:07:37 UTC 2024 x86_64 GNU/Linux) via OrbStack.

Nowhere on Internet I'm able to find this error, any source code repositories, archives, nothing, nowhere.

Upvotes: 4

Views: 86

Answers (0)

Related Questions