wasmtime/runtime/
debug.rs

1use 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    // 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
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            // Patch vaddr, and save file location and its size.
139            section.sh_addr.set(e, code_region.0 as u64);
140            range
141        }
142        None => None,
143    };
144
145    // LLDB wants segment with virtual address set, placing them at the end of ELF.
146    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    // It is somewhat loadable ELF file at this moment.
165    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}