├── .github ├── dependabot.yml └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── btf_index.rs ├── c_dumper.rs ├── lib.rs ├── main.rs ├── relocator.rs └── types.rs └── tests ├── samples ├── bitfields.c ├── bpf │ ├── chained_relocs.c │ ├── prog.c │ ├── relocs.c │ ├── simple_relocs.c │ └── tracex1_kern.c ├── cycles.c ├── embed_array.c ├── embed_array2.c ├── embed_func_proto.c ├── embed_func_proto2.c ├── embed_struct.c ├── embed_typedef.c ├── embed_typedef_cycle.c ├── embed_typedef_cycle2.c ├── funcs.c ├── naming_conflicts.c ├── order_test.c ├── order_test2.c ├── order_test3.c ├── order_test4.c ├── ordering.c ├── printing_arrays.c ├── ptrs.c └── simple.c └── tests.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "cargo" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "weekly" 14 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | schedule: 11 | - cron: "0 0 * * *" 12 | 13 | env: 14 | CARGO_TERM_COLOR: always 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | profile: [dev, release] 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: dtolnay/rust-toolchain@stable 25 | - uses: Swatinem/rust-cache@v2 26 | with: 27 | key: ${{ matrix.profile }} 28 | 29 | - run: cargo build --profile ${{ matrix.profile }} --all-targets 30 | - run: cargo test --profile ${{ matrix.profile }} --all-targets 31 | - run: cargo clippy --profile ${{ matrix.profile }} --all-targets -- --deny warnings 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | **/*.o 13 | **/*.out 14 | /tests/samples/bpf/vmlinux.h 15 | /tests/samples/bpf/vmlinux_no_unions.h 16 | 17 | #Added by cargo 18 | # 19 | #already existing elements are commented out 20 | 21 | /target 22 | #**/*.rs.bk 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "btfdump" 3 | description = "BTF introspection tool" 4 | version = "0.0.4" 5 | authors = ["Andrii Nakryiko "] 6 | license = "BSD-2-Clause" 7 | edition = "2018" 8 | readme = "README.md" 9 | repository = "https://github.com/anakryiko/btfdump" 10 | keywords = ["btf", "bpf"] 11 | 12 | [lib] 13 | name = "btf" 14 | path = "src/lib.rs" 15 | 16 | [[bin]] 17 | name = "btf" 18 | path = "src/main.rs" 19 | 20 | [dependencies] 21 | clap = { version = "4.0", features = ["derive", "wrap_help"] } 22 | goblin = "0.9.2" 23 | object = "0.36.0" 24 | memmap2 = "0.9.0" 25 | scroll = "0.12.0" 26 | scroll_derive = "0.12.1" 27 | regex = "1" 28 | bitflags = "2" 29 | 30 | [dev-dependencies] 31 | tempfile = "3.8.0" 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2019, Andrii Nakryiko 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # btfdump 2 | BTF introspection tool. 3 | 4 | # Compiling 5 | `btfdump` is written in Rust and relies on standard Cargo package manager. 6 | 7 | To compile btfdump: 8 | 1. Get rust toolchain. See https://www.rust-lang.org/tools/install for instructions. 9 | a. Using rustup is the simplest option: just run `curl https://sh.rustup.rs -sSf | sh`. 10 | b. If you are behind HTTP/HTTPS proxy, you can specify it using `http_proxy` and `https_proxy` envvars: 11 | ``` 12 | $ export http_proxy=http://fwdproxy:8080 13 | $ export https_proxy=http://fwdproxy:8080 14 | $ curl https://sh.rustup.rs -sSf | sh 15 | ``` 16 | 2. Once Cargo and rustc is installed, run `cargo build` or `cargo build --release` to compile it. This will build `btf` binary in `target/{debug,release}/` directory. 17 | 3. Alternatively, you can use `cargo run -- ` to compile and run through Cargo. 18 | 19 | # Supported commands 20 | 21 | ## Dump 22 | 23 | 1. Dump BTF types in various formats: 24 | ``` 25 | btf dump --format [human|c] 26 | ``` 27 | 2. You can filter out which types to print out using `--type`, `--name`, and `--id` options. See `btf dump --help` for more details. 28 | 3. Check also `--dataset` option for dumping .BTF.ext data as well. 29 | 30 | ## Stat 31 | 32 | Output high-level stats about .BTF and .BTF.ext data. 33 | 34 | ``` 35 | btf stat 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /src/btf_index.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::types::*; 4 | 5 | #[derive(Debug)] 6 | pub struct BtfIndex<'a> { 7 | name_index: HashMap<&'a str, Vec>, 8 | } 9 | 10 | const EMPTY_ID_SLICE: &[u32] = &[]; 11 | 12 | impl<'a> BtfIndex<'a> { 13 | pub fn new(btf: &'a Btf<'a>) -> BtfIndex<'a> { 14 | let mut index = BtfIndex { 15 | name_index: HashMap::new(), 16 | }; 17 | for (i, t) in btf.types().iter().enumerate() { 18 | let e = index.name_index.entry(t.name()).or_default(); 19 | e.push(i as u32); 20 | } 21 | index 22 | } 23 | 24 | pub fn get_by_name(&self, name: &str) -> &[u32] { 25 | self.name_index 26 | .get(name) 27 | .map(|x| &x[..]) 28 | .unwrap_or_else(|| EMPTY_ID_SLICE) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/c_dumper.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::sync::LazyLock; 3 | 4 | use regex::RegexSet; 5 | 6 | use crate::types::*; 7 | use crate::{btf_error, BtfResult}; 8 | 9 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] 10 | enum OrderState { 11 | #[default] 12 | NotOrdered, 13 | Ordering, 14 | Ordered, 15 | } 16 | 17 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] 18 | enum EmitState { 19 | #[default] 20 | NotEmitted, 21 | Emitting, 22 | Emitted, 23 | } 24 | 25 | #[derive(Default)] 26 | struct TypeState { 27 | order_state: OrderState, 28 | emit_state: EmitState, 29 | fwd_emitted: bool, 30 | name: String, 31 | } 32 | 33 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] 34 | enum NamedKind { 35 | Type, 36 | Ident, 37 | } 38 | 39 | #[derive(Debug)] 40 | pub struct CDumperCfg { 41 | pub verbose: bool, 42 | pub union_as_struct: bool, 43 | } 44 | 45 | pub struct CDumper<'a> { 46 | btf: &'a Btf<'a>, 47 | cfg: CDumperCfg, 48 | state: Vec, 49 | names: HashMap<(NamedKind, &'a str), u32>, 50 | } 51 | 52 | impl<'a> CDumper<'a> { 53 | pub fn new(btf: &'a Btf<'a>, cfg: CDumperCfg) -> CDumper<'a> { 54 | let mut dumper = CDumper { 55 | btf, 56 | cfg, 57 | state: Vec::new(), 58 | names: HashMap::new(), 59 | }; 60 | dumper 61 | .state 62 | .resize_with(btf.type_cnt() as usize, Default::default); 63 | dumper 64 | } 65 | 66 | pub fn dump_types( 67 | &mut self, 68 | filter: Box) -> bool>, 69 | ) -> BtfResult<()> { 70 | for id in 1..self.btf.type_cnt() { 71 | let bt = self.btf.type_by_id(id); 72 | if filter(id, bt) { 73 | self.dump_type(id)?; 74 | } 75 | } 76 | Ok(()) 77 | } 78 | 79 | pub fn dump_type(&mut self, id: u32) -> BtfResult<()> { 80 | let mut order = Vec::new(); 81 | if self.cfg.verbose { 82 | println!("==================================================="); 83 | println!("ORDERING id: {}, type: {}", id, self.btf.type_by_id(id)); 84 | } 85 | self.order_type(id, false, &mut order)?; 86 | if self.cfg.verbose { 87 | for (i, &id) in order.iter().enumerate() { 88 | println!("ORDER #{} id: {}, type: {}", i, id, self.btf.type_by_id(id)); 89 | } 90 | } 91 | // emit struct/union and fwds required by them in correct order 92 | for id in order { 93 | self.emit_type(id, 0)?; 94 | } 95 | Ok(()) 96 | } 97 | 98 | fn order_type(&mut self, id: u32, has_ptr: bool, order: &mut Vec) -> BtfResult { 99 | if self.cfg.verbose && self.get_order_state(id) != OrderState::Ordered { 100 | println!( 101 | "ORDER TYPE id:{}, has_ptr:{}, type:{}, order_state:{:?}", 102 | id, 103 | has_ptr, 104 | self.btf.type_by_id(id), 105 | self.get_order_state(id) 106 | ); 107 | } 108 | // order state is used to detect strong link cycles, but only for BTF kinds that are or 109 | // could be an independent definition (i.e., stand-alone fwd decl, enum, typedef, struct, 110 | // union). Ptrs, arrays, func_protos, modifiers are just means to get to these definitions. 111 | // Int/void don't need definitions, they are assumed to be always properly defined. 112 | // We also ignore datasec, var, and funcs. So for all non-defining kinds, we never even set 113 | // ordering state, for defining kinds we set OrderState::Ordering and subsequently 114 | // OrderState::Ordered only if it forms a strong link. 115 | match self.get_order_state(id) { 116 | OrderState::NotOrdered => {} 117 | OrderState::Ordering => match self.btf.type_by_id(id) { 118 | BtfType::Struct(t) | BtfType::Union(t) if has_ptr && !t.name.is_empty() => { 119 | return Ok(false); 120 | } 121 | _ => { 122 | return btf_error(format!( 123 | "Unsatisfiable type cycle, id: {}, type: {}", 124 | id, 125 | self.btf.type_by_id(id) 126 | )); 127 | } 128 | }, 129 | // return true, letting typedefs know that it's ok to be emitted 130 | OrderState::Ordered => return Ok(true), 131 | } 132 | match self.btf.type_by_id(id) { 133 | BtfType::Func(_) | BtfType::Var(_) | BtfType::Datasec(_) | BtfType::DeclTag(_) => {} 134 | BtfType::Void | BtfType::Int(_) | BtfType::Float(_) => { 135 | self.set_order_state(id, OrderState::Ordered); 136 | return Ok(false); 137 | } 138 | BtfType::Volatile(t) => return self.order_type(t.type_id, has_ptr, order), 139 | BtfType::Const(t) => return self.order_type(t.type_id, has_ptr, order), 140 | BtfType::Restrict(t) => return self.order_type(t.type_id, has_ptr, order), 141 | BtfType::TypeTag(t) => return self.order_type(t.type_id, has_ptr, order), 142 | BtfType::Ptr(t) => { 143 | let res = self.order_type(t.type_id, true, order); 144 | self.set_order_state(id, OrderState::Ordered); 145 | return res; 146 | } 147 | BtfType::Array(t) => return self.order_type(t.val_type_id, has_ptr, order), 148 | BtfType::FuncProto(t) => { 149 | let mut is_strong = self.order_type(t.res_type_id, has_ptr, order)?; 150 | for p in &t.params { 151 | if self.order_type(p.type_id, has_ptr, order)? { 152 | is_strong = true; 153 | } 154 | } 155 | return Ok(is_strong); 156 | } 157 | BtfType::Struct(t) | BtfType::Union(t) => { 158 | // struct/union is part of strong link, only if it's embedded (so no ptr in a path) 159 | // or it's anonymous (so has to be defined inline, even if declared through ptr) 160 | if !has_ptr || t.name.is_empty() { 161 | self.set_order_state(id, OrderState::Ordering); 162 | 163 | for m in &t.members { 164 | self.order_type(m.type_id, false, order)?; 165 | } 166 | // no need to explicitly order anonymous embedded struct 167 | if !t.name.is_empty() { 168 | order.push(id); 169 | } 170 | 171 | self.set_order_state(id, OrderState::Ordered); 172 | // report this was strong link 173 | return Ok(true); 174 | } 175 | } 176 | BtfType::Enum(t) => { 177 | if !t.name.is_empty() { 178 | order.push(id); 179 | } 180 | self.set_order_state(id, OrderState::Ordered); 181 | // report this was strong link 182 | return Ok(true); 183 | } 184 | BtfType::Enum64(t) => { 185 | if !t.name.is_empty() { 186 | order.push(id); 187 | } 188 | self.set_order_state(id, OrderState::Ordered); 189 | // report this was strong link 190 | return Ok(true); 191 | } 192 | BtfType::Fwd(t) => { 193 | if !t.name.is_empty() { 194 | order.push(id); 195 | } 196 | self.set_order_state(id, OrderState::Ordered); 197 | // report this was strong link 198 | return Ok(true); 199 | } 200 | BtfType::Typedef(t) => { 201 | let is_strong = self.order_type(t.type_id, has_ptr, order)?; 202 | if !has_ptr || is_strong { 203 | order.push(id); 204 | self.set_order_state(id, OrderState::Ordered); 205 | // report this was strong link 206 | return Ok(true); 207 | } 208 | } 209 | } 210 | Ok(false) 211 | } 212 | 213 | fn emit_type(&mut self, id: u32, cont_id: u32) -> BtfResult<()> { 214 | let top_level_def = cont_id == 0; 215 | if self.cfg.verbose { 216 | println!( 217 | "EMIT_TYPE id: {}, cont_id: {}, is_def: {}, state: {:?}, type: {}", 218 | id, 219 | cont_id, 220 | top_level_def, 221 | self.get_emit_state(id), 222 | self.btf.type_by_id(id) 223 | ); 224 | } 225 | match self.get_emit_state(id) { 226 | EmitState::NotEmitted => {} 227 | EmitState::Emitting => { 228 | if self.get_fwd_emitted(id) { 229 | return Ok(()); 230 | } 231 | match self.btf.type_by_id(id) { 232 | BtfType::Struct(t) | BtfType::Union(t) => { 233 | // fwd was already emitted or no need for fwd declare if we are referencing 234 | // a struct/union we are part of 235 | if id == cont_id { 236 | return Ok(()); 237 | } 238 | if t.name.is_empty() { 239 | return btf_error(format!( 240 | "anonymous struct loop, id: {}, type: {}", 241 | id, 242 | self.btf.type_by_id(id) 243 | )); 244 | } 245 | if self.emit_composite_fwd(id, t) { 246 | println!(";\n"); 247 | } 248 | self.set_fwd_emitted(id, true); 249 | return Ok(()); 250 | } 251 | BtfType::Typedef(t) => { 252 | // for typedef fwd_emitted means typedef definition was emitted, but it can 253 | // be used only for "weak" references through pointer only 254 | if self.emit_typedef_def(id, t, 0) { 255 | println!(";\n"); 256 | } 257 | self.set_fwd_emitted(id, true); 258 | return Ok(()); 259 | } 260 | _ => return Ok(()), 261 | }; 262 | } 263 | EmitState::Emitted => return Ok(()), 264 | } 265 | 266 | if top_level_def && self.btf.type_by_id(id).name().is_empty() { 267 | return btf_error(format!( 268 | "unexpected nameless definition, id: {}, type: {}", 269 | id, 270 | self.btf.type_by_id(id) 271 | )); 272 | } 273 | 274 | match self.btf.type_by_id(id) { 275 | BtfType::Func(_) | BtfType::Var(_) | BtfType::Datasec(_) | BtfType::DeclTag(_) => {} 276 | BtfType::Void | BtfType::Int(_) | BtfType::Float(_) => {} 277 | BtfType::Volatile(t) => self.emit_type(t.type_id, cont_id)?, 278 | BtfType::Const(t) => self.emit_type(t.type_id, cont_id)?, 279 | BtfType::Restrict(t) => self.emit_type(t.type_id, cont_id)?, 280 | BtfType::TypeTag(t) => self.emit_type(t.type_id, cont_id)?, 281 | BtfType::Ptr(t) => self.emit_type(t.type_id, cont_id)?, 282 | BtfType::Array(t) => self.emit_type(t.val_type_id, cont_id)?, 283 | BtfType::FuncProto(t) => { 284 | self.emit_type(t.res_type_id, cont_id)?; 285 | for p in &t.params { 286 | self.emit_type(p.type_id, cont_id)?; 287 | } 288 | } 289 | BtfType::Struct(t) | BtfType::Union(t) => { 290 | self.set_emit_state(id, EmitState::Emitting); 291 | if top_level_def || t.name.is_empty() { 292 | // top-level struct definition or embedded anonymous struct, ensure all field 293 | // types have their fwds declared 294 | for m in &t.members { 295 | self.emit_type(m.type_id, if t.name.is_empty() { cont_id } else { id })?; 296 | } 297 | } else if !self.get_fwd_emitted(id) && id != cont_id { 298 | if self.emit_composite_fwd(id, t) { 299 | println!(";\n"); 300 | } 301 | self.set_fwd_emitted(id, true); 302 | } 303 | if top_level_def { 304 | self.emit_composite_def(id, t, 0); 305 | println!(";\n"); 306 | self.set_emit_state(id, EmitState::Emitted); 307 | } else { 308 | self.set_emit_state(id, EmitState::NotEmitted); 309 | } 310 | } 311 | BtfType::Enum(t) => { 312 | if top_level_def { 313 | self.emit_enum_def(id, t, 0); 314 | println!(";\n"); 315 | } 316 | self.set_emit_state(id, EmitState::Emitted); 317 | } 318 | BtfType::Enum64(t) => { 319 | if top_level_def { 320 | self.emit_enum64_def(id, t, 0); 321 | println!(";\n"); 322 | } 323 | self.set_emit_state(id, EmitState::Emitted); 324 | } 325 | BtfType::Fwd(t) => { 326 | self.emit_fwd_def(id, t); 327 | println!(";\n"); 328 | self.set_emit_state(id, EmitState::Emitted); 329 | } 330 | BtfType::Typedef(t) => { 331 | self.set_emit_state(id, EmitState::Emitting); 332 | self.emit_type(t.type_id, id)?; 333 | if !self.get_fwd_emitted(id) { 334 | // emit typedef right now, if someone depends on it "weakly" (though pointer) 335 | if self.emit_typedef_def(id, t, 0) { 336 | println!(";\n"); 337 | } 338 | self.set_fwd_emitted(id, true); 339 | } 340 | self.set_emit_state(id, EmitState::Emitted); 341 | } 342 | } 343 | Ok(()) 344 | } 345 | 346 | fn get_fwd_emitted(&self, id: u32) -> bool { 347 | self.state[id as usize].fwd_emitted 348 | } 349 | 350 | fn set_fwd_emitted(&mut self, id: u32, emitted: bool) { 351 | self.state[id as usize].fwd_emitted = emitted; 352 | } 353 | 354 | fn get_order_state(&self, id: u32) -> OrderState { 355 | self.state[id as usize].order_state 356 | } 357 | 358 | fn set_order_state(&mut self, id: u32, state: OrderState) { 359 | self.state[id as usize].order_state = state; 360 | } 361 | 362 | fn get_emit_state(&self, id: u32) -> EmitState { 363 | self.state[id as usize].emit_state 364 | } 365 | 366 | fn set_emit_state(&mut self, id: u32, state: EmitState) { 367 | self.state[id as usize].emit_state = state; 368 | } 369 | 370 | fn emit_composite_fwd(&mut self, id: u32, t: &'a BtfComposite) -> bool { 371 | if NAMES_BLACKLIST.is_match(t.name) { 372 | return false; 373 | } 374 | let keyword = if !t.is_struct && self.cfg.union_as_struct { 375 | "struct /*union*/" 376 | } else if t.is_struct { 377 | "struct" 378 | } else { 379 | "union" 380 | }; 381 | print!( 382 | "{} {}", 383 | keyword, 384 | self.resolve_type_name(NamedKind::Type, id, t.name) 385 | ); 386 | true 387 | } 388 | 389 | fn emit_composite_def(&mut self, id: u32, t: &'a BtfComposite, lvl: usize) { 390 | if NAMES_BLACKLIST.is_match(t.name) { 391 | return; 392 | } 393 | let keyword = if !t.is_struct && self.cfg.union_as_struct { 394 | "struct /*union*/" 395 | } else if t.is_struct { 396 | "struct" 397 | } else { 398 | "union" 399 | }; 400 | let packed = self.is_struct_packed(id, t); 401 | let name = self.resolve_type_name(NamedKind::Type, id, t.name); 402 | print!("{}{}{} {{", keyword, sep(&name), name); 403 | let mut offset = 0; 404 | for m in &t.members { 405 | self.emit_bit_padding(offset, m, packed, lvl + 1); 406 | 407 | print!("\n{}", pfx(lvl + 1)); 408 | self.emit_type_decl(m.type_id, m.name, lvl + 1); 409 | 410 | if m.bit_size == 0 { 411 | offset = m.bit_offset + self.btf.get_size_of(m.type_id) * 8; 412 | } else { 413 | print!(": {}", m.bit_size); 414 | offset = m.bit_offset + m.bit_size as u32; 415 | } 416 | print!(";"); 417 | } 418 | if !t.members.is_empty() { 419 | println!(); 420 | } 421 | print!("{}}}", pfx(lvl)); 422 | if packed { 423 | print!(" __attribute__((packed))"); 424 | } 425 | } 426 | 427 | fn is_struct_packed(&self, id: u32, t: &BtfComposite) -> bool { 428 | if !t.is_struct { 429 | return false; 430 | } 431 | // size of a struct has to be a multiple of its alignment 432 | if t.sz % self.btf.get_align_of(id) != 0 { 433 | return true; 434 | } 435 | // all the non-bitfield fields have to be naturally aligned 436 | for m in &t.members { 437 | if m.bit_size == 0 && m.bit_offset % (self.btf.get_align_of(m.type_id) * 8) != 0 { 438 | return true; 439 | } 440 | } 441 | // even if original struct was marked as packed, we haven't detected any misalignment, so 442 | // there is no effect of packedness for given struct 443 | false 444 | } 445 | 446 | fn emit_bit_padding(&self, offset: u32, m: &BtfMember, packed: bool, lvl: usize) { 447 | if offset >= m.bit_offset { 448 | return; 449 | } 450 | let mut bit_diff = m.bit_offset - offset; 451 | let align = if packed { 452 | 1 453 | } else { 454 | self.btf.get_align_of(m.type_id) 455 | }; 456 | if m.bit_size == 0 && bit_diff < align * 8 { 457 | // natural padding will take care of a gap 458 | return; 459 | } 460 | let ptr_sz_bits = self.btf.ptr_sz() * 8; 461 | while bit_diff > 0 { 462 | let (pad_type, pad_bits) = if ptr_sz_bits > 32 && bit_diff > 32 { 463 | ("long", CDumper::chip_away_bits(bit_diff, ptr_sz_bits)) 464 | } else if bit_diff > 16 { 465 | ("int", CDumper::chip_away_bits(bit_diff, 32)) 466 | } else if bit_diff > 8 { 467 | ("short", CDumper::chip_away_bits(bit_diff, 16)) 468 | } else { 469 | ("char", CDumper::chip_away_bits(bit_diff, 8)) 470 | }; 471 | bit_diff -= pad_bits; 472 | print!("\n{}{}: {};", pfx(lvl), pad_type, pad_bits); 473 | } 474 | } 475 | 476 | fn chip_away_bits(total: u32, at_most: u32) -> u32 { 477 | if total % at_most == 0 { 478 | at_most 479 | } else { 480 | total % at_most 481 | } 482 | } 483 | 484 | fn emit_enum_def(&mut self, id: u32, t: &'a BtfEnum, lvl: usize) { 485 | if NAMES_BLACKLIST.is_match(t.name) { 486 | return; 487 | } 488 | let name = self.resolve_type_name(NamedKind::Type, id, t.name); 489 | if t.values.is_empty() { 490 | // enum fwd 491 | print!("enum{}{}", sep(&name), name); 492 | } else { 493 | print!("enum{}{} {{", sep(&name), name); 494 | for v in &t.values { 495 | let val_uniq_name = self.resolve_name(NamedKind::Ident, v.name); 496 | print!("\n{}{} = {},", pfx(lvl + 1), &val_uniq_name, v.value); 497 | } 498 | print!("\n{}}}", pfx(lvl)); 499 | } 500 | } 501 | 502 | fn emit_enum64_def(&mut self, id: u32, t: &'a BtfEnum64, lvl: usize) { 503 | if NAMES_BLACKLIST.is_match(t.name) { 504 | return; 505 | } 506 | let name = self.resolve_type_name(NamedKind::Type, id, t.name); 507 | if t.values.is_empty() { 508 | // enum fwd 509 | print!("enum{}{}", sep(&name), name); 510 | } else { 511 | print!("enum{}{} {{", sep(&name), name); 512 | for v in &t.values { 513 | let val_uniq_name = self.resolve_name(NamedKind::Ident, v.name); 514 | print!("\n{}{} = {},", pfx(lvl + 1), &val_uniq_name, v.value); 515 | } 516 | print!("\n{}}}", pfx(lvl)); 517 | } 518 | } 519 | 520 | fn emit_fwd_def(&mut self, id: u32, t: &'a BtfFwd) { 521 | if NAMES_BLACKLIST.is_match(t.name) { 522 | return; 523 | } 524 | let name = self.resolve_type_name(NamedKind::Type, id, t.name); 525 | match t.kind { 526 | BtfFwdKind::Struct => print!("struct {}", name), 527 | BtfFwdKind::Union => { 528 | if self.cfg.union_as_struct { 529 | print!("struct /*union*/ {}", name) 530 | } else { 531 | print!("union {}", name) 532 | } 533 | } 534 | } 535 | } 536 | 537 | fn emit_typedef_def(&mut self, id: u32, t: &'a BtfTypedef, lvl: usize) -> bool { 538 | if NAMES_BLACKLIST.is_match(t.name) { 539 | return false; 540 | } 541 | let name = self.resolve_type_name(NamedKind::Ident, id, t.name); 542 | print!("typedef "); 543 | self.emit_type_decl(t.type_id, &name, lvl); 544 | true 545 | } 546 | 547 | fn emit_type_decl(&mut self, mut id: u32, fname: &str, lvl: usize) { 548 | // This algorithm emits correct C syntax for any type definition. 549 | // 550 | // For most types it's trivial, but there are few quirky type declaration cases worth 551 | // mentioning: 552 | // - function prototypes; 553 | // - arrays; 554 | // - const/volatile/restrict for pointers vs other types. 555 | // See Peter van der Linden's "Expert C Programming: Deep C Secrets", Ch.3 "Unscrambling 556 | // Declarations in C" for good discussion of this topic. 557 | // 558 | // This algorithm is in reverse to van der Linden's parsing algorithm. It goes from 559 | // structured BTF representation of type declaration to a valid compilable C syntax. 560 | let mut chain = Vec::new(); 561 | loop { 562 | chain.push(id); 563 | match self.btf.type_by_id(id) { 564 | BtfType::Ptr(t) => id = t.type_id, 565 | BtfType::Const(t) => id = t.type_id, 566 | BtfType::Volatile(t) => id = t.type_id, 567 | BtfType::Restrict(t) => id = t.type_id, 568 | BtfType::Array(t) => id = t.val_type_id, 569 | BtfType::FuncProto(t) => id = t.res_type_id, 570 | BtfType::Var(_) | BtfType::Datasec(_) | BtfType::Func(_) => { 571 | chain.pop(); 572 | print!("!@#! UNEXPECT TYPE DECL CHAIN "); 573 | for parent_id in chain.iter().rev() { 574 | print!("[{}] --> ", parent_id); 575 | } 576 | print!("[{}] {}", id, self.btf.type_by_id(id)); 577 | return; 578 | } 579 | _ => break, 580 | } 581 | } 582 | self.emit_type_chain(chain, fname, lvl); 583 | } 584 | 585 | fn emit_type_chain(&mut self, mut chain: Vec, fname: &str, lvl: usize) { 586 | // default to true, in case we have single ptr in a chain. E.g., in ptr -> func_proto case. 587 | // func_proto will start a new emit_type_chain with just ptr, which should be emitted as 588 | // (*) or (*), so we don't want to preprend space for that last ptr. 589 | let mut last_was_ptr = true; 590 | while let Some(id) = chain.pop() { 591 | match self.btf.type_by_id(id) { 592 | BtfType::Void => { 593 | self.emit_mods(&mut chain); 594 | print!("void"); 595 | } 596 | BtfType::Int(t) => { 597 | self.emit_mods(&mut chain); 598 | print!("{}", t.name); 599 | } 600 | BtfType::Struct(t) | BtfType::Union(t) => { 601 | self.emit_mods(&mut chain); 602 | if t.name.is_empty() { 603 | self.emit_composite_def(id, t, lvl); // inline anonymous struct 604 | } else { 605 | self.emit_composite_fwd(id, t); 606 | } 607 | } 608 | BtfType::Enum(t) => { 609 | self.emit_mods(&mut chain); 610 | if t.name.is_empty() { 611 | self.emit_enum_def(id, t, lvl); // inline anonymous enum 612 | } else { 613 | let uniq_name = self.resolve_type_name(NamedKind::Type, id, t.name); 614 | print!("enum {}", &uniq_name); 615 | } 616 | } 617 | BtfType::Enum64(t) => { 618 | self.emit_mods(&mut chain); 619 | if t.name.is_empty() { 620 | self.emit_enum64_def(id, t, lvl); // inline anonymous enum 621 | } else { 622 | let uniq_name = self.resolve_type_name(NamedKind::Type, id, t.name); 623 | print!("enum {}", &uniq_name); 624 | } 625 | } 626 | BtfType::Fwd(t) => { 627 | self.emit_mods(&mut chain); 628 | self.emit_fwd_def(id, t); 629 | } 630 | BtfType::Typedef(t) => { 631 | self.emit_mods(&mut chain); 632 | let uniq_name = self.resolve_type_name(NamedKind::Ident, id, t.name); 633 | print!("{}", &uniq_name); 634 | } 635 | BtfType::Ptr(_) => { 636 | if last_was_ptr { 637 | print!("*") 638 | } else { 639 | print!(" *") 640 | } 641 | } 642 | BtfType::Volatile(_) => { 643 | print!(" volatile"); 644 | } 645 | BtfType::Const(_) => { 646 | print!(" const"); 647 | } 648 | BtfType::Restrict(_) => { 649 | print!(" restrict"); 650 | } 651 | BtfType::Array(t) => { 652 | // GCC has a bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=8354) which 653 | // causes it to emit extra const/volatile modifier for array, if array's 654 | // element type has const/volatile modifier. Clang doesn't do that. 655 | // In general, it doesn't seem very meaningful to have a const/volatile 656 | // modifier for array, so we are going to silently skip them here. 657 | while let Some(id) = chain.pop() { 658 | match self.btf.type_by_id(id) { 659 | BtfType::Volatile(_) | BtfType::Const(_) | BtfType::Restrict(_) => {} 660 | _ => { 661 | chain.push(id); 662 | break; 663 | } 664 | } 665 | } 666 | if let Some(&next_id) = chain.last() { 667 | let t = self.btf.type_by_id(next_id); 668 | if !fname.is_empty() && !last_was_ptr { 669 | print!(" "); 670 | } 671 | if t.kind() != BtfKind::Array { 672 | print!("("); 673 | } 674 | self.emit_type_chain(chain, fname, lvl); 675 | if t.kind() != BtfKind::Array { 676 | print!(")"); 677 | } 678 | } else { 679 | self.emit_name(fname, last_was_ptr); 680 | } 681 | print!("[{}]", t.nelems); 682 | return; 683 | } 684 | BtfType::FuncProto(t) => { 685 | self.emit_mods(&mut chain); 686 | if chain.is_empty() { 687 | self.emit_name(fname, last_was_ptr); 688 | } else { 689 | print!(" ("); 690 | self.emit_type_chain(chain, fname, lvl); 691 | print!(")"); 692 | } 693 | print!("("); 694 | // 695 | // Clang for BPF target generates func_proto with no args as a func_proto with 696 | // a single void arg (i.e., (*f)(void) vs just (*f)()). 697 | // We are going to pretend there are no args for such case. 698 | let arg_cnt = t.params.len(); 699 | if arg_cnt == 1 && t.params[0].type_id == 0 { 700 | print!(")"); 701 | return; 702 | } 703 | 704 | for (i, p) in t.params.iter().enumerate() { 705 | if i > 0 { 706 | print!(", "); 707 | } 708 | // func_proto with vararg has last arg of type 'void' 709 | if i == arg_cnt - 1 && t.params[arg_cnt - 1].type_id == 0 { 710 | print!("..."); 711 | } else { 712 | self.emit_type_decl(p.type_id, p.name, lvl); 713 | } 714 | } 715 | print!(")"); 716 | return; 717 | } 718 | BtfType::Float(t) => { 719 | self.emit_mods(&mut chain); 720 | print!("{}", t.name); 721 | } 722 | BtfType::TypeTag(t) => { 723 | self.emit_mods(&mut chain); 724 | print!(" __attribute__((btf_tag((\"{}\")))", &t.name); 725 | } 726 | BtfType::Func(_) | BtfType::Var(_) | BtfType::Datasec(_) | BtfType::DeclTag(_) => { 727 | print!( 728 | "!@#! UNEXPECT TYPE DECL id: {}, type: {}", 729 | id, 730 | self.btf.type_by_id(id) 731 | ); 732 | } 733 | } 734 | if let BtfType::Ptr(_) = self.btf.type_by_id(id) { 735 | last_was_ptr = true; 736 | } else { 737 | last_was_ptr = false; 738 | } 739 | } 740 | self.emit_name(fname, last_was_ptr); 741 | } 742 | 743 | fn emit_name(&self, fname: &str, last_was_ptr: bool) { 744 | if last_was_ptr { 745 | print!("{}", fname); 746 | } else { 747 | print!("{}{}", sep(fname), fname); 748 | } 749 | } 750 | 751 | fn emit_mods(&self, chain: &mut Vec) { 752 | while let Some(id) = chain.pop() { 753 | match self.btf.type_by_id(id) { 754 | BtfType::Volatile(_) => { 755 | print!("volatile "); 756 | } 757 | BtfType::Const(_) => { 758 | print!("const "); 759 | } 760 | BtfType::Restrict(_) => { 761 | print!("restrict "); 762 | } 763 | _ => { 764 | chain.push(id); 765 | break; 766 | } 767 | } 768 | } 769 | } 770 | 771 | fn resolve_type_name(&mut self, kind: NamedKind, id: u32, name: &'a str) -> String { 772 | if name.is_empty() { 773 | return EMPTY.to_owned(); 774 | } 775 | let s = &mut self.state[id as usize]; 776 | if s.name.is_empty() { 777 | let version = self.names.entry((kind, name)).or_insert(0); 778 | *version += 1; 779 | if *version == 1 { 780 | s.name = name.to_string() 781 | } else { 782 | s.name = format!("{}___{}", name, version) 783 | } 784 | } 785 | s.name.clone() 786 | } 787 | 788 | fn resolve_name(&mut self, kind: NamedKind, name: &'a str) -> String { 789 | let version = self.names.entry((kind, name)).or_insert(0); 790 | *version += 1; 791 | if *version == 1 { 792 | name.to_string() 793 | } else { 794 | format!("{}___{}", name, version) 795 | } 796 | } 797 | } 798 | 799 | static NAMES_BLACKLIST: LazyLock = 800 | LazyLock::new(|| RegexSet::new(["__builtin_va_list"]).expect("invalid blacklist regexes")); 801 | 802 | const EMPTY: &str = ""; 803 | const SPACE: &str = " "; 804 | const PREFIXES: &str = "\t\t\t\t\t\t\t\t\t\t\t\t"; 805 | 806 | fn sep(name: &str) -> &str { 807 | if name.is_empty() { 808 | EMPTY 809 | } else { 810 | SPACE 811 | } 812 | } 813 | 814 | fn pfx(lvl: usize) -> &'static str { 815 | if lvl >= PREFIXES.len() { 816 | PREFIXES 817 | } else { 818 | &PREFIXES[0..lvl] 819 | } 820 | } 821 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt; 3 | 4 | pub mod btf_index; 5 | pub mod c_dumper; 6 | pub mod relocator; 7 | pub mod types; 8 | 9 | #[derive(Clone, Debug)] 10 | pub struct BtfError { 11 | details: String, 12 | } 13 | 14 | impl BtfError { 15 | pub fn new(msg: &str) -> BtfError { 16 | BtfError { 17 | details: msg.to_string(), 18 | } 19 | } 20 | pub fn new_owned(msg: String) -> BtfError { 21 | BtfError { details: msg } 22 | } 23 | } 24 | 25 | impl fmt::Display for BtfError { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | write!(f, "{}", self.details) 28 | } 29 | } 30 | 31 | impl Error for BtfError { 32 | fn description(&self) -> &str { 33 | &self.details 34 | } 35 | } 36 | 37 | pub type BtfResult = Result>; 38 | 39 | pub fn btf_error(msg: String) -> BtfResult { 40 | Err(Box::new(BtfError::new_owned(msg))) 41 | } 42 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::collections::HashMap; 3 | use std::convert::TryInto as _; 4 | use std::error::Error; 5 | use std::io::Read as _; 6 | use std::io::Write as _; 7 | 8 | use bitflags::bitflags; 9 | use clap::builder::TypedValueParser as _; 10 | use memmap2 as memmap; 11 | use object::{Object, ObjectSection}; 12 | use regex::Regex; 13 | use scroll::Pread; 14 | use std::mem::size_of; 15 | use std::mem::size_of_val; 16 | use std::str::FromStr as _; 17 | 18 | use btf::c_dumper; 19 | use btf::relocator::{Relocator, RelocatorCfg}; 20 | use btf::types::*; 21 | use btf::{btf_error, BtfError, BtfResult}; 22 | 23 | const VERSION: &str = env!("CARGO_PKG_VERSION"); 24 | 25 | #[derive(Clone, Debug)] 26 | enum DumpFormat { 27 | Human, 28 | Json, 29 | JsonPretty, 30 | C, 31 | } 32 | 33 | impl std::str::FromStr for DumpFormat { 34 | type Err = BtfError; 35 | 36 | fn from_str(s: &str) -> Result { 37 | match s { 38 | "human" | "h" => Ok(DumpFormat::Human), 39 | "json" | "j" => Ok(DumpFormat::Json), 40 | "json-pretty" | "jp" => Ok(DumpFormat::JsonPretty), 41 | "c" => Ok(DumpFormat::C), 42 | _ => Err(BtfError::new_owned(format!( 43 | "unrecognized dump format: '{}'", 44 | s 45 | ))), 46 | } 47 | } 48 | } 49 | 50 | bitflags! { 51 | #[derive(Clone)] 52 | struct Datasets : u32 { 53 | const NONE = 0b0000; 54 | const TYPES = 0b0001; 55 | const FUNCINFOS = 0b0010; 56 | const LINEINFOS = 0b0100; 57 | const RELOCS = 0b1000; 58 | 59 | const DEFAULT = Self::TYPES.bits() | Self::RELOCS.bits(); 60 | const EXT = Self::FUNCINFOS.bits() | Self::LINEINFOS.bits() | Self::RELOCS.bits(); 61 | const ALL = Self::TYPES.bits() | Self::EXT.bits(); 62 | } 63 | } 64 | 65 | impl Default for Datasets { 66 | fn default() -> Datasets { 67 | Datasets::NONE 68 | } 69 | } 70 | 71 | impl std::str::FromStr for Datasets { 72 | type Err = BtfError; 73 | 74 | fn from_str(s: &str) -> Result { 75 | match s { 76 | "none" => Ok(Datasets::NONE), 77 | "types" | "type" | "t" => Ok(Datasets::TYPES), 78 | "funcs" | "func" | "f" => Ok(Datasets::FUNCINFOS), 79 | "lines" | "line" | "l" => Ok(Datasets::LINEINFOS), 80 | "relocs" | "reloc" | "r" => Ok(Datasets::RELOCS), 81 | "exts" | "ext" | "e" => Ok(Datasets::EXT), 82 | "all" | "a" => Ok(Datasets::ALL), 83 | "default" | "def" | "d" => Ok(Datasets::DEFAULT), 84 | _ => Err(BtfError::new_owned(format!( 85 | "unrecognized dataset: '{}'", 86 | s 87 | ))), 88 | } 89 | } 90 | } 91 | 92 | #[derive(clap::Parser)] 93 | struct QueryArgs { 94 | #[clap(short = 'n', long = "name")] 95 | /// Regex of type names to include 96 | name: Option, 97 | #[clap(short = 't', long = "type", use_value_delimiter = true)] 98 | /// BTF type kinds to include 99 | kinds: Vec, 100 | #[clap(long = "id", use_value_delimiter = true)] 101 | /// Type IDs to include 102 | ids: Vec, 103 | } 104 | 105 | #[derive(clap::Parser)] 106 | #[clap(name = "btfdump")] 107 | /// BTF introspection and manipulation tool 108 | enum Cmd { 109 | #[clap(name = "dump")] 110 | /// Query and pretty-print matching BTF data 111 | Dump { 112 | file: std::path::PathBuf, 113 | #[clap( 114 | short = 'f', 115 | long = "format", 116 | default_value = "human", 117 | value_parser = clap::builder::PossibleValuesParser::new([ 118 | "human", 119 | "h", 120 | "c", 121 | "json", 122 | "j", 123 | "json-pretty", 124 | "jp", 125 | ]).map(|s| DumpFormat::from_str(&s).unwrap()), 126 | )] 127 | /// Output format 128 | format: DumpFormat, 129 | #[clap( 130 | short = 'd', 131 | long = "dataset", 132 | default_value = "default", 133 | value_parser = clap::builder::PossibleValuesParser::new([ 134 | "default", 135 | "def", 136 | "d", 137 | "types", 138 | "type", 139 | "t", 140 | "funcs", 141 | "func", 142 | "f", 143 | "lines", 144 | "line", 145 | "l", 146 | "relocs", 147 | "reloc", 148 | "r", 149 | "all", 150 | "a", 151 | "exts", 152 | "ext", 153 | "none", 154 | ]).map(|s| Datasets::from_str(&s).unwrap()), 155 | )] 156 | /// Datasets to output 157 | datasets: Datasets, 158 | #[clap(flatten)] 159 | query: QueryArgs, 160 | #[clap(short = 'v', long = "verbose")] 161 | /// Output verbose log 162 | verbose: bool, 163 | #[clap(long = "union-as-struct")] 164 | /// Replace unions with structs (for BPF CORE) 165 | union_as_struct: bool, 166 | }, 167 | #[clap(name = "reloc")] 168 | /// Print detailed relocation information 169 | Reloc { 170 | /// Kernel image (target BTF) 171 | targ_file: std::path::PathBuf, 172 | /// BPF program (local BTF) 173 | local_file: std::path::PathBuf, 174 | #[clap(short = 'v', long = "verbose")] 175 | /// Output verbose log 176 | verbose: bool, 177 | }, 178 | #[clap(name = "stat")] 179 | /// Stats about .BTF and .BTF.ext data 180 | Stat { file: std::path::PathBuf }, 181 | 182 | #[clap(name = "version")] 183 | /// Print btfdump version 184 | Version, 185 | } 186 | 187 | fn load_file<'a>( 188 | file: impl AsRef, 189 | contents: &'a mut Vec, 190 | mmap: &'a mut Option, 191 | ) -> BtfResult> { 192 | let mut file = std::fs::File::open(file)?; 193 | 194 | // Read the magic number first. 195 | let size = size_of_val(&BTF_MAGIC).try_into()?; 196 | std::io::Read::by_ref(&mut file) 197 | .take(size) 198 | .read_to_end(contents)?; 199 | 200 | if *contents == BTF_MAGIC.to_ne_bytes() { 201 | // If the file starts with BTF magic number, parse BTF from the 202 | // full file content. 203 | 204 | file.read_to_end(contents)?; 205 | Btf::load_raw(&*contents) 206 | } else { 207 | // Otherwise, assume it's an object file and parse BTF from 208 | // the `.BTF` section. 209 | 210 | let file = unsafe { memmap::Mmap::map(&file) }?; 211 | let file = &*mmap.insert(file); 212 | let file = object::File::parse(file.as_ref())?; 213 | Btf::load_elf(&file) 214 | } 215 | } 216 | 217 | macro_rules! load_btf { 218 | ($ident:ident, $file:expr) => { 219 | // These variables must be declared in the caller because the return 220 | // value of load_file is borrowed from them. 221 | let mut contents = Vec::new(); 222 | let mut mmap = None; 223 | 224 | let $ident = load_file($file, &mut contents, &mut mmap)?; 225 | }; 226 | } 227 | 228 | fn main() -> Result<(), Box> { 229 | let cmd = clap::Parser::parse(); 230 | 231 | match cmd { 232 | Cmd::Dump { 233 | file, 234 | format, 235 | datasets, 236 | query, 237 | verbose, 238 | union_as_struct, 239 | } => { 240 | load_btf!(btf, file); 241 | let filter = create_query_filter(query)?; 242 | 243 | match format { 244 | DumpFormat::Human => { 245 | if datasets.contains(Datasets::TYPES) { 246 | for (i, t) in btf.types().iter().enumerate() { 247 | if filter(i as u32, t) { 248 | println!("#{}: {}", i, t); 249 | } 250 | } 251 | } 252 | if datasets.contains(Datasets::FUNCINFOS) { 253 | for (i, sec) in btf.func_secs().iter().enumerate() { 254 | println!("\nFunc section #{} '{}':", i, sec.name); 255 | for (j, rec) in sec.recs.iter().enumerate() { 256 | println!("#{}: {}", j, rec); 257 | } 258 | } 259 | } 260 | if datasets.contains(Datasets::LINEINFOS) { 261 | for (i, sec) in btf.line_secs().iter().enumerate() { 262 | println!("\nLine section #{} '{}':", i, sec.name); 263 | for (j, rec) in sec.recs.iter().enumerate() { 264 | println!("#{}: {}", j, rec); 265 | } 266 | } 267 | } 268 | if datasets.contains(Datasets::RELOCS) { 269 | for (i, sec) in btf.core_reloc_secs().iter().enumerate() { 270 | println!("\nCore reloc section #{} '{}':", i, sec.name); 271 | for (j, rec) in sec.recs.iter().enumerate() { 272 | print!("#{}: {} --> ", j, rec); 273 | std::io::stdout().flush()?; 274 | match Relocator::pretty_print_access_spec(&btf, rec) { 275 | Ok(s) => print!("{}", s), 276 | Err(e) => print!(" ERROR: {}", e), 277 | }; 278 | println!(); 279 | } 280 | } 281 | } 282 | } 283 | DumpFormat::Json => panic!("JSON output is not yet supported!"), 284 | DumpFormat::JsonPretty => panic!("JSON output is not yet supported!"), 285 | DumpFormat::C => { 286 | let cfg = c_dumper::CDumperCfg { 287 | verbose, 288 | union_as_struct, 289 | }; 290 | let mut dumper = c_dumper::CDumper::new(&btf, cfg); 291 | dumper.dump_types(filter)?; 292 | } 293 | } 294 | } 295 | Cmd::Reloc { 296 | targ_file, 297 | local_file, 298 | verbose, 299 | } => { 300 | load_btf!(local_btf, local_file); 301 | if !local_btf.has_ext() { 302 | return btf_error(format!( 303 | "No {} section found for local ELF file, can't perform relocations.", 304 | BTF_EXT_ELF_SEC 305 | )); 306 | } 307 | load_btf!(targ_btf, targ_file); 308 | let cfg = RelocatorCfg { verbose }; 309 | let mut relocator = Relocator::new(&targ_btf, &local_btf, cfg); 310 | let relocs = relocator.relocate()?; 311 | for r in relocs { 312 | println!("{}", r); 313 | } 314 | } 315 | Cmd::Stat { file } => { 316 | let file = std::fs::File::open(&file)?; 317 | let file = unsafe { memmap::Mmap::map(&file) }?; 318 | let file = object::File::parse(&*file)?; 319 | stat_elf(&file)?; 320 | } 321 | Cmd::Version => { 322 | println!("btfdump v{}", VERSION); 323 | } 324 | } 325 | Ok(()) 326 | } 327 | 328 | type Filter = Box bool>; 329 | 330 | fn create_query_filter(q: QueryArgs) -> BtfResult { 331 | let mut filters: Vec = Vec::new(); 332 | if !q.kinds.is_empty() { 333 | let kinds = q.kinds; 334 | filters.push(Box::new(move |_id: u32, bt: &BtfType| -> bool { 335 | kinds.contains(&bt.kind()) 336 | })); 337 | } 338 | if !q.ids.is_empty() { 339 | let ids = q.ids; 340 | filters.push(Box::new(move |id: u32, _bt: &BtfType| -> bool { 341 | ids.contains(&id) 342 | })); 343 | } 344 | if let Some(name) = q.name { 345 | let name_regex = Regex::new(&name)?; 346 | filters.push(Box::new(move |_id: u32, bt: &BtfType| -> bool { 347 | name_regex.is_match(bt.name()) 348 | })); 349 | } 350 | if !filters.is_empty() { 351 | Ok(Box::new(move |id: u32, bt: &BtfType| -> bool { 352 | for f in &filters { 353 | if f(id, bt) { 354 | return true; 355 | } 356 | } 357 | false 358 | })) 359 | } else { 360 | Ok(Box::new(|_: u32, _: &BtfType| true)) 361 | } 362 | } 363 | 364 | fn stat_elf(elf: &object::File) -> BtfResult<()> { 365 | let endian = if elf.is_little_endian() { 366 | scroll::LE 367 | } else { 368 | scroll::BE 369 | }; 370 | if let Some(btf_section) = elf.section_by_name(BTF_ELF_SEC) { 371 | let data = btf_section.data()?; 372 | let hdr = data.pread_with::(0, endian)?; 373 | println!( 374 | "{} ELF section\n=======================================", 375 | BTF_ELF_SEC 376 | ); 377 | println!("Data size:\t{}", data.len()); 378 | println!("Header size:\t{}", hdr.hdr_len); 379 | println!("Types size:\t{}", hdr.type_len); 380 | println!("Strings size:\t{}", hdr.str_len); 381 | } else { 382 | println!("{} not found.", BTF_ELF_SEC); 383 | return Ok(()); 384 | } 385 | println!( 386 | "\n{} ELF section\n========================================", 387 | BTF_EXT_ELF_SEC 388 | ); 389 | if let Some(ext_section) = elf.section_by_name(BTF_EXT_ELF_SEC) { 390 | let ext_data = ext_section.data()?; 391 | let ext_hdr = ext_data.pread_with::(0, endian)?; 392 | println!("Data size:\t{}", ext_data.len()); 393 | println!("Header size:\t{}", ext_hdr.hdr_len); 394 | println!("Func info size:\t{}", ext_hdr.func_info_len); 395 | println!("Line info size:\t{}", ext_hdr.line_info_len); 396 | if ext_hdr.hdr_len >= size_of::() as u32 { 397 | let ext_hdr2 = ext_data.pread_with::(0, endian)?; 398 | println!("Relocs size:\t{}", ext_hdr2.core_reloc_len); 399 | } 400 | } else { 401 | println!("{} not found.", BTF_EXT_ELF_SEC); 402 | } 403 | match Btf::load_elf(elf) { 404 | Err(e) => println!("Failed to parse BTF data: {}", e), 405 | Ok(btf) => { 406 | let mut type_stats: HashMap = HashMap::new(); 407 | for t in &btf.types()[1..] { 408 | let (cnt, sz) = type_stats.entry(t.kind()).or_insert((0, 0)); 409 | *cnt += 1; 410 | *sz += Btf::type_size(t); 411 | } 412 | let mut total_cnt = 0; 413 | let mut total_sz = 0; 414 | for (cnt, sz) in type_stats.values() { 415 | total_cnt += cnt; 416 | total_sz += sz; 417 | } 418 | let mut type_stats = type_stats 419 | .into_iter() 420 | .map(|(k, (cnt, sz))| (k, cnt, sz)) 421 | .collect::>(); 422 | type_stats.sort_by_key(|&(_, _, sz)| std::cmp::Reverse(sz)); 423 | println!("\nBTF types\n======================================="); 424 | println!("{:10} {:9} bytes ({} types)", "Total", total_sz, total_cnt); 425 | for (k, cnt, sz) in type_stats { 426 | println!("{:10} {:9} bytes ({} types)", format!("{:?}:", k), sz, cnt); 427 | } 428 | 429 | if btf.has_ext() { 430 | #[derive(Default)] 431 | struct Section { 432 | func_cnt: usize, 433 | func_sz: usize, 434 | line_cnt: usize, 435 | line_sz: usize, 436 | core_reloc_cnt: usize, 437 | core_reloc_sz: usize, 438 | } 439 | let mut sec_stats = BTreeMap::<_, Section>::new(); 440 | let mut total = Section::default(); 441 | for sec in btf.func_secs() { 442 | let s = sec_stats.entry(&sec.name).or_default(); 443 | s.func_cnt += sec.recs.len(); 444 | s.func_sz += sec.rec_sz * sec.recs.len(); 445 | total.func_cnt += sec.recs.len(); 446 | total.func_sz += sec.rec_sz * sec.recs.len(); 447 | } 448 | for sec in btf.line_secs() { 449 | let s = sec_stats.entry(&sec.name).or_default(); 450 | s.line_cnt += sec.recs.len(); 451 | s.line_sz += sec.rec_sz * sec.recs.len(); 452 | total.line_cnt += sec.recs.len(); 453 | total.line_sz += sec.rec_sz * sec.recs.len(); 454 | } 455 | for sec in btf.core_reloc_secs() { 456 | let s = sec_stats.entry(&sec.name).or_default(); 457 | s.core_reloc_cnt += sec.recs.len(); 458 | s.core_reloc_sz += sec.rec_sz * sec.recs.len(); 459 | total.core_reloc_cnt += sec.recs.len(); 460 | total.core_reloc_sz += sec.rec_sz * sec.recs.len(); 461 | } 462 | println!("\nBTF ext sections\n======================================="); 463 | println!( 464 | "{:32} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}", 465 | "Section", 466 | "Func sz", 467 | "Func cnt", 468 | "Line sz", 469 | "Line cnt", 470 | "Reloc sz", 471 | "Reloc cnt" 472 | ); 473 | println!( 474 | "{:32} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}", 475 | "--------------------------------", 476 | "----------", 477 | "----------", 478 | "----------", 479 | "----------", 480 | "----------", 481 | "----------", 482 | ); 483 | for (k, s) in sec_stats { 484 | println!( 485 | "{:32} {:10} {:10} {:10} {:10} {:10} {:10}", 486 | k, 487 | s.func_sz, 488 | s.func_cnt, 489 | s.line_sz, 490 | s.line_cnt, 491 | s.core_reloc_sz, 492 | s.core_reloc_cnt 493 | ); 494 | } 495 | println!( 496 | "{:32} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10}", 497 | "--------------------------------", 498 | "----------", 499 | "----------", 500 | "----------", 501 | "----------", 502 | "----------", 503 | "----------", 504 | ); 505 | println!( 506 | "{:32} {:10} {:10} {:10} {:10} {:10} {:10}", 507 | "Total", 508 | total.func_sz, 509 | total.func_cnt, 510 | total.line_sz, 511 | total.line_cnt, 512 | total.core_reloc_sz, 513 | total.core_reloc_cnt 514 | ); 515 | } 516 | } 517 | } 518 | Ok(()) 519 | } 520 | -------------------------------------------------------------------------------- /src/relocator.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fmt; 3 | use std::fmt::Write; 4 | 5 | use crate::btf_index::BtfIndex; 6 | use crate::types::*; 7 | use crate::{btf_error, BtfResult}; 8 | 9 | #[derive(Debug)] 10 | pub 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, 16 | pub targ_type_id: u32, 17 | pub targ_offset: usize, 18 | pub targ_spec: Vec, 19 | } 20 | 21 | impl 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)] 39 | enum 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 | 51 | impl 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)] 65 | pub struct RelocatorCfg { 66 | pub verbose: bool, 67 | } 68 | 69 | #[derive(Debug)] 70 | pub 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>, 76 | } 77 | 78 | impl<'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, 82 | targ_btf, 83 | local_btf, 84 | targ_index: BtfIndex::new(targ_btf), 85 | type_map: HashMap::new(), 86 | } 87 | } 88 | 89 | pub fn relocate(&mut self) -> BtfResult> { 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, 175 | 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, 180 | targ_offset: targ_off as usize, 181 | 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> { 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, s) in spec.iter().enumerate().skip(1) { 201 | let s = *s; 202 | id = btf.skip_mods_and_typedefs(id); 203 | match btf.type_by_id(id) { 204 | BtfType::Struct(t) => { 205 | let m = &t.members[s]; 206 | let next_id = btf.skip_mods_and_typedefs(m.type_id); 207 | if !m.name.is_empty() { 208 | res.push(Accessor::Field { 209 | type_id: id, 210 | field_idx: s, 211 | field_name: m.name.to_owned(), 212 | }); 213 | } 214 | id = next_id; 215 | } 216 | BtfType::Union(t) => { 217 | let m = &t.members[s]; 218 | let next_id = btf.skip_mods_and_typedefs(m.type_id); 219 | if !m.name.is_empty() { 220 | res.push(Accessor::Field { 221 | type_id: id, 222 | field_idx: s, 223 | field_name: m.name.to_owned(), 224 | }); 225 | } 226 | id = next_id; 227 | } 228 | BtfType::Array(t) => { 229 | id = btf.skip_mods_and_typedefs(t.val_type_id); 230 | res.push(Accessor::Array { 231 | type_id: id, 232 | arr_idx: s, 233 | }); 234 | } 235 | _ => spec_error( 236 | spec, 237 | i, 238 | "must be struct/union/array", 239 | id, 240 | btf.type_by_id(id), 241 | )?, 242 | } 243 | } 244 | Ok(res) 245 | } 246 | 247 | fn calc_off(&self, btf: &Btf, type_id: u32, spec: &[usize]) -> BtfResult { 248 | let mut id = btf.skip_mods_and_typedefs(type_id); 249 | let mut off = spec[0] as u32 * Relocator::type_size(btf, id)?; 250 | 251 | for (i, s) in spec.iter().enumerate().skip(1) { 252 | let s = *s; 253 | id = btf.skip_mods_and_typedefs(id); 254 | match btf.type_by_id(id) { 255 | BtfType::Struct(t) => { 256 | let m = &t.members[s]; 257 | off += m.bit_offset / 8; 258 | id = m.type_id; 259 | } 260 | BtfType::Union(t) => { 261 | let m = &t.members[s]; 262 | off += m.bit_offset / 8; 263 | id = m.type_id; 264 | } 265 | BtfType::Array(t) => { 266 | off += s as u32 * Relocator::type_size(btf, t.val_type_id)?; 267 | id = t.val_type_id; 268 | } 269 | _ => spec_error( 270 | spec, 271 | i, 272 | "must be struct/union/array", 273 | id, 274 | btf.type_by_id(id), 275 | )?, 276 | } 277 | } 278 | Ok(off) 279 | } 280 | 281 | fn calc_targ_spec(&self, local_spec: &[Accessor], mut targ_id: u32) -> BtfResult> { 282 | targ_id = self.targ_btf.skip_mods_and_typedefs(targ_id); 283 | let mut targ_type = self.targ_btf.type_by_id(targ_id); 284 | let mut targ_spec = Vec::new(); 285 | 286 | match local_spec[0] { 287 | Accessor::Array { arr_idx, .. } => targ_spec.push(arr_idx), 288 | _ => btf_error(format!( 289 | "first spec must be array access, but is: {}", 290 | local_spec[0] 291 | ))?, 292 | } 293 | 294 | for (i, s) in local_spec.iter().enumerate().skip(1) { 295 | match s { 296 | &Accessor::Array { arr_idx, .. } => match targ_type { 297 | BtfType::Array(t) => { 298 | targ_id = self.targ_btf.skip_mods_and_typedefs(t.val_type_id); 299 | targ_type = self.targ_btf.type_by_id(targ_id); 300 | targ_spec.push(arr_idx); 301 | } 302 | _ => access_error(s, i, "target must be array", targ_id, targ_type)?, 303 | }, 304 | Accessor::Field { 305 | type_id: local_id, 306 | field_idx, 307 | .. 308 | } => { 309 | let local_type = self.local_btf.type_by_id(*local_id); 310 | let local_members = match local_type { 311 | BtfType::Struct(t) => &t.members, 312 | BtfType::Union(t) => &t.members, 313 | _ => { 314 | access_error(s, i, "local must be struct/union", *local_id, local_type)? 315 | } 316 | }; 317 | let local_member = &local_members[*field_idx]; 318 | let targ_members = match targ_type { 319 | BtfType::Struct(t) => &t.members, 320 | BtfType::Union(t) => &t.members, 321 | _ => access_error(s, i, "target must be struct/union", targ_id, targ_type)?, 322 | }; 323 | match self.targ_member_spec(local_member, targ_members) { 324 | Ok(Some((t_id, mut t_spec))) => { 325 | targ_id = t_id; 326 | targ_type = self.targ_btf.type_by_id(targ_id); 327 | targ_spec.append(&mut t_spec); 328 | } 329 | Ok(None) => { 330 | access_error(s, i, "target field not found", targ_id, targ_type)? 331 | } 332 | Err(e) => access_error(s, i, &format!("{}", e), targ_id, targ_type)?, 333 | } 334 | } 335 | } 336 | } 337 | Ok(targ_spec) 338 | } 339 | 340 | fn targ_member_spec( 341 | &self, 342 | local_member: &BtfMember, 343 | targ_members: &[BtfMember], 344 | ) -> BtfResult)>> { 345 | for (i, m) in targ_members.iter().enumerate() { 346 | if m.name == local_member.name { 347 | let local_id = self.local_btf.skip_mods_and_typedefs(local_member.type_id); 348 | let targ_id = self.targ_btf.skip_mods_and_typedefs(m.type_id); 349 | if self.are_kinds_compat(local_id, targ_id) { 350 | return Ok(Some((targ_id, vec![i]))); 351 | } else { 352 | return btf_error(format!( 353 | concat!( 354 | "incompatible types for field '{}', ", 355 | "local_id: {}, local_kind: {:?}, ", 356 | "targ_id: {}, targ_kind: {:?}" 357 | ), 358 | local_member.name, 359 | local_id, 360 | self.local_btf.type_by_id(local_id).kind(), 361 | targ_id, 362 | self.targ_btf.type_by_id(targ_id).kind() 363 | )); 364 | } 365 | } else if m.name.is_empty() { 366 | if let Some(members) = self.get_composite_members(self.targ_btf, m.type_id) { 367 | match self.targ_member_spec(local_member, members) { 368 | Ok(Some((t_id, mut spec))) => { 369 | spec.insert(0, i); 370 | return Ok(Some((t_id, spec))); 371 | } 372 | Ok(None) => {} 373 | e @ Err(_) => return e, 374 | } 375 | } 376 | } 377 | } 378 | Ok(None) 379 | } 380 | 381 | fn get_composite_members<'c>(&self, btf: &'c Btf, type_id: u32) -> Option<&'c [BtfMember<'c>]> { 382 | let id = btf.skip_mods(type_id); 383 | match btf.type_by_id(id) { 384 | BtfType::Struct(t) => Some(&t.members), 385 | BtfType::Union(t) => Some(&t.members), 386 | _ => None, 387 | } 388 | } 389 | 390 | fn are_kinds_compat(&self, local_id: u32, targ_id: u32) -> bool { 391 | let local_kind = self.local_btf.type_by_id(local_id).kind(); 392 | let targ_kind = self.targ_btf.type_by_id(targ_id).kind(); 393 | local_kind == targ_kind || (local_kind == BtfKind::Struct && targ_kind == BtfKind::Union) 394 | } 395 | 396 | fn type_size(btf: &Btf, type_id: u32) -> BtfResult { 397 | let id = btf.skip_mods_and_typedefs(type_id); 398 | Ok(match btf.type_by_id(id) { 399 | BtfType::Int(t) if t.offset == 0 && t.bits % 8 == 0 => t.bits / 8, 400 | BtfType::Enum(t) => t.sz, 401 | BtfType::Struct(t) => t.sz, 402 | BtfType::Union(t) => t.sz, 403 | BtfType::Array(t) => t.nelems * Relocator::type_size(btf, t.val_type_id)?, 404 | BtfType::Ptr(_) => btf.ptr_sz(), 405 | _ => btf_error(format!( 406 | "can't calculate byte size of type_id: {}, type: {}", 407 | id, 408 | btf.type_by_id(id), 409 | ))?, 410 | }) 411 | } 412 | 413 | fn relo_is_field_based(kind: BtfCoreRelocKind) -> bool { 414 | matches!( 415 | kind, 416 | BtfCoreRelocKind::ByteOff 417 | | BtfCoreRelocKind::ByteSz 418 | | BtfCoreRelocKind::FieldExists 419 | | BtfCoreRelocKind::Signed 420 | | BtfCoreRelocKind::LShiftU64 421 | | BtfCoreRelocKind::RShiftU64 422 | ) 423 | } 424 | 425 | fn relo_is_type_based(kind: BtfCoreRelocKind) -> bool { 426 | matches!( 427 | kind, 428 | BtfCoreRelocKind::LocalTypeId 429 | | BtfCoreRelocKind::TargetTypeId 430 | | BtfCoreRelocKind::TypeExists 431 | | BtfCoreRelocKind::TypeMatches 432 | | BtfCoreRelocKind::TypeSize 433 | ) 434 | } 435 | 436 | fn relo_is_enumval_based(kind: BtfCoreRelocKind) -> bool { 437 | matches!( 438 | kind, 439 | BtfCoreRelocKind::EnumvalExists | BtfCoreRelocKind::EnumvalValue 440 | ) 441 | } 442 | 443 | pub fn pretty_print_access_spec(btf: &Btf, rec: &BtfExtCoreReloc) -> BtfResult { 444 | let mut buf = String::new(); 445 | let spec = &rec.access_spec; 446 | let mut id = rec.type_id; 447 | match btf.type_by_id(id) { 448 | BtfType::Struct(t) => { 449 | write!( 450 | buf, 451 | "struct {}", 452 | if t.name.is_empty() { "" } else { &t.name } 453 | )?; 454 | } 455 | BtfType::Union(t) => { 456 | write!( 457 | buf, 458 | "union {}", 459 | if t.name.is_empty() { "" } else { &t.name } 460 | )?; 461 | } 462 | BtfType::Typedef(t) => { 463 | write!( 464 | buf, 465 | "typedef {}", 466 | if t.name.is_empty() { "" } else { &t.name } 467 | )?; 468 | } 469 | BtfType::Enum(t) => { 470 | write!( 471 | buf, 472 | "enum {}", 473 | if t.name.is_empty() { "" } else { &t.name } 474 | )?; 475 | } 476 | BtfType::Ptr(t) => { 477 | write!(buf, "ptr -> [{}]", t.type_id)?; 478 | } 479 | BtfType::Array(t) => { 480 | write!(buf, "arr[{}] -> [{}]", t.nelems, t.val_type_id)?; 481 | } 482 | BtfType::Int(t) => { 483 | write!(buf, "int {}", t.name)?; 484 | } 485 | _ => spec_error( 486 | spec, 487 | 0, 488 | "must be struct/union/typedef/enum/ptr/array", 489 | id, 490 | btf.type_by_id(id), 491 | )?, 492 | } 493 | 494 | if Relocator::relo_is_type_based(rec.kind) { 495 | return Ok(buf); 496 | } 497 | 498 | if Relocator::relo_is_enumval_based(rec.kind) { 499 | id = btf.skip_mods_and_typedefs(rec.type_id); 500 | match btf.type_by_id(id) { 501 | BtfType::Enum(t) => { 502 | let e = &t.values[spec[0]]; 503 | write!(buf, "::{} = {}", &e.name, e.value)?; 504 | } 505 | _ => spec_error(spec, 0, "must be enum", id, btf.type_by_id(id))?, 506 | } 507 | return Ok(buf); 508 | } 509 | 510 | if !Relocator::relo_is_field_based(rec.kind) { 511 | write!(buf, " ... ERROR: unexpected relocation kind")?; 512 | return Ok(buf); 513 | } 514 | 515 | if spec[0] > 0 { 516 | write!(buf, "[{}]", spec[0])?; 517 | } 518 | 519 | for (i, s) in spec.iter().enumerate().skip(1) { 520 | let s = *s; 521 | match btf.type_by_id(id) { 522 | BtfType::Struct(t) => { 523 | let m = &t.members[s]; 524 | write!(buf, ".{}", m.name)?; 525 | id = btf.skip_mods_and_typedefs(m.type_id); 526 | } 527 | BtfType::Union(t) => { 528 | let m = &t.members[s]; 529 | if !m.name.is_empty() { 530 | write!(buf, ".{}", m.name)?; 531 | } else { 532 | write!(buf, ".")?; 533 | } 534 | id = btf.skip_mods_and_typedefs(m.type_id); 535 | } 536 | BtfType::Array(t) => { 537 | write!(buf, "[{}]", s)?; 538 | id = btf.skip_mods_and_typedefs(t.val_type_id); 539 | } 540 | _ => spec_error( 541 | spec, 542 | i, 543 | "must be struct/union/array", 544 | id, 545 | btf.type_by_id(id), 546 | )?, 547 | } 548 | } 549 | Ok(buf) 550 | } 551 | 552 | fn spec_to_str(spec: &[usize]) -> String { 553 | spec.iter() 554 | .map(|x| x.to_string()) 555 | .collect::>() 556 | .join(":") 557 | } 558 | } 559 | 560 | fn spec_error( 561 | spec: &[usize], 562 | idx: usize, 563 | details: &str, 564 | type_id: u32, 565 | bt: &BtfType, 566 | ) -> BtfResult { 567 | btf_error(format!( 568 | "Unsupported accessor: {}, at #{}: {}, but is type_id: {}, type: {}", 569 | Relocator::spec_to_str(spec), 570 | idx, 571 | details, 572 | type_id, 573 | bt, 574 | ))? 575 | } 576 | fn access_error( 577 | spec: &Accessor, 578 | idx: usize, 579 | details: &str, 580 | type_id: u32, 581 | bt: &BtfType, 582 | ) -> BtfResult { 583 | btf_error(format!( 584 | "Unsupported accessor: {}, at #{}: {}, but is type_id: {}, type: {}", 585 | spec, idx, details, type_id, bt, 586 | ))? 587 | } 588 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::{max, min}; 2 | use std::ffi::{c_char, CStr}; 3 | use std::fmt; 4 | use std::mem::size_of; 5 | 6 | use object::{Object, ObjectSection}; 7 | use scroll::{Endian, Pread}; 8 | use scroll_derive::{IOread, IOwrite, Pread as DerivePread, Pwrite, SizeWith}; 9 | 10 | use crate::{btf_error, BtfError, BtfResult}; 11 | 12 | pub const BTF_ELF_SEC: &str = ".BTF"; 13 | pub const BTF_EXT_ELF_SEC: &str = ".BTF.ext"; 14 | 15 | pub const BTF_MAGIC: u16 = 0xeb9f; 16 | pub const BTF_VERSION: u8 = 1; 17 | 18 | pub const BTF_KIND_UNKN: u32 = 0; 19 | pub const BTF_KIND_INT: u32 = 1; 20 | pub const BTF_KIND_PTR: u32 = 2; 21 | pub const BTF_KIND_ARRAY: u32 = 3; 22 | pub const BTF_KIND_STRUCT: u32 = 4; 23 | pub const BTF_KIND_UNION: u32 = 5; 24 | pub const BTF_KIND_ENUM: u32 = 6; 25 | pub const BTF_KIND_FWD: u32 = 7; 26 | pub const BTF_KIND_TYPEDEF: u32 = 8; 27 | pub const BTF_KIND_VOLATILE: u32 = 9; 28 | pub const BTF_KIND_CONST: u32 = 10; 29 | pub const BTF_KIND_RESTRICT: u32 = 11; 30 | pub const BTF_KIND_FUNC: u32 = 12; 31 | pub const BTF_KIND_FUNC_PROTO: u32 = 13; 32 | pub const BTF_KIND_VAR: u32 = 14; 33 | pub const BTF_KIND_DATASEC: u32 = 15; 34 | pub const BTF_KIND_FLOAT: u32 = 16; 35 | pub const BTF_KIND_DECL_TAG: u32 = 17; 36 | pub const BTF_KIND_TYPE_TAG: u32 = 18; 37 | pub const BTF_KIND_ENUM64: u32 = 19; 38 | pub const BTF_KIND_MAX: u32 = 19; 39 | pub const NR_BTF_KINDS: u32 = BTF_KIND_MAX + 1; 40 | 41 | pub const BTF_INT_SIGNED: u32 = 0b001; 42 | pub const BTF_INT_CHAR: u32 = 0b010; 43 | pub const BTF_INT_BOOL: u32 = 0b100; 44 | 45 | pub const BTF_VAR_STATIC: u32 = 0; 46 | pub const BTF_VAR_GLOBAL_ALLOCATED: u32 = 1; 47 | pub const BTF_VAR_GLOBAL_EXTERNAL: u32 = 2; 48 | 49 | pub const BTF_FUNC_STATIC: u32 = 0; 50 | pub const BTF_FUNC_GLOBAL: u32 = 1; 51 | pub const BTF_FUNC_EXTERN: u32 = 2; 52 | 53 | #[repr(C)] 54 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 55 | pub struct btf_header { 56 | pub magic: u16, 57 | pub version: u8, 58 | pub flags: u8, 59 | pub hdr_len: u32, 60 | pub type_off: u32, 61 | pub type_len: u32, 62 | pub str_off: u32, 63 | pub str_len: u32, 64 | } 65 | 66 | #[repr(C)] 67 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 68 | pub struct btf_type { 69 | pub name_off: u32, 70 | pub info: u32, 71 | pub type_id: u32, 72 | } 73 | 74 | #[repr(C)] 75 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 76 | pub struct btf_enum { 77 | pub name_off: u32, 78 | pub val: i32, 79 | } 80 | 81 | #[repr(C)] 82 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 83 | pub struct btf_array { 84 | pub val_type_id: u32, 85 | pub idx_type_id: u32, 86 | pub nelems: u32, 87 | } 88 | 89 | #[repr(C)] 90 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 91 | pub struct btf_member { 92 | pub name_off: u32, 93 | pub type_id: u32, 94 | pub offset: u32, 95 | } 96 | 97 | #[repr(C)] 98 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 99 | pub struct btf_param { 100 | pub name_off: u32, 101 | pub type_id: u32, 102 | } 103 | 104 | #[repr(C)] 105 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 106 | pub struct btf_datasec_var { 107 | pub type_id: u32, 108 | pub offset: u32, 109 | pub size: u32, 110 | } 111 | 112 | #[repr(C)] 113 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 114 | pub struct btf_enum64 { 115 | pub name_off: u32, 116 | pub val_lo32: u32, 117 | pub val_hi32: u32, 118 | } 119 | 120 | #[repr(C)] 121 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 122 | pub struct btf_ext_min_header { 123 | pub magic: u16, 124 | pub version: u8, 125 | pub flags: u8, 126 | pub hdr_len: u32, 127 | } 128 | 129 | #[repr(C)] 130 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 131 | pub struct btf_ext_header_v1 { 132 | pub magic: u16, 133 | pub version: u8, 134 | pub flags: u8, 135 | pub hdr_len: u32, 136 | pub func_info_off: u32, 137 | pub func_info_len: u32, 138 | pub line_info_off: u32, 139 | pub line_info_len: u32, 140 | } 141 | 142 | #[repr(C)] 143 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 144 | pub struct btf_ext_header_v2 { 145 | pub magic: u16, 146 | pub version: u8, 147 | pub flags: u8, 148 | pub hdr_len: u32, 149 | pub func_info_off: u32, 150 | pub func_info_len: u32, 151 | pub line_info_off: u32, 152 | pub line_info_len: u32, 153 | pub core_reloc_off: u32, 154 | pub core_reloc_len: u32, 155 | } 156 | 157 | #[repr(C)] 158 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 159 | pub struct btf_ext_info_sec { 160 | pub sec_name_off: u32, 161 | pub num_info: u32, 162 | } 163 | 164 | #[repr(C)] 165 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 166 | pub struct btf_ext_func_info { 167 | pub insn_off: u32, 168 | pub type_id: u32, 169 | } 170 | 171 | #[repr(C)] 172 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 173 | pub struct btf_ext_line_info { 174 | pub insn_off: u32, 175 | pub file_name_off: u32, 176 | pub line_off: u32, 177 | pub line_col: u32, 178 | } 179 | 180 | pub const BTF_FIELD_BYTE_OFFSET: u32 = 0; 181 | pub const BTF_FIELD_BYTE_SIZE: u32 = 1; 182 | pub const BTF_FIELD_EXISTS: u32 = 2; 183 | pub const BTF_FIELD_SIGNED: u32 = 3; 184 | pub const BTF_FIELD_LSHIFT_U64: u32 = 4; 185 | pub const BTF_FIELD_RSHIFT_U64: u32 = 5; 186 | pub const BTF_TYPE_LOCAL_ID: u32 = 6; 187 | pub const BTF_TYPE_TARGET_ID: u32 = 7; 188 | pub const BTF_TYPE_EXISTS: u32 = 8; 189 | pub const BTF_TYPE_SIZE: u32 = 9; 190 | pub const BTF_ENUMVAL_EXISTS: u32 = 10; 191 | pub const BTF_ENUMVAL_VALUE: u32 = 11; 192 | pub const BTF_TYPE_MATCHES: u32 = 12; 193 | 194 | #[repr(C)] 195 | #[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)] 196 | pub struct btf_ext_core_reloc { 197 | pub insn_off: u32, 198 | pub type_id: u32, 199 | pub access_spec_off: u32, 200 | pub kind: u32, 201 | } 202 | 203 | const EMPTY: &str = ""; 204 | const ANON_NAME: &str = ""; 205 | 206 | fn disp_name(s: &str) -> &str { 207 | if s.is_empty() { 208 | ANON_NAME 209 | } else { 210 | s 211 | } 212 | } 213 | 214 | #[derive(Debug, Copy, Clone, PartialEq)] 215 | pub enum BtfIntEncoding { 216 | None, 217 | Signed, 218 | Char, 219 | Bool, 220 | } 221 | 222 | impl fmt::Display for BtfIntEncoding { 223 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 224 | match self { 225 | BtfIntEncoding::None => write!(f, "none"), 226 | BtfIntEncoding::Signed => write!(f, "signed"), 227 | BtfIntEncoding::Char => write!(f, "char"), 228 | BtfIntEncoding::Bool => write!(f, "bool"), 229 | } 230 | } 231 | } 232 | 233 | #[derive(Debug)] 234 | pub struct BtfInt<'a> { 235 | pub name: &'a str, 236 | pub bits: u32, 237 | pub offset: u32, 238 | pub encoding: BtfIntEncoding, 239 | } 240 | 241 | impl fmt::Display for BtfInt<'_> { 242 | #[expect(clippy::write_literal)] 243 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 244 | write!( 245 | f, 246 | "<{}> '{}' bits:{} off:{}", 247 | "INT", 248 | disp_name(self.name), 249 | self.bits, 250 | self.offset 251 | )?; 252 | match self.encoding { 253 | BtfIntEncoding::None => (), 254 | _ => write!(f, " enc:{}", self.encoding)?, 255 | } 256 | Ok(()) 257 | } 258 | } 259 | 260 | #[derive(Debug)] 261 | pub struct BtfPtr { 262 | pub type_id: u32, 263 | } 264 | 265 | impl fmt::Display for BtfPtr { 266 | #[expect(clippy::write_literal)] 267 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 268 | write!(f, "<{}> --> [{}]", "PTR", self.type_id) 269 | } 270 | } 271 | 272 | #[derive(Debug)] 273 | pub struct BtfArray { 274 | pub nelems: u32, 275 | pub idx_type_id: u32, 276 | pub val_type_id: u32, 277 | } 278 | 279 | impl fmt::Display for BtfArray { 280 | #[expect(clippy::write_literal)] 281 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 282 | write!( 283 | f, 284 | "<{}> n:{} idx-->[{}] val-->[{}]", 285 | "ARRAY", self.nelems, self.idx_type_id, self.val_type_id 286 | ) 287 | } 288 | } 289 | 290 | #[derive(Debug)] 291 | pub struct BtfMember<'a> { 292 | pub name: &'a str, 293 | pub type_id: u32, 294 | pub bit_offset: u32, 295 | pub bit_size: u8, 296 | } 297 | 298 | impl fmt::Display for BtfMember<'_> { 299 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 300 | write!(f, "'{}' off:{}", disp_name(self.name), self.bit_offset)?; 301 | if self.bit_size != 0 { 302 | write!(f, " sz:{}", self.bit_size)?; 303 | } 304 | write!(f, " --> [{}]", self.type_id) 305 | } 306 | } 307 | 308 | #[derive(Debug)] 309 | pub struct BtfComposite<'a> { 310 | pub is_struct: bool, 311 | pub name: &'a str, 312 | pub sz: u32, 313 | pub members: Vec>, 314 | } 315 | 316 | impl fmt::Display for BtfComposite<'_> { 317 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 318 | write!( 319 | f, 320 | "<{}> '{}' sz:{} n:{}", 321 | if self.is_struct { "STRUCT" } else { "UNION" }, 322 | disp_name(self.name), 323 | self.sz, 324 | self.members.len() 325 | )?; 326 | for i in 0..self.members.len() { 327 | write!(f, "\n\t#{:02} {}", i, self.members[i])?; 328 | } 329 | Ok(()) 330 | } 331 | } 332 | 333 | #[derive(Debug)] 334 | pub struct BtfEnumValue<'a> { 335 | pub name: &'a str, 336 | pub value: i32, 337 | } 338 | 339 | impl fmt::Display for BtfEnumValue<'_> { 340 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 341 | write!(f, "{} = {}", disp_name(self.name), self.value) 342 | } 343 | } 344 | 345 | #[derive(Debug)] 346 | pub struct BtfEnum<'a> { 347 | pub name: &'a str, 348 | pub sz: u32, 349 | pub values: Vec>, 350 | } 351 | 352 | impl fmt::Display for BtfEnum<'_> { 353 | #[expect(clippy::write_literal)] 354 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 355 | write!( 356 | f, 357 | "<{}> '{}' sz:{} n:{}", 358 | "ENUM", 359 | disp_name(self.name), 360 | self.sz, 361 | self.values.len() 362 | )?; 363 | for i in 0..self.values.len() { 364 | write!(f, "\n\t#{:02} {}", i, self.values[i])?; 365 | } 366 | Ok(()) 367 | } 368 | } 369 | 370 | #[derive(Debug)] 371 | pub struct BtfEnum64Value<'a> { 372 | pub name: &'a str, 373 | pub value: i64, 374 | } 375 | 376 | impl fmt::Display for BtfEnum64Value<'_> { 377 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 378 | write!(f, "{} = {}", disp_name(self.name), self.value) 379 | } 380 | } 381 | 382 | #[derive(Debug)] 383 | pub struct BtfEnum64<'a> { 384 | pub name: &'a str, 385 | pub sz: u32, 386 | pub values: Vec>, 387 | } 388 | 389 | impl fmt::Display for BtfEnum64<'_> { 390 | #[expect(clippy::write_literal)] 391 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 392 | write!( 393 | f, 394 | "<{}> '{}' sz:{} n:{}", 395 | "ENUM64", 396 | disp_name(self.name), 397 | self.sz, 398 | self.values.len() 399 | )?; 400 | for i in 0..self.values.len() { 401 | write!(f, "\n\t#{:02} {}", i, self.values[i])?; 402 | } 403 | Ok(()) 404 | } 405 | } 406 | 407 | #[derive(Debug, Copy, Clone, PartialEq)] 408 | pub enum BtfFwdKind { 409 | Struct, 410 | Union, 411 | } 412 | 413 | impl fmt::Display for BtfFwdKind { 414 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 415 | match self { 416 | BtfFwdKind::Struct => write!(f, "struct"), 417 | BtfFwdKind::Union => write!(f, "union"), 418 | } 419 | } 420 | } 421 | 422 | #[derive(Debug)] 423 | pub struct BtfFwd<'a> { 424 | pub name: &'a str, 425 | pub kind: BtfFwdKind, 426 | } 427 | 428 | impl fmt::Display for BtfFwd<'_> { 429 | #[expect(clippy::write_literal)] 430 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 431 | write!( 432 | f, 433 | "<{}> '{}' kind:{}", 434 | "FWD", 435 | disp_name(self.name), 436 | self.kind 437 | ) 438 | } 439 | } 440 | 441 | #[derive(Debug)] 442 | pub struct BtfTypedef<'a> { 443 | pub name: &'a str, 444 | pub type_id: u32, 445 | } 446 | 447 | impl fmt::Display for BtfTypedef<'_> { 448 | #[expect(clippy::write_literal)] 449 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 450 | write!( 451 | f, 452 | "<{}> '{}' --> [{}]", 453 | "TYPEDEF", 454 | disp_name(self.name), 455 | self.type_id 456 | ) 457 | } 458 | } 459 | 460 | #[derive(Debug)] 461 | pub struct BtfVolatile { 462 | pub type_id: u32, 463 | } 464 | 465 | impl fmt::Display for BtfVolatile { 466 | #[expect(clippy::write_literal)] 467 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 468 | write!(f, "<{}> --> [{}]", "VOLATILE", self.type_id) 469 | } 470 | } 471 | 472 | #[derive(Debug)] 473 | pub struct BtfConst { 474 | pub type_id: u32, 475 | } 476 | 477 | impl fmt::Display for BtfConst { 478 | #[expect(clippy::write_literal)] 479 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 480 | write!(f, "<{}> --> [{}]", "CONST", self.type_id) 481 | } 482 | } 483 | 484 | #[derive(Debug)] 485 | pub struct BtfRestrict { 486 | pub type_id: u32, 487 | } 488 | 489 | impl fmt::Display for BtfRestrict { 490 | #[expect(clippy::write_literal)] 491 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 492 | write!(f, "<{}> --> [{}]", "RESTRICT", self.type_id) 493 | } 494 | } 495 | 496 | #[derive(Debug, Copy, Clone, PartialEq)] 497 | pub enum BtfFuncKind { 498 | Unknown, 499 | Static, 500 | Global, 501 | Extern, 502 | } 503 | 504 | impl fmt::Display for BtfFuncKind { 505 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 506 | match self { 507 | BtfFuncKind::Unknown => write!(f, ""), 508 | BtfFuncKind::Static => write!(f, "static"), 509 | BtfFuncKind::Global => write!(f, "global"), 510 | BtfFuncKind::Extern => write!(f, "extern"), 511 | } 512 | } 513 | } 514 | 515 | #[derive(Debug)] 516 | pub struct BtfFunc<'a> { 517 | pub name: &'a str, 518 | pub proto_type_id: u32, 519 | pub kind: BtfFuncKind, 520 | } 521 | 522 | impl fmt::Display for BtfFunc<'_> { 523 | #[expect(clippy::write_literal)] 524 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 525 | write!( 526 | f, 527 | "<{}> '{}' --> {} [{}]", 528 | "FUNC", 529 | disp_name(self.name), 530 | self.kind, 531 | self.proto_type_id 532 | ) 533 | } 534 | } 535 | 536 | #[derive(Debug)] 537 | pub struct BtfFuncParam<'a> { 538 | pub name: &'a str, 539 | pub type_id: u32, 540 | } 541 | 542 | impl fmt::Display for BtfFuncParam<'_> { 543 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 544 | write!(f, "'{}' --> [{}]", disp_name(self.name), self.type_id) 545 | } 546 | } 547 | 548 | #[derive(Debug)] 549 | pub struct BtfFuncProto<'a> { 550 | pub res_type_id: u32, 551 | pub params: Vec>, 552 | } 553 | 554 | impl fmt::Display for BtfFuncProto<'_> { 555 | #[expect(clippy::write_literal)] 556 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 557 | write!( 558 | f, 559 | "<{}> r-->[{}] n:{}", 560 | "FUNC_PROTO", 561 | self.res_type_id, 562 | self.params.len() 563 | )?; 564 | for i in 0..self.params.len() { 565 | write!(f, "\n\t#{:02} {}", i, self.params[i])?; 566 | } 567 | Ok(()) 568 | } 569 | } 570 | 571 | #[derive(Debug, Copy, Clone, PartialEq)] 572 | pub enum BtfVarKind { 573 | Static, 574 | GlobalAlloc, 575 | GlobalExtern, 576 | } 577 | 578 | impl fmt::Display for BtfVarKind { 579 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 580 | match self { 581 | BtfVarKind::Static => write!(f, "static"), 582 | BtfVarKind::GlobalAlloc => write!(f, "global-alloc"), 583 | BtfVarKind::GlobalExtern => write!(f, "global-extern"), 584 | } 585 | } 586 | } 587 | 588 | #[derive(Debug)] 589 | pub struct BtfVar<'a> { 590 | pub name: &'a str, 591 | pub type_id: u32, 592 | pub kind: BtfVarKind, 593 | } 594 | 595 | impl fmt::Display for BtfVar<'_> { 596 | #[expect(clippy::write_literal)] 597 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 598 | write!( 599 | f, 600 | "<{}> '{}' kind:{} --> [{}]", 601 | "VAR", 602 | disp_name(self.name), 603 | self.kind, 604 | self.type_id 605 | ) 606 | } 607 | } 608 | 609 | #[derive(Debug)] 610 | pub struct BtfDatasecVar { 611 | pub type_id: u32, 612 | pub offset: u32, 613 | pub sz: u32, 614 | } 615 | 616 | impl fmt::Display for BtfDatasecVar { 617 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 618 | write!( 619 | f, 620 | "off:{} sz:{} --> [{}]", 621 | self.offset, self.sz, self.type_id 622 | ) 623 | } 624 | } 625 | 626 | #[derive(Debug)] 627 | pub struct BtfDatasec<'a> { 628 | pub name: &'a str, 629 | pub sz: u32, 630 | pub vars: Vec, 631 | } 632 | 633 | impl fmt::Display for BtfDatasec<'_> { 634 | #[expect(clippy::write_literal)] 635 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 636 | write!( 637 | f, 638 | "<{}> '{}' sz:{} n:{}", 639 | "DATASEC", 640 | disp_name(self.name), 641 | self.sz, 642 | self.vars.len() 643 | )?; 644 | for i in 0..self.vars.len() { 645 | write!(f, "\n\t#{:02} {}", i, self.vars[i])?; 646 | } 647 | Ok(()) 648 | } 649 | } 650 | 651 | #[derive(Debug)] 652 | pub struct BtfFloat<'a> { 653 | pub name: &'a str, 654 | pub sz: u32, 655 | } 656 | 657 | impl fmt::Display for BtfFloat<'_> { 658 | #[expect(clippy::write_literal)] 659 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 660 | write!(f, "<{}> '{}' sz:{}", "FLOAT", disp_name(self.name), self.sz)?; 661 | Ok(()) 662 | } 663 | } 664 | 665 | #[derive(Debug)] 666 | pub struct BtfDeclTag<'a> { 667 | pub name: &'a str, 668 | pub type_id: u32, 669 | pub comp_idx: u32, 670 | } 671 | 672 | impl fmt::Display for BtfDeclTag<'_> { 673 | #[expect(clippy::write_literal)] 674 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 675 | write!( 676 | f, 677 | "<{}> '{}' --> [{}] comp_idx:{}", 678 | "DECL_TAG", 679 | disp_name(self.name), 680 | self.type_id, 681 | self.comp_idx, 682 | ) 683 | } 684 | } 685 | 686 | #[derive(Debug)] 687 | pub struct BtfTypeTag<'a> { 688 | pub name: &'a str, 689 | pub type_id: u32, 690 | } 691 | 692 | impl fmt::Display for BtfTypeTag<'_> { 693 | #[expect(clippy::write_literal)] 694 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 695 | write!( 696 | f, 697 | "<{}> '{}' --> [{}]", 698 | "TYPE_TAG", 699 | disp_name(self.name), 700 | self.type_id 701 | ) 702 | } 703 | } 704 | 705 | #[derive(Debug)] 706 | pub enum BtfType<'a> { 707 | Void, 708 | Int(BtfInt<'a>), 709 | Ptr(BtfPtr), 710 | Array(BtfArray), 711 | Struct(BtfComposite<'a>), 712 | Union(BtfComposite<'a>), 713 | Enum(BtfEnum<'a>), 714 | Fwd(BtfFwd<'a>), 715 | Typedef(BtfTypedef<'a>), 716 | Volatile(BtfVolatile), 717 | Const(BtfConst), 718 | Restrict(BtfRestrict), 719 | Func(BtfFunc<'a>), 720 | FuncProto(BtfFuncProto<'a>), 721 | Var(BtfVar<'a>), 722 | Datasec(BtfDatasec<'a>), 723 | Float(BtfFloat<'a>), 724 | DeclTag(BtfDeclTag<'a>), 725 | TypeTag(BtfTypeTag<'a>), 726 | Enum64(BtfEnum64<'a>), 727 | } 728 | 729 | impl fmt::Display for BtfType<'_> { 730 | #[expect(clippy::write_literal)] 731 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 732 | match self { 733 | BtfType::Void => write!(f, "<{}>", "VOID"), 734 | BtfType::Int(t) => t.fmt(f), 735 | BtfType::Ptr(t) => t.fmt(f), 736 | BtfType::Array(t) => t.fmt(f), 737 | BtfType::Struct(t) => t.fmt(f), 738 | BtfType::Union(t) => t.fmt(f), 739 | BtfType::Enum(t) => t.fmt(f), 740 | BtfType::Fwd(t) => t.fmt(f), 741 | BtfType::Typedef(t) => t.fmt(f), 742 | BtfType::Volatile(t) => t.fmt(f), 743 | BtfType::Const(t) => t.fmt(f), 744 | BtfType::Restrict(t) => t.fmt(f), 745 | BtfType::Func(t) => t.fmt(f), 746 | BtfType::FuncProto(t) => t.fmt(f), 747 | BtfType::Var(t) => t.fmt(f), 748 | BtfType::Datasec(t) => t.fmt(f), 749 | BtfType::Float(t) => t.fmt(f), 750 | BtfType::DeclTag(t) => t.fmt(f), 751 | BtfType::TypeTag(t) => t.fmt(f), 752 | BtfType::Enum64(t) => t.fmt(f), 753 | } 754 | } 755 | } 756 | 757 | impl BtfType<'_> { 758 | pub fn kind(&self) -> BtfKind { 759 | match self { 760 | BtfType::Void => BtfKind::Void, 761 | BtfType::Int(_) => BtfKind::Int, 762 | BtfType::Ptr(_) => BtfKind::Ptr, 763 | BtfType::Array(_) => BtfKind::Array, 764 | BtfType::Struct(_) => BtfKind::Struct, 765 | BtfType::Union(_) => BtfKind::Union, 766 | BtfType::Enum(_) => BtfKind::Enum, 767 | BtfType::Fwd(_) => BtfKind::Fwd, 768 | BtfType::Typedef(_) => BtfKind::Typedef, 769 | BtfType::Volatile(_) => BtfKind::Volatile, 770 | BtfType::Const(_) => BtfKind::Const, 771 | BtfType::Restrict(_) => BtfKind::Restrict, 772 | BtfType::Func(_) => BtfKind::Func, 773 | BtfType::FuncProto(_) => BtfKind::FuncProto, 774 | BtfType::Var(_) => BtfKind::Var, 775 | BtfType::Datasec(_) => BtfKind::Datasec, 776 | BtfType::Float(_) => BtfKind::Float, 777 | BtfType::DeclTag(_) => BtfKind::DeclTag, 778 | BtfType::TypeTag(_) => BtfKind::TypeTag, 779 | BtfType::Enum64(_) => BtfKind::Enum64, 780 | } 781 | } 782 | 783 | pub fn name(&self) -> &str { 784 | match self { 785 | BtfType::Void => EMPTY, 786 | BtfType::Int(t) => t.name, 787 | BtfType::Ptr(_) => EMPTY, 788 | BtfType::Array(_) => EMPTY, 789 | BtfType::Struct(t) => t.name, 790 | BtfType::Union(t) => t.name, 791 | BtfType::Enum(t) => t.name, 792 | BtfType::Fwd(t) => t.name, 793 | BtfType::Typedef(t) => t.name, 794 | BtfType::Volatile(_) => EMPTY, 795 | BtfType::Const(_) => EMPTY, 796 | BtfType::Restrict(_) => EMPTY, 797 | BtfType::Func(t) => t.name, 798 | BtfType::FuncProto(_) => EMPTY, 799 | BtfType::Var(t) => t.name, 800 | BtfType::Datasec(t) => t.name, 801 | BtfType::Float(t) => t.name, 802 | BtfType::DeclTag(t) => t.name, 803 | BtfType::TypeTag(t) => t.name, 804 | BtfType::Enum64(t) => t.name, 805 | } 806 | } 807 | } 808 | 809 | #[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] 810 | pub enum BtfKind { 811 | Void, 812 | Int, 813 | Ptr, 814 | Array, 815 | Struct, 816 | Union, 817 | Enum, 818 | Fwd, 819 | Typedef, 820 | Volatile, 821 | Const, 822 | Restrict, 823 | Func, 824 | FuncProto, 825 | Var, 826 | Datasec, 827 | Float, 828 | DeclTag, 829 | TypeTag, 830 | Enum64, 831 | } 832 | 833 | impl std::str::FromStr for BtfKind { 834 | type Err = BtfError; 835 | 836 | fn from_str(s: &str) -> Result { 837 | match s { 838 | "void" => Ok(BtfKind::Void), 839 | "int" | "i" => Ok(BtfKind::Int), 840 | "ptr" | "p" => Ok(BtfKind::Ptr), 841 | "array" | "arr" | "a" => Ok(BtfKind::Array), 842 | "struct" | "s" => Ok(BtfKind::Struct), 843 | "union" | "u" => Ok(BtfKind::Union), 844 | "enum" | "e" => Ok(BtfKind::Enum), 845 | "fwd" => Ok(BtfKind::Fwd), 846 | "typedef" | "t" => Ok(BtfKind::Typedef), 847 | "volatile" => Ok(BtfKind::Volatile), 848 | "const" => Ok(BtfKind::Const), 849 | "restrict" => Ok(BtfKind::Restrict), 850 | "func_proto" | "funcproto" | "fnproto" | "fp" => Ok(BtfKind::FuncProto), 851 | "func" | "fn" => Ok(BtfKind::Func), 852 | "var" | "v" => Ok(BtfKind::Var), 853 | "datasec" => Ok(BtfKind::Datasec), 854 | "float" => Ok(BtfKind::Float), 855 | "decl_tag" => Ok(BtfKind::DeclTag), 856 | "type_tag" => Ok(BtfKind::TypeTag), 857 | "enum64" | "e64" => Ok(BtfKind::Enum64), 858 | _ => Err(BtfError::new_owned(format!( 859 | "unrecognized btf kind: '{}'", 860 | s 861 | ))), 862 | } 863 | } 864 | } 865 | 866 | #[derive(Debug)] 867 | pub struct BtfExtSection<'a, T> { 868 | pub name: &'a str, 869 | pub rec_sz: usize, 870 | pub recs: Vec, 871 | } 872 | 873 | #[derive(Debug)] 874 | pub struct BtfExtFunc { 875 | pub insn_off: u32, 876 | pub type_id: u32, 877 | } 878 | 879 | impl fmt::Display for BtfExtFunc { 880 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 881 | write!( 882 | f, 883 | "func: insn #{} --> [{}]", 884 | self.insn_off / 8, 885 | self.type_id 886 | ) 887 | } 888 | } 889 | 890 | #[derive(Debug)] 891 | pub struct BtfExtLine<'a> { 892 | pub insn_off: u32, 893 | pub file_name: &'a str, 894 | pub src_line: &'a str, 895 | pub line_num: u32, 896 | pub col_num: u32, 897 | } 898 | 899 | impl fmt::Display for BtfExtLine<'_> { 900 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 901 | write!( 902 | f, 903 | "line: insn #{} --> {}:{} @ {}\n\t{}", 904 | self.insn_off / 8, 905 | self.line_num, 906 | self.col_num, 907 | self.file_name, 908 | self.src_line 909 | ) 910 | } 911 | } 912 | 913 | #[derive(Debug, Copy, Clone, PartialEq)] 914 | pub enum BtfCoreRelocKind { 915 | ByteOff = 0, 916 | ByteSz = 1, 917 | FieldExists = 2, 918 | Signed = 3, 919 | LShiftU64 = 4, 920 | RShiftU64 = 5, 921 | LocalTypeId = 6, 922 | TargetTypeId = 7, 923 | TypeExists = 8, 924 | TypeSize = 9, 925 | EnumvalExists = 10, 926 | EnumvalValue = 11, 927 | TypeMatches = 12, 928 | } 929 | 930 | impl fmt::Display for BtfCoreRelocKind { 931 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 932 | match self { 933 | BtfCoreRelocKind::ByteOff => write!(f, "byte_off"), 934 | BtfCoreRelocKind::ByteSz => write!(f, "byte_sz"), 935 | BtfCoreRelocKind::FieldExists => write!(f, "field_exists"), 936 | BtfCoreRelocKind::Signed => write!(f, "signed"), 937 | BtfCoreRelocKind::LShiftU64 => write!(f, "lshift_u64"), 938 | BtfCoreRelocKind::RShiftU64 => write!(f, "rshift_u64"), 939 | BtfCoreRelocKind::LocalTypeId => write!(f, "local_type_id"), 940 | BtfCoreRelocKind::TargetTypeId => write!(f, "target_type_id"), 941 | BtfCoreRelocKind::TypeExists => write!(f, "type_exists"), 942 | BtfCoreRelocKind::TypeMatches => write!(f, "type_matches"), 943 | BtfCoreRelocKind::TypeSize => write!(f, "type_size"), 944 | BtfCoreRelocKind::EnumvalExists => write!(f, "enumval_exists"), 945 | BtfCoreRelocKind::EnumvalValue => write!(f, "enumval_value"), 946 | } 947 | } 948 | } 949 | 950 | #[derive(Debug)] 951 | pub struct BtfExtCoreReloc<'a> { 952 | pub insn_off: u32, 953 | pub type_id: u32, 954 | pub access_spec_str: &'a str, 955 | pub access_spec: Vec, 956 | pub kind: BtfCoreRelocKind, 957 | } 958 | 959 | impl fmt::Display for BtfExtCoreReloc<'_> { 960 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 961 | write!( 962 | f, 963 | "core_reloc: insn #{} --> [{}] + {}: {}", 964 | self.insn_off / 8, 965 | self.type_id, 966 | self.access_spec_str, 967 | self.kind, 968 | ) 969 | } 970 | } 971 | 972 | #[derive(Debug)] 973 | pub struct Btf<'a> { 974 | endian: scroll::Endian, 975 | types: Vec>, 976 | ptr_sz: u32, 977 | 978 | // .BTF.ext stuff 979 | has_ext: bool, 980 | func_secs: Vec>, 981 | line_secs: Vec>>, 982 | core_reloc_secs: Vec>>, 983 | } 984 | 985 | impl<'a> Btf<'a> { 986 | pub fn ptr_sz(&self) -> u32 { 987 | self.ptr_sz 988 | } 989 | 990 | pub fn types(&self) -> &[BtfType] { 991 | &self.types 992 | } 993 | 994 | pub fn type_by_id(&self, type_id: u32) -> &BtfType { 995 | &self.types[type_id as usize] 996 | } 997 | 998 | pub fn type_cnt(&self) -> u32 { 999 | self.types.len() as u32 1000 | } 1001 | 1002 | pub fn has_ext(&self) -> bool { 1003 | self.has_ext 1004 | } 1005 | 1006 | pub fn func_secs(&self) -> &[BtfExtSection] { 1007 | &self.func_secs 1008 | } 1009 | 1010 | pub fn line_secs(&self) -> &[BtfExtSection] { 1011 | &self.line_secs 1012 | } 1013 | 1014 | pub fn core_reloc_secs(&self) -> &[BtfExtSection] { 1015 | &self.core_reloc_secs 1016 | } 1017 | 1018 | pub fn get_size_of(&self, type_id: u32) -> u32 { 1019 | match self.type_by_id(type_id) { 1020 | BtfType::Void => 0, 1021 | BtfType::Int(t) => t.bits.div_ceil(8), 1022 | BtfType::Volatile(t) => self.get_size_of(t.type_id), 1023 | BtfType::Const(t) => self.get_size_of(t.type_id), 1024 | BtfType::Restrict(t) => self.get_size_of(t.type_id), 1025 | BtfType::Ptr(_) => self.ptr_sz, 1026 | BtfType::Array(t) => t.nelems * self.get_size_of(t.val_type_id), 1027 | BtfType::FuncProto(_) => 0, 1028 | BtfType::Struct(t) => t.sz, 1029 | BtfType::Union(t) => t.sz, 1030 | BtfType::Enum(t) => t.sz, 1031 | BtfType::Fwd(_) => 0, 1032 | BtfType::Typedef(t) => self.get_size_of(t.type_id), 1033 | BtfType::Func(_) => 0, 1034 | BtfType::Var(_) => 0, 1035 | BtfType::Datasec(t) => t.sz, 1036 | BtfType::Float(t) => t.sz, 1037 | BtfType::DeclTag(t) => self.get_size_of(t.type_id), 1038 | BtfType::TypeTag(t) => self.get_size_of(t.type_id), 1039 | BtfType::Enum64(t) => t.sz, 1040 | } 1041 | } 1042 | 1043 | pub fn get_align_of(&self, type_id: u32) -> u32 { 1044 | match self.type_by_id(type_id) { 1045 | BtfType::Void => 0, 1046 | BtfType::Int(t) => min(self.ptr_sz, t.bits.div_ceil(8)), 1047 | BtfType::Volatile(t) => self.get_align_of(t.type_id), 1048 | BtfType::Const(t) => self.get_align_of(t.type_id), 1049 | BtfType::Restrict(t) => self.get_align_of(t.type_id), 1050 | BtfType::Ptr(_) => self.ptr_sz, 1051 | BtfType::Array(t) => self.get_align_of(t.val_type_id), 1052 | BtfType::FuncProto(_) => 0, 1053 | BtfType::Struct(t) => { 1054 | let mut align = 1; 1055 | for m in &t.members { 1056 | align = max(align, self.get_align_of(m.type_id)); 1057 | } 1058 | align 1059 | } 1060 | BtfType::Union(t) => { 1061 | let mut align = 1; 1062 | for m in &t.members { 1063 | align = max(align, self.get_align_of(m.type_id)); 1064 | } 1065 | align 1066 | } 1067 | BtfType::Enum(t) => min(self.ptr_sz, t.sz), 1068 | BtfType::Fwd(_) => 0, 1069 | BtfType::Typedef(t) => self.get_align_of(t.type_id), 1070 | BtfType::Func(_) => 0, 1071 | BtfType::Var(_) => 0, 1072 | BtfType::Datasec(_) => 0, 1073 | BtfType::Float(t) => min(self.ptr_sz, t.sz), 1074 | BtfType::DeclTag(_) => 0, 1075 | BtfType::TypeTag(t) => self.get_align_of(t.type_id), 1076 | BtfType::Enum64(t) => min(self.ptr_sz, t.sz), 1077 | } 1078 | } 1079 | 1080 | pub fn skip_mods(&self, mut type_id: u32) -> u32 { 1081 | loop { 1082 | match self.type_by_id(type_id) { 1083 | BtfType::Volatile(t) => type_id = t.type_id, 1084 | BtfType::Const(t) => type_id = t.type_id, 1085 | BtfType::Restrict(t) => type_id = t.type_id, 1086 | BtfType::TypeTag(t) => type_id = t.type_id, 1087 | _ => return type_id, 1088 | } 1089 | } 1090 | } 1091 | 1092 | pub fn skip_mods_and_typedefs(&self, mut type_id: u32) -> u32 { 1093 | loop { 1094 | match self.type_by_id(type_id) { 1095 | BtfType::Volatile(t) => type_id = t.type_id, 1096 | BtfType::Const(t) => type_id = t.type_id, 1097 | BtfType::Restrict(t) => type_id = t.type_id, 1098 | BtfType::Typedef(t) => type_id = t.type_id, 1099 | BtfType::TypeTag(t) => type_id = t.type_id, 1100 | _ => return type_id, 1101 | } 1102 | } 1103 | } 1104 | 1105 | /// Helper method which keeps the common BTF loading logic and returns 1106 | /// both `Btf` and a subslice with the string section. 1107 | fn load(endian: Endian, ptr_sz: u32, data: &'a [u8]) -> BtfResult<(Self, &'a [u8])> { 1108 | let mut btf = Self { 1109 | endian, 1110 | ptr_sz, 1111 | types: vec![BtfType::Void], 1112 | has_ext: false, 1113 | func_secs: Vec::new(), 1114 | line_secs: Vec::new(), 1115 | core_reloc_secs: Vec::new(), 1116 | }; 1117 | 1118 | let hdr = data.pread_with::(0, endian)?; 1119 | if hdr.magic != BTF_MAGIC { 1120 | return btf_error(format!("Invalid BTF magic: {}", hdr.magic)); 1121 | } 1122 | if hdr.version != BTF_VERSION { 1123 | return btf_error(format!( 1124 | "Unsupported BTF version: {}, expect: {}", 1125 | hdr.version, BTF_VERSION 1126 | )); 1127 | } 1128 | 1129 | let str_off = (hdr.hdr_len + hdr.str_off) as usize; 1130 | let str_data = &data[str_off..str_off + hdr.str_len as usize]; 1131 | 1132 | let type_off = (hdr.hdr_len + hdr.type_off) as usize; 1133 | let type_data = &data[type_off..type_off + hdr.type_len as usize]; 1134 | let mut off: usize = 0; 1135 | while off < hdr.type_len as usize { 1136 | let t = btf.load_type(&type_data[off..], str_data)?; 1137 | off += Btf::type_size(&t); 1138 | btf.types.push(t); 1139 | } 1140 | 1141 | Ok((btf, str_data)) 1142 | } 1143 | 1144 | /// Loads BTF information from the given ELF object file. 1145 | pub fn load_elf(elf: &object::File<'a>) -> BtfResult { 1146 | let endian = if elf.is_little_endian() { 1147 | scroll::LE 1148 | } else { 1149 | scroll::BE 1150 | }; 1151 | 1152 | let ptr_sz = if elf.is_64() { 8 } else { 4 }; 1153 | 1154 | let btf_section = elf 1155 | .section_by_name(BTF_ELF_SEC) 1156 | .ok_or_else(|| Box::new(BtfError::new("No .BTF section found!")))?; 1157 | let data = btf_section.data().expect("expected borrowed data"); 1158 | 1159 | let (mut btf, str_data) = Self::load(endian, ptr_sz, data)?; 1160 | 1161 | if let Some(ext_section) = elf.section_by_name(BTF_EXT_ELF_SEC) { 1162 | btf.has_ext = true; 1163 | let ext_data = ext_section.data().expect("expected borrowed data"); 1164 | let ext_hdr = ext_data.pread_with::(0, endian)?; 1165 | if ext_hdr.magic != BTF_MAGIC { 1166 | return btf_error(format!("Invalid .BTF.ext magic: {}", ext_hdr.magic)); 1167 | } 1168 | if ext_hdr.version != BTF_VERSION { 1169 | return btf_error(format!( 1170 | "Unsupported .BTF.ext version: {}, expect: {}", 1171 | ext_hdr.version, BTF_VERSION 1172 | )); 1173 | } 1174 | let ext_hdr2 = if ext_hdr.hdr_len >= size_of::() as u32 { 1175 | Some(ext_data.pread_with::(0, endian)?) 1176 | } else { 1177 | None 1178 | }; 1179 | if ext_hdr.func_info_len > 0 { 1180 | let func_off = (ext_hdr.hdr_len + ext_hdr.func_info_off) as usize; 1181 | let func_data = &ext_data[func_off..func_off + ext_hdr.func_info_len as usize]; 1182 | btf.func_secs = btf.load_func_secs(func_data, str_data)?; 1183 | } 1184 | if ext_hdr.line_info_len > 0 { 1185 | let line_off = (ext_hdr.hdr_len + ext_hdr.line_info_off) as usize; 1186 | let line_data = &ext_data[line_off..line_off + ext_hdr.line_info_len as usize]; 1187 | btf.line_secs = btf.load_line_secs(line_data, str_data)?; 1188 | } 1189 | if let Some(h) = ext_hdr2 { 1190 | if h.core_reloc_len > 0 { 1191 | let reloc_off = (h.hdr_len + h.core_reloc_off) as usize; 1192 | let reloc_data = &ext_data[reloc_off..reloc_off + h.core_reloc_len as usize]; 1193 | btf.core_reloc_secs = btf.load_core_reloc_secs(reloc_data, str_data)?; 1194 | } 1195 | } 1196 | } 1197 | 1198 | Ok(btf) 1199 | } 1200 | 1201 | /// Loads BTF information from the given slice of bytes. 1202 | pub fn load_raw(data: &'a [u8]) -> BtfResult { 1203 | #[cfg(target_endian = "little")] 1204 | let endian = scroll::LE; 1205 | #[cfg(target_endian = "big")] 1206 | let endian = scroll::BE; 1207 | 1208 | #[cfg(target_pointer_width = "64")] 1209 | let ptr_sz = 8_u32; 1210 | #[cfg(target_pointer_width = "32")] 1211 | let ptr_sz = 4_u32; 1212 | 1213 | let (btf, _) = Self::load(endian, ptr_sz, data)?; 1214 | 1215 | Ok(btf) 1216 | } 1217 | 1218 | pub fn type_size(t: &BtfType) -> usize { 1219 | let common = size_of::(); 1220 | match t { 1221 | BtfType::Void => 0, 1222 | BtfType::Ptr(_) 1223 | | BtfType::Fwd(_) 1224 | | BtfType::Typedef(_) 1225 | | BtfType::Volatile(_) 1226 | | BtfType::Const(_) 1227 | | BtfType::Restrict(_) 1228 | | BtfType::Func(_) 1229 | | BtfType::Float(_) 1230 | | BtfType::TypeTag(_) => common, 1231 | BtfType::Int(_) | BtfType::Var(_) | BtfType::DeclTag(_) => common + size_of::(), 1232 | BtfType::Array(_) => common + size_of::(), 1233 | BtfType::Struct(t) => common + t.members.len() * size_of::(), 1234 | BtfType::Union(t) => common + t.members.len() * size_of::(), 1235 | BtfType::Enum(t) => common + t.values.len() * size_of::(), 1236 | BtfType::Enum64(t) => common + t.values.len() * size_of::(), 1237 | BtfType::FuncProto(t) => common + t.params.len() * size_of::(), 1238 | BtfType::Datasec(t) => common + t.vars.len() * size_of::(), 1239 | } 1240 | } 1241 | 1242 | fn load_type(&self, data: &'a [u8], strs: &'a [u8]) -> BtfResult> { 1243 | let t = data.pread_with::(0, self.endian)?; 1244 | let extra = &data[size_of::()..]; 1245 | let kind = Btf::get_kind(t.info); 1246 | match kind { 1247 | BTF_KIND_INT => self.load_int(&t, extra, strs), 1248 | BTF_KIND_PTR => Ok(BtfType::Ptr(BtfPtr { type_id: t.type_id })), 1249 | BTF_KIND_ARRAY => self.load_array(extra), 1250 | BTF_KIND_STRUCT => self.load_struct(&t, extra, strs), 1251 | BTF_KIND_UNION => self.load_union(&t, extra, strs), 1252 | BTF_KIND_ENUM => self.load_enum(&t, extra, strs), 1253 | BTF_KIND_FWD => self.load_fwd(&t, strs), 1254 | BTF_KIND_TYPEDEF => Ok(BtfType::Typedef(BtfTypedef { 1255 | name: Btf::get_btf_str(strs, t.name_off)?, 1256 | type_id: t.type_id, 1257 | })), 1258 | BTF_KIND_VOLATILE => Ok(BtfType::Volatile(BtfVolatile { type_id: t.type_id })), 1259 | BTF_KIND_CONST => Ok(BtfType::Const(BtfConst { type_id: t.type_id })), 1260 | BTF_KIND_RESTRICT => Ok(BtfType::Restrict(BtfRestrict { type_id: t.type_id })), 1261 | BTF_KIND_FUNC => Ok(BtfType::Func(BtfFunc { 1262 | name: Btf::get_btf_str(strs, t.name_off)?, 1263 | proto_type_id: t.type_id, 1264 | kind: match Btf::get_vlen(t.info) { 1265 | BTF_FUNC_STATIC => BtfFuncKind::Static, 1266 | BTF_FUNC_GLOBAL => BtfFuncKind::Global, 1267 | BTF_FUNC_EXTERN => BtfFuncKind::Extern, 1268 | _ => BtfFuncKind::Unknown, 1269 | }, 1270 | })), 1271 | BTF_KIND_FUNC_PROTO => self.load_func_proto(&t, extra, strs), 1272 | BTF_KIND_VAR => self.load_var(&t, extra, strs), 1273 | BTF_KIND_DATASEC => self.load_datasec(&t, extra, strs), 1274 | BTF_KIND_FLOAT => Ok(BtfType::Float(BtfFloat { 1275 | name: Btf::get_btf_str(strs, t.name_off)?, 1276 | sz: t.type_id, 1277 | })), 1278 | BTF_KIND_DECL_TAG => self.load_decl_tag(&t, extra, strs), 1279 | BTF_KIND_TYPE_TAG => Ok(BtfType::TypeTag(BtfTypeTag { 1280 | name: Btf::get_btf_str(strs, t.name_off)?, 1281 | type_id: t.type_id, 1282 | })), 1283 | BTF_KIND_ENUM64 => self.load_enum64(&t, extra, strs), 1284 | _ => btf_error(format!("Unknown BTF kind: {}", kind)), 1285 | } 1286 | } 1287 | 1288 | fn load_int(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult> { 1289 | let info = extra.pread_with::(0, self.endian)?; 1290 | let enc = (info >> 24) & 0xf; 1291 | let off = (info >> 16) & 0xff; 1292 | let bits = info & 0xff; 1293 | Ok(BtfType::Int(BtfInt { 1294 | name: Btf::get_btf_str(strs, t.name_off)?, 1295 | bits, 1296 | offset: off, 1297 | encoding: match enc { 1298 | 0 => BtfIntEncoding::None, 1299 | BTF_INT_SIGNED => BtfIntEncoding::Signed, 1300 | BTF_INT_CHAR => BtfIntEncoding::Char, 1301 | BTF_INT_BOOL => BtfIntEncoding::Bool, 1302 | _ => { 1303 | return btf_error(format!("Unknown BTF int encoding: {}", enc)); 1304 | } 1305 | }, 1306 | })) 1307 | } 1308 | 1309 | fn load_array(&self, extra: &'a [u8]) -> BtfResult> { 1310 | let info = extra.pread_with::(0, self.endian)?; 1311 | Ok(BtfType::Array(BtfArray { 1312 | nelems: info.nelems, 1313 | idx_type_id: info.idx_type_id, 1314 | val_type_id: info.val_type_id, 1315 | })) 1316 | } 1317 | 1318 | fn load_struct(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult> { 1319 | Ok(BtfType::Struct(BtfComposite { 1320 | is_struct: true, 1321 | name: Btf::get_btf_str(strs, t.name_off)?, 1322 | sz: t.type_id, // it's a type/size union in C 1323 | members: self.load_members(t, extra, strs)?, 1324 | })) 1325 | } 1326 | 1327 | fn load_union(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult> { 1328 | Ok(BtfType::Union(BtfComposite { 1329 | is_struct: false, 1330 | name: Btf::get_btf_str(strs, t.name_off)?, 1331 | sz: t.type_id, // it's a type/size union in C 1332 | members: self.load_members(t, extra, strs)?, 1333 | })) 1334 | } 1335 | 1336 | fn load_members( 1337 | &self, 1338 | t: &btf_type, 1339 | extra: &'a [u8], 1340 | strs: &'a [u8], 1341 | ) -> BtfResult>> { 1342 | let mut res = Vec::new(); 1343 | let mut off: usize = 0; 1344 | let bits = Btf::get_kind_flag(t.info); 1345 | 1346 | for _ in 0..Btf::get_vlen(t.info) { 1347 | let m = extra.pread_with::(off, self.endian)?; 1348 | res.push(BtfMember { 1349 | name: Btf::get_btf_str(strs, m.name_off)?, 1350 | type_id: m.type_id, 1351 | bit_size: if bits { (m.offset >> 24) as u8 } else { 0 }, 1352 | bit_offset: if bits { m.offset & 0xffffff } else { m.offset }, 1353 | }); 1354 | off += size_of::(); 1355 | } 1356 | Ok(res) 1357 | } 1358 | 1359 | fn load_enum(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult> { 1360 | let mut vals = Vec::new(); 1361 | let mut off: usize = 0; 1362 | 1363 | for _ in 0..Btf::get_vlen(t.info) { 1364 | let v = extra.pread_with::(off, self.endian)?; 1365 | vals.push(BtfEnumValue { 1366 | name: Btf::get_btf_str(strs, v.name_off)?, 1367 | value: v.val, 1368 | }); 1369 | off += size_of::(); 1370 | } 1371 | Ok(BtfType::Enum(BtfEnum { 1372 | name: Btf::get_btf_str(strs, t.name_off)?, 1373 | sz: t.type_id, // it's a type/size union in C 1374 | values: vals, 1375 | })) 1376 | } 1377 | 1378 | fn load_enum64(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult> { 1379 | let mut vals = Vec::new(); 1380 | let mut off: usize = 0; 1381 | 1382 | for _ in 0..Btf::get_vlen(t.info) { 1383 | let v = extra.pread_with::(off, self.endian)?; 1384 | vals.push(BtfEnum64Value { 1385 | name: Btf::get_btf_str(strs, v.name_off)?, 1386 | value: (i64::from(v.val_lo32) + i64::from(v.val_hi32)) << 32, 1387 | }); 1388 | off += size_of::(); 1389 | } 1390 | Ok(BtfType::Enum64(BtfEnum64 { 1391 | name: Btf::get_btf_str(strs, t.name_off)?, 1392 | sz: t.type_id, // it's a type/size union in C 1393 | values: vals, 1394 | })) 1395 | } 1396 | 1397 | fn load_fwd(&self, t: &btf_type, strs: &'a [u8]) -> BtfResult> { 1398 | Ok(BtfType::Fwd(BtfFwd { 1399 | name: Btf::get_btf_str(strs, t.name_off)?, 1400 | kind: if Btf::get_kind_flag(t.info) { 1401 | BtfFwdKind::Union 1402 | } else { 1403 | BtfFwdKind::Struct 1404 | }, 1405 | })) 1406 | } 1407 | 1408 | fn load_func_proto( 1409 | &self, 1410 | t: &btf_type, 1411 | extra: &'a [u8], 1412 | strs: &'a [u8], 1413 | ) -> BtfResult> { 1414 | let mut params = Vec::new(); 1415 | let mut off: usize = 0; 1416 | 1417 | for _ in 0..Btf::get_vlen(t.info) { 1418 | let p = extra.pread_with::(off, self.endian)?; 1419 | params.push(BtfFuncParam { 1420 | name: Btf::get_btf_str(strs, p.name_off)?, 1421 | type_id: p.type_id, 1422 | }); 1423 | off += size_of::(); 1424 | } 1425 | Ok(BtfType::FuncProto(BtfFuncProto { 1426 | res_type_id: t.type_id, 1427 | params, 1428 | })) 1429 | } 1430 | 1431 | fn load_var(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult> { 1432 | let kind = extra.pread_with::(0, self.endian)?; 1433 | Ok(BtfType::Var(BtfVar { 1434 | name: Btf::get_btf_str(strs, t.name_off)?, 1435 | type_id: t.type_id, 1436 | kind: match kind { 1437 | BTF_VAR_STATIC => BtfVarKind::Static, 1438 | BTF_VAR_GLOBAL_ALLOCATED => BtfVarKind::GlobalAlloc, 1439 | BTF_VAR_GLOBAL_EXTERNAL => BtfVarKind::GlobalExtern, 1440 | _ => { 1441 | return btf_error(format!("Unknown BTF var kind: {}", kind)); 1442 | } 1443 | }, 1444 | })) 1445 | } 1446 | 1447 | fn load_datasec( 1448 | &self, 1449 | t: &btf_type, 1450 | extra: &'a [u8], 1451 | strs: &'a [u8], 1452 | ) -> BtfResult> { 1453 | let mut vars = Vec::new(); 1454 | let mut off: usize = 0; 1455 | 1456 | for _ in 0..Btf::get_vlen(t.info) { 1457 | let v = extra.pread_with::(off, self.endian)?; 1458 | vars.push(BtfDatasecVar { 1459 | type_id: v.type_id, 1460 | offset: v.offset, 1461 | sz: v.size, 1462 | }); 1463 | off += size_of::(); 1464 | } 1465 | Ok(BtfType::Datasec(BtfDatasec { 1466 | name: Btf::get_btf_str(strs, t.name_off)?, 1467 | sz: t.type_id, // it's a type/size union in C 1468 | vars, 1469 | })) 1470 | } 1471 | 1472 | fn load_decl_tag( 1473 | &self, 1474 | t: &btf_type, 1475 | extra: &'a [u8], 1476 | strs: &'a [u8], 1477 | ) -> BtfResult> { 1478 | let comp_idx = extra.pread_with::(0, self.endian)?; 1479 | Ok(BtfType::DeclTag(BtfDeclTag { 1480 | name: Btf::get_btf_str(strs, t.name_off)?, 1481 | type_id: t.type_id, 1482 | comp_idx, 1483 | })) 1484 | } 1485 | 1486 | fn get_vlen(info: u32) -> u32 { 1487 | info & 0xffff 1488 | } 1489 | 1490 | fn get_kind(info: u32) -> u32 { 1491 | (info >> 24) & 0x1f 1492 | } 1493 | 1494 | fn get_kind_flag(info: u32) -> bool { 1495 | (info >> 31) == 1 1496 | } 1497 | 1498 | fn load_func_secs( 1499 | &self, 1500 | mut data: &'a [u8], 1501 | strs: &'a [u8], 1502 | ) -> BtfResult>> { 1503 | let rec_sz = data.pread_with::(0, self.endian)?; 1504 | if rec_sz < size_of::() as u32 { 1505 | return btf_error(format!( 1506 | "Too small func info record size: {}, expect at least: {}", 1507 | rec_sz, 1508 | size_of::() 1509 | )); 1510 | } 1511 | 1512 | data = &data[size_of::()..]; 1513 | let mut secs = Vec::new(); 1514 | while !data.is_empty() { 1515 | let sec_hdr = data.pread_with::(0, self.endian)?; 1516 | data = &data[size_of::()..]; 1517 | 1518 | let mut recs = Vec::new(); 1519 | for i in 0..sec_hdr.num_info { 1520 | let off = (i * rec_sz) as usize; 1521 | let rec = data.pread_with::(off, self.endian)?; 1522 | recs.push(BtfExtFunc { 1523 | insn_off: rec.insn_off, 1524 | type_id: rec.type_id, 1525 | }); 1526 | } 1527 | secs.push(BtfExtSection:: { 1528 | name: Btf::get_btf_str(strs, sec_hdr.sec_name_off)?, 1529 | rec_sz: rec_sz as usize, 1530 | recs, 1531 | }); 1532 | 1533 | data = &data[(sec_hdr.num_info * rec_sz) as usize..]; 1534 | } 1535 | Ok(secs) 1536 | } 1537 | 1538 | fn load_line_secs( 1539 | &self, 1540 | mut data: &'a [u8], 1541 | strs: &'a [u8], 1542 | ) -> BtfResult>>> { 1543 | let rec_sz = data.pread_with::(0, self.endian)?; 1544 | if rec_sz < size_of::() as u32 { 1545 | return btf_error(format!( 1546 | "Too small line info record size: {}, expect at least: {}", 1547 | rec_sz, 1548 | size_of::() 1549 | )); 1550 | } 1551 | data = &data[size_of::()..]; 1552 | let mut secs = Vec::new(); 1553 | while !data.is_empty() { 1554 | let sec_hdr = data.pread_with::(0, self.endian)?; 1555 | data = &data[size_of::()..]; 1556 | 1557 | let mut recs = Vec::new(); 1558 | for i in 0..sec_hdr.num_info { 1559 | let off = (i * rec_sz) as usize; 1560 | let rec = data.pread_with::(off, self.endian)?; 1561 | recs.push(BtfExtLine { 1562 | insn_off: rec.insn_off, 1563 | file_name: Btf::get_btf_str(strs, rec.file_name_off)?, 1564 | src_line: Btf::get_btf_str(strs, rec.line_off)?, 1565 | line_num: rec.line_col >> 10, 1566 | col_num: rec.line_col & 0x3ff, 1567 | }); 1568 | } 1569 | secs.push(BtfExtSection:: { 1570 | name: Btf::get_btf_str(strs, sec_hdr.sec_name_off)?, 1571 | rec_sz: rec_sz as usize, 1572 | recs, 1573 | }); 1574 | 1575 | data = &data[(sec_hdr.num_info * rec_sz) as usize..]; 1576 | } 1577 | Ok(secs) 1578 | } 1579 | 1580 | fn load_core_reloc_secs( 1581 | &self, 1582 | mut data: &'a [u8], 1583 | strs: &'a [u8], 1584 | ) -> BtfResult>>> { 1585 | let rec_sz = data.pread_with::(0, self.endian)?; 1586 | if rec_sz < size_of::() as u32 { 1587 | return btf_error(format!( 1588 | "Too small CO-RE reloc record size: {}, expect at least: {}", 1589 | rec_sz, 1590 | size_of::() 1591 | )); 1592 | } 1593 | data = &data[size_of::()..]; 1594 | let mut secs = Vec::new(); 1595 | while !data.is_empty() { 1596 | let sec_hdr = data.pread_with::(0, self.endian)?; 1597 | data = &data[size_of::()..]; 1598 | 1599 | let mut recs = Vec::new(); 1600 | for i in 0..sec_hdr.num_info { 1601 | let off = (i * rec_sz) as usize; 1602 | let rec = data.pread_with::(off, self.endian)?; 1603 | let kind = match rec.kind { 1604 | BTF_FIELD_BYTE_OFFSET => BtfCoreRelocKind::ByteOff, 1605 | BTF_FIELD_BYTE_SIZE => BtfCoreRelocKind::ByteSz, 1606 | BTF_FIELD_EXISTS => BtfCoreRelocKind::FieldExists, 1607 | BTF_FIELD_SIGNED => BtfCoreRelocKind::Signed, 1608 | BTF_FIELD_LSHIFT_U64 => BtfCoreRelocKind::LShiftU64, 1609 | BTF_FIELD_RSHIFT_U64 => BtfCoreRelocKind::RShiftU64, 1610 | BTF_TYPE_LOCAL_ID => BtfCoreRelocKind::LocalTypeId, 1611 | BTF_TYPE_TARGET_ID => BtfCoreRelocKind::TargetTypeId, 1612 | BTF_TYPE_EXISTS => BtfCoreRelocKind::TypeExists, 1613 | BTF_TYPE_MATCHES => BtfCoreRelocKind::TypeMatches, 1614 | BTF_TYPE_SIZE => BtfCoreRelocKind::TypeSize, 1615 | BTF_ENUMVAL_EXISTS => BtfCoreRelocKind::EnumvalExists, 1616 | BTF_ENUMVAL_VALUE => BtfCoreRelocKind::EnumvalValue, 1617 | _ => { 1618 | return btf_error(format!("Unknown BTF CO-RE reloc kind: {}", rec.kind)); 1619 | } 1620 | }; 1621 | let relo = { 1622 | let access_spec_str = Btf::get_btf_str(strs, rec.access_spec_off)?; 1623 | let access_spec = Btf::parse_reloc_access_spec(access_spec_str)?; 1624 | BtfExtCoreReloc { 1625 | insn_off: rec.insn_off, 1626 | type_id: rec.type_id, 1627 | access_spec_str, 1628 | access_spec, 1629 | kind, 1630 | } 1631 | }; 1632 | recs.push(relo); 1633 | } 1634 | secs.push(BtfExtSection:: { 1635 | name: Btf::get_btf_str(strs, sec_hdr.sec_name_off)?, 1636 | rec_sz: rec_sz as usize, 1637 | recs, 1638 | }); 1639 | 1640 | data = &data[(sec_hdr.num_info * rec_sz) as usize..]; 1641 | } 1642 | Ok(secs) 1643 | } 1644 | fn parse_reloc_access_spec(access_spec_str: &str) -> BtfResult> { 1645 | let mut spec = Vec::new(); 1646 | for p in access_spec_str.split(':') { 1647 | spec.push(p.parse::()?); 1648 | } 1649 | Ok(spec) 1650 | } 1651 | 1652 | fn get_btf_str(strs: &[u8], off: u32) -> BtfResult<&str> { 1653 | let c_str = unsafe { CStr::from_ptr(&strs[off as usize] as *const u8 as *const c_char) }; 1654 | Ok(c_str.to_str()?) 1655 | } 1656 | } 1657 | -------------------------------------------------------------------------------- /tests/samples/bitfields.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | unsigned int: 4; 3 | int a: 4; 4 | long: 57; 5 | long c; 6 | }; 7 | 8 | struct empty {}; 9 | 10 | struct p { 11 | unsigned int: 4; 12 | int a: 4; 13 | long c; 14 | struct { 15 | char x; 16 | int y; 17 | } __attribute__((packed)) d; 18 | } __attribute__((packed)); 19 | 20 | union u { 21 | int a: 4; 22 | char b; 23 | char c: 1; 24 | }; 25 | 26 | int main() { 27 | static struct empty empty; 28 | static struct s s; 29 | static struct p p; 30 | static union u u; 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /tests/samples/bpf/chained_relocs.c: -------------------------------------------------------------------------------- 1 | static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = 2 | (void *) 4; 3 | 4 | #define SEC(NAME) __attribute__((section(NAME), used)) 5 | #define R(P) do { void *x; bpf_probe_read(x, sizeof(1), (void*)&P); } while (0); 6 | 7 | struct S { 8 | union { 9 | int a; 10 | union { 11 | char b; 12 | union { 13 | long q; 14 | int r; 15 | } p; 16 | }; 17 | }; 18 | }; 19 | 20 | extern unsigned __kernel_version; 21 | 22 | SEC("__reloc_test") 23 | int reloc_test(struct S* s) { 24 | R(s->p); 25 | R(s->p.q); 26 | R(s->p.r); 27 | 28 | return 0; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /tests/samples/bpf/prog.c: -------------------------------------------------------------------------------- 1 | struct pt_regs { 2 | long arg1; 3 | long arg2; 4 | }; 5 | 6 | struct sk_buff { 7 | int i; 8 | struct net_device *dev; 9 | }; 10 | 11 | static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = 12 | (void *) 4; 13 | 14 | extern unsigned __kernel_version; 15 | 16 | int bpf_prog(struct pt_regs *ctx) { 17 | struct net_device *dev = 0; 18 | 19 | // ctx->arg* does not need bpf_probe_read 20 | if (__kernel_version >= 41608) 21 | bpf_probe_read(&dev, sizeof(dev), &((struct sk_buff *)ctx->arg1)->dev); 22 | else 23 | bpf_probe_read(&dev, sizeof(dev), &((struct sk_buff *)ctx->arg2)->dev); 24 | return dev != 0; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /tests/samples/bpf/relocs.c: -------------------------------------------------------------------------------- 1 | static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = 2 | (void *) 4; 3 | 4 | #define SEC(NAME) __attribute__((section(NAME), used)) 5 | #define R(P) do { bpf_probe_read(x, sizeof(1), (void*)&P); } while (0); 6 | 7 | struct T { 8 | int t1; 9 | int t2; 10 | }; 11 | typedef struct { int x; } W; 12 | struct S { 13 | const volatile union { 14 | const int a; 15 | const union { 16 | char b; 17 | struct { 18 | char c; 19 | int d; 20 | } e; 21 | struct { 22 | long q; 23 | int r; 24 | } p; 25 | struct { 26 | long q2; 27 | int r2; 28 | } p2; 29 | }; 30 | }; 31 | struct T f[4]; 32 | struct V { 33 | const char *g; 34 | void (*h)(int); 35 | } v; 36 | W w; 37 | struct { 38 | struct T x[5]; 39 | } y[4]; 40 | }; 41 | 42 | extern unsigned __kernel_version; 43 | 44 | SEC("__reloc_test") 45 | int reloc_test(struct S* s) { 46 | void *x; 47 | 48 | R(s[1].y[2].x[3].t2); 49 | R(s[0].y[1].x[2]); 50 | 51 | R(s->a); 52 | R(s->b); 53 | R(s->e); 54 | R(s->e.c); 55 | R(s->e.d); 56 | R(s->p); 57 | R(s->p.q); 58 | R(s->p.r); 59 | R(s->p2); 60 | R(s->p2.q2); 61 | R(s->p2.r2); 62 | R(s->f[3]); 63 | R(s->f[2].t1); 64 | R(s->v); 65 | R(s->v.g); 66 | R(s->v.h); 67 | if (__kernel_version > 41608) { 68 | R(s->w); 69 | R(s->w.x); 70 | } 71 | R(s->y[1]); 72 | R(s->y[2].x[3]); 73 | R(s->y[3].x[4].t2); 74 | 75 | return 0; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /tests/samples/bpf/simple_relocs.c: -------------------------------------------------------------------------------- 1 | static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = 2 | (void *) 4; 3 | 4 | #define SEC(NAME) __attribute__((section(NAME), used)) 5 | #define R(P) do { void *x; bpf_probe_read(x, sizeof(1), (void*)&P); } while (0); 6 | 7 | struct T { 8 | int t1; 9 | int t2; 10 | }; 11 | 12 | struct S { 13 | int bit1: 1; 14 | int bit2: 1; 15 | int bit3: 1; 16 | int x; 17 | struct T t; 18 | union { 19 | int t1; 20 | int t2; 21 | int t3; 22 | }; 23 | }; 24 | 25 | SEC("__simple_reloc_test") 26 | int simple_reloc_test(struct S* s) { 27 | R(s->x); 28 | R(s->t); 29 | R(s->t.t1); 30 | R(s->t.t2); 31 | 32 | R(s->t1); 33 | R(s->t2); 34 | R(s->t3); 35 | return 0; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /tests/samples/bpf/tracex1_kern.c: -------------------------------------------------------------------------------- 1 | #ifdef bpf 2 | #define __has_bpf__ 1 3 | #endif 4 | 5 | #undef bpf 6 | 7 | #include "vmlinux_no_unions.h" 8 | 9 | #ifdef __has_bpf__ 10 | #define bpf 1 11 | #endif 12 | 13 | #define IFNAMSIZ 16 14 | #define LINUX_VERSION_CODE 0 15 | 16 | #define PT_REGS_PARM1(x) ((x)->di) 17 | #define PT_REGS_PARM2(x) ((x)->si) 18 | #define PT_REGS_PARM3(x) ((x)->dx) 19 | #define PT_REGS_PARM4(x) ((x)->cx) 20 | #define PT_REGS_PARM5(x) ((x)->r8) 21 | #define PT_REGS_RET(x) ((x)->sp) 22 | #define PT_REGS_FP(x) ((x)->bp) 23 | #define PT_REGS_RC(x) ((x)->ax) 24 | #define PT_REGS_SP(x) ((x)->sp) 25 | #define PT_REGS_IP(x) ((x)->ip) 26 | 27 | static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = 28 | (void *) BPF_FUNC_probe_read; 29 | static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = 30 | (void *) BPF_FUNC_trace_printk; 31 | 32 | #define SEC(NAME) __attribute__((section(NAME), used)) 33 | #define _(P) ({typeof(P) val = 0; bpf_probe_read(&val, sizeof(val), &P); val;}) 34 | #define R(P) do { void *x; bpf_probe_read(x, sizeof(1), (void*)&P); } while (0); 35 | 36 | int bpf_prog1(struct pt_regs *ctx) 37 | { 38 | /* attaches to kprobe netif_receive_skb, 39 | * looks for packets on loobpack device and prints them 40 | */ 41 | char devname[IFNAMSIZ]; 42 | struct net_device *dev; 43 | struct sk_buff *skb; 44 | int len; 45 | 46 | /* non-portable! works for the given kernel only */ 47 | skb = (struct sk_buff *) PT_REGS_PARM1(ctx); 48 | dev = _(skb->dev); 49 | len = _(skb->len); 50 | 51 | bpf_probe_read(devname, sizeof(devname), dev->name); 52 | 53 | if (devname[0] == 'l' && devname[1] == 'o') { 54 | char fmt[] = "skb %p len %d\n"; 55 | /* using bpf_trace_printk() for DEBUG ONLY */ 56 | bpf_trace_printk(fmt, sizeof(fmt), skb, len); 57 | } 58 | 59 | R(skb[2].__pkt_type_offset); 60 | R(skb->tcp_tsorted_anchor); 61 | return 0; 62 | } 63 | 64 | char _license[] SEC("license") = "GPL"; 65 | u32 _version SEC("version") = LINUX_VERSION_CODE; 66 | -------------------------------------------------------------------------------- /tests/samples/cycles.c: -------------------------------------------------------------------------------- 1 | struct list_head { 2 | struct list_head *next; 3 | struct list_head *prev; 4 | }; 5 | 6 | struct hlist_head { 7 | struct hlist_node *first; 8 | }; 9 | 10 | struct hlist_node { 11 | struct hlist_node *next; 12 | struct hlist_node **pprev; 13 | }; 14 | 15 | struct a; 16 | 17 | struct b { 18 | struct a *p; 19 | }; 20 | 21 | struct a { 22 | struct b *p; 23 | }; 24 | 25 | 26 | struct X { 27 | const struct X * const arr[10]; 28 | struct { 29 | struct X* x1; 30 | }; 31 | struct Y { 32 | struct X* x2; 33 | struct Y* y2; 34 | } y; 35 | }; 36 | 37 | struct Test {}; 38 | typedef struct Test Test; 39 | 40 | int main() { 41 | static struct list_head s1; 42 | static struct hlist_head s2; 43 | static struct a a; 44 | static struct b b; 45 | static struct X x; 46 | static struct Y y; 47 | static struct Test t1; 48 | static Test t2; 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /tests/samples/embed_array.c: -------------------------------------------------------------------------------- 1 | struct weak; 2 | 3 | struct s0 {}; 4 | 5 | struct s1 { 6 | struct s0 a; 7 | }; 8 | 9 | struct s2 { 10 | struct s1 a[2]; 11 | struct weak* w; 12 | }; 13 | 14 | struct s3 { 15 | struct s1 a[3]; 16 | struct s2 b; 17 | struct weak* w; 18 | }; 19 | 20 | struct weak { 21 | struct s1 *arr[10]; 22 | struct s2* b; 23 | struct s3* c; 24 | struct weak* w; 25 | }; 26 | 27 | int main() { 28 | static struct s1 s1; 29 | static struct s2 s2; 30 | static struct s3 s3; 31 | static struct weak w; 32 | return 0; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /tests/samples/embed_array2.c: -------------------------------------------------------------------------------- 1 | struct weak; 2 | 3 | struct s0 {}; 4 | 5 | struct s2 { 6 | struct { struct s0 a; } a[2]; 7 | struct weak* w; 8 | }; 9 | 10 | struct s3 { 11 | struct { struct s0 a; } a[3]; 12 | struct s2 b; 13 | struct weak* w; 14 | }; 15 | 16 | struct weak { 17 | struct { struct s0 a; } *arr[10]; 18 | struct s2* b; 19 | struct s3* c; 20 | struct weak* w; 21 | }; 22 | 23 | int main() { 24 | static struct s2 s2; 25 | static struct s3 s3; 26 | static struct weak w; 27 | return 0; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tests/samples/embed_func_proto.c: -------------------------------------------------------------------------------- 1 | struct weak; 2 | 3 | struct s0 {}; 4 | 5 | struct s2 { 6 | struct { struct s0 a; } (*a)(); 7 | struct weak* w; 8 | }; 9 | 10 | struct s3 { 11 | struct { struct s0 a; } (*a)(); 12 | struct s2 b; 13 | struct weak* w; 14 | }; 15 | 16 | struct weak { 17 | struct { struct s0 a; } *a; 18 | struct s2* b; 19 | struct s3* c; 20 | struct weak* w; 21 | }; 22 | 23 | int main() { 24 | static struct s2 s2; 25 | static struct s3 s3; 26 | static struct weak w; 27 | return 0; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tests/samples/embed_func_proto2.c: -------------------------------------------------------------------------------- 1 | struct s0; 2 | 3 | struct s1 { 4 | struct { struct s0* a; } (*a)(); 5 | }; 6 | 7 | struct s0 { 8 | struct s1 a[10]; 9 | }; 10 | 11 | typedef struct { struct s0* a; } (* fn)(struct { struct s0 a; struct s1 b;} a); 12 | 13 | struct s2 { 14 | struct s0* _a; 15 | struct s1* _b; 16 | fn* _c; 17 | fn a; 18 | struct { struct s0* a; } (*b)(struct { struct s0 a; struct s1 b; } a); 19 | }; 20 | 21 | int main() { 22 | static struct s2 s2; 23 | return 0; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /tests/samples/embed_struct.c: -------------------------------------------------------------------------------- 1 | struct weak; 2 | 3 | struct s0 {}; 4 | 5 | struct s1 { 6 | struct s0 a; 7 | struct weak* w; 8 | }; 9 | 10 | struct s2 { 11 | struct s1 a; 12 | struct weak* w; 13 | }; 14 | 15 | struct s3 { 16 | struct s1 a; 17 | struct s2 b; 18 | struct weak* w; 19 | }; 20 | 21 | struct weak { 22 | struct s1* a; 23 | struct s2* b; 24 | struct s3* c; 25 | struct weak* w; 26 | }; 27 | 28 | int main() { 29 | static struct s1 s1; 30 | static struct s2 s2; 31 | static struct s3 s3; 32 | static struct weak w; 33 | return 0; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /tests/samples/embed_typedef.c: -------------------------------------------------------------------------------- 1 | struct weak; 2 | 3 | struct s0 {}; 4 | 5 | typedef struct { 6 | struct s0 a; 7 | struct weak* w; 8 | } s1_t; 9 | 10 | struct s2 { 11 | s1_t a; 12 | struct weak* w; 13 | }; 14 | 15 | struct s3 { 16 | s1_t a; 17 | struct s2 b; 18 | struct weak* w; 19 | }; 20 | 21 | struct weak { 22 | s1_t* a; 23 | struct s2* b; 24 | struct s3* c; 25 | struct weak* w; 26 | }; 27 | 28 | int main() { 29 | static s1_t s1; 30 | static struct s2 s2; 31 | static struct s3 s3; 32 | static struct weak w; 33 | return 0; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /tests/samples/embed_typedef_cycle.c: -------------------------------------------------------------------------------- 1 | typedef struct s0 s0_t; 2 | 3 | struct s1 { 4 | s0_t* a; 5 | }; 6 | 7 | struct s0 { 8 | struct s1 a; 9 | }; 10 | 11 | struct s2 { 12 | s0_t a; 13 | struct s1 b; 14 | }; 15 | 16 | 17 | int main() { 18 | static struct s2 s2; 19 | static struct s1 s1; 20 | return 0; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /tests/samples/embed_typedef_cycle2.c: -------------------------------------------------------------------------------- 1 | typedef struct s0 s0_t; 2 | 3 | struct s1 { 4 | s0_t* a; 5 | }; 6 | 7 | struct s0 { 8 | struct s1 a; 9 | }; 10 | 11 | struct s2 { 12 | s0_t* a; 13 | s0_t b; 14 | struct s1* c; 15 | struct s1 d; 16 | }; 17 | 18 | 19 | int main() { 20 | static struct s2 s2; 21 | static struct s1 s1; 22 | return 0; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /tests/samples/funcs.c: -------------------------------------------------------------------------------- 1 | typedef int (*fnproto)(); 2 | 3 | static fnproto v = 0; 4 | 5 | int func() { 6 | return 0; 7 | } 8 | 9 | int func_with_args(int a, int b) { 10 | #pragma clang diagnostic push 11 | #pragma clang diagnostic ignored "-Wpointer-to-int-cast" 12 | return (int)v; 13 | #pragma clang diagnostic pop 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/samples/naming_conflicts.c: -------------------------------------------------------------------------------- 1 | enum T {V=0}; 2 | typedef void (*T)(); 3 | 4 | int main() { 5 | static enum T t1 = V; 6 | static T t5 = 0; 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/samples/order_test.c: -------------------------------------------------------------------------------- 1 | struct t1 { 2 | const struct t2* t; 3 | }; 4 | 5 | typedef struct t1 t1_t; 6 | 7 | struct t2 { 8 | t1_t t; 9 | }; 10 | 11 | int main() { 12 | static struct t1 t1; 13 | static struct t2 t2; 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /tests/samples/order_test2.c: -------------------------------------------------------------------------------- 1 | 2 | struct s3 {}; 3 | 4 | struct s1 { 5 | struct s3* s3; 6 | struct s2* s2; 7 | struct s4* s4; 8 | }; 9 | 10 | struct s2 { 11 | const struct s1 s1; 12 | volatile struct s3 s3; 13 | }; 14 | 15 | struct s4 { 16 | struct s2 s2; 17 | struct s1 s1; 18 | }; 19 | 20 | int main() { 21 | static struct s3 s3; 22 | static struct s1 s1; 23 | static struct s2 s2; 24 | static struct s4 s4; 25 | return 0; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /tests/samples/order_test3.c: -------------------------------------------------------------------------------- 1 | 2 | typedef struct s0 s0_t; 3 | 4 | struct s0 {}; 5 | struct s1 { 6 | s0_t* s0p; 7 | }; 8 | 9 | struct s2 { 10 | s0_t s0; 11 | }; 12 | 13 | struct s3 { 14 | struct s2 s3; 15 | }; 16 | 17 | int main() { 18 | static struct s1 s1; 19 | static struct s2 s2; 20 | static struct s3 s3; 21 | return 0; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /tests/samples/order_test4.c: -------------------------------------------------------------------------------- 1 | 2 | typedef struct s0 s0_t; 3 | 4 | struct s0 {}; 5 | struct s1 { 6 | s0_t* s0p; 7 | }; 8 | 9 | struct s2 { 10 | s0_t s0; 11 | }; 12 | 13 | struct s3 { 14 | struct s2 s3; 15 | }; 16 | 17 | int main() { 18 | static struct s1 s1; 19 | static struct s2 s2; 20 | static struct s3 s3; 21 | return 0; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /tests/samples/ordering.c: -------------------------------------------------------------------------------- 1 | struct s1; 2 | 3 | typedef void (*f1)(struct s1); 4 | typedef struct s1 (*f2)(); 5 | 6 | struct s2; 7 | 8 | // struct s4 has weak edge to struct s3 9 | struct s4 { 10 | struct s3* a; 11 | }; 12 | 13 | struct s3 { 14 | struct s2* x1; 15 | }; 16 | 17 | // struct s2 has strong edge to struct s3 18 | struct s2 { 19 | struct s3 a; 20 | }; 21 | 22 | struct t1; 23 | 24 | typedef struct t1 t1_t; 25 | 26 | struct t1 { 27 | const struct t2* t; 28 | }; 29 | 30 | struct t2 { 31 | t1_t t; 32 | }; 33 | 34 | int main() { 35 | static f1 f1 = 0; 36 | static f2 f2 = 0; 37 | static struct s4 s4; 38 | static struct s2 s2; 39 | static struct s3 s3; 40 | static struct t1 t1; 41 | static struct t2 t2; 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /tests/samples/printing_arrays.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | char a[0]; 3 | char b[1]; 4 | const char volatile c[2]; 5 | const volatile char * const volatile (* const volatile restrict z)[10]; 6 | char d[]; 7 | }; 8 | 9 | struct s2 { 10 | char a[0]; 11 | char b[1]; 12 | const char c[2]; 13 | const char d[]; 14 | }; 15 | 16 | int main() { 17 | static struct s s; 18 | static struct s2 s2; 19 | return 0; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /tests/samples/ptrs.c: -------------------------------------------------------------------------------- 1 | typedef int *int_ptr; 2 | typedef int **int_pptr; 3 | typedef const int * const * volatile const cint_cptr_cvptr; 4 | typedef const volatile int * const volatile restrict * volatile restrict const cint_cptr_cvptr2; 5 | 6 | typedef int (*fn_ptr1)(int); 7 | typedef char * const * (*fn_ptr2)(); 8 | 9 | typedef int* ptr_arr1[10]; 10 | 11 | typedef char * (*fn_ptr_arr1[10])(int **p); 12 | typedef int (*fn_ptr_arr2[10])(void*); 13 | typedef char * (* const (* const fn_ptr_arr3[5])()) (char * (*)(int)); 14 | 15 | char *(*f1())(int) { 16 | return 0; 17 | } 18 | 19 | int main() { 20 | static int_ptr s1; 21 | static int_pptr s2; 22 | static cint_cptr_cvptr s3; 23 | static cint_cptr_cvptr2 s3_2; 24 | static fn_ptr1 s4; 25 | static fn_ptr2 s5; 26 | static ptr_arr1 s6; 27 | static fn_ptr_arr1 s7; 28 | static fn_ptr_arr2 s8; 29 | static fn_ptr_arr3 s9; 30 | 31 | f1(); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /tests/samples/simple.c: -------------------------------------------------------------------------------- 1 | typedef unsigned int u32; 2 | 3 | typedef void (*fn)(int); 4 | 5 | typedef struct { 6 | int x, y, z; 7 | } anon_struct_t; 8 | 9 | void f(void (*fn)(int)) { 10 | } 11 | 12 | enum E { 13 | V1 = 0, 14 | V2 = 1, 15 | }; 16 | 17 | enum E e = V1; 18 | 19 | struct S; 20 | union U; 21 | struct S; 22 | 23 | u32 func(enum E bla, const struct S* fwd_s, volatile union U* fwd_u) { 24 | return bla; 25 | } 26 | 27 | struct SimpleStruct { 28 | int a; 29 | u32 b; 30 | void (*f)(int a, enum E b); 31 | enum E arr[10]; 32 | }; 33 | 34 | union SimpleUnion { 35 | int a; 36 | struct SimpleStruct s; 37 | char arr[128]; 38 | }; 39 | 40 | union NestedAnonUnion { 41 | struct { 42 | int a; 43 | union { 44 | int b; 45 | int c; 46 | } d; 47 | struct { 48 | int x; 49 | char y; 50 | u32 z; 51 | }; 52 | } A; 53 | int B; 54 | union SimpleUnion C; 55 | union { 56 | struct SimpleStruct Q; 57 | union SimpleUnion T; 58 | }; 59 | }; 60 | 61 | 62 | int main() { 63 | static struct SimpleStruct s1; 64 | static union SimpleUnion s2; 65 | static union NestedAnonUnion s3; 66 | static anon_struct_t s4; 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn dump() { 3 | let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); 4 | let bpf_arch = match std::env::consts::ARCH { 5 | "x86_64" => "bpfel", 6 | arch => panic!("unsupported arch {}", arch), 7 | }; 8 | let tempdir = tempfile::tempdir().unwrap(); 9 | 10 | for entry in manifest_dir 11 | .join("tests") 12 | .join("samples") 13 | .read_dir() 14 | .unwrap() 15 | { 16 | let entry = entry.unwrap(); 17 | let path = entry.path(); 18 | 19 | if path.is_dir() { 20 | continue; 21 | } 22 | 23 | let dst = { 24 | let path = path.strip_prefix(manifest_dir).unwrap(); 25 | 26 | println!("compiling {}", path.display()); 27 | 28 | let dst = tempdir.path().join(path); 29 | let parent = dst.parent().unwrap(); 30 | std::fs::create_dir_all(parent).unwrap(); 31 | dst 32 | }; 33 | 34 | // Compile the sample. 35 | { 36 | let mut cmd = std::process::Command::new("clang"); 37 | let std::process::Output { 38 | status, 39 | stdout, 40 | stderr, 41 | } = cmd 42 | .args(["-g", "-target", bpf_arch, "-nostdinc", "-c", "-o"]) 43 | .args([&dst, &path]) 44 | .output() 45 | .unwrap(); 46 | let stdout = std::str::from_utf8(&stdout); 47 | let stderr = std::str::from_utf8(&stderr); 48 | assert_eq!( 49 | status.code(), 50 | Some(0), 51 | "{:?} failed: stdout={:?} stderr={:?}", 52 | cmd, 53 | stdout, 54 | stderr 55 | ); 56 | let stdout = stdout.unwrap(); 57 | let stderr = stderr.unwrap(); 58 | assert!(stdout.is_empty(), "{:?}", stdout); 59 | assert!(stderr.is_empty(), "{:?}", stderr); 60 | } 61 | 62 | // Run `dump` on the result. 63 | { 64 | let mut cmd = std::process::Command::new(env!("CARGO_BIN_EXE_btf")); 65 | let std::process::Output { 66 | status, 67 | stdout, 68 | stderr, 69 | } = cmd.arg("dump").arg(dst).output().unwrap(); 70 | let stdout = std::str::from_utf8(&stdout); 71 | let stderr = std::str::from_utf8(&stderr); 72 | assert_eq!( 73 | status.code(), 74 | Some(0), 75 | "{:?} failed: stdout={:?} stderr={:?}", 76 | cmd, 77 | stdout, 78 | stderr 79 | ); 80 | let stdout = stdout.unwrap(); 81 | let stderr = stderr.unwrap(); 82 | assert!(!stdout.is_empty(), "{:?}", stdout); 83 | assert!(stderr.is_empty(), "{:?}", stderr); 84 | } 85 | } 86 | } 87 | --------------------------------------------------------------------------------