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