1#![allow(clippy::cast_ptr_alignment)]
4
5use anyhow::{bail, ensure, Error};
6use object::endian::{BigEndian, Endian, Endianness, LittleEndian};
7use object::{RelocationEncoding, RelocationKind};
8use std::collections::HashMap;
9
10pub use crate::write_debuginfo::{emit_dwarf, DwarfSection, DwarfSectionRelocTarget};
11
12mod gc;
13mod transform;
14mod write_debuginfo;
15
16pub fn create_gdbjit_image(
17 mut bytes: Vec<u8>,
18 code_region: (*const u8, usize),
19 defined_funcs_offset: usize,
20 funcs: &[*const u8],
21) -> Result<Vec<u8>, Error> {
22 let e = ensure_supported_elf_format(&bytes)?;
23
24 relocate_dwarf_sections(&bytes, defined_funcs_offset, funcs)?;
26
27 match e {
29 Endianness::Little => {
30 convert_object_elf_to_loadable_file::<LittleEndian>(&mut bytes, code_region)
31 }
32 Endianness::Big => {
33 convert_object_elf_to_loadable_file::<BigEndian>(&mut bytes, code_region)
34 }
35 }
36
37 Ok(bytes)
41}
42
43fn relocate_dwarf_sections(
44 bytes: &[u8],
45 defined_funcs_offset: usize,
46 funcs: &[*const u8],
47) -> Result<(), Error> {
48 use object::read::{File, Object, ObjectSection, ObjectSymbol, RelocationTarget};
49
50 let obj = File::parse(bytes)?;
51 let mut func_symbols = HashMap::new();
52 for sym in obj.symbols() {
53 match (sym.name(), sym.section_index()) {
54 (Ok(name), Some(_section_index)) if name.starts_with("_wasm_function_") => {
55 let index = name["_wasm_function_".len()..].parse::<usize>()?;
56 let data = funcs[index - defined_funcs_offset];
57 func_symbols.insert(sym.index(), data);
58 }
59 _ => (),
60 }
61 }
62
63 for section in obj.sections() {
64 for (off, r) in section.relocations() {
65 if r.kind() != RelocationKind::Absolute
66 || r.encoding() != RelocationEncoding::Generic
67 || r.size() != 64
68 {
69 continue;
70 }
71
72 let data = match r.target() {
73 RelocationTarget::Symbol(ref index) => func_symbols.get(index),
74 _ => None,
75 };
76 let data: *const u8 = match data {
77 Some(data) => *data,
78 None => {
79 continue;
80 }
81 };
82
83 let target = (data as u64).wrapping_add(r.addend() as u64);
84
85 let entry_ptr = section.data_range(off, 8).unwrap().unwrap().as_ptr();
86 unsafe {
87 std::ptr::write(entry_ptr as *mut u64, target);
88 }
89 }
90 }
91 Ok(())
92}
93
94fn ensure_supported_elf_format(bytes: &[u8]) -> Result<Endianness, Error> {
95 use object::elf::*;
96 use object::read::elf::*;
97 use std::mem::size_of;
98
99 let kind = match object::FileKind::parse(bytes) {
100 Ok(file) => file,
101 Err(err) => {
102 bail!("Failed to parse file: {}", err);
103 }
104 };
105 let header = match kind {
106 object::FileKind::Elf64 => match object::elf::FileHeader64::<Endianness>::parse(bytes) {
107 Ok(header) => header,
108 Err(err) => {
109 bail!("Unsupported ELF file: {}", err);
110 }
111 },
112 _ => {
113 bail!("only 64-bit ELF files currently supported")
114 }
115 };
116 let e = header.endian().unwrap();
117
118 match header.e_machine.get(e) {
119 EM_AARCH64 => (),
120 EM_X86_64 => (),
121 EM_S390 => (),
122 machine => {
123 bail!("Unsupported ELF target machine: {:x}", machine);
124 }
125 }
126 ensure!(
127 header.e_phoff.get(e) == 0 && header.e_phnum.get(e) == 0,
128 "program header table is empty"
129 );
130 let e_shentsize = header.e_shentsize.get(e);
131 let req_shentsize = match e {
132 Endianness::Little => size_of::<SectionHeader64<LittleEndian>>(),
133 Endianness::Big => size_of::<SectionHeader64<BigEndian>>(),
134 };
135 ensure!(e_shentsize as usize == req_shentsize, "size of sh");
136 Ok(e)
137}
138
139fn convert_object_elf_to_loadable_file<E: Endian>(
140 bytes: &mut Vec<u8>,
141 code_region: (*const u8, usize),
142) {
143 use object::elf::*;
144 use std::ffi::CStr;
145 use std::mem::size_of;
146 use std::os::raw::c_char;
147
148 let e = E::default();
149 let header: &FileHeader64<E> = unsafe { &*(bytes.as_mut_ptr() as *const FileHeader64<_>) };
150
151 let e_shentsize = header.e_shentsize.get(e);
152 let e_shoff = header.e_shoff.get(e);
153 let e_shnum = header.e_shnum.get(e);
154 let mut shstrtab_off = 0;
155 for i in 0..e_shnum {
156 let off = e_shoff as isize + i as isize * e_shentsize as isize;
157 let section: &SectionHeader64<E> =
158 unsafe { &*(bytes.as_ptr().offset(off) as *const SectionHeader64<_>) };
159 if section.sh_type.get(e) != SHT_STRTAB {
160 continue;
161 }
162 shstrtab_off = section.sh_offset.get(e);
163 }
164 let mut segment: Option<_> = None;
165 for i in 0..e_shnum {
166 let off = e_shoff as isize + i as isize * e_shentsize as isize;
167 let section: &mut SectionHeader64<E> =
168 unsafe { &mut *(bytes.as_mut_ptr().offset(off) as *mut SectionHeader64<_>) };
169 if section.sh_type.get(e) != SHT_PROGBITS {
170 continue;
171 }
172 let sh_name_off = section.sh_name.get(e);
174 let sh_name = unsafe {
175 CStr::from_ptr(
176 bytes
177 .as_ptr()
178 .offset((shstrtab_off + sh_name_off as u64) as isize)
179 as *const c_char,
180 )
181 .to_str()
182 .expect("name")
183 };
184 if sh_name != ".text" {
185 continue;
186 }
187
188 assert!(segment.is_none());
189 section.sh_addr.set(e, code_region.0 as u64);
191 let sh_offset = section.sh_offset.get(e);
192 let sh_size = section.sh_size.get(e);
193 segment = Some((sh_offset, sh_size));
194 }
195
196 let ph_off = bytes.len();
198 let e_phentsize = size_of::<ProgramHeader64<E>>();
199 let e_phnum = 1;
200 bytes.resize(ph_off + e_phentsize * e_phnum, 0);
201 if let Some((sh_offset, sh_size)) = segment {
202 let (v_offset, size) = code_region;
203 let program: &mut ProgramHeader64<E> =
204 unsafe { &mut *(bytes.as_ptr().add(ph_off) as *mut ProgramHeader64<_>) };
205 program.p_type.set(e, PT_LOAD);
206 program.p_offset.set(e, sh_offset);
207 program.p_vaddr.set(e, v_offset as u64);
208 program.p_paddr.set(e, v_offset as u64);
209 program.p_filesz.set(e, sh_size as u64);
210 program.p_memsz.set(e, size as u64);
211 } else {
212 unreachable!();
213 }
214
215 let header: &mut FileHeader64<E> =
217 unsafe { &mut *(bytes.as_mut_ptr() as *mut FileHeader64<_>) };
218 header.e_type.set(e, ET_DYN);
219 header.e_phoff.set(e, ph_off as u64);
220 header.e_phentsize.set(e, e_phentsize as u16);
221 header.e_phnum.set(e, e_phnum as u16);
222}