sbpf_assembler/
header.rs

1#[derive(Debug)]
2pub struct ElfHeader {
3    pub e_ident: [u8; 16], // ELF identification bytes = [127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
4    pub e_type: u16,       // Object file type = 3 (ET_DYN)
5    pub e_machine: u16,    // Machine architecture = 247 (BPF)
6    pub e_version: u32,    // Object file version = 1
7    pub e_entry: u64,      // Entry point address
8    pub e_phoff: u64,      // Program header offset
9    pub e_shoff: u64,      // Section header offset
10    pub e_flags: u32,      // Processor-specific flags
11    pub e_ehsize: u16,     // ELF header size = 64
12    pub e_phentsize: u16,  // Size of program header entry = 56
13    pub e_phnum: u16,      // Number of program header entries
14    pub e_shentsize: u16,  // Size of section header entry = 64
15    pub e_shnum: u16,      // Number of section header entries
16    pub e_shstrndx: u16,   // Section name string table index
17}
18
19#[derive(Debug)]
20pub struct ProgramHeader {
21    pub p_type: u32,   // Type of segment
22    pub p_flags: u32,  // Segment attributes
23    pub p_offset: u64, // Offset in file
24    pub p_vaddr: u64,  // Virtual address in memory
25    pub p_paddr: u64,  // Physical address (reserved)
26    pub p_filesz: u64, // Size of segment in file
27    pub p_memsz: u64,  // Size of segment in memory
28    pub p_align: u64,  // Alignment of segment
29}
30
31impl Default for ElfHeader {
32    fn default() -> Self {
33        Self {
34            e_ident: Self::SOLANA_IDENT,
35            e_type: Self::SOLANA_TYPE,
36            e_machine: Self::SOLANA_MACHINE,
37            e_version: Self::SOLANA_VERSION,
38            e_entry: 0,
39            e_phoff: Self::ELF64_HEADER_SIZE as u64,
40            e_shoff: 0,
41            e_flags: 0,
42            e_ehsize: Self::ELF64_HEADER_SIZE,
43            e_phentsize: Self::PROGRAM_HEADER_SIZE,
44            e_phnum: 0,
45            e_shentsize: Self::SECTION_HEADER_SIZE,
46            e_shnum: 0,
47            e_shstrndx: 0,
48        }
49    }
50}
51
52impl ElfHeader {
53    const SOLANA_IDENT: [u8; 16] = [
54        0x7f, 0x45, 0x4c, 0x46, // EI_MAG0..EI_MAG3 ("\x7FELF")
55        0x02, // EI_CLASS (64-bit)
56        0x01, // EI_DATA (little endian)
57        0x01, // EI_VERSION
58        0x00, // EI_OSABI
59        0x00, // EI_ABIVERSION
60        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // EI_PAD
61    ];
62    const SOLANA_TYPE: u16 = 3; // ET_DYN
63    const SOLANA_MACHINE: u16 = 247; // BPF
64    const SOLANA_VERSION: u32 = 1; // EV_CURRENT
65    const ELF64_HEADER_SIZE: u16 = 64;
66    const PROGRAM_HEADER_SIZE: u16 = 56;
67    const SECTION_HEADER_SIZE: u16 = 64;
68
69    pub fn new() -> Self {
70        Self::default()
71    }
72
73    pub fn bytecode(&self) -> Vec<u8> {
74        let mut bytecode = Vec::with_capacity(Self::ELF64_HEADER_SIZE as usize);
75
76        // e_ident (16 bytes)
77        bytecode.extend_from_slice(&self.e_ident);
78
79        // Emit remaining fields in little-endian order
80        bytecode.extend_from_slice(&self.e_type.to_le_bytes());
81        bytecode.extend_from_slice(&self.e_machine.to_le_bytes());
82        bytecode.extend_from_slice(&self.e_version.to_le_bytes());
83        bytecode.extend_from_slice(&self.e_entry.to_le_bytes());
84        bytecode.extend_from_slice(&self.e_phoff.to_le_bytes());
85        bytecode.extend_from_slice(&self.e_shoff.to_le_bytes());
86        bytecode.extend_from_slice(&self.e_flags.to_le_bytes());
87        bytecode.extend_from_slice(&self.e_ehsize.to_le_bytes());
88        bytecode.extend_from_slice(&self.e_phentsize.to_le_bytes());
89        bytecode.extend_from_slice(&self.e_phnum.to_le_bytes());
90        bytecode.extend_from_slice(&self.e_shentsize.to_le_bytes());
91        bytecode.extend_from_slice(&self.e_shnum.to_le_bytes());
92        bytecode.extend_from_slice(&self.e_shstrndx.to_le_bytes());
93
94        bytecode
95    }
96}
97
98#[rustfmt::skip]
99impl ProgramHeader {
100
101    const PT_LOAD: u32 = 1;      // Loadable segment
102    const PT_DYNAMIC: u32 = 2;   // Dynamic linking information
103    
104    const PF_X: u32 = 1;         // Executable
105    const PF_W: u32 = 2;         // Writable
106    const PF_R: u32 = 4;         // Readable
107    
108    const PAGE_SIZE: u64 = 4096;          // Standard page size
109
110    pub fn new_load(offset: u64, size: u64, executable: bool) -> Self {
111        let flags = if executable {
112            Self::PF_R | Self::PF_X  // Read + Execute
113        } else {
114            Self::PF_R        // Read only
115        };
116
117        ProgramHeader {
118            p_type: Self::PT_LOAD,
119            p_flags: flags,
120            p_offset: offset,
121            p_vaddr: offset,
122            p_paddr: offset,
123            p_filesz: size,
124            p_memsz: size,
125            p_align: Self::PAGE_SIZE
126        }
127    }
128
129    pub fn new_dynamic(offset: u64, size: u64) -> Self {
130        ProgramHeader {
131            p_type: Self::PT_DYNAMIC,
132            p_flags: Self::PF_R | Self::PF_W,
133            p_offset: offset,
134            p_vaddr: offset,
135            p_paddr: offset,
136            p_filesz: size,
137            p_memsz: size,
138            p_align: 8
139        }
140    }
141
142    pub fn bytecode(&self) -> Vec<u8> {
143        let mut bytecode = Vec::with_capacity(56); // Size of program header is 56 bytes
144        
145        bytecode.extend_from_slice(&self.p_type.to_le_bytes());
146        bytecode.extend_from_slice(&self.p_flags.to_le_bytes());
147        bytecode.extend_from_slice(&self.p_offset.to_le_bytes());
148        bytecode.extend_from_slice(&self.p_vaddr.to_le_bytes());
149        bytecode.extend_from_slice(&self.p_paddr.to_le_bytes());
150        bytecode.extend_from_slice(&self.p_filesz.to_le_bytes());
151        bytecode.extend_from_slice(&self.p_memsz.to_le_bytes());
152        bytecode.extend_from_slice(&self.p_align.to_le_bytes());
153
154        bytecode
155    }
156}
157#[derive(Debug)]
158pub struct SectionHeader {
159    sh_name: u32,      // Section name (string table index)
160    sh_type: u32,      // Section type
161    sh_flags: u64,     // Section flags
162    sh_addr: u64,      // Section virtual addr at execution
163    sh_offset: u64,    // Section file offset
164    sh_size: u64,      // Section size in bytes
165    sh_link: u32,      // Link to another section
166    sh_info: u32,      // Additional section info
167    sh_addralign: u64, // Section alignment
168    sh_entsize: u64,   // Entry size if section holds table
169}
170
171#[rustfmt::skip]
172impl SectionHeader {
173    // Section types
174    pub const SHT_NULL: u32 = 0;          // Section header table entry unused
175    pub const SHT_PROGBITS: u32 = 1;      // Program data
176    pub const SHT_STRTAB: u32 = 3;        // String table
177    pub const SHT_NOBITS: u32 = 8;        // Program space with no data (bss)
178    pub const SHT_DYNAMIC: u32 = 6;       // Dynamic section
179    pub const SHT_DYNSYM: u32 = 11;       // Dynamic symbol table
180    pub const SHT_REL: u32 = 9;           // Relocation table
181    
182    // Section flags
183    pub const SHF_WRITE: u64 = 0x1;       // Writable
184    pub const SHF_ALLOC: u64 = 0x2;       // Occupies memory during execution
185    pub const SHF_EXECINSTR: u64 = 0x4;   // Executable
186    
187    #[allow(clippy::too_many_arguments)]
188    pub fn new(name_offset: u32, sh_type: u32, flags: u64, addr: u64, offset: u64, size: u64, link: u32, info: u32, addralign: u64, entsize: u64) -> Self {
189        Self {
190            sh_name: name_offset,
191            sh_type,
192            sh_flags: flags,
193            sh_addr: addr,
194            sh_offset: offset,
195            sh_size: size,
196            sh_link: link,
197            sh_info: info,
198            sh_addralign: addralign,
199            sh_entsize: entsize,
200        }
201    }
202
203    pub fn bytecode(&self) -> Vec<u8> {
204        let mut bytecode = Vec::with_capacity(64); // Size of section header is 64 bytes
205        
206        bytecode.extend_from_slice(&self.sh_name.to_le_bytes());
207        bytecode.extend_from_slice(&self.sh_type.to_le_bytes());
208        bytecode.extend_from_slice(&self.sh_flags.to_le_bytes());
209        bytecode.extend_from_slice(&self.sh_addr.to_le_bytes());
210        bytecode.extend_from_slice(&self.sh_offset.to_le_bytes());
211        bytecode.extend_from_slice(&self.sh_size.to_le_bytes());
212        bytecode.extend_from_slice(&self.sh_link.to_le_bytes());
213        bytecode.extend_from_slice(&self.sh_info.to_le_bytes());
214        bytecode.extend_from_slice(&self.sh_addralign.to_le_bytes());
215        bytecode.extend_from_slice(&self.sh_entsize.to_le_bytes());
216
217        bytecode
218    }
219}