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, _) = object::from_bytes_mut::<U64Bytes<NE>>(&mut bytes[offset as usize..])
67             .map_err(|()| anyhow!("invalid dwarf relocations"))?;
68         loc.set(NE, value);
69     }
70     Ok(())
71 }
72 
73 fn ensure_supported_elf_format(bytes: &[u8]) -> Result<Endianness, Error> {
74     use object::elf::*;
75     use object::read::elf::*;
76 
77     let kind = match object::FileKind::parse(bytes) {
78         Ok(file) => file,
79         Err(err) => {
80             bail!("Failed to parse file: {}", err);
81         }
82     };
83     let header = match kind {
84         object::FileKind::Elf64 => match object::elf::FileHeader64::<Endianness>::parse(bytes) {
85             Ok(header) => header,
86             Err(err) => {
87                 bail!("Unsupported ELF file: {}", err);
88             }
89         },
90         _ => {
91             bail!("only 64-bit ELF files currently supported")
92         }
93     };
94     let e = header.endian().unwrap();
95 
96     match header.e_machine.get(e) {
97         EM_AARCH64 => (),
98         EM_X86_64 => (),
99         EM_S390 => (),
100         EM_RISCV => (),
101         machine => {
102             bail!("Unsupported ELF target machine: {:x}", machine);
103         }
104     }
105     ensure!(
106         header.e_phoff.get(e) == 0 && header.e_phnum.get(e) == 0,
107         "program header table is empty"
108     );
109     let e_shentsize = header.e_shentsize.get(e);
110     let req_shentsize = match e {
111         Endianness::Little => size_of::<SectionHeader64<LittleEndian>>(),
112         Endianness::Big => size_of::<SectionHeader64<BigEndian>>(),
113     };
114     ensure!(e_shentsize as usize == req_shentsize, "size of sh");
115     Ok(e)
116 }
117 
118 fn convert_object_elf_to_loadable_file<E: Endian>(
119     bytes: &mut Vec<u8>,
120     code_region: (*const u8, usize),
121 ) {
122     let e = E::default();
123 
124     let header = FileHeader64::<E>::parse(&bytes[..]).unwrap();
125     let sections = header.sections(e, &bytes[..]).unwrap();
126     let text_range = match sections.section_by_name(e, b".text") {
127         Some((i, text)) => {
128             let range = text.file_range(e);
129             let off = header.e_shoff.get(e) as usize + i.0 * header.e_shentsize.get(e) as usize;
130 
131             let section: &mut SectionHeader64<E> =
132                 object::from_bytes_mut(&mut bytes[off..]).unwrap().0;
133             // Patch vaddr, and save file location and its size.
134             section.sh_addr.set(e, code_region.0 as u64);
135             range
136         }
137         None => None,
138     };
139 
140     // LLDB wants segment with virtual address set, placing them at the end of ELF.
141     let ph_off = bytes.len();
142     let e_phentsize = size_of::<ProgramHeader64<E>>();
143     let e_phnum = 1;
144     bytes.resize(ph_off + e_phentsize * e_phnum, 0);
145     if let Some((sh_offset, sh_size)) = text_range {
146         let (v_offset, size) = code_region;
147         let program: &mut ProgramHeader64<E> =
148             object::from_bytes_mut(&mut bytes[ph_off..]).unwrap().0;
149         program.p_type.set(e, PT_LOAD);
150         program.p_offset.set(e, sh_offset);
151         program.p_vaddr.set(e, v_offset as u64);
152         program.p_paddr.set(e, v_offset as u64);
153         program.p_filesz.set(e, sh_size);
154         program.p_memsz.set(e, size as u64);
155     } else {
156         unreachable!();
157     }
158 
159     // It is somewhat loadable ELF file at this moment.
160     let header: &mut FileHeader64<E> = object::from_bytes_mut(bytes).unwrap().0;
161     header.e_type.set(e, ET_DYN);
162     header.e_phoff.set(e, ph_off as u64);
163     header.e_phentsize.set(e, e_phentsize as u16);
164     header.e_phnum.set(e, e_phnum as u16);
165 }
166