Skip to main content

sbpf_assembler/
header.rs

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