relocation.rs 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259
  1. use alloc::{
  2. borrow::{Cow, ToOwned as _},
  3. collections::BTreeMap,
  4. format,
  5. string::{String, ToString},
  6. vec,
  7. vec::Vec,
  8. };
  9. use core::{mem, ops::Bound::Included, ptr};
  10. use object::SectionIndex;
  11. use crate::{
  12. btf::{
  13. fields_are_compatible, types_are_compatible, Array, Btf, BtfError, BtfMember, BtfType,
  14. IntEncoding, Struct, Union, MAX_SPEC_LEN,
  15. },
  16. generated::{
  17. bpf_core_relo, bpf_core_relo_kind::*, bpf_insn, BPF_ALU, BPF_ALU64, BPF_B, BPF_CALL,
  18. BPF_DW, BPF_H, BPF_JMP, BPF_K, BPF_LD, BPF_LDX, BPF_ST, BPF_STX, BPF_W, BTF_INT_SIGNED,
  19. },
  20. util::HashMap,
  21. Function, Object,
  22. };
  23. /// The error type returned by [`Object::relocate_btf`].
  24. #[derive(thiserror::Error, Debug)]
  25. #[error("error relocating `{section}`")]
  26. pub struct BtfRelocationError {
  27. /// The function name
  28. pub section: String,
  29. #[source]
  30. /// The original error
  31. error: RelocationError,
  32. }
  33. /// Relocation failures
  34. #[derive(thiserror::Error, Debug)]
  35. enum RelocationError {
  36. #[cfg(feature = "std")]
  37. /// I/O error
  38. #[error(transparent)]
  39. IOError(#[from] std::io::Error),
  40. /// Section not found
  41. #[error("section not found")]
  42. SectionNotFound,
  43. /// Function not found
  44. #[error("function not found")]
  45. FunctionNotFound,
  46. /// Invalid relocation access string
  47. #[error("invalid relocation access string {access_str}")]
  48. InvalidAccessString {
  49. /// The access string
  50. access_str: String,
  51. },
  52. /// Invalid instruction index referenced by relocation
  53. #[error("invalid instruction index #{index} referenced by relocation #{relocation_number}, the program contains {num_instructions} instructions")]
  54. InvalidInstructionIndex {
  55. /// The invalid instruction index
  56. index: usize,
  57. /// Number of instructions in the program
  58. num_instructions: usize,
  59. /// The relocation number
  60. relocation_number: usize,
  61. },
  62. /// Multiple candidate target types found with different memory layouts
  63. #[error("error relocating {type_name}, multiple candidate target types found with different memory layouts: {candidates:?}")]
  64. ConflictingCandidates {
  65. /// The type name
  66. type_name: String,
  67. /// The candidates
  68. candidates: Vec<String>,
  69. },
  70. /// Maximum nesting level reached evaluating candidate type
  71. #[error("maximum nesting level reached evaluating candidate type `{}`", err_type_name(.type_name))]
  72. MaximumNestingLevelReached {
  73. /// The type name
  74. type_name: Option<String>,
  75. },
  76. /// Invalid access string
  77. #[error("invalid access string `{spec}` for type `{}`: {error}", err_type_name(.type_name))]
  78. InvalidAccessIndex {
  79. /// The type name
  80. type_name: Option<String>,
  81. /// The access string
  82. spec: String,
  83. /// The index
  84. index: usize,
  85. /// The max index
  86. max_index: usize,
  87. /// The error message
  88. error: &'static str,
  89. },
  90. /// Relocation not valid for type
  91. #[error(
  92. "relocation #{relocation_number} of kind `{relocation_kind}` not valid for type `{type_kind}`: {error}"
  93. )]
  94. InvalidRelocationKindForType {
  95. /// The relocation number
  96. relocation_number: usize,
  97. /// The relocation kind
  98. relocation_kind: String,
  99. /// The type kind
  100. type_kind: String,
  101. /// The error message
  102. error: &'static str,
  103. },
  104. /// Invalid instruction referenced by relocation
  105. #[error(
  106. "instruction #{index} referenced by relocation #{relocation_number} is invalid: {error}"
  107. )]
  108. InvalidInstruction {
  109. /// The relocation number
  110. relocation_number: usize,
  111. /// The instruction index
  112. index: usize,
  113. /// The error message
  114. error: Cow<'static, str>,
  115. },
  116. #[error("applying relocation `{kind:?}` missing target BTF info for type `{type_id}` at instruction #{ins_index}")]
  117. MissingTargetDefinition {
  118. kind: RelocationKind,
  119. type_id: u32,
  120. ins_index: usize,
  121. },
  122. /// BTF error
  123. #[error("invalid BTF")]
  124. BtfError(#[from] BtfError),
  125. }
  126. fn err_type_name(name: &Option<String>) -> &str {
  127. name.as_deref().unwrap_or("[unknown name]")
  128. }
  129. #[derive(Copy, Clone, Debug)]
  130. #[repr(u32)]
  131. enum RelocationKind {
  132. FieldByteOffset = BPF_CORE_FIELD_BYTE_OFFSET,
  133. FieldByteSize = BPF_CORE_FIELD_BYTE_SIZE,
  134. FieldExists = BPF_CORE_FIELD_EXISTS,
  135. FieldSigned = BPF_CORE_FIELD_SIGNED,
  136. FieldLShift64 = BPF_CORE_FIELD_LSHIFT_U64,
  137. FieldRShift64 = BPF_CORE_FIELD_RSHIFT_U64,
  138. TypeIdLocal = BPF_CORE_TYPE_ID_LOCAL,
  139. TypeIdTarget = BPF_CORE_TYPE_ID_TARGET,
  140. TypeExists = BPF_CORE_TYPE_EXISTS,
  141. TypeSize = BPF_CORE_TYPE_SIZE,
  142. EnumVariantExists = BPF_CORE_ENUMVAL_EXISTS,
  143. EnumVariantValue = BPF_CORE_ENUMVAL_VALUE,
  144. }
  145. impl TryFrom<u32> for RelocationKind {
  146. type Error = BtfError;
  147. fn try_from(v: u32) -> Result<Self, Self::Error> {
  148. use RelocationKind::*;
  149. Ok(match v {
  150. BPF_CORE_FIELD_BYTE_OFFSET => FieldByteOffset,
  151. BPF_CORE_FIELD_BYTE_SIZE => FieldByteSize,
  152. BPF_CORE_FIELD_EXISTS => FieldExists,
  153. BPF_CORE_FIELD_SIGNED => FieldSigned,
  154. BPF_CORE_FIELD_LSHIFT_U64 => FieldLShift64,
  155. BPF_CORE_FIELD_RSHIFT_U64 => FieldRShift64,
  156. BPF_CORE_TYPE_ID_LOCAL => TypeIdLocal,
  157. BPF_CORE_TYPE_ID_TARGET => TypeIdTarget,
  158. BPF_CORE_TYPE_EXISTS => TypeExists,
  159. BPF_CORE_TYPE_SIZE => TypeSize,
  160. BPF_CORE_ENUMVAL_EXISTS => EnumVariantExists,
  161. BPF_CORE_ENUMVAL_VALUE => EnumVariantValue,
  162. kind => return Err(BtfError::InvalidRelocationKind { kind }),
  163. })
  164. }
  165. }
  166. #[derive(Debug, Copy, Clone)]
  167. pub(crate) struct Relocation {
  168. kind: RelocationKind,
  169. ins_offset: usize,
  170. type_id: u32,
  171. access_str_offset: u32,
  172. number: usize,
  173. }
  174. impl Relocation {
  175. #[allow(unused_unsafe)]
  176. pub(crate) unsafe fn parse(data: &[u8], number: usize) -> Result<Relocation, BtfError> {
  177. if mem::size_of::<bpf_core_relo>() > data.len() {
  178. return Err(BtfError::InvalidRelocationInfo);
  179. }
  180. let rel = unsafe { ptr::read_unaligned::<bpf_core_relo>(data.as_ptr() as *const _) };
  181. Ok(Relocation {
  182. kind: rel.kind.try_into()?,
  183. ins_offset: rel.insn_off as usize,
  184. type_id: rel.type_id,
  185. access_str_offset: rel.access_str_off,
  186. number,
  187. })
  188. }
  189. }
  190. impl Object {
  191. /// Relocates programs inside this object file with loaded BTF info.
  192. pub fn relocate_btf(&mut self, target_btf: &Btf) -> Result<(), BtfRelocationError> {
  193. let (local_btf, btf_ext) = match (&self.btf, &self.btf_ext) {
  194. (Some(btf), Some(btf_ext)) => (btf, btf_ext),
  195. _ => return Ok(()),
  196. };
  197. let mut candidates_cache = HashMap::<u32, Vec<Candidate>>::new();
  198. for (sec_name_off, relos) in btf_ext.relocations() {
  199. let section_name =
  200. local_btf
  201. .string_at(*sec_name_off)
  202. .map_err(|e| BtfRelocationError {
  203. section: format!("section@{sec_name_off}"),
  204. error: RelocationError::BtfError(e),
  205. })?;
  206. let (section_index, _) = self
  207. .section_infos
  208. .get(&section_name.to_string())
  209. .ok_or_else(|| BtfRelocationError {
  210. section: section_name.to_string(),
  211. error: RelocationError::SectionNotFound,
  212. })?;
  213. match relocate_btf_functions(
  214. section_index,
  215. &mut self.functions,
  216. relos,
  217. local_btf,
  218. target_btf,
  219. &mut candidates_cache,
  220. ) {
  221. Ok(_) => {}
  222. Err(error) => {
  223. return Err(BtfRelocationError {
  224. section: section_name.to_string(),
  225. error,
  226. })
  227. }
  228. }
  229. }
  230. Ok(())
  231. }
  232. }
  233. fn is_relocation_inside_function(
  234. section_index: &SectionIndex,
  235. func: &Function,
  236. rel: &Relocation,
  237. ) -> bool {
  238. if section_index.0 != func.section_index.0 {
  239. return false;
  240. }
  241. let ins_offset = rel.ins_offset / mem::size_of::<bpf_insn>();
  242. let func_offset = func.section_offset / mem::size_of::<bpf_insn>();
  243. let func_size = func.instructions.len();
  244. (func_offset..func_offset + func_size).contains(&ins_offset)
  245. }
  246. fn function_by_relocation<'a>(
  247. section_index: &SectionIndex,
  248. functions: &'a mut BTreeMap<(usize, u64), Function>,
  249. rel: &Relocation,
  250. ) -> Option<&'a mut Function> {
  251. functions
  252. .range_mut((
  253. Included(&(section_index.0, 0)),
  254. Included(&(section_index.0, u64::MAX)),
  255. ))
  256. .map(|(_, func)| func)
  257. .find(|func| is_relocation_inside_function(section_index, func, rel))
  258. }
  259. fn relocate_btf_functions<'target>(
  260. section_index: &SectionIndex,
  261. functions: &mut BTreeMap<(usize, u64), Function>,
  262. relos: &[Relocation],
  263. local_btf: &Btf,
  264. target_btf: &'target Btf,
  265. candidates_cache: &mut HashMap<u32, Vec<Candidate<'target>>>,
  266. ) -> Result<(), RelocationError> {
  267. let mut last_function_opt: Option<&mut Function> = None;
  268. for rel in relos {
  269. let function = match last_function_opt.take() {
  270. Some(func) if is_relocation_inside_function(section_index, func, rel) => func,
  271. _ => function_by_relocation(section_index, functions, rel)
  272. .ok_or(RelocationError::FunctionNotFound)?,
  273. };
  274. let instructions = &mut function.instructions;
  275. let ins_index = (rel.ins_offset - function.section_offset) / mem::size_of::<bpf_insn>();
  276. if ins_index >= instructions.len() {
  277. return Err(RelocationError::InvalidInstructionIndex {
  278. index: ins_index,
  279. num_instructions: instructions.len(),
  280. relocation_number: rel.number,
  281. });
  282. }
  283. let local_ty = local_btf.type_by_id(rel.type_id)?;
  284. let local_name = &*local_btf.type_name(local_ty)?;
  285. let access_str = &*local_btf.string_at(rel.access_str_offset)?;
  286. let local_spec = AccessSpec::new(local_btf, rel.type_id, access_str, *rel)?;
  287. let matches = match rel.kind {
  288. RelocationKind::TypeIdLocal => Vec::new(), // we don't need to look at target types to relocate this value
  289. _ => {
  290. let candidates = match candidates_cache.get(&rel.type_id) {
  291. Some(cands) => cands,
  292. None => {
  293. candidates_cache.insert(
  294. rel.type_id,
  295. find_candidates(local_ty, local_name, target_btf)?,
  296. );
  297. candidates_cache.get(&rel.type_id).unwrap()
  298. }
  299. };
  300. let mut matches = Vec::new();
  301. for candidate in candidates {
  302. if let Some(candidate_spec) = match_candidate(&local_spec, candidate)? {
  303. let comp_rel =
  304. ComputedRelocation::new(rel, &local_spec, Some(&candidate_spec))?;
  305. matches.push((candidate.name.clone(), candidate_spec, comp_rel));
  306. }
  307. }
  308. matches
  309. }
  310. };
  311. let comp_rel = if !matches.is_empty() {
  312. let mut matches = matches.into_iter();
  313. let (_, target_spec, target_comp_rel) = matches.next().unwrap();
  314. // if there's more than one candidate, make sure that they all resolve to the
  315. // same value, else the relocation is ambiguous and can't be applied
  316. let conflicts = matches
  317. .filter_map(|(cand_name, cand_spec, cand_comp_rel)| {
  318. if cand_spec.bit_offset != target_spec.bit_offset {
  319. return Some(cand_name);
  320. } else if let (Some(cand_comp_rel_target), Some(target_comp_rel_target)) = (
  321. cand_comp_rel.target.as_ref(),
  322. target_comp_rel.target.as_ref(),
  323. ) {
  324. if cand_comp_rel_target.value != target_comp_rel_target.value {
  325. return Some(cand_name);
  326. }
  327. }
  328. None
  329. })
  330. .collect::<Vec<_>>();
  331. if !conflicts.is_empty() {
  332. return Err(RelocationError::ConflictingCandidates {
  333. type_name: local_name.to_string(),
  334. candidates: conflicts,
  335. });
  336. }
  337. target_comp_rel
  338. } else {
  339. // there are no candidate matches and therefore no target_spec. This might mean
  340. // that matching failed, or that the relocation can be applied looking at local
  341. // types only (eg with EnumVariantExists, FieldExists etc)
  342. ComputedRelocation::new(rel, &local_spec, None)?
  343. };
  344. comp_rel.apply(function, rel, local_btf, target_btf)?;
  345. last_function_opt = Some(function);
  346. }
  347. Ok(())
  348. }
  349. fn flavorless_name(name: &str) -> &str {
  350. name.split_once("___").map_or(name, |x| x.0)
  351. }
  352. fn find_candidates<'target>(
  353. local_ty: &BtfType,
  354. local_name: &str,
  355. target_btf: &'target Btf,
  356. ) -> Result<Vec<Candidate<'target>>, BtfError> {
  357. let mut candidates = Vec::new();
  358. let local_name = flavorless_name(local_name);
  359. for (type_id, ty) in target_btf.types().enumerate() {
  360. if local_ty.kind() != ty.kind() {
  361. continue;
  362. }
  363. let name = &*target_btf.type_name(ty)?;
  364. if local_name != flavorless_name(name) {
  365. continue;
  366. }
  367. candidates.push(Candidate {
  368. name: name.to_owned(),
  369. btf: target_btf,
  370. _ty: ty,
  371. type_id: type_id as u32,
  372. });
  373. }
  374. Ok(candidates)
  375. }
  376. fn match_candidate<'target>(
  377. local_spec: &AccessSpec,
  378. candidate: &'target Candidate,
  379. ) -> Result<Option<AccessSpec<'target>>, RelocationError> {
  380. let mut target_spec = AccessSpec {
  381. btf: candidate.btf,
  382. root_type_id: candidate.type_id,
  383. relocation: local_spec.relocation,
  384. parts: Vec::new(),
  385. accessors: Vec::new(),
  386. bit_offset: 0,
  387. };
  388. match local_spec.relocation.kind {
  389. RelocationKind::TypeIdLocal
  390. | RelocationKind::TypeIdTarget
  391. | RelocationKind::TypeExists
  392. | RelocationKind::TypeSize => {
  393. if types_are_compatible(
  394. local_spec.btf,
  395. local_spec.root_type_id,
  396. candidate.btf,
  397. candidate.type_id,
  398. )? {
  399. Ok(Some(target_spec))
  400. } else {
  401. Ok(None)
  402. }
  403. }
  404. RelocationKind::EnumVariantExists | RelocationKind::EnumVariantValue => {
  405. let target_id = candidate.btf.resolve_type(candidate.type_id)?;
  406. let target_ty = candidate.btf.type_by_id(target_id)?;
  407. // the first accessor is guaranteed to have a name by construction
  408. let local_variant_name = local_spec.accessors[0].name.as_ref().unwrap();
  409. fn match_enum<'a>(
  410. iterator: impl Iterator<Item = (usize, u32)>,
  411. candidate: &Candidate,
  412. local_variant_name: &str,
  413. target_id: u32,
  414. mut target_spec: AccessSpec<'a>,
  415. ) -> Result<Option<AccessSpec<'a>>, RelocationError> {
  416. for (index, name_offset) in iterator {
  417. let target_variant_name = candidate.btf.string_at(name_offset)?;
  418. if flavorless_name(local_variant_name) == flavorless_name(&target_variant_name)
  419. {
  420. target_spec.parts.push(index);
  421. target_spec.accessors.push(Accessor {
  422. index,
  423. type_id: target_id,
  424. name: None,
  425. });
  426. return Ok(Some(target_spec));
  427. }
  428. }
  429. Ok(None)
  430. }
  431. match target_ty {
  432. BtfType::Enum(en) => match_enum(
  433. en.variants
  434. .iter()
  435. .map(|member| member.name_offset)
  436. .enumerate(),
  437. candidate,
  438. local_variant_name,
  439. target_id,
  440. target_spec,
  441. ),
  442. BtfType::Enum64(en) => match_enum(
  443. en.variants
  444. .iter()
  445. .map(|member| member.name_offset)
  446. .enumerate(),
  447. candidate,
  448. local_variant_name,
  449. target_id,
  450. target_spec,
  451. ),
  452. _ => Ok(None),
  453. }
  454. }
  455. RelocationKind::FieldByteOffset
  456. | RelocationKind::FieldByteSize
  457. | RelocationKind::FieldExists
  458. | RelocationKind::FieldSigned
  459. | RelocationKind::FieldLShift64
  460. | RelocationKind::FieldRShift64 => {
  461. let mut target_id = candidate.type_id;
  462. for (i, accessor) in local_spec.accessors.iter().enumerate() {
  463. target_id = candidate.btf.resolve_type(target_id)?;
  464. if accessor.name.is_some() {
  465. if let Some(next_id) = match_member(
  466. local_spec.btf,
  467. local_spec,
  468. accessor,
  469. candidate.btf,
  470. target_id,
  471. &mut target_spec,
  472. )? {
  473. target_id = next_id;
  474. } else {
  475. return Ok(None);
  476. }
  477. } else {
  478. // i = 0 is the base struct. for i > 0, we need to potentially do bounds checking
  479. if i > 0 {
  480. let target_ty = candidate.btf.type_by_id(target_id)?;
  481. let array = match target_ty {
  482. BtfType::Array(Array { array, .. }) => array,
  483. _ => return Ok(None),
  484. };
  485. let var_len = array.len == 0 && {
  486. // an array is potentially variable length if it's the last field
  487. // of the parent struct and has 0 elements
  488. let parent = target_spec.accessors.last().unwrap();
  489. let parent_ty = candidate.btf.type_by_id(parent.type_id)?;
  490. match parent_ty {
  491. BtfType::Struct(s) => parent.index == s.members.len() - 1,
  492. _ => false,
  493. }
  494. };
  495. if !var_len && accessor.index >= array.len as usize {
  496. return Ok(None);
  497. }
  498. target_id = candidate.btf.resolve_type(array.element_type)?;
  499. }
  500. if target_spec.parts.len() == MAX_SPEC_LEN {
  501. return Err(RelocationError::MaximumNestingLevelReached {
  502. type_name: Some(candidate.name.clone()),
  503. });
  504. }
  505. target_spec.parts.push(accessor.index);
  506. target_spec.accessors.push(Accessor {
  507. index: accessor.index,
  508. type_id: target_id,
  509. name: None,
  510. });
  511. target_spec.bit_offset +=
  512. accessor.index * candidate.btf.type_size(target_id)? * 8;
  513. }
  514. }
  515. Ok(Some(target_spec))
  516. }
  517. }
  518. }
  519. fn match_member<'target>(
  520. local_btf: &Btf,
  521. local_spec: &AccessSpec<'_>,
  522. local_accessor: &Accessor,
  523. target_btf: &'target Btf,
  524. target_id: u32,
  525. target_spec: &mut AccessSpec<'target>,
  526. ) -> Result<Option<u32>, RelocationError> {
  527. let local_ty = local_btf.type_by_id(local_accessor.type_id)?;
  528. let local_member = match local_ty {
  529. // this won't panic, bounds are checked when local_spec is built in AccessSpec::new
  530. BtfType::Struct(s) => s.members.get(local_accessor.index).unwrap(),
  531. BtfType::Union(u) => u.members.get(local_accessor.index).unwrap(),
  532. local_ty => panic!("unexpected type {:?}", local_ty),
  533. };
  534. let local_name = &*local_btf.string_at(local_member.name_offset)?;
  535. let target_id = target_btf.resolve_type(target_id)?;
  536. let target_ty = target_btf.type_by_id(target_id)?;
  537. let target_members: Vec<&BtfMember> = match target_ty.members() {
  538. Some(members) => members.collect(),
  539. // not a fields type, no match
  540. None => return Ok(None),
  541. };
  542. for (index, target_member) in target_members.iter().enumerate() {
  543. if target_spec.parts.len() == MAX_SPEC_LEN {
  544. let root_ty = target_spec.btf.type_by_id(target_spec.root_type_id)?;
  545. return Err(RelocationError::MaximumNestingLevelReached {
  546. type_name: target_spec.btf.err_type_name(root_ty),
  547. });
  548. }
  549. // this will not panic as we've already established these are fields types
  550. let bit_offset = target_ty.member_bit_offset(target_member).unwrap();
  551. let target_name = &*target_btf.string_at(target_member.name_offset)?;
  552. if target_name.is_empty() {
  553. let ret = match_member(
  554. local_btf,
  555. local_spec,
  556. local_accessor,
  557. target_btf,
  558. target_member.btf_type,
  559. target_spec,
  560. )?;
  561. if ret.is_some() {
  562. target_spec.bit_offset += bit_offset;
  563. target_spec.parts.push(index);
  564. return Ok(ret);
  565. }
  566. } else if local_name == target_name {
  567. if fields_are_compatible(
  568. local_spec.btf,
  569. local_member.btf_type,
  570. target_btf,
  571. target_member.btf_type,
  572. )? {
  573. target_spec.bit_offset += bit_offset;
  574. target_spec.parts.push(index);
  575. target_spec.accessors.push(Accessor {
  576. type_id: target_id,
  577. index,
  578. name: Some(target_name.to_owned()),
  579. });
  580. return Ok(Some(target_member.btf_type));
  581. } else {
  582. return Ok(None);
  583. }
  584. }
  585. }
  586. Ok(None)
  587. }
  588. #[derive(Debug)]
  589. struct AccessSpec<'a> {
  590. btf: &'a Btf,
  591. root_type_id: u32,
  592. parts: Vec<usize>,
  593. accessors: Vec<Accessor>,
  594. relocation: Relocation,
  595. bit_offset: usize,
  596. }
  597. impl<'a> AccessSpec<'a> {
  598. fn new(
  599. btf: &'a Btf,
  600. root_type_id: u32,
  601. spec: &str,
  602. relocation: Relocation,
  603. ) -> Result<AccessSpec<'a>, RelocationError> {
  604. let parts = spec
  605. .split(':')
  606. .map(|s| s.parse::<usize>())
  607. .collect::<Result<Vec<_>, _>>()
  608. .map_err(|_| RelocationError::InvalidAccessString {
  609. access_str: spec.to_string(),
  610. })?;
  611. let mut type_id = btf.resolve_type(root_type_id)?;
  612. let ty = btf.type_by_id(type_id)?;
  613. let spec = match relocation.kind {
  614. RelocationKind::TypeIdLocal
  615. | RelocationKind::TypeIdTarget
  616. | RelocationKind::TypeExists
  617. | RelocationKind::TypeSize => {
  618. if parts != [0] {
  619. return Err(RelocationError::InvalidAccessString {
  620. access_str: spec.to_string(),
  621. });
  622. }
  623. AccessSpec {
  624. btf,
  625. root_type_id,
  626. relocation,
  627. parts,
  628. accessors: Vec::new(),
  629. bit_offset: 0,
  630. }
  631. }
  632. RelocationKind::EnumVariantExists | RelocationKind::EnumVariantValue => match ty {
  633. BtfType::Enum(_) | BtfType::Enum64(_) => {
  634. if parts.len() != 1 {
  635. return Err(RelocationError::InvalidAccessString {
  636. access_str: spec.to_string(),
  637. });
  638. }
  639. let index = parts[0];
  640. let (n_variants, name_offset) = match ty {
  641. BtfType::Enum(en) => (
  642. en.variants.len(),
  643. en.variants.get(index).map(|v| v.name_offset),
  644. ),
  645. BtfType::Enum64(en) => (
  646. en.variants.len(),
  647. en.variants.get(index).map(|v| v.name_offset),
  648. ),
  649. _ => unreachable!(),
  650. };
  651. if name_offset.is_none() {
  652. return Err(RelocationError::InvalidAccessIndex {
  653. type_name: btf.err_type_name(ty),
  654. spec: spec.to_string(),
  655. index,
  656. max_index: n_variants,
  657. error: "tried to access nonexistant enum variant",
  658. });
  659. }
  660. let accessors = vec![Accessor {
  661. type_id,
  662. index,
  663. name: Some(btf.string_at(name_offset.unwrap())?.to_string()),
  664. }];
  665. AccessSpec {
  666. btf,
  667. root_type_id,
  668. relocation,
  669. parts,
  670. accessors,
  671. bit_offset: 0,
  672. }
  673. }
  674. _ => {
  675. return Err(RelocationError::InvalidRelocationKindForType {
  676. relocation_number: relocation.number,
  677. relocation_kind: format!("{:?}", relocation.kind),
  678. type_kind: format!("{:?}", ty.kind()),
  679. error: "enum relocation on non-enum type",
  680. })
  681. }
  682. },
  683. RelocationKind::FieldByteOffset
  684. | RelocationKind::FieldByteSize
  685. | RelocationKind::FieldExists
  686. | RelocationKind::FieldSigned
  687. | RelocationKind::FieldLShift64
  688. | RelocationKind::FieldRShift64 => {
  689. let mut accessors = vec![Accessor {
  690. type_id,
  691. index: parts[0],
  692. name: None,
  693. }];
  694. let mut bit_offset = accessors[0].index * btf.type_size(type_id)?;
  695. for index in parts.iter().skip(1).cloned() {
  696. type_id = btf.resolve_type(type_id)?;
  697. let ty = btf.type_by_id(type_id)?;
  698. match ty {
  699. BtfType::Struct(Struct { members, .. })
  700. | BtfType::Union(Union { members, .. }) => {
  701. if index >= members.len() {
  702. return Err(RelocationError::InvalidAccessIndex {
  703. type_name: btf.err_type_name(ty),
  704. spec: spec.to_string(),
  705. index,
  706. max_index: members.len(),
  707. error: "out of bounds struct or union access",
  708. });
  709. }
  710. let member = &members[index];
  711. bit_offset += ty.member_bit_offset(member).unwrap();
  712. if member.name_offset != 0 {
  713. accessors.push(Accessor {
  714. type_id,
  715. index,
  716. name: Some(btf.string_at(member.name_offset)?.to_string()),
  717. });
  718. }
  719. type_id = member.btf_type;
  720. }
  721. BtfType::Array(Array { array, .. }) => {
  722. type_id = btf.resolve_type(array.element_type)?;
  723. let var_len = array.len == 0 && {
  724. // an array is potentially variable length if it's the last field
  725. // of the parent struct and has 0 elements
  726. let parent = accessors.last().unwrap();
  727. let parent_ty = btf.type_by_id(parent.type_id)?;
  728. match parent_ty {
  729. BtfType::Struct(s) => index == s.members.len() - 1,
  730. _ => false,
  731. }
  732. };
  733. if !var_len && index >= array.len as usize {
  734. return Err(RelocationError::InvalidAccessIndex {
  735. type_name: btf.err_type_name(ty),
  736. spec: spec.to_string(),
  737. index,
  738. max_index: array.len as usize,
  739. error: "array index out of bounds",
  740. });
  741. }
  742. accessors.push(Accessor {
  743. type_id,
  744. index,
  745. name: None,
  746. });
  747. let size = btf.type_size(type_id)?;
  748. bit_offset += index * size * 8;
  749. }
  750. rel_kind => {
  751. return Err(RelocationError::InvalidRelocationKindForType {
  752. relocation_number: relocation.number,
  753. relocation_kind: format!("{rel_kind:?}"),
  754. type_kind: format!("{:?}", ty.kind()),
  755. error: "field relocation on a type that doesn't have fields",
  756. });
  757. }
  758. };
  759. }
  760. AccessSpec {
  761. btf,
  762. root_type_id,
  763. parts,
  764. accessors,
  765. relocation,
  766. bit_offset,
  767. }
  768. }
  769. };
  770. Ok(spec)
  771. }
  772. }
  773. #[derive(Debug)]
  774. struct Accessor {
  775. type_id: u32,
  776. index: usize,
  777. name: Option<String>,
  778. }
  779. #[derive(Debug)]
  780. struct Candidate<'a> {
  781. name: String,
  782. btf: &'a Btf,
  783. _ty: &'a BtfType,
  784. type_id: u32,
  785. }
  786. #[derive(Debug)]
  787. struct ComputedRelocation {
  788. local: ComputedRelocationValue,
  789. target: Option<ComputedRelocationValue>,
  790. }
  791. #[derive(Debug)]
  792. struct ComputedRelocationValue {
  793. value: u64,
  794. size: u32,
  795. type_id: Option<u32>,
  796. }
  797. fn poison_insn(ins: &mut bpf_insn) {
  798. ins.code = (BPF_JMP | BPF_CALL) as u8;
  799. ins.set_dst_reg(0);
  800. ins.set_src_reg(0);
  801. ins.off = 0;
  802. ins.imm = 0xBAD2310;
  803. }
  804. impl ComputedRelocation {
  805. fn new(
  806. rel: &Relocation,
  807. local_spec: &AccessSpec,
  808. target_spec: Option<&AccessSpec>,
  809. ) -> Result<ComputedRelocation, RelocationError> {
  810. use RelocationKind::*;
  811. let ret = match rel.kind {
  812. FieldByteOffset | FieldByteSize | FieldExists | FieldSigned | FieldLShift64
  813. | FieldRShift64 => ComputedRelocation {
  814. local: Self::compute_field_relocation(rel, Some(local_spec))?,
  815. target: Self::compute_field_relocation(rel, target_spec).ok(),
  816. },
  817. TypeIdLocal | TypeIdTarget | TypeExists | TypeSize => ComputedRelocation {
  818. local: Self::compute_type_relocation(rel, local_spec, target_spec)?,
  819. target: Self::compute_type_relocation(rel, local_spec, target_spec).ok(),
  820. },
  821. EnumVariantExists | EnumVariantValue => ComputedRelocation {
  822. local: Self::compute_enum_relocation(rel, Some(local_spec))?,
  823. target: Self::compute_enum_relocation(rel, target_spec).ok(),
  824. },
  825. };
  826. Ok(ret)
  827. }
  828. fn apply(
  829. &self,
  830. function: &mut Function,
  831. rel: &Relocation,
  832. local_btf: &Btf,
  833. target_btf: &Btf,
  834. ) -> Result<(), RelocationError> {
  835. let instructions = &mut function.instructions;
  836. let num_instructions = instructions.len();
  837. let ins_index = (rel.ins_offset - function.section_offset) / mem::size_of::<bpf_insn>();
  838. let ins =
  839. instructions
  840. .get_mut(ins_index)
  841. .ok_or(RelocationError::InvalidInstructionIndex {
  842. index: rel.ins_offset,
  843. num_instructions,
  844. relocation_number: rel.number,
  845. })?;
  846. let target = if let Some(target) = self.target.as_ref() {
  847. target
  848. } else {
  849. let is_ld_imm64 = ins.code == (BPF_LD | BPF_DW) as u8;
  850. poison_insn(ins);
  851. if is_ld_imm64 {
  852. let next_ins = instructions.get_mut(ins_index + 1).ok_or(
  853. RelocationError::InvalidInstructionIndex {
  854. index: (ins_index + 1) * mem::size_of::<bpf_insn>(),
  855. num_instructions,
  856. relocation_number: rel.number,
  857. },
  858. )?;
  859. poison_insn(next_ins);
  860. }
  861. return Ok(());
  862. };
  863. let class = (ins.code & 0x07) as u32;
  864. let target_value = target.value;
  865. match class {
  866. BPF_ALU | BPF_ALU64 => {
  867. let src_reg = ins.src_reg();
  868. if src_reg != BPF_K as u8 {
  869. return Err(RelocationError::InvalidInstruction {
  870. relocation_number: rel.number,
  871. index: ins_index,
  872. error: format!("invalid src_reg={src_reg:x} expected {BPF_K:x}").into(),
  873. });
  874. }
  875. ins.imm = target_value as i32;
  876. }
  877. BPF_LDX | BPF_ST | BPF_STX => {
  878. if target_value > i16::MAX as u64 {
  879. return Err(RelocationError::InvalidInstruction {
  880. relocation_number: rel.number,
  881. index: ins_index,
  882. error: format!("value `{target_value}` overflows 16 bits offset field")
  883. .into(),
  884. });
  885. }
  886. ins.off = target_value as i16;
  887. if self.local.size != target.size {
  888. let local_ty = local_btf.type_by_id(self.local.type_id.unwrap())?;
  889. let target_ty = target_btf.type_by_id(target.type_id.unwrap())?;
  890. let unsigned = |info: u32| ((info >> 24) & 0x0F) & BTF_INT_SIGNED == 0;
  891. use BtfType::*;
  892. match (local_ty, target_ty) {
  893. (Ptr(_), Ptr(_)) => {}
  894. (Int(local), Int(target))
  895. if unsigned(local.data) && unsigned(target.data) => {}
  896. _ => {
  897. return Err(RelocationError::InvalidInstruction {
  898. relocation_number: rel.number,
  899. index: ins_index,
  900. error: format!(
  901. "original type {} has size {} but target type {} has size {}",
  902. err_type_name(&local_btf.err_type_name(local_ty)),
  903. self.local.size,
  904. err_type_name(&target_btf.err_type_name(target_ty)),
  905. target.size,
  906. )
  907. .into(),
  908. })
  909. }
  910. }
  911. let size = match target.size {
  912. 8 => BPF_DW,
  913. 4 => BPF_W,
  914. 2 => BPF_H,
  915. 1 => BPF_B,
  916. size => {
  917. return Err(RelocationError::InvalidInstruction {
  918. relocation_number: rel.number,
  919. index: ins_index,
  920. error: format!("invalid target size {size}").into(),
  921. })
  922. }
  923. } as u8;
  924. ins.code = ins.code & 0xE0 | size | ins.code & 0x07;
  925. }
  926. }
  927. BPF_LD => {
  928. ins.imm = target_value as i32;
  929. let next_ins = instructions.get_mut(ins_index + 1).ok_or(
  930. RelocationError::InvalidInstructionIndex {
  931. index: ins_index + 1,
  932. num_instructions,
  933. relocation_number: rel.number,
  934. },
  935. )?;
  936. next_ins.imm = (target_value >> 32) as i32;
  937. }
  938. class => {
  939. return Err(RelocationError::InvalidInstruction {
  940. relocation_number: rel.number,
  941. index: ins_index,
  942. error: format!("invalid instruction class {class:x}").into(),
  943. })
  944. }
  945. };
  946. Ok(())
  947. }
  948. fn compute_enum_relocation(
  949. rel: &Relocation,
  950. spec: Option<&AccessSpec>,
  951. ) -> Result<ComputedRelocationValue, RelocationError> {
  952. use RelocationKind::*;
  953. let value = match (rel.kind, spec) {
  954. (EnumVariantExists, spec) => spec.is_some() as u64,
  955. (EnumVariantValue, Some(spec)) => {
  956. let accessor = &spec.accessors[0];
  957. match spec.btf.type_by_id(accessor.type_id)? {
  958. BtfType::Enum(en) => {
  959. let value = en.variants[accessor.index].value;
  960. if en.is_signed() {
  961. value as i32 as u64
  962. } else {
  963. value as u64
  964. }
  965. }
  966. BtfType::Enum64(en) => {
  967. let variant = &en.variants[accessor.index];
  968. ((variant.value_high as u64) << 32) | variant.value_low as u64
  969. }
  970. // candidate selection ensures that rel_kind == local_kind == target_kind
  971. _ => unreachable!(),
  972. }
  973. }
  974. _ => {
  975. return Err(RelocationError::MissingTargetDefinition {
  976. kind: rel.kind,
  977. type_id: rel.type_id,
  978. ins_index: rel.ins_offset / mem::size_of::<bpf_insn>(),
  979. })?;
  980. }
  981. };
  982. Ok(ComputedRelocationValue {
  983. value,
  984. size: 0,
  985. type_id: None,
  986. })
  987. }
  988. fn compute_field_relocation(
  989. rel: &Relocation,
  990. spec: Option<&AccessSpec>,
  991. ) -> Result<ComputedRelocationValue, RelocationError> {
  992. use RelocationKind::*;
  993. if let FieldExists = rel.kind {
  994. // this is the bpf_preserve_field_info(member_access, FIELD_EXISTENCE) case. If we
  995. // managed to build a spec, it means the field exists.
  996. return Ok(ComputedRelocationValue {
  997. value: spec.is_some() as u64,
  998. size: 0,
  999. type_id: None,
  1000. });
  1001. }
  1002. let spec = match spec {
  1003. Some(spec) => spec,
  1004. None => {
  1005. return Err(RelocationError::MissingTargetDefinition {
  1006. kind: rel.kind,
  1007. type_id: rel.type_id,
  1008. ins_index: rel.ins_offset / mem::size_of::<bpf_insn>(),
  1009. })?;
  1010. }
  1011. };
  1012. let accessor = spec.accessors.last().unwrap();
  1013. if accessor.name.is_none() {
  1014. // the last accessor is unnamed, meaning that this is an array access
  1015. return match rel.kind {
  1016. FieldByteOffset => Ok(ComputedRelocationValue {
  1017. value: (spec.bit_offset / 8) as u64,
  1018. size: spec.btf.type_size(accessor.type_id)? as u32,
  1019. type_id: Some(accessor.type_id),
  1020. }),
  1021. FieldByteSize => Ok(ComputedRelocationValue {
  1022. value: spec.btf.type_size(accessor.type_id)? as u64,
  1023. size: 0,
  1024. type_id: Some(accessor.type_id),
  1025. }),
  1026. rel_kind => {
  1027. let ty = spec.btf.type_by_id(accessor.type_id)?;
  1028. return Err(RelocationError::InvalidRelocationKindForType {
  1029. relocation_number: rel.number,
  1030. relocation_kind: format!("{rel_kind:?}"),
  1031. type_kind: format!("{:?}", ty.kind()),
  1032. error: "invalid relocation kind for array type",
  1033. });
  1034. }
  1035. };
  1036. }
  1037. let ty = spec.btf.type_by_id(accessor.type_id)?;
  1038. let (ll_ty, member) = match ty {
  1039. BtfType::Struct(t) => (ty, t.members.get(accessor.index).unwrap()),
  1040. BtfType::Union(t) => (ty, t.members.get(accessor.index).unwrap()),
  1041. _ => {
  1042. return Err(RelocationError::InvalidRelocationKindForType {
  1043. relocation_number: rel.number,
  1044. relocation_kind: format!("{:?}", rel.kind),
  1045. type_kind: format!("{:?}", ty.kind()),
  1046. error: "field relocation on a type that doesn't have fields",
  1047. });
  1048. }
  1049. };
  1050. let bit_off = spec.bit_offset as u32;
  1051. let member_type_id = spec.btf.resolve_type(member.btf_type)?;
  1052. let member_ty = spec.btf.type_by_id(member_type_id)?;
  1053. let mut byte_size;
  1054. let mut byte_off;
  1055. let mut bit_size = ll_ty.member_bit_field_size(member).unwrap() as u32;
  1056. let is_bitfield = bit_size > 0;
  1057. if is_bitfield {
  1058. // find out the smallest int size to load the bitfield
  1059. byte_size = member_ty.size().unwrap();
  1060. byte_off = bit_off / 8 / byte_size * byte_size;
  1061. while bit_off + bit_size - byte_off * 8 > byte_size * 8 {
  1062. if byte_size >= 8 {
  1063. // the bitfield is larger than 8 bytes!?
  1064. return Err(BtfError::InvalidTypeInfo.into());
  1065. }
  1066. byte_size *= 2;
  1067. byte_off = bit_off / 8 / byte_size * byte_size;
  1068. }
  1069. } else {
  1070. byte_size = spec.btf.type_size(member_type_id)? as u32;
  1071. bit_size = byte_size * 8;
  1072. byte_off = spec.bit_offset as u32 / 8;
  1073. }
  1074. let mut value = ComputedRelocationValue {
  1075. value: 0,
  1076. size: 0,
  1077. type_id: None,
  1078. };
  1079. #[allow(clippy::wildcard_in_or_patterns)]
  1080. match rel.kind {
  1081. FieldByteOffset => {
  1082. value.value = byte_off as u64;
  1083. if !is_bitfield {
  1084. value.size = byte_size;
  1085. value.type_id = Some(member_type_id);
  1086. }
  1087. }
  1088. FieldByteSize => {
  1089. value.value = byte_size as u64;
  1090. }
  1091. FieldSigned => match member_ty {
  1092. BtfType::Enum(en) => value.value = en.is_signed() as u64,
  1093. BtfType::Enum64(en) => value.value = en.is_signed() as u64,
  1094. BtfType::Int(i) => value.value = i.encoding() as u64 & IntEncoding::Signed as u64,
  1095. _ => (),
  1096. },
  1097. #[cfg(target_endian = "little")]
  1098. FieldLShift64 => {
  1099. value.value = 64 - (bit_off + bit_size - byte_off * 8) as u64;
  1100. }
  1101. #[cfg(target_endian = "big")]
  1102. FieldLShift64 => {
  1103. value.value = ((8 - byte_size) * 8 + (bit_off - byte_off * 8)) as u64;
  1104. }
  1105. FieldRShift64 => {
  1106. value.value = 64 - bit_size as u64;
  1107. }
  1108. kind @ (FieldExists | TypeIdLocal | TypeIdTarget | TypeExists | TypeSize
  1109. | EnumVariantExists | EnumVariantValue) => {
  1110. panic!("unexpected relocation kind {:?}", kind)
  1111. }
  1112. }
  1113. Ok(value)
  1114. }
  1115. fn compute_type_relocation(
  1116. rel: &Relocation,
  1117. local_spec: &AccessSpec,
  1118. target_spec: Option<&AccessSpec>,
  1119. ) -> Result<ComputedRelocationValue, RelocationError> {
  1120. use RelocationKind::*;
  1121. let value = match (rel.kind, target_spec) {
  1122. (TypeIdLocal, _) => local_spec.root_type_id as u64,
  1123. (TypeIdTarget, Some(target_spec)) => target_spec.root_type_id as u64,
  1124. (TypeExists, target_spec) => target_spec.is_some() as u64,
  1125. (TypeSize, Some(target_spec)) => {
  1126. target_spec.btf.type_size(target_spec.root_type_id)? as u64
  1127. }
  1128. _ => {
  1129. return Err(RelocationError::MissingTargetDefinition {
  1130. kind: rel.kind,
  1131. type_id: rel.type_id,
  1132. ins_index: rel.ins_offset / mem::size_of::<bpf_insn>(),
  1133. })?;
  1134. }
  1135. };
  1136. Ok(ComputedRelocationValue {
  1137. value,
  1138. size: 0,
  1139. type_id: None,
  1140. })
  1141. }
  1142. }