1 use crate::prelude::*; 2 use core::mem::size_of; 3 use object::elf::*; 4 use object::endian::{BigEndian, Endian, Endianness, LittleEndian}; 5 use object::read::elf::{FileHeader, SectionHeader}; 6 use object::{ 7 File, NativeEndian as NE, Object, ObjectSection, ObjectSymbol, RelocationEncoding, 8 RelocationKind, RelocationTarget, U64Bytes, 9 }; 10 use wasmtime_environ::obj; 11 12 pub(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 34 fn 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(|| format_err!("invalid dwarf relocations"))?; 72 loc.set(NE, value); 73 } 74 Ok(()) 75 } 76 77 fn 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: {machine:x}"); 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 122 fn 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 } 173