1 use crate::prelude::*;
2 use core::mem::size_of;
3 use object::elf::*;
4 use object::endian::{BigEndian, Endian, Endianness, LittleEndian};
5 use object::read::elf::{FileHeader, SectionHeader};
6 use object::{
7     File, NativeEndian as NE, Object, ObjectSection, ObjectSymbol, RelocationEncoding,
8     RelocationKind, RelocationTarget, U64Bytes,
9 };
10 
11 pub(crate) fn create_gdbjit_image(
12     mut bytes: Vec<u8>,
13     code_region: (*const u8, usize),
14 ) -> Result<Vec<u8>, Error> {
15     let e = ensure_supported_elf_format(&bytes)?;
16 
17     // patch relocs
18     relocate_dwarf_sections(&mut bytes, code_region)?;
19 
20     // elf is still missing details...
21     match e {
22         Endianness::Little => {
23             convert_object_elf_to_loadable_file::<LittleEndian>(&mut bytes, code_region)
24         }
25         Endianness::Big => {
26             convert_object_elf_to_loadable_file::<BigEndian>(&mut bytes, code_region)
27         }
28     }
29 
30     Ok(bytes)
31 }
32 
33 fn relocate_dwarf_sections(bytes: &mut [u8], code_region: (*const u8, usize)) -> Result<(), Error> {
34     let mut relocations = Vec::new();
35     let obj = File::parse(&bytes[..]).err2anyhow()?;
36     for section in obj.sections() {
37         let section_start = match section.file_range() {
38             Some((start, _)) => start,
39             None => continue,
40         };
41         for (off, r) in section.relocations() {
42             if r.kind() != RelocationKind::Absolute
43                 || r.encoding() != RelocationEncoding::Generic
44                 || r.size() != 64
45             {
46                 continue;
47             }
48 
49             let sym = match r.target() {
50                 RelocationTarget::Symbol(index) => match obj.symbol_by_index(index) {
51                     Ok(sym) => sym,
52                     Err(_) => continue,
53                 },
54                 _ => continue,
55             };
56             relocations.push((
57                 section_start + off,
58                 (code_region.0 as u64)
59                     .wrapping_add(sym.address())
60                     .wrapping_add(r.addend() as u64),
61             ));
62         }
63     }
64 
65     for (offset, value) in relocations {
66         let (loc, _) = offset
67             .try_into()
68             .ok()
69             .and_then(|offset| object::from_bytes_mut::<U64Bytes<NE>>(&mut bytes[offset..]).ok())
70             .ok_or_else(|| anyhow!("invalid dwarf relocations"))?;
71         loc.set(NE, value);
72     }
73     Ok(())
74 }
75 
76 fn ensure_supported_elf_format(bytes: &[u8]) -> Result<Endianness, Error> {
77     use object::elf::*;
78     use object::read::elf::*;
79 
80     let kind = match object::FileKind::parse(bytes) {
81         Ok(file) => file,
82         Err(err) => {
83             bail!("Failed to parse file: {}", err);
84         }
85     };
86     let header = match kind {
87         object::FileKind::Elf64 => match object::elf::FileHeader64::<Endianness>::parse(bytes) {
88             Ok(header) => header,
89             Err(err) => {
90                 bail!("Unsupported ELF file: {}", err);
91             }
92         },
93         _ => {
94             bail!("only 64-bit ELF files currently supported")
95         }
96     };
97     let e = header.endian().unwrap();
98 
99     match header.e_machine.get(e) {
100         EM_AARCH64 => (),
101         EM_X86_64 => (),
102         EM_S390 => (),
103         EM_RISCV => (),
104         machine => {
105             bail!("Unsupported ELF target machine: {:x}", machine);
106         }
107     }
108     ensure!(
109         header.e_phoff.get(e) == 0 && header.e_phnum.get(e) == 0,
110         "program header table is empty"
111     );
112     let e_shentsize = header.e_shentsize.get(e);
113     let req_shentsize = match e {
114         Endianness::Little => size_of::<SectionHeader64<LittleEndian>>(),
115         Endianness::Big => size_of::<SectionHeader64<BigEndian>>(),
116     };
117     ensure!(e_shentsize as usize == req_shentsize, "size of sh");
118     Ok(e)
119 }
120 
121 fn convert_object_elf_to_loadable_file<E: Endian>(
122     bytes: &mut Vec<u8>,
123     code_region: (*const u8, usize),
124 ) {
125     let e = E::default();
126 
127     let header = FileHeader64::<E>::parse(&bytes[..]).unwrap();
128     let sections = header.sections(e, &bytes[..]).unwrap();
129     let text_range = match sections.section_by_name(e, b".text") {
130         Some((i, text)) => {
131             let range = text.file_range(e);
132             let e_shoff = usize::try_from(header.e_shoff.get(e)).unwrap();
133             let off = e_shoff + i.0 * header.e_shentsize.get(e) as usize;
134 
135             let section: &mut SectionHeader64<E> =
136                 object::from_bytes_mut(&mut bytes[off..]).unwrap().0;
137             // Patch vaddr, and save file location and its size.
138             section.sh_addr.set(e, code_region.0 as u64);
139             range
140         }
141         None => None,
142     };
143 
144     // LLDB wants segment with virtual address set, placing them at the end of ELF.
145     let ph_off = bytes.len();
146     let e_phentsize = size_of::<ProgramHeader64<E>>();
147     let e_phnum = 1;
148     bytes.resize(ph_off + e_phentsize * e_phnum, 0);
149     if let Some((sh_offset, sh_size)) = text_range {
150         let (v_offset, size) = code_region;
151         let program: &mut ProgramHeader64<E> =
152             object::from_bytes_mut(&mut bytes[ph_off..]).unwrap().0;
153         program.p_type.set(e, PT_LOAD);
154         program.p_offset.set(e, sh_offset);
155         program.p_vaddr.set(e, v_offset as u64);
156         program.p_paddr.set(e, v_offset as u64);
157         program.p_filesz.set(e, sh_size);
158         program.p_memsz.set(e, size as u64);
159     } else {
160         unreachable!();
161     }
162 
163     // It is somewhat loadable ELF file at this moment.
164     let header: &mut FileHeader64<E> = object::from_bytes_mut(bytes).unwrap().0;
165     header.e_type.set(e, ET_DYN);
166     header.e_phoff.set(e, ph_off as u64);
167     header
168         .e_phentsize
169         .set(e, u16::try_from(e_phentsize).unwrap());
170     header.e_phnum.set(e, u16::try_from(e_phnum).unwrap());
171 }
172