1use anyhow::{anyhow, bail, ensure, Error};
2use object::elf::*;
3use object::endian::{BigEndian, Endian, Endianness, LittleEndian};
4use object::read::elf::{FileHeader, SectionHeader};
5use object::{
6 File, NativeEndian as NE, Object, ObjectSection, ObjectSymbol, RelocationEncoding,
7 RelocationKind, RelocationTarget, U64Bytes,
8};
9use std::mem::size_of;
10
11pub 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 relocate_dwarf_sections(&mut bytes, code_region)?;
19
20 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
33fn 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[..])?;
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
73fn 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
118fn 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 * 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 section.sh_addr.set(e, code_region.0 as u64);
135 range
136 }
137 None => None,
138 };
139
140 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 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}