1 use anyhow::{anyhow, bail, ensure, Error}; 2 use object::elf::*; 3 use object::endian::{BigEndian, Endian, Endianness, LittleEndian}; 4 use object::read::elf::{FileHeader, SectionHeader}; 5 use object::{ 6 File, NativeEndian as NE, Object, ObjectSection, ObjectSymbol, RelocationEncoding, 7 RelocationKind, RelocationTarget, U64Bytes, 8 }; 9 use std::mem::size_of; 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[..])?; 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 73 fn 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 118 fn 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 } 166