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