I am trying to create an ELF64 file from scratch; the program I'm using to test it right now just calls the exit() syscall using assembly; there's no linking of libraries etc. Here's the output of "dumpelf":
#include <elf.h>
/*
* ELF dump of 'elftest'
* 4120 (0x1018) bytes
*/
Elf64_Dyn dumpedelf_dyn_0[];
struct {
Elf64_Ehdr ehdr;
Elf64_Phdr phdrs[3];
Elf64_Shdr shdrs[3];
Elf64_Dyn *dyns;
} dumpedelf_0 = {
.ehdr = {
.e_ident = { /* (EI_NIDENT bytes) */
/* [0] EI_MAG: */ 0x7F,'E','L','F',
/* [4] EI_CLASS: */ 2 , /* (ELFCLASS64) */
/* [5] EI_DATA: */ 1 , /* (ELFDATA2LSB) */
/* [6] EI_VERSION: */ 1 , /* (EV_CURRENT) */
/* [7] EI_OSABI: */ 0 , /* (ELFOSABI_NONE) */
/* [8] EI_ABIVERSION: */ 0 ,
/* [9-15] EI_PAD: */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
},
.e_type = 2 , /* (ET_EXEC) */
.e_machine = 62 , /* (EM_X86_64) */
.e_version = 1 , /* (EV_CURRENT) */
.e_entry = 0x400000 , /* (start address at runtime) */
.e_phoff = 64 , /* (bytes into file) */
.e_shoff = 232 , /* (bytes into file) */
.e_flags = 0x0 ,
.e_ehsize = 64 , /* (bytes) */
.e_phentsize = 56 , /* (bytes) */
.e_phnum = 3 , /* (program headers) */
.e_shentsize = 64 , /* (bytes) */
.e_shnum = 3 , /* (section headers) */
.e_shstrndx = 1
},
.phdrs = {
/* Program Header #0 0x40 */
{
.p_type = 6 , /* [PT_PHDR] */
.p_offset = 64 , /* (bytes into file) */
.p_vaddr = 0x40 , /* (virtual addr at runtime) */
.p_paddr = 0x40 , /* (physical addr at runtime) */
.p_filesz = 168 , /* (bytes in file) */
.p_memsz = 168 , /* (bytes in mem at runtime) */
.p_flags = 0x4 , /* PF_R */
.p_align = 8 , /* (min mem alignment in bytes) */
},
/* Program Header #1 0x78 */ <-- PT_NULL=OK, PT_LOAD=SEGFAULT
{
.p_type = 1 , /* [PT_LOAD] */
.p_offset = 0 , /* (bytes into file) */
.p_vaddr = 0x0 , /* (virtual addr at runtime) */
.p_paddr = 0x0 , /* (physical addr at runtime) */
.p_filesz = 441 , /* (bytes in file) */
.p_memsz = 441 , /* (bytes in mem at runtime) */
.p_flags = 0x4 , /* PF_R */
.p_align = 4096 , /* (min mem alignment in bytes) */
},
/* Program Header #2 0xB0 */ <-- WORKS AS EXPECTED
{
.p_type = 1 , /* [PT_LOAD] */
.p_offset = 4096 , /* (bytes into file) */
.p_vaddr = 0x400000 , /* (virtual addr at runtime) */
.p_paddr = 0x400000 , /* (physical addr at runtime) */
.p_filesz = 24 , /* (bytes in file) */
.p_memsz = 24 , /* (bytes in mem at runtime) */
.p_flags = 0x5 , /* PF_R | PF_X */
.p_align = 4096 , /* (min mem alignment in bytes) */
},
},
.shdrs = {
/* Section Header #0 '' 0xE8 */
{
.sh_name = 0 ,
.sh_type = 0 , /* [SHT_NULL] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 0 , /* (bytes) */
.sh_size = 0 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 0 ,
.sh_entsize = 0
},
/* Section Header #1 '.shstrtab' 0x128 */
{
.sh_name = 1 ,
.sh_type = 3 , /* [SHT_STRTAB] */
.sh_flags = 32 ,
.sh_addr = 0x1A8 ,
.sh_offset = 424 , /* (bytes) */
.sh_size = 17 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 1 ,
.sh_entsize = 1
},
/* Section Header #2 '.text' 0x168 */
{
.sh_name = 11 ,
.sh_type = 1 , /* [SHT_PROGBITS] */
.sh_flags = 6 ,
.sh_addr = 0x400000 ,
.sh_offset = 4096 , /* (bytes) */
.sh_size = 24 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 16 ,
.sh_entsize = 0
},
},
.dyns = dumpedelf_dyn_0,
};
Elf64_Dyn dumpedelf_dyn_0[] = {
/* no dynamic tags ! */ };
If I try to execute the program in x86_64 Linux, the program segfaults. Attempting to debug it in gdb is literally a non-starter, GDB tells me the program segfaults "during startup" so no help there.
After several days of beating my head against this problem, I can not for the life of me figure out why this is causing a segfault; I have looked at numerous other executable files including system binaries and files compiled with various different programs, they all load the ELF headers at vaddr=0x0 without problems so why does it fail in this case?
If I change Program Header #1 0x78
from PT_LOAD
to PT_NULL
to not load the ELF64 headers into memory, the program runs as expected and exits without error so I know the executable part is OK. This narrows the problem down to that header but it isn't a workable solution.
Edit: Here's the same file as viewed with readelf -a
:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400000
Start of program headers: 64 (bytes into file)
Start of section headers: 232 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 3
Size of section headers: 64 (bytes)
Number of section headers: 3
Section header string table index: 1
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .shstrtab STRTAB 00000000000001a8 000001a8
0000000000000011 0000000000000001 S 0 0 1
[ 2] .text PROGBITS 0000000000400000 00001000
0000000000000018 0000000000000000 AX 0 0 16
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), l (large), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000000a8 0x00000000000000a8 R 0x8
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000001b9 0x00000000000001b9 R 0x1000
LOAD 0x0000000000001000 0x0000000000400000 0x0000000000400000
0x0000000000000018 0x0000000000000018 R E 0x1000
Section to Segment mapping:
Segment Sections...
00
01
02 .text
There is no dynamic section in this file.
There are no relocations in this file.
No processor specific unwind information to decode
No version information found in this file.
Edit #2: Added output of strace as both ordinary user, and as root
which surprisingly works:
$ strace ./filename
execve("./filename", ["./filename"], 0x7ffd660b1910 /* 28 vars */) = -1 EPERM (Operation not permitted)
+++ killed by SIGSEGV +++
Segmentation fault
(as root)
# strace ./filename
execve("./filename", ["./filename"], 0x7ffd06138b90 /* 27 vars */) = 0
exit(0) = ?
+++ exited with 0 +++
$ stat filename
File: filename
Size: 4120 Blocks: 16 IO Block: 4096 regular file
Device: 820h/2080d Inode: 1920 Links: 1
Access: (0755/-rwxr-xr-x) Uid: ( 1000/ username) Gid: ( 1000/ username)
Access: 2023-06-15 13:24:24.007538545 +0200
Modify: 2023-06-15 13:24:16.787538566 +0200
Change: 2023-06-15 13:24:16.787538566 +0200
Birth: 2023-06-15 13:24:16.787538566 +0200