Skip to main content

gen_elf/dylib/
mod.rs

1use crate::Arch;
2use crate::common::{RelocEntry, SectionKind, SymbolDesc};
3use crate::dylib::data::DataMetaData;
4use crate::dylib::dynamic::DynamicMetadata;
5use crate::dylib::layout::ElfLayout;
6use crate::dylib::reloc::RelocMetaData;
7use crate::dylib::shdr::{SectionAllocator, ShdrManager};
8use crate::dylib::symtab::SymTabMetadata;
9use crate::dylib::text::CodeMetaData;
10use crate::dylib::tls::TlsMetaData;
11use anyhow::Result;
12use byteorder::{LittleEndian, WriteBytesExt};
13use object::elf::*;
14use std::path::Path;
15
16mod data;
17mod dynamic;
18mod layout;
19pub(crate) mod reloc;
20pub(crate) mod shdr;
21pub(crate) mod symtab;
22pub(crate) mod text;
23mod tls;
24
25fn align_up(val: u64, align: u64) -> u64 {
26    val.div_ceil(align) * align
27}
28
29pub(crate) struct StringTable {
30    data: Vec<u8>,
31}
32
33impl StringTable {
34    pub(crate) fn new() -> Self {
35        Self { data: vec![0u8] } // initial null byte
36    }
37
38    pub(crate) fn add(&mut self, s: &str) -> u32 {
39        let idx = self.data.len() as u32;
40        self.data.extend_from_slice(s.as_bytes());
41        self.data.push(0);
42        idx
43    }
44
45    pub(crate) fn cur_idx(&self) -> u32 {
46        self.data.len() as u32
47    }
48
49    pub(crate) fn data(&self) -> &[u8] {
50        &self.data
51    }
52}
53
54#[derive(Clone)]
55pub struct ElfWriterConfig {
56    /// Base address for memory mapping (default: 0x400000)
57    pub base_addr: u64,
58    /// Page size for alignment (default: 0x1000)
59    pub page_size: u64,
60    /// Custom value for IFUNC resolver to return (default: None, returns PLT0 address)
61    pub ifunc_resolver_val: Option<u64>,
62}
63
64impl Default for ElfWriterConfig {
65    fn default() -> Self {
66        Self {
67            base_addr: 0,
68            page_size: 0x1000,
69            ifunc_resolver_val: None,
70        }
71    }
72}
73
74impl ElfWriterConfig {
75    /// Set custom base address
76    pub fn with_base_addr(mut self, addr: u64) -> Self {
77        self.base_addr = addr;
78        self
79    }
80
81    /// Set custom page size
82    pub fn with_page_size(mut self, size: u64) -> Self {
83        self.page_size = size;
84        self
85    }
86
87    /// Set custom IFUNC resolver return value
88    pub fn with_ifunc_resolver_val(mut self, val: u64) -> Self {
89        self.ifunc_resolver_val = Some(val);
90        self
91    }
92}
93
94/// Relocation metadata for testing and verification
95/// Relocation metadata for testing and verification.
96#[derive(Clone, Debug)]
97pub struct RelocationInfo {
98    /// Virtual address where the relocation is applied.
99    pub vaddr: u64,
100    /// Architecture-specific relocation type.
101    pub r_type: u32,
102    /// Index of the symbol in the dynamic symbol table.
103    pub sym_idx: u64,
104    /// Addend for the relocation.
105    pub addend: i64,
106    /// Size of the referenced symbol (useful for COPY relocations).
107    pub sym_size: u64,
108}
109
110/// Result of an ELF generation process.
111#[derive(Clone)]
112pub struct ElfWriteOutput {
113    /// Raw bytes of the generated ELF file.
114    pub data: Vec<u8>,
115    /// Base address used for virtual address calculations.
116    pub base_addr: u64,
117    /// Virtual address of the data segment.
118    pub data_vaddr: u64,
119    /// Virtual address of the text segment.
120    pub text_vaddr: u64,
121    /// List of dynamic relocations generated.
122    pub relocations: Vec<RelocationInfo>,
123    /// The value returned by the IFUNC resolver.
124    pub ifunc_resolver_val: u64,
125}
126
127// Constants to replace magic numbers
128const EHDR_SIZE_64: u64 = 64;
129const EHDR_SIZE_32: u64 = 52;
130const PHDR_SIZE_64: u64 = 56;
131const PHDR_SIZE_32: u64 = 32;
132const SHDR_SIZE_64: u64 = 64;
133const SHDR_SIZE_32: u64 = 40;
134const SYM_SIZE_64: u64 = 24;
135const SYM_SIZE_32: u64 = 16;
136const RELA_SIZE_64: u64 = 24;
137const RELA_SIZE_32: u64 = 12;
138const REL_SIZE_64: u64 = 16;
139const REL_SIZE_32: u64 = 8;
140const DYN_SIZE_64: u64 = 16;
141const DYN_SIZE_32: u64 = 8;
142const HASH_SIZE: u64 = 4;
143
144/// A writer for generating dynamic library (Shared Object) ELF files.
145pub struct DylibWriter {
146    arch: Arch,
147    config: ElfWriterConfig,
148}
149
150impl DylibWriter {
151    /// Create a new DylibWriter for the specified architecture with default configuration.
152    pub fn new(arch: Arch) -> Self {
153        let config = ElfWriterConfig::default();
154        Self { arch, config }
155    }
156
157    /// Create a new DylibWriter with a custom configuration.
158    pub fn with_config(arch: Arch, config: ElfWriterConfig) -> Self {
159        Self { arch, config }
160    }
161
162    /// Write the generated ELF to a file and return the metadata.
163    pub fn write_file(
164        &self,
165        out_path: &Path,
166        relocs: &[RelocEntry],
167        symbols: &[SymbolDesc],
168    ) -> Result<ElfWriteOutput> {
169        let output = self.write(relocs, symbols)?;
170        std::fs::write(out_path, &output.data)?;
171        Ok(output)
172    }
173
174    /// Generate the ELF bytes and metadata.
175    pub fn write(
176        &self,
177        raw_relocs: &[RelocEntry],
178        symbols: &[SymbolDesc],
179    ) -> Result<ElfWriteOutput> {
180        let is_64 = self.arch.is_64();
181
182        // Preprocess relocations
183        let final_relocs = RelocMetaData::preprocess(self.arch, raw_relocs);
184
185        let mut allocator = SectionAllocator::new();
186        let mut symtab = SymTabMetadata::new(self.arch, symbols, &final_relocs, &mut allocator);
187        let mut reloc = RelocMetaData::new(self.arch, &final_relocs, &symtab, &mut allocator)?;
188
189        let data = DataMetaData::new(&reloc, &symtab, &mut allocator);
190        let mut text = CodeMetaData::new(&symtab, &mut allocator);
191        let tls = TlsMetaData::new(&symtab, &mut allocator);
192
193        // 1. Create initial sections
194        let mut sections = vec![];
195        text.create_sections(&mut sections);
196        data.create_sections(&mut sections);
197        tls.create_section(&mut sections);
198        symtab.create_sections(&mut sections);
199        reloc.create_sections(&mut sections)?;
200
201        // 2. Create .dynamic section (placeholder)
202        let mut dyn_meta = DynamicMetadata::new(self.arch, &sections, &mut allocator);
203        dyn_meta.create_section(&mut sections);
204
205        // 3. Initialize ShdrManager and Layout
206        let mut shdr_manager = ShdrManager::new();
207        for sec in sections {
208            shdr_manager.add_section(sec.header, sec.data);
209        }
210
211        // 4. Build .shstrtab
212        shdr_manager.create_shstrtab_section(&mut allocator);
213
214        // 5. Layout
215        let mut layout = ElfLayout::new(&self.config);
216        let ehdr_size = if is_64 { EHDR_SIZE_64 } else { EHDR_SIZE_32 };
217        let ph_size =
218            (if is_64 { PHDR_SIZE_64 } else { PHDR_SIZE_32 }) * shdr_manager.get_phnum() as u64;
219        layout.add_header(ehdr_size, ph_size);
220        shdr_manager.layout(&mut layout);
221
222        // 6. Update all metadata with final addresses
223        let shdr_map = shdr_manager.get_shdr_map();
224        let plt_vaddr = shdr_manager.get_vaddr(SectionKind::Plt);
225        let text_vaddr = shdr_manager.get_vaddr(SectionKind::Text);
226        let data_vaddr = shdr_manager.get_vaddr(SectionKind::Data);
227        let got_plt_vaddr = shdr_manager.get_vaddr(SectionKind::GotPlt);
228
229        // Update symbols
230        symtab.patch_symtab(plt_vaddr, text_vaddr, data_vaddr, &shdr_map, &mut allocator);
231
232        // Update PLT and Text
233        let resolver_val = self.config.ifunc_resolver_val.unwrap_or(plt_vaddr);
234        text.patch_text(
235            plt_vaddr,
236            text_vaddr,
237            got_plt_vaddr,
238            resolver_val,
239            &symtab,
240            &reloc,
241            &shdr_manager,
242            &mut allocator,
243        );
244
245        // Update GOT and Relocations
246        reloc.patch_all(&shdr_manager, &symtab, &mut allocator)?;
247
248        // Update Dynamic
249        dyn_meta.patch_dynamic(&shdr_manager, &reloc, &mut allocator, got_plt_vaddr)?;
250
251        // 7. Write final ELF
252        let mut out_bytes = vec![];
253        // Write EHDR
254        let ehdr = ElfHeader {
255            ident: self.get_ident(),
256            type_: ET_DYN,
257            machine: self.get_machine(),
258            version: EV_CURRENT as u32,
259            entry: 0,
260            phoff: ehdr_size,
261            shoff: layout.file_off,
262            flags: self.get_flags(),
263            ehsize: ehdr_size as u16,
264            phentsize: (if is_64 { PHDR_SIZE_64 } else { PHDR_SIZE_32 }) as u16,
265            phnum: shdr_manager.get_phnum(),
266            shentsize: (if is_64 { SHDR_SIZE_64 } else { SHDR_SIZE_32 }) as u16,
267            shnum: (shdr_manager.get_shdr_count() + 1) as u16,
268            shstrndx: *shdr_map.get(&SectionKind::ShStrTab).unwrap() as u16,
269        };
270        ehdr.write(&mut out_bytes, is_64)?;
271
272        // Write PHDRs
273        shdr_manager.write_phdrs(&mut out_bytes, is_64, self.config.page_size)?;
274
275        // Write Sections
276        for sec in shdr_manager.get_sections() {
277            self.write_at(&mut out_bytes, sec.header.offset, allocator.get(&sec.data));
278        }
279
280        // Write SHDRs
281        let mut shdr_buf = vec![];
282        shdr_manager.write_shdrs(&mut shdr_buf, is_64)?;
283        self.write_at(&mut out_bytes, layout.file_off, &shdr_buf);
284
285        Ok(ElfWriteOutput {
286            data: out_bytes,
287            base_addr: self.config.base_addr,
288            data_vaddr,
289            text_vaddr,
290            relocations: reloc.get_relocation_infos(),
291            ifunc_resolver_val: resolver_val,
292        })
293    }
294
295    fn get_ident(&self) -> [u8; 16] {
296        let mut ident = [0u8; 16];
297        ident[0] = 0x7f;
298        ident[1] = b'E';
299        ident[2] = b'L';
300        ident[3] = b'F';
301        ident[4] = if self.arch.is_64() { 2 } else { 1 };
302        ident[5] = 1; // Little endian
303        ident[6] = 1; // Version
304        ident[7] = 0; // OS ABI
305        ident
306    }
307
308    fn write_at(&self, buf: &mut Vec<u8>, offset: u64, data: &[u8]) {
309        let offset = offset as usize;
310        if buf.len() < offset + data.len() {
311            buf.resize(offset + data.len(), 0);
312        }
313        buf[offset..offset + data.len()].copy_from_slice(data);
314    }
315
316    fn get_flags(&self) -> u32 {
317        match self.arch {
318            Arch::Riscv64 | Arch::Riscv32 => {
319                // EF_RISCV_RVC | EF_RISCV_FLOAT_ABI_DOUBLE
320                0x0001 | 0x0004
321            }
322            Arch::Arm => {
323                // EF_ARM_EABI_VER5 | EF_ARM_ABI_FLOAT_HARD
324                0x05000000 | 0x00000400
325            }
326            _ => 0,
327        }
328    }
329
330    fn get_machine(&self) -> u16 {
331        match self.arch {
332            Arch::X86_64 => EM_X86_64,
333            Arch::X86 => EM_386,
334            Arch::Aarch64 => EM_AARCH64,
335            Arch::Riscv64 => EM_RISCV,
336            Arch::Riscv32 => EM_RISCV,
337            Arch::Arm => EM_ARM,
338            Arch::Loongarch64 => EM_LOONGARCH,
339        }
340    }
341}
342
343struct ElfHeader {
344    ident: [u8; 16],
345    type_: u16,
346    machine: u16,
347    version: u32,
348    entry: u64,
349    phoff: u64,
350    shoff: u64,
351    flags: u32,
352    ehsize: u16,
353    phentsize: u16,
354    phnum: u16,
355    shentsize: u16,
356    shnum: u16,
357    shstrndx: u16,
358}
359
360impl ElfHeader {
361    fn write(&self, buf: &mut Vec<u8>, is_64: bool) -> Result<()> {
362        buf.extend_from_slice(&self.ident);
363        buf.write_u16::<LittleEndian>(self.type_)?;
364        buf.write_u16::<LittleEndian>(self.machine)?;
365        buf.write_u32::<LittleEndian>(self.version)?;
366        if is_64 {
367            buf.write_u64::<LittleEndian>(self.entry)?;
368            buf.write_u64::<LittleEndian>(self.phoff)?;
369            buf.write_u64::<LittleEndian>(self.shoff)?;
370        } else {
371            buf.write_u32::<LittleEndian>(self.entry as u32)?;
372            buf.write_u32::<LittleEndian>(self.phoff as u32)?;
373            buf.write_u32::<LittleEndian>(self.shoff as u32)?;
374        }
375        buf.write_u32::<LittleEndian>(self.flags)?;
376        buf.write_u16::<LittleEndian>(self.ehsize)?;
377        buf.write_u16::<LittleEndian>(self.phentsize)?;
378        buf.write_u16::<LittleEndian>(self.phnum)?;
379        buf.write_u16::<LittleEndian>(self.shentsize)?;
380        buf.write_u16::<LittleEndian>(self.shnum)?;
381        buf.write_u16::<LittleEndian>(self.shstrndx)?;
382        Ok(())
383    }
384}