1use std::collections::HashMap;
2use std::fmt;
3use std::fmt::Write;
4
5use crate::btf_index::BtfIndex;
6use crate::types::*;
7use crate::{btf_error, BtfResult};
8
9#[derive(Debug)]
10pub struct Reloc {
11 pub sec_id: usize,
12 pub reloc_id: usize,
13 pub local_type_id: u32,
14 pub local_offset: usize,
15 pub local_spec: Vec<usize>,
16 pub targ_type_id: u32,
17 pub targ_offset: usize,
18 pub targ_spec: Vec<usize>,
19}
20
21impl fmt::Display for Reloc {
22 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23 write!(
24 f,
25 "sec#{}, r#{}: [{}] + {} ({}) --> [{}] + {} ({})",
26 self.sec_id,
27 self.reloc_id,
28 self.local_type_id,
29 self.local_offset,
30 Relocator::spec_to_str(&self.local_spec),
31 self.targ_type_id,
32 self.targ_offset,
33 Relocator::spec_to_str(&self.targ_spec),
34 )
35 }
36}
37
38#[derive(Debug)]
39enum Accessor {
40 Field {
41 type_id: u32,
42 field_idx: usize,
43 field_name: String,
44 },
45 Array {
46 type_id: u32,
47 arr_idx: usize,
48 },
49}
50
51impl fmt::Display for Accessor {
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 match self {
54 Accessor::Field {
55 type_id,
56 field_idx,
57 field_name,
58 } => write!(f, "field:[{}].#{}('{}')", type_id, field_idx, field_name),
59 Accessor::Array { type_id, arr_idx } => write!(f, "array:*[{}] + {}", type_id, arr_idx),
60 }
61 }
62}
63
64#[derive(Debug)]
65pub struct RelocatorCfg {
66 pub verbose: bool,
67}
68
69#[derive(Debug)]
70pub struct Relocator<'a, 'b> {
71 cfg: RelocatorCfg,
72 targ_btf: &'a Btf<'a>,
73 local_btf: &'b Btf<'b>,
74 targ_index: BtfIndex<'a>,
75 type_map: HashMap<u32, Vec<u32>>,
76}
77
78impl<'a, 'b> Relocator<'a, 'b> {
79 pub fn new(targ_btf: &'a Btf, local_btf: &'b Btf, cfg: RelocatorCfg) -> Relocator<'a, 'b> {
80 Relocator {
81 cfg: cfg,
82 targ_btf: targ_btf,
83 local_btf: local_btf,
84 targ_index: BtfIndex::new(targ_btf),
85 type_map: HashMap::new(),
86 }
87 }
88
89 pub fn relocate(&mut self) -> BtfResult<Vec<Reloc>> {
90 let mut relocs = Vec::new();
91 for (sec_id, sec) in self.local_btf.core_reloc_secs().iter().enumerate() {
92 for (reloc_id, rec) in sec.recs.iter().enumerate() {
93 let local_type = self.local_btf.type_by_id(rec.type_id);
94 let local_off = self.calc_off(self.local_btf, rec.type_id, &rec.access_spec)?;
95 let local_access =
96 self.transform_access(self.local_btf, rec.type_id, &rec.access_spec)?;
97 if self.cfg.verbose {
98 print!("sec#{}, r#{}: accessors = ", sec_id, reloc_id);
99 for a in &local_access {
100 print!("{}, ", a);
101 }
102 println!("");
103 }
104
105 let mut targ_off = 0;
106 let mut targ_type_id = 0;
107 let mut targ_spec = Vec::new();
108
109 let mut matched_ids = Vec::new();
110 let cand_targ_ids = if self.type_map.contains_key(&rec.type_id) {
111 self.type_map.get(&rec.type_id).unwrap()
112 } else {
113 self.targ_index.get_by_name(local_type.name())
115 };
116 for &id in cand_targ_ids {
117 if self.cfg.verbose {
118 println!("sec#{}, r#{}: matching to [{}]", sec_id, reloc_id, id);
119 }
120 match self.calc_targ_spec(&local_access, id) {
121 Ok(spec) => {
122 if self.cfg.verbose {
123 println!(
124 "sec#{}, r#{}: targ_spec: {}",
125 sec_id,
126 reloc_id,
127 Relocator::spec_to_str(&spec)
128 );
129 }
130 let off = self.calc_off(self.targ_btf, id, &spec)?;
131 if !matched_ids.is_empty() {
132 if off != targ_off {
133 btf_error(format!(
134 concat!(
135 "ambiguous offset for local type (id: {}, spec: {}),",
136 " at least 2 different target type matched",
137 " with different offsets: ",
138 "(id: {}, off: {}, spec: {}) vs ",
139 "(id: {}, off: {}, spec: {})"
140 ),
141 rec.type_id,
142 rec.access_spec_str,
143 targ_type_id,
144 targ_off,
145 Relocator::spec_to_str(&targ_spec),
146 id,
147 off,
148 Relocator::spec_to_str(&spec)
149 ))?;
150 }
151 } else {
152 targ_off = off;
153 targ_type_id = id;
154 targ_spec = spec;
155 }
156 matched_ids.push(id);
157 }
158 Err(e) => {
159 if self.cfg.verbose {
160 println!(
161 "sec#{}, r#{}: failed to match targ [{}]: {}",
162 sec_id, reloc_id, id, e
163 );
164 }
165 continue;
166 }
167 }
168 }
169 if matched_ids.is_empty() {
170 btf_error(format!("failed to find any candidate for reloc {}", rec))?;
171 }
172 self.type_map.insert(rec.type_id, matched_ids);
173 relocs.push(Reloc {
174 sec_id: sec_id,
175 reloc_id: reloc_id,
176 local_type_id: rec.type_id,
177 local_offset: local_off as usize,
178 local_spec: rec.access_spec.clone(),
179 targ_type_id: targ_type_id,
180 targ_offset: targ_off as usize,
181 targ_spec: targ_spec,
182 });
183 }
184 }
185 Ok(relocs)
186 }
187
188 fn transform_access(
189 &self,
190 btf: &Btf,
191 type_id: u32,
192 spec: &[usize],
193 ) -> BtfResult<Vec<Accessor>> {
194 let mut res = Vec::new();
195 let mut id = btf.skip_mods_and_typedefs(type_id);
196 res.push(Accessor::Array {
197 type_id: id,
198 arr_idx: spec[0],
199 });
200 for i in 1..spec.len() {
201 id = btf.skip_mods_and_typedefs(id);
202 match btf.type_by_id(id) {
203 BtfType::Struct(t) => {
204 let m = &t.members[spec[i]];
205 let next_id = btf.skip_mods_and_typedefs(m.type_id);
206 if !m.name.is_empty() {
207 res.push(Accessor::Field {
208 type_id: id,
209 field_idx: spec[i],
210 field_name: m.name.to_owned(),
211 });
212 }
213 id = next_id;
214 }
215 BtfType::Union(t) => {
216 let m = &t.members[spec[i]];
217 let next_id = btf.skip_mods_and_typedefs(m.type_id);
218 if !m.name.is_empty() {
219 res.push(Accessor::Field {
220 type_id: id,
221 field_idx: spec[i],
222 field_name: m.name.to_owned(),
223 });
224 }
225 id = next_id;
226 }
227 BtfType::Array(t) => {
228 id = btf.skip_mods_and_typedefs(t.val_type_id);
229 res.push(Accessor::Array {
230 type_id: id,
231 arr_idx: spec[i],
232 });
233 }
234 _ => spec_error(
235 spec,
236 i,
237 "must be struct/union/array",
238 id,
239 btf.type_by_id(id),
240 )?,
241 }
242 }
243 Ok(res)
244 }
245
246 fn calc_off(&self, btf: &Btf, type_id: u32, spec: &[usize]) -> BtfResult<u32> {
247 let mut id = btf.skip_mods_and_typedefs(type_id);
248 let mut off = spec[0] as u32 * Relocator::type_size(btf, id)?;
249
250 for i in 1..spec.len() {
251 id = btf.skip_mods_and_typedefs(id);
252 match btf.type_by_id(id) {
253 BtfType::Struct(t) => {
254 let m = &t.members[spec[i]];
255 off += m.bit_offset / 8;
256 id = m.type_id;
257 }
258 BtfType::Union(t) => {
259 let m = &t.members[spec[i]];
260 off += m.bit_offset / 8;
261 id = m.type_id;
262 }
263 BtfType::Array(t) => {
264 off += spec[i] as u32 * Relocator::type_size(btf, t.val_type_id)?;
265 id = t.val_type_id;
266 }
267 _ => spec_error(
268 spec,
269 i,
270 "must be struct/union/array",
271 id,
272 btf.type_by_id(id),
273 )?,
274 }
275 }
276 Ok(off)
277 }
278
279 fn calc_targ_spec(&self, local_spec: &[Accessor], mut targ_id: u32) -> BtfResult<Vec<usize>> {
280 targ_id = self.targ_btf.skip_mods_and_typedefs(targ_id);
281 let mut targ_type = self.targ_btf.type_by_id(targ_id);
282 let mut targ_spec = Vec::new();
283
284 match local_spec[0] {
285 Accessor::Array { arr_idx, .. } => targ_spec.push(arr_idx),
286 _ => btf_error(format!(
287 "first spec must be array access, but is: {}",
288 local_spec[0]
289 ))?,
290 }
291
292 for i in 1..local_spec.len() {
293 let s = &local_spec[i];
294 match s {
295 &Accessor::Array { arr_idx, .. } => match targ_type {
296 BtfType::Array(t) => {
297 targ_id = self.targ_btf.skip_mods_and_typedefs(t.val_type_id);
298 targ_type = self.targ_btf.type_by_id(targ_id);
299 targ_spec.push(arr_idx);
300 }
301 _ => access_error(s, i, "target must be array", targ_id, targ_type)?,
302 },
303 Accessor::Field {
304 type_id: local_id,
305 field_idx,
306 ..
307 } => {
308 let local_type = self.local_btf.type_by_id(*local_id);
309 let local_members = match local_type {
310 BtfType::Struct(t) => &t.members,
311 BtfType::Union(t) => &t.members,
312 _ => {
313 access_error(s, i, "local must be struct/union", *local_id, local_type)?
314 }
315 };
316 let local_member = &local_members[*field_idx];
317 let targ_members = match targ_type {
318 BtfType::Struct(t) => &t.members,
319 BtfType::Union(t) => &t.members,
320 _ => access_error(s, i, "target must be struct/union", targ_id, targ_type)?,
321 };
322 match self.targ_member_spec(local_member, targ_members) {
323 Ok(Some((t_id, mut t_spec))) => {
324 targ_id = t_id;
325 targ_type = self.targ_btf.type_by_id(targ_id);
326 targ_spec.append(&mut t_spec);
327 }
328 Ok(None) => {
329 access_error(s, i, "target field not found", targ_id, targ_type)?
330 }
331 Err(e) => access_error(s, i, &format!("{}", e), targ_id, targ_type)?,
332 }
333 }
334 }
335 }
336 Ok(targ_spec)
337 }
338
339 fn targ_member_spec(
340 &self,
341 local_member: &BtfMember,
342 targ_members: &[BtfMember],
343 ) -> BtfResult<Option<(u32, Vec<usize>)>> {
344 for (i, m) in targ_members.iter().enumerate() {
345 if m.name == local_member.name {
346 let local_id = self.local_btf.skip_mods_and_typedefs(local_member.type_id);
347 let targ_id = self.targ_btf.skip_mods_and_typedefs(m.type_id);
348 if self.are_kinds_compat(local_id, targ_id) {
349 return Ok(Some((targ_id, vec![i])));
350 } else {
351 return btf_error(format!(
352 concat!(
353 "incompatible types for field '{}', ",
354 "local_id: {}, local_kind: {:?}, ",
355 "targ_id: {}, targ_kind: {:?}"
356 ),
357 local_member.name,
358 local_id,
359 self.local_btf.type_by_id(local_id).kind(),
360 targ_id,
361 self.targ_btf.type_by_id(targ_id).kind()
362 ));
363 }
364 } else if m.name.is_empty() {
365 if let Some(members) = self.get_composite_members(self.targ_btf, m.type_id) {
366 match self.targ_member_spec(local_member, members) {
367 Ok(Some((t_id, mut spec))) => {
368 spec.insert(0, i);
369 return Ok(Some((t_id, spec)));
370 }
371 Ok(None) => {}
372 e @ Err(_) => return e,
373 }
374 }
375 }
376 }
377 Ok(None)
378 }
379
380 fn get_composite_members<'c>(&self, btf: &'c Btf, type_id: u32) -> Option<&'c [BtfMember<'c>]> {
381 let id = btf.skip_mods(type_id);
382 match btf.type_by_id(id) {
383 BtfType::Struct(t) => Some(&t.members),
384 BtfType::Union(t) => Some(&t.members),
385 _ => None,
386 }
387 }
388
389 fn are_kinds_compat(&self, local_id: u32, targ_id: u32) -> bool {
390 let local_kind = self.local_btf.type_by_id(local_id).kind();
391 let targ_kind = self.targ_btf.type_by_id(targ_id).kind();
392 local_kind == targ_kind || (local_kind == BtfKind::Struct && targ_kind == BtfKind::Union)
393 }
394
395 fn type_size(btf: &Btf, type_id: u32) -> BtfResult<u32> {
396 let id = btf.skip_mods_and_typedefs(type_id);
397 Ok(match btf.type_by_id(id) {
398 BtfType::Int(t) if t.offset == 0 && t.bits % 8 == 0 => t.bits / 8,
399 BtfType::Enum(t) => t.sz,
400 BtfType::Struct(t) => t.sz,
401 BtfType::Union(t) => t.sz,
402 BtfType::Array(t) => t.nelems * Relocator::type_size(btf, t.val_type_id)?,
403 BtfType::Ptr(_) => btf.ptr_sz(),
404 _ => btf_error(format!(
405 "can't calculate byte size of type_id: {}, type: {}",
406 id,
407 btf.type_by_id(id),
408 ))?,
409 })
410 }
411
412 fn relo_is_field_based(kind: BtfCoreRelocKind) -> bool {
413 match kind {
414 BtfCoreRelocKind::ByteOff
415 | BtfCoreRelocKind::ByteSz
416 | BtfCoreRelocKind::FieldExists
417 | BtfCoreRelocKind::Signed
418 | BtfCoreRelocKind::LShiftU64
419 | BtfCoreRelocKind::RShiftU64 => true,
420 _ => false,
421 }
422 }
423
424 fn relo_is_type_based(kind: BtfCoreRelocKind) -> bool {
425 match kind {
426 BtfCoreRelocKind::LocalTypeId
427 | BtfCoreRelocKind::TargetTypeId
428 | BtfCoreRelocKind::TypeExists
429 | BtfCoreRelocKind::TypeMatches
430 | BtfCoreRelocKind::TypeSize => true,
431 _ => false,
432 }
433 }
434
435 fn relo_is_enumval_based(kind: BtfCoreRelocKind) -> bool {
436 match kind {
437 BtfCoreRelocKind::EnumvalExists | BtfCoreRelocKind::EnumvalValue => true,
438 _ => false,
439 }
440 }
441
442 pub fn pretty_print_access_spec(btf: &Btf, rec: &BtfExtCoreReloc) -> BtfResult<String> {
443 let mut buf = String::new();
444 let spec = &rec.access_spec;
445 let mut id = rec.type_id;
446 match btf.type_by_id(id) {
447 BtfType::Struct(t) => {
448 write!(
449 buf,
450 "struct {}",
451 if t.name.is_empty() { "<anon>" } else { &t.name }
452 )?;
453 }
454 BtfType::Union(t) => {
455 write!(
456 buf,
457 "union {}",
458 if t.name.is_empty() { "<anon>" } else { &t.name }
459 )?;
460 }
461 BtfType::Typedef(t) => {
462 write!(
463 buf,
464 "typedef {}",
465 if t.name.is_empty() { "<anon>" } else { &t.name }
466 )?;
467 }
468 BtfType::Enum(t) => {
469 write!(
470 buf,
471 "enum {}",
472 if t.name.is_empty() { "<anon>" } else { &t.name }
473 )?;
474 }
475 BtfType::Ptr(t) => {
476 write!(buf, "ptr -> [{}]", t.type_id)?;
477 }
478 BtfType::Array(t) => {
479 write!(buf, "arr[{}] -> [{}]", t.nelems, t.val_type_id)?;
480 }
481 BtfType::Int(t) => {
482 write!(buf, "int {}", t.name)?;
483 }
484 _ => spec_error(
485 spec,
486 0,
487 "must be struct/union/typedef/enum/ptr/array",
488 id,
489 btf.type_by_id(id),
490 )?,
491 }
492
493 if Relocator::relo_is_type_based(rec.kind) {
494 return Ok(buf);
495 }
496
497 if Relocator::relo_is_enumval_based(rec.kind) {
498 id = btf.skip_mods_and_typedefs(rec.type_id);
499 match btf.type_by_id(id) {
500 BtfType::Enum(t) => {
501 let e = &t.values[spec[0]];
502 write!(buf, "::{} = {}", &e.name, e.value)?;
503 }
504 _ => spec_error(spec, 0, "must be enum", id, btf.type_by_id(id))?,
505 }
506 return Ok(buf);
507 }
508
509 if !Relocator::relo_is_field_based(rec.kind) {
510 write!(buf, " ... ERROR: unexpected relocation kind")?;
511 return Ok(buf);
512 }
513
514 if spec[0] > 0 {
515 write!(buf, "[{}]", spec[0])?;
516 }
517
518 for i in 1..spec.len() {
519 match btf.type_by_id(id) {
520 BtfType::Struct(t) => {
521 let m = &t.members[spec[i] as usize];
522 write!(buf, ".{}", m.name)?;
523 id = btf.skip_mods_and_typedefs(m.type_id);
524 }
525 BtfType::Union(t) => {
526 let m = &t.members[spec[i] as usize];
527 if !m.name.is_empty() {
528 write!(buf, ".{}", m.name)?;
529 } else {
530 write!(buf, ".<anon>")?;
531 }
532 id = btf.skip_mods_and_typedefs(m.type_id);
533 }
534 BtfType::Array(t) => {
535 write!(buf, "[{}]", spec[i] as usize)?;
536 id = btf.skip_mods_and_typedefs(t.val_type_id);
537 }
538 _ => spec_error(
539 spec,
540 i,
541 "must be struct/union/array",
542 id,
543 btf.type_by_id(id),
544 )?,
545 }
546 }
547 Ok(buf)
548 }
549
550 fn spec_to_str(spec: &[usize]) -> String {
551 spec.iter()
552 .map(|x| x.to_string())
553 .collect::<Vec<String>>()
554 .join(":")
555 }
556}
557
558fn spec_error<T>(
559 spec: &[usize],
560 idx: usize,
561 details: &str,
562 type_id: u32,
563 bt: &BtfType,
564) -> BtfResult<T> {
565 btf_error(format!(
566 "Unsupported accessor: {}, at #{}: {}, but is type_id: {}, type: {}",
567 Relocator::spec_to_str(spec),
568 idx,
569 details,
570 type_id,
571 bt,
572 ))?
573}
574fn access_error<T>(
575 spec: &Accessor,
576 idx: usize,
577 details: &str,
578 type_id: u32,
579 bt: &BtfType,
580) -> BtfResult<T> {
581 btf_error(format!(
582 "Unsupported accessor: {}, at #{}: {}, but is type_id: {}, type: {}",
583 spec, idx, details, type_id, bt,
584 ))?
585}