btf/
relocator.rs

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                    //TODO: strip __suffix, kernel version suffix, etc
114                    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}