wasmtime_jit/
debug.rs

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    // 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
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            // 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}