wasmtime/runtime/
debug.rs1use crate::prelude::*;
2use core::mem::size_of;
3use object::elf::*;
4use object::endian::{BigEndian, Endian, Endianness, LittleEndian};
5use object::read::elf::{FileHeader, SectionHeader};
6use object::{
7 File, NativeEndian as NE, Object, ObjectSection, ObjectSymbol, RelocationEncoding,
8 RelocationKind, RelocationTarget, U64Bytes,
9};
10use wasmtime_environ::obj;
11
12pub(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 relocate_dwarf_sections(&mut bytes, code_region)?;
20
21 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
34fn 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[..]).map_err(obj::ObjectCrateErrorWrapper)?;
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, _) = offset
68 .try_into()
69 .ok()
70 .and_then(|offset| object::from_bytes_mut::<U64Bytes<NE>>(&mut bytes[offset..]).ok())
71 .ok_or_else(|| anyhow!("invalid dwarf relocations"))?;
72 loc.set(NE, value);
73 }
74 Ok(())
75}
76
77fn ensure_supported_elf_format(bytes: &[u8]) -> Result<Endianness, Error> {
78 use object::elf::*;
79 use object::read::elf::*;
80
81 let kind = match object::FileKind::parse(bytes) {
82 Ok(file) => file,
83 Err(err) => {
84 bail!("Failed to parse file: {}", err);
85 }
86 };
87 let header = match kind {
88 object::FileKind::Elf64 => match object::elf::FileHeader64::<Endianness>::parse(bytes) {
89 Ok(header) => header,
90 Err(err) => {
91 bail!("Unsupported ELF file: {}", err);
92 }
93 },
94 _ => {
95 bail!("only 64-bit ELF files currently supported")
96 }
97 };
98 let e = header.endian().unwrap();
99
100 match header.e_machine.get(e) {
101 EM_AARCH64 => (),
102 EM_X86_64 => (),
103 EM_S390 => (),
104 EM_RISCV => (),
105 machine => {
106 bail!("Unsupported ELF target machine: {:x}", machine);
107 }
108 }
109 ensure!(
110 header.e_phoff.get(e) == 0 && header.e_phnum.get(e) == 0,
111 "program header table is empty"
112 );
113 let e_shentsize = header.e_shentsize.get(e);
114 let req_shentsize = match e {
115 Endianness::Little => size_of::<SectionHeader64<LittleEndian>>(),
116 Endianness::Big => size_of::<SectionHeader64<BigEndian>>(),
117 };
118 ensure!(e_shentsize as usize == req_shentsize, "size of sh");
119 Ok(e)
120}
121
122fn convert_object_elf_to_loadable_file<E: Endian>(
123 bytes: &mut Vec<u8>,
124 code_region: (*const u8, usize),
125) {
126 let e = E::default();
127
128 let header = FileHeader64::<E>::parse(&bytes[..]).unwrap();
129 let sections = header.sections(e, &bytes[..]).unwrap();
130 let text_range = match sections.section_by_name(e, b".text") {
131 Some((i, text)) => {
132 let range = text.file_range(e);
133 let e_shoff = usize::try_from(header.e_shoff.get(e)).unwrap();
134 let off = e_shoff + i.0 * header.e_shentsize.get(e) as usize;
135
136 let section: &mut SectionHeader64<E> =
137 object::from_bytes_mut(&mut bytes[off..]).unwrap().0;
138 section.sh_addr.set(e, code_region.0 as u64);
140 range
141 }
142 None => None,
143 };
144
145 let ph_off = bytes.len();
147 let e_phentsize = size_of::<ProgramHeader64<E>>();
148 let e_phnum = 1;
149 bytes.resize(ph_off + e_phentsize * e_phnum, 0);
150 if let Some((sh_offset, sh_size)) = text_range {
151 let (v_offset, size) = code_region;
152 let program: &mut ProgramHeader64<E> =
153 object::from_bytes_mut(&mut bytes[ph_off..]).unwrap().0;
154 program.p_type.set(e, PT_LOAD);
155 program.p_offset.set(e, sh_offset);
156 program.p_vaddr.set(e, v_offset as u64);
157 program.p_paddr.set(e, v_offset as u64);
158 program.p_filesz.set(e, sh_size);
159 program.p_memsz.set(e, size as u64);
160 } else {
161 unreachable!();
162 }
163
164 let header: &mut FileHeader64<E> = object::from_bytes_mut(bytes).unwrap().0;
166 header.e_type.set(e, ET_DYN);
167 header.e_phoff.set(e, ph_off as u64);
168 header
169 .e_phentsize
170 .set(e, u16::try_from(e_phentsize).unwrap());
171 header.e_phnum.set(e, u16::try_from(e_phnum).unwrap());
172}