1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
use elf_rs::{ Elf, Elf64, GenElf, SectionHeader64, }; use std::{ collections::HashMap, fs::{ self, File, OpenOptions, }, io::{ Seek, SeekFrom, Write, }, mem, slice, str, }; use super::Result; const ELF_ENDIAN_LITTLE: u8 = 1; const ELF_MACHINE_BPF: u16 = 247; const SHT_PROGBITS: u32 = 0x1; const SHN_UNDEF: u16 = 0; #[repr(C)] struct Sym64 { st_name: u32, st_info: u8, st_other: u8, st_shndx: u16, st_value: u64, st_size: u64, } #[repr(C)] struct Rel64 { r_offset: u64, r_info: u64, } impl Rel64 { fn sym_idx(&self) -> usize { (self.r_info >> 32) as usize } } #[repr(C)] pub struct Insn { opcode: u8, srcdst: u8, offset: u16, imm: u32, } fn get_elems<'a, T>(elf: &'a Elf64, header: &SectionHeader64) -> &'a [T] { let off = header.sh_offset as isize; let num = header.sh_size as usize / mem::size_of::<T>(); let ptr = elf.as_bytes() as *const _ as *const u8; unsafe { slice::from_raw_parts(ptr.offset(off) as *const T, num) } } fn get_str(strtab: &[u8], start: usize) -> &str { let len = strtab[start..].iter().position(|&x| x == b'\0').unwrap(); unsafe { str::from_utf8_unchecked(&strtab[start..start + len]) } } fn get_section_name<'a>(elf: &'a Elf64, header: &SectionHeader64) -> &'a str { let strtab = elf.shstr_section(); let start = header.sh_name as usize; get_str(strtab, start) } #[derive(Debug)] pub struct RelSym { pub insn_idx: usize, pub file_off: u64, pub value: u64, pub name: String, } fn get_relsym( rel: &Rel64, sym: &Sym64, strtab: &[u8], insns: &[Insn], insns_off: usize ) -> RelSym { let idx = rel.r_offset as usize / mem::size_of::<Insn>(); let val = u64::from(insns[idx + 1].imm) << 32 | u64::from(insns[idx].imm); RelSym { insn_idx: idx, file_off: insns_off as u64 + rel.r_offset, value: val, name: String::from(get_str(strtab, sym.st_name as usize)), } } pub fn get_relsyms(path: &str, sec_name: &str) -> Result<Vec<RelSym>> { let content = fs::read(path)?; let elf: Elf64 = match Elf::from_bytes(&content)? { Elf::Elf64(e) => e, _ => return Err(error!("invalid elf type")), }; let elf_header = elf.header(); if elf_header.endianness != ELF_ENDIAN_LITTLE { return Err(error!("invalid elf endianness")); } if elf_header.machine != ELF_MACHINE_BPF { return Err(error!("invalid elf machine")); } let section_headers = elf.section_headers(); let (prog_index, prog_sec) = section_headers .iter() .enumerate() .find(|(_, s)| { s.sh_type == SHT_PROGBITS && get_section_name(&elf, s) == sec_name }) .ok_or_else(|| error!("section {} not found", sec_name))?; let rel_sec = section_headers .iter() .find(|s| s.sh_info == prog_index as u32) .ok_or_else(|| error!("rel section for {} not found", sec_name))?; let sym_sec = &elf.section_headers()[rel_sec.sh_link as usize]; let str_sec = &elf.section_headers()[sym_sec.sh_link as usize]; let insns = get_elems::<Insn>(&elf, prog_sec); let rels = get_elems::<Rel64>(&elf, rel_sec); let syms = get_elems::<Sym64>(&elf, sym_sec); let strs = get_elems::<u8>(&elf, str_sec); let relsyms = rels .iter() .map(|r| (r, &syms[r.sym_idx()])) .filter(|(_, s)| s.st_shndx == SHN_UNDEF) .map(|(r, s)| { get_relsym(r, s, strs, insns, prog_sec.sh_offset as usize) }) .collect(); Ok(relsyms) } fn resolv_relsym( mut f: &File, s: &RelSym, sym_vals: &HashMap<&str, u64> ) -> Result<()> { let val = sym_vals .get(s.name.as_str()) .ok_or_else(|| error!("sym {} not fount", s.name))? .to_le_bytes(); f.seek(SeekFrom::Start(s.file_off + 4))?; f.write_all(&val[..4])?; f.seek(SeekFrom::Start(s.file_off + 12))?; f.write_all(&val[4..])?; Ok(()) } pub fn resolve_relsyms( path: &str, sec_name: &str, sym_vals: HashMap<&str, u64> ) -> Result<Vec<RelSym>> { let f = OpenOptions::new().write(true).open(path)?; for s in get_relsyms(path, sec_name)? { resolv_relsym(&f, &s, &sym_vals)?; } get_relsyms(path, sec_name) }