I'm writing a compiler in Rust and right now trying to generate an elf. I have
0x400000
and file offset elf_header_size + elf_program_header_size + 3 * elf_section_header_size
,0x400000 + text_size
and file offset elf_header_size + elf_program_header_size + 3 * elf_section_header_size + text_size
My program header has p_vaddr and p_paddr set to 0x400000
and p_filesz and p_memsz set to text_len + data_len
.
readelf prints:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] .text PROGBITS 0000000000400000 00000138
0000000000000053 0000000000000000 AX 0 0 16
[ 1] .data PROGBITS 0000000000400053 0000018b
0000000000000004 0000000000000000 WA 0 0 16
[ 2] .shstrtab STRTAB 0000000000000000 0000018f
0000000000000016 0000000000000000 0 0 1
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),
R (retain), 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
LOAD 0x0000000000000138 0x0000000000400000 0x0000000000400000
0x0000000000000057 0x0000000000000057 R E 0x1000
But then:
Section to Segment mapping:
Segment Sections...
00 .data
Why should segment 00 only contain .data? objdump also generates nothing besides:
main: file format elf64-x86-64
This is my code:
impl Program {
fn generate_elf(mut self) -> Vec<u8> {
let mut vec = Vec::new();
let bin = |slice: &[&str]| {
slice
.iter()
.map(|x| x.as_bytes().to_vec())
.flatten()
.collect::<Vec<u8>>()
};
let sections = [".text\0", ".data\0", ".shstrtab\0"];
let mut sections_bin = bin(§ions);
let mut n = 0;
let mut next_section = || {
let res = bin(§ions[0..n as usize]).len() as u32;
n += 1;
res
};
let text_len = self.text.len() as u64;
let data_len = self.data.len() as u64;
let elf_header_size = size_of::<Elf64Header>() as u64;
let elf_program_header_size = size_of::<Elf64ProgramHeader>() as u64;
let elf_section_header_size = size_of::<Elf64SectionHeader>() as u64;
let mut head = elf_header_size + elf_program_header_size + 3 * elf_section_header_size;
let elf_header = Elf64Header {
e_ident: [0x7F, b'E', b'L', b'F', 2, 1, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0],
e_type: 2,
e_machine: 0x3E,
e_version: 1,
e_entry: 0x400000,
e_phoff: elf_header_size,
e_shoff: elf_header_size + elf_program_header_size,
e_flags: 0,
e_ehsize: elf_header_size as u16,
e_phentsize: elf_program_header_size as u16,
e_phnum: 1,
e_shentsize: elf_section_header_size as u16,
e_shnum: 3,
e_shstrndx: 2,
};
let program_header = Elf64ProgramHeader {
p_type: 1,
p_flags: 1 | 4,
p_offset: head,
p_vaddr: 0x400000,
p_paddr: 0x400000,
p_filesz: text_len + data_len,
p_memsz: text_len + data_len,
p_align: 0x1000,
};
let text_section_header = Elf64SectionHeader {
sh_name: next_section(),
sh_type: 1,
sh_flags: 2 | 4,
sh_addr: 0x400000,
sh_offset: head,
sh_size: text_len,
sh_link: 0,
sh_info: 0,
sh_addralign: 0x10,
sh_entsize: 0,
};
head += text_section_header.sh_size;
let data_section_header = Elf64SectionHeader {
sh_name: next_section(),
sh_type: 1,
sh_flags: 1 | 2,
sh_addr: 0x400000 + text_section_header.sh_size,
sh_offset: head,
sh_size: data_len,
sh_link: 0,
sh_info: 0,
sh_addralign: 0x10,
sh_entsize: 0,
};
head += data_section_header.sh_size;
let strings_section_header = Elf64SectionHeader {
sh_name: next_section(),
sh_type: 3,
sh_flags: 0,
sh_addr: 0,
sh_offset: head,
sh_size: sections_bin.len() as u64,
sh_link: 0,
sh_info: 0,
sh_addralign: 1,
sh_entsize: 0,
};
extend(&mut vec, elf_header);
extend(&mut vec, program_header);
extend(&mut vec, text_section_header);
extend(&mut vec, data_section_header);
extend(&mut vec, strings_section_header);
vec.append(&mut self.text);
vec.append(&mut self.data);
vec.append(&mut sections_bin);
vec
}
}
and this is the whole readelf output:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
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: 120 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 1
Size of section headers: 64 (bytes)
Number of section headers: 3
Section header string table index: 2
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] .text PROGBITS 0000000000400000 00000138
0000000000000053 0000000000000000 AX 0 0 16
[ 1] .data PROGBITS 0000000000400053 0000018b
0000000000000004 0000000000000000 WA 0 0 16
[ 2] .shstrtab STRTAB 0000000000000000 0000018f
0000000000000016 0000000000000000 0 0 1
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),
R (retain), 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
LOAD 0x0000000000000138 0x0000000000400000 0x0000000000400000
0x0000000000000057 0x0000000000000057 R E 0x1000
Section to Segment mapping:
Segment Sections...
00 .data
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.
I also tried creating two program headers, one for .text and one for .data. The .data one showed .data as a section but the .text one did not.
Edit: Thanks to @Employed Russian's comment i changed the code to make the segments pass
const PAGE_SIZE: u64 = 0x1000;
impl Elf64ProgramHeader {
fn assert_valid(&self) {
assert_eq!((self.p_vaddr - self.p_offset) % PAGE_SIZE, 0);
assert_eq!((self.p_paddr - self.p_offset) % PAGE_SIZE, 0);
}
}
which sadly didn't fix the issue. I also tried use to different segments again, one for .text and one for .data, which also didn't change anything. I then aligned all sections to 0x10 - which changed nothing - and then even to PAGE_SIZE, still with no success.
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
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: 176 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 2
Size of section headers: 64 (bytes)
Number of section headers: 3
Section header string table index: 2
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] .text PROGBITS 0000000000400000 00001000
0000000000001000 0000000000000000 AX 0 0 4096
[ 1] .data PROGBITS 0000000000401000 00002000
0000000000001000 0000000000000000 WA 0 0 4096
[ 2] .shstrtab STRTAB 0000000000000000 00003000
0000000000001000 0000000000000000 0 0 4096
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),
R (retain), 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
LOAD 0x0000000000001000 0x0000000000400000 0x0000000000400000
0x0000000000001000 0x0000000000001000 R E 0x1000
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000
0x0000000000001000 0x0000000000001000 RW 0x1000
Section to Segment mapping:
Segment Sections...
00
01
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.
main: file format elf64-x86-64
Edit 2: I had some stupid bugs in the first version, like LOAD 2 having the wrong offset. I fixed that:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] .text PROGBITS 0000000000400000 00001000
0000000000001000 0000000000000000 AX 0 0 4096
[ 1] .data PROGBITS 0000000000401000 00002000
0000000000001000 0000000000000000 WA 0 0 4096
[ 2] .shstrtab STRTAB 0000000000000000 00003000
0000000000000016 0000000000000000 0 0 0
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),
R (retain), 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
LOAD 0x0000000000001000 0x0000000000400000 0x0000000000400000
0x0000000000001000 0x0000000000001000 R E 0x1000
LOAD 0x0000000000002000 0x0000000000401000 0x0000000000401000
0x0000000000001000 0x0000000000001000 RW 0x1000
Section to Segment mapping:
Segment Sections...
00
01 .data
And now .data
even shows up in the section to segment mapping.
When trying to start the elf i still get segmentation fault, and gdb
reports:
#0 0x0000000000000001 in ?? ()
>>> info files
Symbols from "/home/[name]/dev/rust/toylang/main".
Local core dump file:
`/home/[name]/dev/rust/toylang/core', file type elf64-x86-64.
0x0000000000400000 - 0x0000000000401000 is load1
0x0000000000401000 - 0x0000000000402000 is load2
0x00007f1a35b8e000 - 0x00007f1a35b92000 is load3
0x00007f1a35b92000 - 0x00007f1a35b94000 is load4
0x00007ffcda9ac000 - 0x00007ffcda9ce000 is load5
0xffffffffff600000 - 0xffffffffff601000 is load6
While running this, GDB does not access memory from...
Local exec file:
`/home/[name]/dev/rust/toylang/main', file type elf64-x86-64.
warning: Cannot find section for the entry point of /home/[name]/dev/rust/toylang/main.
Entry point: 0x400000
0x0000000000401000 - 0x0000000000402000 is .data
0x00007f1a35b92120 - 0x00007f1a35b92170 is .hash in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92170 - 0x00007f1a35b921d4 is .gnu.hash in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b921d8 - 0x00007f1a35b92340 is .dynsym in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92340 - 0x00007f1a35b923dc is .dynstr in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b923dc - 0x00007f1a35b923fa is .gnu.version in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92400 - 0x00007f1a35b92438 is .gnu.version_d in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92438 - 0x00007f1a35b92558 is .dynamic in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92560 - 0x00007f1a35b92570 is .rodata in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92570 - 0x00007f1a35b92580 is .vvar__vdso_rng_data in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92580 - 0x00007f1a35b92770 is .vvar__vdso_data in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92770 - 0x00007f1a35b927c4 is .note in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b927c4 - 0x00007f1a35b92818 is .eh_frame_hdr in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92818 - 0x00007f1a35b92978 is .eh_frame in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b92980 - 0x00007f1a35b93791 is .text in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b93791 - 0x00007f1a35b93863 is .altinstructions in system-supplied DSO at 0x7f1a35b92000
0x00007f1a35b93863 - 0x00007f1a35b9389f is .altinstr_replacement in system-supplied DSO at 0x7f1a35b92000
but it seems like it loaded something to 0x400000:
>>> x/16x 0x400000
0x400000: 0x48f78b48 0x000001b8 0x00000000 0x01bf4800
0x400010: 0x00000000 0x48000000 0x000001ba 0x00000000
0x400020: 0xc3050f00 0x003cb848 0x00000000 0xbf480000
0x400030: 0x00000000 0x00000000 0x48c3050f 0x000041bf
which somewhat resembles my assembly:
│* │ ┊ │ ┊ │
│00001000│ 48 8b f7 48 b8 01 00 00 ┊ 00 00 00 00 00 48 bf 01 │H××Hו⋄⋄┊⋄⋄⋄⋄⋄Hו│
│00001010│ 00 00 00 00 00 00 00 48 ┊ ba 01 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄H┊ו⋄⋄⋄⋄⋄⋄│
│00001020│ 00 0f 05 c3 48 b8 3c 00 ┊ 00 00 00 00 00 00 48 bf │⋄••×H×<⋄┊⋄⋄⋄⋄⋄⋄H×│
│00001030│ 00 00 00 00 00 00 00 00 ┊ 0f 05 c3 48 bf 41 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊••×H×A⋄⋄│
│00001040│ 00 00 00 00 00 50 e8 40 ┊ 00 00 00 58 50 e8 2d 00 │⋄⋄⋄⋄⋄P×@┊⋄⋄⋄XP×-⋄│
│00001050│ 00 00 58 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄X⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│00001060│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│* │ ┊ │ ┊ │
Edit 3: The code is at https://github.com/Einfachirgendwa1/toylang/blob/c1d67f285fe82e4483a97be7fc955012fada4d54/src/elf.rs
The output of readelf -Wl
is:
Elf file type is EXEC (Executable file)
Entry point 0x400000
There are 2 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x0000000000400000 0x0000000000400000 0x001000 0x001000 R E 0x1000
LOAD 0x002000 0x0000000000401000 0x0000000000401000 0x001000 0x001000 RW 0x1000
Section to Segment mapping:
Segment Sections...
00
01 .data
Thank you for your time!
Edit 4:
I changed my code to also run on stable.
Here's a standalone elf.rs
which saves a simple binary to output
:
use std::{fs::File, io::Write, slice};
const PAGE_SIZE: u64 = 0x1000;
const SECTION_ALIGNMENT: u64 = PAGE_SIZE;
fn main() {
// Tries to generate an elf out of:
// .text:
// b8 3c 00 00 00 mov $0x3c,%eax
// bf 00 00 00 00 mov $0x0,%edi
// 0f 05 syscall
//
// .data:
// "test"
let elf = generate_elf(
vec![
0xb8, 0x3c, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0f, 0x05,
],
vec![b'T', b'E', b'S', b'T'],
);
File::create("output").unwrap().write_all(&elf).unwrap();
}
struct Aligned {
required_padding: u64,
padding: Vec<u8>,
}
fn align(number: u64, alignment: u64) -> Aligned {
let required_padding = alignment - number % alignment;
Aligned {
required_padding,
padding: vec![0; required_padding as usize],
}
}
fn align_vector(vec: &mut Vec<u8>, alignment: u64) -> u64 {
vec.append(&mut align(vec.len() as u64, alignment).padding);
vec.len() as u64
}
fn growing_subslice<'a, T, A, F>(vec: &'a [T], f: F) -> impl FnMut() -> A + 'a
where
T: 'a,
F: Fn(&'a [T]) -> A + 'a,
{
let mut n = 0;
move || {
let res = f(&vec[0..n]);
n += 1;
res
}
}
pub fn extend<A: Clone, B>(vec: &mut Vec<A>, b: B) {
unsafe {
vec.extend_from_slice(slice::from_raw_parts(
&b as *const B as *const A,
size_of::<B>(),
))
}
}
pub fn generate_elf(mut text: Vec<u8>, mut data: Vec<u8>) -> Vec<u8> {
let mut p_vaddr = 0x400000;
let mut vec = Vec::new();
let as_vec_u8 = |slice: &[&str]| {
slice
.iter()
.flat_map(|x| x.as_bytes().to_vec())
.collect::<Vec<u8>>()
};
let sections = [".text\0", ".data\0", ".shstrtab\0"];
let mut sections_bin = as_vec_u8(§ions);
let mut next_section = growing_subslice(§ions, |slice| as_vec_u8(slice).len() as u32);
let text_len = align_vector(&mut text, SECTION_ALIGNMENT);
let data_len = align_vector(&mut data, SECTION_ALIGNMENT);
let elf_header_size = size_of::<Elf64Header>() as u64;
let elf_program_header_size = size_of::<Elf64ProgramHeader>() as u64;
let elf_section_header_size = size_of::<Elf64SectionHeader>() as u64;
let headers = elf_header_size + 2 * elf_program_header_size + 3 * elf_section_header_size;
let mut header_padding = align(headers, PAGE_SIZE);
let header_len = headers + header_padding.required_padding;
let elf_header = Elf64Header {
e_ident: [0x7F, b'E', b'L', b'F', 2, 1, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0],
e_type: 2,
e_machine: 0x3E,
e_version: 1,
e_entry: p_vaddr,
e_phoff: elf_header_size,
e_shoff: elf_header_size + 2 * elf_program_header_size,
e_flags: 0,
e_ehsize: elf_header_size as u16,
e_phentsize: elf_program_header_size as u16,
e_phnum: 2,
e_shentsize: elf_section_header_size as u16,
e_shnum: 3,
e_shstrndx: 2,
};
let text_program_header = Elf64ProgramHeader {
p_type: 1,
p_flags: 1 | 4,
p_offset: header_len,
p_vaddr,
p_paddr: p_vaddr,
p_filesz: text_len,
p_memsz: text_len,
p_align: PAGE_SIZE,
};
text_program_header.assert_valid();
p_vaddr += text_len;
let data_program_header = Elf64ProgramHeader {
p_type: 1,
p_flags: 2 | 4,
p_offset: header_len + text_len,
p_vaddr,
p_paddr: p_vaddr,
p_filesz: data_len,
p_memsz: data_len,
p_align: PAGE_SIZE,
};
data_program_header.assert_valid();
let text_section_header = Elf64SectionHeader {
sh_name: next_section(),
sh_type: 1,
sh_flags: 2 | 4,
sh_addr: 0x400000,
sh_offset: header_len,
sh_size: text_len,
sh_link: 0,
sh_info: 0,
sh_addralign: SECTION_ALIGNMENT,
sh_entsize: 0,
};
let data_section_header = Elf64SectionHeader {
sh_name: next_section(),
sh_type: 1,
sh_flags: 1 | 2,
sh_addr: 0x400000 + text_section_header.sh_size,
sh_offset: header_len + text_len,
sh_size: data_len,
sh_link: 0,
sh_info: 0,
sh_addralign: SECTION_ALIGNMENT,
sh_entsize: 0,
};
let strings_section_header = Elf64SectionHeader {
sh_name: next_section(),
sh_type: 3,
sh_flags: 0,
sh_addr: 0,
sh_offset: header_len + text_len + data_len,
sh_size: sections_bin.len() as u64,
sh_link: 0,
sh_info: 0,
sh_addralign: 0x0,
sh_entsize: 0,
};
extend(&mut vec, elf_header);
extend(&mut vec, text_program_header);
extend(&mut vec, data_program_header);
extend(&mut vec, text_section_header);
extend(&mut vec, data_section_header);
extend(&mut vec, strings_section_header);
vec.append(&mut header_padding.padding);
vec.append(&mut text);
vec.append(&mut data);
vec.append(&mut sections_bin);
vec
}
#[repr(C)]
struct Elf64Header {
e_ident: [u8; 16],
e_type: u16,
e_machine: u16,
e_version: u32,
e_entry: u64,
e_phoff: u64,
e_shoff: u64,
e_flags: u32,
e_ehsize: u16,
e_phentsize: u16,
e_phnum: u16,
e_shentsize: u16,
e_shnum: u16,
e_shstrndx: u16,
}
#[derive(Debug)]
#[repr(C)]
struct Elf64ProgramHeader {
p_type: u32,
p_flags: u32,
p_offset: u64,
p_vaddr: u64,
p_paddr: u64,
p_filesz: u64,
p_memsz: u64,
p_align: u64,
}
impl Elf64ProgramHeader {
fn assert_valid(&self) {
assert_eq!((self.p_vaddr - self.p_offset) % PAGE_SIZE, 0);
assert_eq!((self.p_paddr - self.p_offset) % PAGE_SIZE, 0);
}
}
#[repr(C)]
struct Elf64SectionHeader {
sh_name: u32,
sh_type: u32,
sh_flags: u64,
sh_addr: u64,
sh_offset: u64,
sh_size: u64,
sh_link: u32,
sh_info: u32,
sh_addralign: u64,
sh_entsize: u64,
}
The binary itself works, but objdump
still doesn't show the source code when using objdump -d
and .text
also doesn't show up in the section to segment mapping of readelf
.
The issue is that section at index 0 is reserved and should be of SHT_NULL
type.
After applying this diff to your edit#4:
$ diff -u elf.rs.orig elf.rs
--- elf.rs.orig 2024-11-29 22:09:07.785140746 +0000
+++ elf.rs 2024-11-29 22:06:47.622954606 +0000
@@ -1,4 +1,5 @@
use std::{fs::File, io::Write, slice};
+use std::mem::size_of;
const PAGE_SIZE: u64 = 0x1000;
const SECTION_ALIGNMENT: u64 = PAGE_SIZE;
@@ -75,7 +76,7 @@
.collect::<Vec<u8>>()
};
- let sections = [".text\0", ".data\0", ".shstrtab\0"];
+ let sections = ["\0", ".text\0", ".data\0", ".shstrtab\0"];
let mut sections_bin = as_vec_u8(§ions);
let mut next_section = growing_subslice(§ions, |slice| as_vec_u8(slice).len() as u32);
@@ -86,7 +87,7 @@
let elf_program_header_size = size_of::<Elf64ProgramHeader>() as u64;
let elf_section_header_size = size_of::<Elf64SectionHeader>() as u64;
- let headers = elf_header_size + 2 * elf_program_header_size + 3 * elf_section_header_size;
+ let headers = elf_header_size + 2 * elf_program_header_size + 4 * elf_section_header_size;
let mut header_padding = align(headers, PAGE_SIZE);
let header_len = headers + header_padding.required_padding;
@@ -103,8 +104,8 @@
e_phentsize: elf_program_header_size as u16,
e_phnum: 2,
e_shentsize: elf_section_header_size as u16,
- e_shnum: 3,
- e_shstrndx: 2,
+ e_shnum: 4,
+ e_shstrndx: 3,
};
let text_program_header = Elf64ProgramHeader {
@@ -132,6 +133,18 @@
};
data_program_header.assert_valid();
+ let null_section_header = Elf64SectionHeader {
+ sh_name: next_section(),
+ sh_type: 0,
+ sh_flags: 0,
+ sh_addr: 0,
+ sh_offset: 0,
+ sh_size: 0,
+ sh_link: 0,
+ sh_info: 0,
+ sh_addralign: 0,
+ sh_entsize: 0,
+ };
let text_section_header = Elf64SectionHeader {
sh_name: next_section(),
sh_type: 1,
@@ -172,6 +185,8 @@
extend(&mut vec, elf_header);
extend(&mut vec, text_program_header);
extend(&mut vec, data_program_header);
+
+ extend(&mut vec, null_section_header);
extend(&mut vec, text_section_header);
extend(&mut vec, data_section_header);
extend(&mut vec, strings_section_header);
Using rustc elf.rs && ./elf && readelf -WSl output
:
There are 4 section headers, starting at offset 0xb0:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000400000 001000 001000 00 AX 0 0 4096
[ 2] .data PROGBITS 0000000000401000 002000 001000 00 WA 0 0 4096
[ 3] .shstrtab STRTAB 0000000000000000 003000 000017 00 0 0 0
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),
R (retain), D (mbind), l (large), p (processor specific)
Elf file type is EXEC (Executable file)
Entry point 0x400000
There are 2 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x0000000000400000 0x0000000000400000 0x001000 0x001000 R E 0x1000
LOAD 0x002000 0x0000000000401000 0x0000000000401000 0x001000 0x001000 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .text
01 .data
$ objdump -d output
output: file format elf64-x86-64
Disassembly of section .text:
0000000000400000 <.text>:
400000: b8 3c 00 00 00 mov $0x3c,%eax
400005: bf 00 00 00 00 mov $0x0,%edi
40000a: 0f 05 syscall
...