sbpf_disassembler/
relocation.rs1use {
2 crate::errors::DisassemblerError,
3 object::{Endianness, Object, ObjectSection, read::elf::ElfFile64},
4 serde::{Deserialize, Serialize},
5};
6
7#[allow(non_camel_case_types)]
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9#[repr(u32)]
10pub enum RelocationType {
11 R_BPF_NONE = 0x00, R_BPF_64_64 = 0x01, R_BPF_64_RELATIVE = 0x08, R_BPF_64_32 = 0x0a, }
16
17impl TryFrom<u32> for RelocationType {
18 type Error = DisassemblerError;
19
20 fn try_from(value: u32) -> Result<Self, Self::Error> {
21 Ok(match value {
22 0x00 => Self::R_BPF_NONE,
23 0x01 => Self::R_BPF_64_64,
24 0x08 => Self::R_BPF_64_RELATIVE,
25 0x0a => Self::R_BPF_64_32,
26 _ => return Err(DisassemblerError::InvalidDataLength),
27 })
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct Relocation {
33 pub offset: u64,
34 pub rel_type: RelocationType,
35 pub symbol_index: u32,
36 pub symbol_name: Option<String>,
37}
38
39impl Relocation {
40 pub fn from_elf_file(elf_file: &ElfFile64<Endianness>) -> Result<Vec<Self>, DisassemblerError> {
42 let rel_dyn_section = match elf_file.section_by_name(".rel.dyn") {
44 Some(s) => s,
45 None => return Ok(Vec::new()),
46 };
47
48 let rel_dyn_data = rel_dyn_section
49 .data()
50 .map_err(|_| DisassemblerError::InvalidDataLength)?;
51
52 let dynsym_data = elf_file
54 .section_by_name(".dynsym")
55 .and_then(|s| s.data().ok());
56 let dynstr_data = elf_file
57 .section_by_name(".dynstr")
58 .and_then(|s| s.data().ok());
59
60 let mut relocations = Vec::new();
61
62 for chunk in rel_dyn_data.chunks_exact(16) {
64 let offset = u64::from_le_bytes(chunk[0..8].try_into().unwrap());
65 let rel_type_val = u32::from_le_bytes(chunk[8..12].try_into().unwrap());
66 let rel_type = RelocationType::try_from(rel_type_val)
67 .map_err(|_| DisassemblerError::InvalidDataLength)?;
68 let symbol_index = u32::from_le_bytes(chunk[12..16].try_into().unwrap());
69
70 let symbol_name = if rel_type == RelocationType::R_BPF_64_32 {
72 match (&dynsym_data, &dynstr_data) {
73 (Some(dynsym), Some(dynstr)) => {
74 resolve_symbol_name(dynsym, dynstr, symbol_index as usize).ok()
75 }
76 _ => None,
77 }
78 } else {
79 None
80 };
81
82 relocations.push(Relocation {
83 offset,
84 rel_type,
85 symbol_index,
86 symbol_name,
87 });
88 }
89
90 Ok(relocations)
91 }
92
93 pub fn relative_offset(&self, base_offset: u64) -> u64 {
95 self.offset.saturating_sub(base_offset)
96 }
97
98 pub fn is_syscall(&self) -> bool {
100 self.rel_type == RelocationType::R_BPF_64_32
101 }
102}
103
104fn resolve_symbol_name(
106 dynsym_data: &[u8],
107 dynstr_data: &[u8],
108 symbol_index: usize,
109) -> Result<String, DisassemblerError> {
110 const DYNSYM_ENTRY_SIZE: usize = 24;
111
112 let symbol_entry_offset = symbol_index * DYNSYM_ENTRY_SIZE;
114 if symbol_entry_offset + 4 > dynsym_data.len() {
115 return Err(DisassemblerError::InvalidDataLength);
116 }
117
118 let dynstr_offset = u32::from_le_bytes(
119 dynsym_data[symbol_entry_offset..symbol_entry_offset + 4]
120 .try_into()
121 .unwrap(),
122 ) as usize;
123 if dynstr_offset >= dynstr_data.len() {
124 return Err(DisassemblerError::InvalidDynstrOffset);
125 }
126
127 let end = dynstr_data[dynstr_offset..]
129 .iter()
130 .position(|&b| b == 0)
131 .ok_or(DisassemblerError::InvalidDynstrOffset)?;
132
133 String::from_utf8(dynstr_data[dynstr_offset..dynstr_offset + end].to_vec())
134 .map_err(|_| DisassemblerError::InvalidUtf8InDynstr)
135}
136
137#[cfg(test)]
138mod tests {
139 use {super::*, hex_literal::hex, object::read::elf::ElfFile64};
140
141 const TEST_PROGRAM: &[u8] = &hex!(
150 "7F454C460201010000000000000000000300F70001000000E8000000000000004000000000000000A002000000000000000000004000380003004000070006000100000005000000E800000000000000E800000000000000E8000000000000003800000000000000380000000000000000100000000000000100000004000000C001000000000000C001000000000000C001000000000000B000000000000000B00000000000000000100000000000000200000006000000200100000000000020010000000000002001000000000000A000000000000000A0000000000000000800000000000000180100000100000000000000000000001802000002000000000000000000000085100000FFFFFFFF85100000FFFFFFFF95000000000000001E0000000000000004000000000000001100000000000000500200000000000012000000000000002000000000000000130000000000000010000000000000000600000000000000C0010000000000000B000000000000001800000000000000050000000000000020020000000000000A00000000000000300000000000000016000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000010000100E80000000000000000000000000000000C000000100000000000000000000000000000000000000018000000100000000000000000000000000000000000000000656E747279706F696E7400736F6C5F6C6F675F36345F00736F6C5F6C6F675F636F6D707574655F756E6974735F000008010000000000000A0000000200000010010000000000000A00000003000000002E74657874002E64796E616D6963002E64796E73796D002E64796E737472002E72656C2E64796E002E7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000010000000600000000000000E800000000000000E80000000000000038000000000000000000000000000000040000000000000000000000000000000700000006000000030000000000000020010000000000002001000000000000A000000000000000040000000000000008000000000000001000000000000000100000000B0000000200000000000000C001000000000000C0010000000000006000000000000000040000000100000008000000000000001800000000000000180000000300000002000000000000002002000000000000200200000000000030000000000000000000000000000000010000000000000000000000000000002000000009000000020000000000000050020000000000005002000000000000200000000000000003000000000000000800000000000000100000000000000029000000030000000000000000000000000000000000000070020000000000002C00000000000000000000000000000001000000000000000000000000000000"
151 );
152
153 #[test]
154 fn test_relocation_parsing() {
155 let elf_file = ElfFile64::<Endianness>::parse(TEST_PROGRAM).expect("Failed to parse ELF");
156 let relocations =
157 Relocation::from_elf_file(&elf_file).expect("Failed to parse relocations");
158
159 assert_eq!(relocations.len(), 2, "Expected 2 relocations");
161
162 assert!(relocations[0].is_syscall());
164 assert!(relocations[1].is_syscall());
165
166 assert_eq!(relocations[0].symbol_name.as_deref(), Some("sol_log_64_"));
168 assert_eq!(
169 relocations[1].symbol_name.as_deref(),
170 Some("sol_log_compute_units_")
171 );
172
173 assert_eq!(relocations[0].symbol_index, 2);
179 assert_eq!(relocations[1].symbol_index, 3);
180 }
181
182 #[test]
183 fn test_relocation_relative_offset() {
184 let elf_file = ElfFile64::<Endianness>::parse(TEST_PROGRAM).expect("Failed to parse ELF");
185 let relocations =
186 Relocation::from_elf_file(&elf_file).expect("Failed to parse relocations");
187
188 let text_section = elf_file
190 .section_by_name(".text")
191 .expect("Failed to find .text section");
192 let text_section_offset = text_section.address();
193
194 let rel0_offset = relocations[0].relative_offset(text_section_offset);
196 let rel1_offset = relocations[1].relative_offset(text_section_offset);
197
198 assert_eq!(rel0_offset, 0x20);
204 assert_eq!(rel1_offset, 0x28);
205 }
206
207 #[test]
208 fn test_no_relocations() {
209 let test_program = &hex!(
211 "7F454C460201010000000000000000000300F700010000002001000000000000400000000000000028020000000000000000000040003800030040000600050001000000050000002001000000000000200100000000000020010000000000003000000000000000300000000000000000100000000000000100000004000000C001000000000000C001000000000000C0010000000000003C000000000000003C000000000000000010000000000000020000000600000050010000000000005001000000000000500100000000000070000000000000007000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007912A000000000007911182900000000B7000000010000002D21010000000000B70000000000000095000000000000001E0000000000000004000000000000000600000000000000C0010000000000000B0000000000000018000000000000000500000000000000F0010000000000000A000000000000000C00000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000120001002001000000000000300000000000000000656E747279706F696E7400002E74657874002E64796E737472002E64796E73796D002E64796E616D6963002E73687374727461620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000010000000600000000000000200100000000000020010000000000003000000000000000000000000000000008000000000000000000000000000000170000000600000003000000000000005001000000000000500100000000000070000000000000000400000000000000080000000000000010000000000000000F0000000B0000000200000000000000C001000000000000C001000000000000300000000000000004000000010000000800000000000000180000000000000007000000030000000200000000000000F001000000000000F0010000000000000C00000000000000000000000000000001000000000000000000000000000000200000000300000000000000000000000000000000000000FC010000000000002A00000000000000000000000000000001000000000000000000000000000000"
212 );
213
214 let elf_file = ElfFile64::<Endianness>::parse(test_program).expect("Failed to parse ELF");
215 let relocations =
216 Relocation::from_elf_file(&elf_file).expect("Failed to parse relocations");
217
218 assert!(relocations.is_empty());
219 }
220}