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