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