Skip to main content

gen_elf/
relocatable.rs

1use crate::Arch;
2use crate::common::{
3    RelocEntry, SectionKind, SymbolDesc, SymbolScope as CommonSymbolScope, SymbolType,
4};
5use anyhow::Result;
6use object::{
7    Architecture, BinaryFormat, Endianness, SectionKind as ObjectSectionKind, SymbolKind,
8    SymbolScope,
9    write::{Object, Relocation, Symbol, SymbolSection},
10};
11use std::collections::HashMap;
12
13/// Result of a relocatable object file generation.
14pub struct ObjectElfOutput {
15    /// Raw bytes of the generated ELF file.
16    pub data: Vec<u8>,
17    /// Offsets where relocations were applied.
18    pub reloc_offsets: Vec<u64>,
19}
20
21/// A writer for generating relocatable object (.o) ELF files.
22pub struct ObjectWriter {
23    arch: Arch,
24}
25
26impl ObjectWriter {
27    /// Create a new ObjectWriter for the specified architecture.
28    pub fn new(arch: Arch) -> Self {
29        Self { arch }
30    }
31
32    /// Generate the relocatable ELF bytes and metadata.
33    pub fn write(&self, symbols: &[SymbolDesc], relocs: &[RelocEntry]) -> Result<ObjectElfOutput> {
34        gen_static_elf(self.arch, symbols, relocs)
35    }
36
37    /// Write the generated relocatable ELF to a file and return the metadata.
38    pub fn write_file(
39        &self,
40        out_path: &std::path::Path,
41        symbols: &[SymbolDesc],
42        relocs: &[RelocEntry],
43    ) -> Result<ObjectElfOutput> {
44        let output = self.write(symbols, relocs)?;
45        std::fs::write(out_path, &output.data)?;
46        Ok(output)
47    }
48}
49
50fn gen_static_elf(
51    arch: Arch,
52    symbols: &[SymbolDesc],
53    relocs: &[RelocEntry],
54) -> Result<ObjectElfOutput> {
55    let obj_arch: Architecture = arch.into();
56    let mut obj = Object::new(BinaryFormat::Elf, obj_arch, Endianness::Little);
57
58    let mut section_map = HashMap::new();
59    let mut symbol_map = HashMap::new();
60    let mut reloc_offsets = Vec::new();
61
62    // First pass: create sections and add defined symbols
63    for sym_desc in symbols {
64        if let Some(content) = &sym_desc.content {
65            let section_id = *section_map.entry(content.kind).or_insert_with(|| {
66                let (name, kind) = match content.kind {
67                    SectionKind::Text => (".text", ObjectSectionKind::Text),
68                    SectionKind::Data => (".data", ObjectSectionKind::Data),
69                    SectionKind::Plt => (".plt", ObjectSectionKind::Text),
70                    SectionKind::Tls => (".tdata", ObjectSectionKind::Tls),
71                    _ => (".data", ObjectSectionKind::Data),
72                };
73                obj.add_section(vec![], name.as_bytes().to_vec(), kind)
74            });
75
76            let offset = obj.append_section_data(section_id, &content.data, 8);
77
78            let symbol_id = obj.add_symbol(Symbol {
79                name: sym_desc.name.as_bytes().to_vec(),
80                value: offset,
81                size: content.data.len() as u64,
82                kind: match sym_desc.sym_type {
83                    SymbolType::Func => SymbolKind::Text,
84                    SymbolType::Object => SymbolKind::Data,
85                    SymbolType::Tls => SymbolKind::Tls,
86                },
87                scope: match sym_desc.scope {
88                    CommonSymbolScope::Global => SymbolScope::Dynamic,
89                    CommonSymbolScope::Local => SymbolScope::Compilation,
90                    CommonSymbolScope::Weak => SymbolScope::Dynamic,
91                },
92                weak: sym_desc.scope == CommonSymbolScope::Weak,
93                section: SymbolSection::Section(section_id),
94                flags: object::SymbolFlags::None,
95            });
96            symbol_map.insert(sym_desc.name.clone(), symbol_id);
97        }
98    }
99
100    // Second pass: add undefined symbols
101    for sym_desc in symbols {
102        if sym_desc.content.is_none() {
103            let symbol_id = obj.add_symbol(Symbol {
104                name: sym_desc.name.as_bytes().to_vec(),
105                value: 0,
106                size: 0,
107                kind: match sym_desc.sym_type {
108                    SymbolType::Func => SymbolKind::Text,
109                    SymbolType::Object => SymbolKind::Data,
110                    SymbolType::Tls => SymbolKind::Tls,
111                },
112                scope: SymbolScope::Dynamic,
113                weak: sym_desc.scope == CommonSymbolScope::Weak,
114                section: SymbolSection::Undefined,
115                flags: object::SymbolFlags::None,
116            });
117            symbol_map.insert(sym_desc.name.clone(), symbol_id);
118        }
119    }
120
121    // Add relocations
122    // For relocatable objects, we usually put relocations in the section they apply to.
123    // Here we assume all relocations apply to the first section that has data, or we need more info.
124    // In the dylib case, relocations are more global.
125    // Let's assume for now they apply to the .text section if it exists, or .data.
126
127    let target_section_id = section_map
128        .get(&SectionKind::Text)
129        .or_else(|| section_map.get(&SectionKind::Data))
130        .copied();
131
132    if let Some(section_id) = target_section_id {
133        let word_size = if arch.is_64() { 8 } else { 4 };
134        for (idx, reloc) in relocs.iter().enumerate() {
135            let symbol_id = if reloc.symbol_name.is_empty() {
136                // Section-relative relocation
137                obj.section_symbol(section_id)
138            } else {
139                *symbol_map.get(&reloc.symbol_name).ok_or_else(|| {
140                    anyhow::anyhow!("Symbol not found for relocation: {}", reloc.symbol_name)
141                })?
142            };
143
144            // Auto-calculate offset based on relocation sequence
145            // Each relocation is word_size bytes apart starting from offset 0x10
146            let offset = 0x10 + (idx as u64 * word_size);
147            reloc_offsets.push(offset);
148
149            let flags = object::write::RelocationFlags::Elf {
150                r_type: reloc.r_type.0,
151            };
152
153            obj.add_relocation(
154                section_id,
155                Relocation {
156                    offset,
157                    symbol: symbol_id,
158                    addend: 0, // Should we allow specifying addend in RelocEntry?
159                    flags,
160                },
161            )?;
162        }
163    }
164
165    // Write object file bytes
166    let elf_data = obj.write()?;
167
168    Ok(ObjectElfOutput {
169        data: elf_data,
170        reloc_offsets,
171    })
172}