Skip to main content

inkwell/
object_file.rs

1use llvm_sys::object::{
2    LLVMBinaryCopyMemoryBuffer, LLVMBinaryGetType, LLVMBinaryRef, LLVMDisposeBinary, LLVMDisposeRelocationIterator,
3    LLVMDisposeSectionIterator, LLVMDisposeSymbolIterator, LLVMGetRelocationOffset, LLVMGetRelocationSymbol,
4    LLVMGetRelocationType, LLVMGetRelocationTypeName, LLVMGetRelocationValueString, LLVMGetRelocations,
5    LLVMGetSectionAddress, LLVMGetSectionContainsSymbol, LLVMGetSectionContents, LLVMGetSectionName,
6    LLVMGetSectionSize, LLVMGetSymbolAddress, LLVMGetSymbolName, LLVMGetSymbolSize, LLVMIsRelocationIteratorAtEnd,
7    LLVMMoveToContainingSection, LLVMMoveToNextRelocation, LLVMMoveToNextSection, LLVMMoveToNextSymbol,
8    LLVMObjectFileCopySectionIterator, LLVMObjectFileCopySymbolIterator, LLVMObjectFileIsSectionIteratorAtEnd,
9    LLVMObjectFileIsSymbolIteratorAtEnd, LLVMOpaqueBinary, LLVMOpaqueRelocationIterator, LLVMOpaqueSectionIterator,
10    LLVMOpaqueSymbolIterator, LLVMRelocationIteratorRef, LLVMSectionIteratorRef, LLVMSymbolIteratorRef,
11};
12
13pub use llvm_sys::object::LLVMBinaryType;
14
15use std::ffi::CStr;
16use std::marker::PhantomData;
17use std::ptr::NonNull;
18
19use crate::memory_buffer::MemoryBuffer;
20use crate::support::{LLVMString, assert_niche};
21
22#[repr(transparent)]
23#[derive(Debug)]
24pub struct BinaryFile<'a> {
25    binary_file: NonNull<LLVMOpaqueBinary>,
26    _phantom: PhantomData<&'a ()>,
27}
28const _: () = assert_niche::<BinaryFile>();
29
30impl<'a> BinaryFile<'a> {
31    pub unsafe fn new(binary_file: LLVMBinaryRef) -> Self {
32        assert!(!binary_file.is_null());
33
34        Self {
35            binary_file: unsafe { NonNull::new_unchecked(binary_file) },
36            _phantom: PhantomData,
37        }
38    }
39
40    pub fn as_mut_ptr(&self) -> LLVMBinaryRef {
41        self.binary_file.as_ptr()
42    }
43
44    pub fn get_binary_type(&self) -> LLVMBinaryType {
45        unsafe { LLVMBinaryGetType(self.as_mut_ptr()) }
46    }
47
48    // the backing buffer must outlive 'a, hence never dangling
49    pub fn get_memory_buffer(&self) -> MemoryBuffer<'a> {
50        unsafe { MemoryBuffer::new(LLVMBinaryCopyMemoryBuffer(self.as_mut_ptr())) }
51    }
52
53    pub fn get_sections(&self) -> Option<Sections<'_>> {
54        let section_iterator = unsafe { LLVMObjectFileCopySectionIterator(self.as_mut_ptr()) };
55
56        if section_iterator.is_null() {
57            return None;
58        }
59
60        Some(unsafe { Sections::new(section_iterator, self.as_mut_ptr()) })
61    }
62
63    pub fn get_symbols(&self) -> Option<Symbols<'_>> {
64        let symbol_iterator = unsafe { LLVMObjectFileCopySymbolIterator(self.as_mut_ptr()) };
65
66        if symbol_iterator.is_null() {
67            return None;
68        }
69
70        Some(unsafe { Symbols::new(symbol_iterator, self.as_mut_ptr()) })
71    }
72}
73
74impl<'a> Drop for BinaryFile<'a> {
75    fn drop(&mut self) {
76        unsafe {
77            LLVMDisposeBinary(self.as_mut_ptr());
78        }
79    }
80}
81
82#[derive(Debug)]
83pub struct Sections<'a> {
84    section_iterator: NonNull<LLVMOpaqueSectionIterator>,
85    binary_file: NonNull<LLVMOpaqueBinary>,
86    at_start: bool,
87    at_end: bool,
88    _phantom: PhantomData<&'a ()>,
89}
90const _: () = assert_niche::<Sections>();
91
92impl<'a> Sections<'a> {
93    pub unsafe fn new(section_iterator: LLVMSectionIteratorRef, binary_file: LLVMBinaryRef) -> Self {
94        assert!(!section_iterator.is_null());
95        assert!(!binary_file.is_null());
96
97        Sections {
98            section_iterator: unsafe { NonNull::new_unchecked(section_iterator) },
99            binary_file: unsafe { NonNull::new_unchecked(binary_file) },
100            at_start: true,
101            at_end: false,
102            _phantom: PhantomData,
103        }
104    }
105
106    pub fn as_mut_ptr(&self) -> (LLVMSectionIteratorRef, LLVMBinaryRef) {
107        (self.section_iterator.as_ptr(), self.binary_file.as_ptr())
108    }
109
110    // Here we cannot use the `Iterator`` trait since `Section` depends on the lifetime of self to
111    // ensure the section cannot be used after another call to `next_section`. If it can be used
112    // after another call, the underlying iterator would have moved to the next section already, and
113    // thus function calls to the old section would return results of the new section.
114    //
115    // This is similar to the `LendingIterator` trait.
116    pub fn next_section(&mut self) -> Option<Section<'_>> {
117        if self.at_end {
118            return None;
119        }
120
121        if !self.at_start {
122            unsafe {
123                LLVMMoveToNextSection(self.section_iterator.as_ptr());
124            }
125        }
126        self.at_start = false;
127
128        self.at_end = unsafe {
129            LLVMObjectFileIsSectionIteratorAtEnd(self.binary_file.as_ptr(), self.section_iterator.as_ptr()) == 1
130        };
131        if self.at_end {
132            return None;
133        }
134
135        let section = unsafe { Section::new(self.section_iterator.as_ptr(), self.binary_file.as_ptr()) };
136        Some(section)
137    }
138
139    // call `next_section` to get the containing section.
140    pub fn move_to_containing_section(&mut self, symbol: &Symbol<'_>) {
141        self.at_start = true;
142        self.at_end = false;
143        unsafe {
144            LLVMMoveToContainingSection(self.section_iterator.as_ptr(), symbol.symbol.as_ptr());
145        }
146    }
147}
148
149impl<'a> Drop for Sections<'a> {
150    fn drop(&mut self) {
151        unsafe { LLVMDisposeSectionIterator(self.section_iterator.as_ptr()) }
152    }
153}
154
155#[derive(Debug)]
156pub struct Section<'a> {
157    section: NonNull<LLVMOpaqueSectionIterator>,
158    binary_file: NonNull<LLVMOpaqueBinary>,
159    _phantom: PhantomData<&'a ()>,
160}
161const _: () = assert_niche::<Section>();
162
163impl<'a> Section<'a> {
164    pub unsafe fn new(section: LLVMSectionIteratorRef, binary_file: LLVMBinaryRef) -> Self {
165        assert!(!section.is_null());
166        assert!(!binary_file.is_null());
167
168        Self {
169            section: unsafe { NonNull::new_unchecked(section) },
170            binary_file: unsafe { NonNull::new_unchecked(binary_file) },
171            _phantom: PhantomData,
172        }
173    }
174
175    pub unsafe fn as_mut_ptr(&self) -> (LLVMSectionIteratorRef, LLVMBinaryRef) {
176        (self.section.as_ptr(), self.binary_file.as_ptr())
177    }
178
179    pub fn get_name(&self) -> Option<&CStr> {
180        let name = unsafe { LLVMGetSectionName(self.section.as_ptr()) };
181        if !name.is_null() {
182            Some(unsafe { CStr::from_ptr(name) })
183        } else {
184            None
185        }
186    }
187
188    pub fn get_size(&self) -> u64 {
189        unsafe { LLVMGetSectionSize(self.section.as_ptr()) }
190    }
191
192    pub fn get_contents(&self) -> &[u8] {
193        unsafe {
194            std::slice::from_raw_parts(
195                LLVMGetSectionContents(self.section.as_ptr()) as *const u8,
196                self.get_size() as usize,
197            )
198        }
199    }
200
201    pub fn get_address(&self) -> u64 {
202        unsafe { LLVMGetSectionAddress(self.section.as_ptr()) }
203    }
204
205    pub fn contains_symbol(&self, symbol: &Symbol<'_>) -> bool {
206        unsafe { LLVMGetSectionContainsSymbol(self.section.as_ptr(), symbol.symbol.as_ptr()) == 1 }
207    }
208
209    pub fn get_relocations(&self) -> Relocations<'_> {
210        let relocation_iterator = unsafe { LLVMGetRelocations(self.section.as_ptr()) };
211
212        unsafe { Relocations::new(relocation_iterator, self.section.as_ptr(), self.binary_file.as_ptr()) }
213    }
214}
215
216#[derive(Debug)]
217pub struct Relocations<'a> {
218    relocation_iterator: NonNull<LLVMOpaqueRelocationIterator>,
219    section_iterator: NonNull<LLVMOpaqueSectionIterator>,
220    binary_file: NonNull<LLVMOpaqueBinary>,
221    at_start: bool,
222    at_end: bool,
223    _phantom: PhantomData<&'a ()>,
224}
225const _: () = assert_niche::<Relocations>();
226
227impl<'a> Relocations<'a> {
228    pub unsafe fn new(
229        relocation_iterator: LLVMRelocationIteratorRef,
230        section_iterator: LLVMSectionIteratorRef,
231        binary_file: LLVMBinaryRef,
232    ) -> Self {
233        assert!(!relocation_iterator.is_null());
234        assert!(!section_iterator.is_null());
235        assert!(!binary_file.is_null());
236
237        Self {
238            relocation_iterator: unsafe { NonNull::new_unchecked(relocation_iterator) },
239            section_iterator: unsafe { NonNull::new_unchecked(section_iterator) },
240            binary_file: unsafe { NonNull::new_unchecked(binary_file) },
241            at_start: true,
242            at_end: false,
243            _phantom: PhantomData,
244        }
245    }
246
247    pub fn as_mut_ptr(&self) -> (LLVMRelocationIteratorRef, LLVMSectionIteratorRef, LLVMBinaryRef) {
248        (
249            self.relocation_iterator.as_ptr(),
250            self.section_iterator.as_ptr(),
251            self.binary_file.as_ptr(),
252        )
253    }
254
255    pub fn next_relocation(&mut self) -> Option<Relocation<'_>> {
256        if self.at_end {
257            return None;
258        }
259
260        if !self.at_start {
261            unsafe {
262                LLVMMoveToNextRelocation(self.relocation_iterator.as_ptr());
263            }
264        }
265        self.at_start = false;
266
267        self.at_end = unsafe {
268            LLVMIsRelocationIteratorAtEnd(self.section_iterator.as_ptr(), self.relocation_iterator.as_ptr()) == 1
269        };
270        if self.at_end {
271            return None;
272        }
273
274        let relocation = unsafe { Relocation::new(self.relocation_iterator.as_ptr(), self.binary_file.as_ptr()) };
275        Some(relocation)
276    }
277}
278
279impl<'a> Drop for Relocations<'a> {
280    fn drop(&mut self) {
281        unsafe { LLVMDisposeRelocationIterator(self.relocation_iterator.as_ptr()) }
282    }
283}
284
285#[derive(Debug)]
286pub struct Relocation<'a> {
287    relocation: NonNull<LLVMOpaqueRelocationIterator>,
288    binary_file: NonNull<LLVMOpaqueBinary>,
289    _phantom: PhantomData<&'a ()>,
290}
291const _: () = assert_niche::<Relocation>();
292
293impl<'a> Relocation<'a> {
294    pub unsafe fn new(relocation: LLVMRelocationIteratorRef, binary_file: LLVMBinaryRef) -> Self {
295        assert!(!relocation.is_null());
296        assert!(!binary_file.is_null());
297
298        Self {
299            relocation: unsafe { NonNull::new_unchecked(relocation) },
300            binary_file: unsafe { NonNull::new_unchecked(binary_file) },
301            _phantom: PhantomData,
302        }
303    }
304
305    pub fn as_mut_ptr(&self) -> (LLVMRelocationIteratorRef, LLVMBinaryRef) {
306        (self.relocation.as_ptr(), self.binary_file.as_ptr())
307    }
308
309    pub fn get_offset(&self) -> u64 {
310        unsafe { LLVMGetRelocationOffset(self.relocation.as_ptr()) }
311    }
312
313    pub fn get_type(&self) -> (u64, LLVMString) {
314        let type_int = unsafe { LLVMGetRelocationType(self.relocation.as_ptr()) };
315        let type_name = unsafe { LLVMString::new(LLVMGetRelocationTypeName(self.relocation.as_ptr())) };
316
317        (type_int, type_name)
318    }
319
320    pub fn get_value(&self) -> LLVMString {
321        unsafe { LLVMString::new(LLVMGetRelocationValueString(self.relocation.as_ptr())) }
322    }
323
324    pub fn get_symbol(&self) -> Symbol<'_> {
325        let symbol = unsafe { LLVMGetRelocationSymbol(self.relocation.as_ptr()) };
326
327        unsafe { Symbol::new(symbol) }
328    }
329}
330
331#[derive(Debug)]
332pub struct Symbols<'a> {
333    symbol_iterator: NonNull<LLVMOpaqueSymbolIterator>,
334    binary_file: NonNull<LLVMOpaqueBinary>,
335    at_start: bool,
336    at_end: bool,
337    _phantom: PhantomData<&'a ()>,
338}
339const _: () = assert_niche::<Symbols>();
340
341impl<'a> Symbols<'a> {
342    pub unsafe fn new(symbol_iterator: LLVMSymbolIteratorRef, binary_file: LLVMBinaryRef) -> Self {
343        assert!(!symbol_iterator.is_null());
344        assert!(!binary_file.is_null());
345
346        Self {
347            symbol_iterator: unsafe { NonNull::new_unchecked(symbol_iterator) },
348            binary_file: unsafe { NonNull::new_unchecked(binary_file) },
349            at_start: true,
350            at_end: false,
351            _phantom: PhantomData,
352        }
353    }
354
355    pub fn as_mut_ptr(&self) -> (LLVMSymbolIteratorRef, LLVMBinaryRef) {
356        (self.symbol_iterator.as_ptr(), self.binary_file.as_ptr())
357    }
358
359    pub fn next_symbol(&mut self) -> Option<Symbol<'_>> {
360        if self.at_end {
361            return None;
362        }
363
364        if !self.at_start {
365            unsafe {
366                LLVMMoveToNextSymbol(self.symbol_iterator.as_ptr());
367            }
368        }
369        self.at_start = false;
370
371        self.at_end = unsafe {
372            LLVMObjectFileIsSymbolIteratorAtEnd(self.binary_file.as_ptr(), self.symbol_iterator.as_ptr()) == 1
373        };
374        if self.at_end {
375            return None;
376        }
377
378        let symbol = unsafe { Symbol::new(self.symbol_iterator.as_ptr()) };
379        Some(symbol)
380    }
381}
382
383impl<'a> Drop for Symbols<'a> {
384    fn drop(&mut self) {
385        unsafe { LLVMDisposeSymbolIterator(self.symbol_iterator.as_ptr()) }
386    }
387}
388
389#[derive(Debug)]
390pub struct Symbol<'a> {
391    symbol: NonNull<LLVMOpaqueSymbolIterator>,
392    _phantom: PhantomData<&'a ()>,
393}
394const _: () = assert_niche::<Symbol>();
395
396impl<'a> Symbol<'a> {
397    pub unsafe fn new(symbol: LLVMSymbolIteratorRef) -> Self {
398        assert!(!symbol.is_null());
399
400        Self {
401            symbol: unsafe { NonNull::new_unchecked(symbol) },
402            _phantom: PhantomData,
403        }
404    }
405
406    pub fn as_mut_ptr(&self) -> LLVMSymbolIteratorRef {
407        self.symbol.as_ptr()
408    }
409
410    pub fn get_name(&self) -> Option<&CStr> {
411        let name = unsafe { LLVMGetSymbolName(self.symbol.as_ptr()) };
412        if !name.is_null() {
413            Some(unsafe { CStr::from_ptr(name) })
414        } else {
415            None
416        }
417    }
418
419    pub fn get_size(&self) -> u64 {
420        unsafe { LLVMGetSymbolSize(self.symbol.as_ptr()) }
421    }
422
423    pub fn get_address(&self) -> u64 {
424        unsafe { LLVMGetSymbolAddress(self.symbol.as_ptr()) }
425    }
426}