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