├── .gitignore ├── .gitmodules ├── Cargo.toml ├── LICENSE.md ├── README.md ├── examples └── list-symbols.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/glibc-abi-tool"] 2 | path = deps/glibc-abi-tool 3 | url = https://github.com/ziglang/glibc-abi-tool 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | description = "Parse `abilists` files from the Zig project." 3 | edition = "2021" 4 | license = "MIT" 5 | name = "abilists" 6 | repository = "https://github.com/ErichDonGubler/abilists" 7 | version = "0.1.0" 8 | 9 | [dependencies] 10 | arcstr = "1.1.3" 11 | arrayvec = "0.7.2" 12 | ascii = "1" 13 | eio = "0.1.2" 14 | log = "0.4" 15 | thiserror = "1.0.30" 16 | 17 | # TODO: Move to `dev-dependencies` 18 | color-eyre = "0.6.0" 19 | 20 | [dev-dependencies] 21 | clap = { version = "3.0.14", features = ["derive"] } 22 | env_logger = "0.9.0" 23 | format = "0.2.4" 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2022 `abilists` developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `abilists` 2 | 3 | This repo is a Rust crate that parses `abilists` generated by the Zig project's 4 | [`glibc-abi-tool` repository](https://github.com/ziglang/glibc-abi-tool/). 5 | 6 | This repo is a nerdsnipe from [this tweet by Andrew 7 | Kelley](https://web.archive.org/web/20220221212148/https://twitter.com/andy_kelley/status/1470338795266457600). 8 | 9 | ## Getting started 10 | 11 | 1. Generate the `abilists` file in `deps/glibc-abi-tool`. 12 | 1. Install the latest [Zig toolchain](https://ziglang.org/). 13 | 2. Change your working directory to `deps/glibc-abi-tool`. 14 | 3. Run `zig run consolidate.zig`. 15 | 2. 🎉Congrats, you're ready to start hacking on this project! 16 | -------------------------------------------------------------------------------- /examples/list-symbols.rs: -------------------------------------------------------------------------------- 1 | //! A Rust rewrite of [`list_symbols.zig`] that attempts to keep the exact same output format on 2 | //! the happy path. 3 | //! 4 | //! [`list_symbols.zig`]: ../deps/glibc-abi-tool/list_symbols.zig 5 | 6 | use abilists::{AbiList, GlibcVersion}; 7 | use clap::Parser; 8 | use color_eyre::eyre::WrapErr; 9 | use format::lazy_format; 10 | use std::{ 11 | fmt::{self, Display, Formatter}, 12 | fs::File, 13 | io::BufReader, 14 | path::PathBuf, 15 | }; 16 | 17 | #[derive(Debug, Parser)] 18 | struct Args { 19 | #[clap(parse(from_os_str))] 20 | file_path: PathBuf, 21 | } 22 | 23 | struct GlibcLibraryName<'a>(pub &'a str); 24 | 25 | impl<'a> Display for GlibcLibraryName<'a> { 26 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 27 | let Self(name) = self; 28 | write!(f, "lib{name}.so") 29 | } 30 | } 31 | 32 | fn main() -> color_eyre::eyre::Result<()> { 33 | color_eyre::install()?; 34 | env_logger::init(); 35 | 36 | let Args { file_path } = Args::parse(); 37 | 38 | let mut file = { 39 | let file = File::open(&file_path).wrap_err("failed to open file path")?; 40 | BufReader::new(file) 41 | }; 42 | 43 | let abi_list = AbiList::from_reader(&mut file) 44 | .wrap_err("failed to read `abilist` structure from given file path")?; 45 | 46 | println!("Libraries:"); 47 | abi_list 48 | .libraries() 49 | .iter() 50 | .enumerate() 51 | .for_each(|(idx, name)| { 52 | println!(" {idx} {}", GlibcLibraryName(&name)); 53 | }); 54 | 55 | println!("Versions:"); 56 | let version_display = |version| { 57 | lazy_format!(move |f| { 58 | let GlibcVersion { 59 | major, 60 | minor, 61 | patch, 62 | } = &version; 63 | match patch { 64 | 0 => write!(f, "{major}.{minor}"), 65 | patch => write!(f, "{major}.{minor}.{patch}"), 66 | } 67 | }) 68 | }; 69 | abi_list 70 | .versions() 71 | .iter() 72 | .enumerate() 73 | .for_each(|(idx, version)| { 74 | println!(" {idx} GLIBC_{}", version_display(version.clone())); 75 | }); 76 | 77 | println!("Targets:"); 78 | abi_list 79 | .targets() 80 | .iter() 81 | .enumerate() 82 | .for_each(|(idx, target)| { 83 | println!(" {idx} {target}"); 84 | }); 85 | 86 | println!("Functions:"); 87 | abi_list.functions().iter().for_each(|func| { 88 | func.inclusions().for_each(|inclusion| { 89 | println!(" {}:", func.symbol_name()); 90 | println!(" library: {}", GlibcLibraryName(inclusion.library())); 91 | 92 | print!(" versions:"); 93 | inclusion.versions().iter().for_each(|version| { 94 | print!(" {}", version_display(version.clone())); 95 | }); 96 | println!(); 97 | 98 | print!(" targets:"); 99 | inclusion.targets().for_each(|target| { 100 | print!(" {target}"); 101 | }); 102 | println!(); 103 | }) 104 | }); 105 | 106 | println!("Objects:"); 107 | abi_list.objects().iter().for_each(|obj| { 108 | obj.inclusions().for_each(|inclusion| { 109 | println!(" {}:", obj.symbol_name()); 110 | println!(" size: {}", inclusion.size()); 111 | println!(" library: {}", GlibcLibraryName(inclusion.library())); 112 | 113 | print!(" versions:"); 114 | inclusion.versions().iter().for_each(|version| { 115 | print!(" {}", version_display(version.clone())); 116 | }); 117 | println!(); 118 | 119 | print!(" targets:"); 120 | inclusion.targets().for_each(|target| { 121 | print!(" {target}"); 122 | }); 123 | println!(); 124 | }) 125 | }); 126 | 127 | Ok(()) 128 | } 129 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use arcstr::ArcStr; 2 | use arrayvec::ArrayVec; 3 | use ascii::AsciiString; 4 | use color_eyre::eyre::{eyre, Context}; 5 | use eio::ReadExt; 6 | use std::{fmt::Display, io::BufRead, iter}; 7 | 8 | /// An ABI list parsed from a consolidated `abilists` data generated by [`glibc-abi-tool`]. 9 | /// 10 | /// [`glibc-abi-tool`]: https://github.com/ziglang/glibc-abi-tool/ 11 | pub struct AbiList { 12 | lib_names: ArrayVec, 13 | versions: ArrayVec, 14 | targets: ArrayVec, 15 | functions: Vec, 16 | objects: Vec, 17 | } 18 | 19 | // TODO 20 | // /// An error case that may happen in [`AbiList::from_bytes`]. 21 | // #[derive(Debug, thiserror::Error)] 22 | // pub enum AbiListParseError {} 23 | 24 | pub type AbiListParseError = color_eyre::eyre::Error; 25 | 26 | impl AbiList { 27 | pub fn from_reader(mut reader: &mut dyn BufRead) -> Result { 28 | let lib_names = { 29 | let mut lib_names = ArrayVec::::new(); 30 | let num_lib_names: u8 = reader 31 | .read_le() 32 | .wrap_err("failed to read number of library names")?; 33 | for idx in 0..num_lib_names { 34 | macro_rules! this_elem_disp { 35 | () => { 36 | format_args!("element {idx} of library names array") 37 | }; 38 | } 39 | let lib_name = parse_nul_term_ascii(&mut reader, &this_elem_disp!()) 40 | .wrap_err_with(|| { 41 | eyre!("failed to read element {idx} of library names array") 42 | })?; 43 | let lib_name: String = lib_name.into(); 44 | lib_names.push(ArcStr::from(lib_name)); 45 | } 46 | lib_names 47 | }; 48 | 49 | let versions = { 50 | let mut versions = ArrayVec::::new(); 51 | let num_versions: u8 = reader 52 | .read_le() 53 | .wrap_err("failed to read number of supported `glibc` versions")?; 54 | for idx in 0..num_versions { 55 | let mut parse = |what| { 56 | reader.read_le().wrap_err_with(|| { 57 | eyre!( 58 | "failed to read {what} field of element {idx} of supported `glibc` \ 59 | versions array" 60 | ) 61 | }) 62 | }; 63 | let major = parse("major")?; 64 | let minor = parse("minor")?; 65 | let patch = parse("patch")?; 66 | versions.push(GlibcVersion { 67 | major, 68 | minor, 69 | patch, 70 | }); 71 | } 72 | versions 73 | }; 74 | 75 | let targets = { 76 | let mut targets = ArrayVec::::new(); 77 | 78 | let num_targets: u8 = reader 79 | .read_le() 80 | .wrap_err("failed to read number of target triples")?; 81 | 82 | for idx in 0..num_targets { 83 | macro_rules! this_elem_disp { 84 | () => { 85 | format_args!("element {idx} of target triples array") 86 | }; 87 | } 88 | let target = parse_nul_term_ascii(&mut reader, &this_elem_disp!())?; 89 | let target: String = target.into(); 90 | let target = ArcStr::from(target); 91 | targets.push(target); 92 | } 93 | 94 | targets 95 | }; 96 | 97 | let functions = { 98 | struct FunctionDataParser; 99 | impl InclusionDataParser for FunctionDataParser { 100 | type Data = (); 101 | 102 | fn type_desc_singular(&self) -> &str { 103 | "function" 104 | } 105 | 106 | fn type_desc_plural(&self) -> &str { 107 | "functions" 108 | } 109 | 110 | fn parse( 111 | &mut self, 112 | _reader: &mut dyn BufRead, 113 | ) -> Result { 114 | Ok(()) 115 | } 116 | } 117 | SymbolInclusions::parse_set( 118 | reader, 119 | &targets, 120 | &lib_names, 121 | &versions, 122 | FunctionDataParser, 123 | )? 124 | .map(|inc_res| inc_res.map(|inc| Function(inc))) 125 | .collect::>()? 126 | }; 127 | 128 | let objects = { 129 | struct ObjectDataParser; 130 | impl InclusionDataParser for ObjectDataParser { 131 | type Data = ObjectData; 132 | 133 | fn type_desc_singular(&self) -> &str { 134 | "object" 135 | } 136 | 137 | fn type_desc_plural(&self) -> &str { 138 | "objects" 139 | } 140 | 141 | fn parse( 142 | &mut self, 143 | mut reader: &mut dyn BufRead, 144 | ) -> Result { 145 | let size = reader.read_le().wrap_err("failed to read object size")?; 146 | Ok(ObjectData { size }) 147 | } 148 | } 149 | SymbolInclusions::parse_set(reader, &targets, &lib_names, &versions, ObjectDataParser)? 150 | .map(|inc_res| inc_res.map(|inc| Object(inc))) 151 | .collect::>()? 152 | }; 153 | 154 | Ok(AbiList { 155 | functions, 156 | lib_names, 157 | objects, 158 | targets, 159 | versions, 160 | }) 161 | } 162 | } 163 | 164 | #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] 165 | pub struct GlibcVersion { 166 | pub major: u8, 167 | pub minor: u8, 168 | pub patch: u8, 169 | } 170 | 171 | impl AbiList { 172 | pub fn libraries(&self) -> &[ArcStr] { 173 | &self.lib_names[..] 174 | } 175 | 176 | pub fn versions(&self) -> &[GlibcVersion] { 177 | &self.versions[..] 178 | } 179 | 180 | pub fn targets(&self) -> &[ArcStr] { 181 | &self.targets[..] 182 | } 183 | 184 | pub fn functions(&self) -> GlibcFunctions<'_> { 185 | let Self { 186 | targets, functions, .. 187 | } = self; 188 | GlibcFunctions { targets, functions } 189 | } 190 | 191 | pub fn objects(&self) -> GlibcObjects<'_> { 192 | let Self { 193 | targets, objects, .. 194 | } = self; 195 | GlibcObjects { targets, objects } 196 | } 197 | } 198 | 199 | pub struct GlibcFunctions<'a> { 200 | targets: &'a [ArcStr], 201 | functions: &'a [Function], 202 | } 203 | 204 | impl<'a> GlibcFunctions<'a> { 205 | pub fn len(&self) -> u16 { 206 | self.functions 207 | .len() 208 | .try_into() 209 | .expect("internal error: length out of `u16` bounds") 210 | } 211 | 212 | pub fn iter(&self) -> impl Iterator> + '_ { 213 | (0..self.len()).map(|idx| self.get(idx).unwrap()) 214 | } 215 | 216 | pub fn get(&self, index: u16) -> Option> { 217 | self.functions 218 | .get(usize::from(index)) 219 | .map(|func| GlibcFunction { 220 | targets: self.targets, 221 | func, 222 | }) 223 | } 224 | } 225 | 226 | #[derive(Debug)] 227 | struct Function(SymbolInclusions<()>); 228 | 229 | pub struct GlibcObjects<'a> { 230 | targets: &'a [ArcStr], 231 | objects: &'a [Object], 232 | } 233 | 234 | impl<'a> GlibcObjects<'a> { 235 | pub fn len(&self) -> u16 { 236 | self.objects 237 | .len() 238 | .try_into() 239 | .expect("internal error: length out of `u16` bounds") 240 | } 241 | 242 | pub fn iter(&self) -> impl Iterator> + '_ { 243 | (0..self.len()).map(|idx| self.get(idx).unwrap()) 244 | } 245 | 246 | pub fn get(&self, index: u16) -> Option> { 247 | self.objects 248 | .get(usize::from(index)) 249 | .map(|func| GlibcObject { 250 | targets: self.targets, 251 | func, 252 | }) 253 | } 254 | } 255 | 256 | #[derive(Debug)] 257 | struct Object(SymbolInclusions); 258 | 259 | #[derive(Debug)] 260 | pub struct ObjectData { 261 | size: u16, 262 | } 263 | 264 | #[derive(Debug)] 265 | struct SymbolInclusions { 266 | symbol_name: ArcStr, 267 | inclusions: Vec>, 268 | } 269 | 270 | impl SymbolInclusions { 271 | fn parse_set<'a, Parser>( 272 | mut reader: &'a mut dyn BufRead, 273 | targets: &'a [ArcStr], 274 | lib_names: &'a [ArcStr], 275 | versions: &'a [GlibcVersion], 276 | mut entry_parser: Parser, 277 | ) -> Result> + 'a, AbiListParseError> 278 | where 279 | Parser: InclusionDataParser + 'a, 280 | { 281 | let disallowed_target_triple_bitmask = (!0u32) << targets.len(); 282 | 283 | let mut num_inclusions_found = 0; 284 | let mut num_symbols_found = 0; 285 | let num_expected_inclusions = reader.read_le::().wrap_err_with(|| { 286 | eyre!( 287 | "failed to read expected number of {}", 288 | entry_parser.type_desc_plural() 289 | ) 290 | })?; 291 | 292 | let mut next_res = move || { 293 | if num_inclusions_found < num_expected_inclusions { 294 | macro_rules! this_elem_disp { 295 | () => { 296 | format_args!( 297 | "element {num_symbols_found} of {} array", 298 | entry_parser.type_desc_singular(), 299 | ) 300 | }; 301 | } 302 | 303 | let symbol_name = parse_nul_term_ascii( 304 | &mut reader, 305 | &format_args!("symbol name of {}", this_elem_disp!()), 306 | )?; 307 | let symbol_name: String = symbol_name.into(); 308 | let symbol_name = ArcStr::from(symbol_name); 309 | 310 | log::debug!( 311 | "found {} symbol with name `{symbol_name}`", 312 | entry_parser.type_desc_singular(), 313 | ); 314 | 315 | macro_rules! this_elem_desc { 316 | () => { 317 | format_args!("`{symbol_name}` ({})", this_elem_disp!()) 318 | }; 319 | } 320 | 321 | let mut inclusions = Vec::new(); 322 | let mut is_last_inclusion_for_symbol_name = false; 323 | while !is_last_inclusion_for_symbol_name { 324 | const LAST_INCLUSION_FOR_SYMBOL_NAME_BITMASK: u32 = !(!0 >> 1); 325 | 326 | let mut targets_bitmask = reader.read_le::().wrap_err_with(|| { 327 | eyre!("failed to read target bitmask of {}", this_elem_desc!()) 328 | })?; 329 | 330 | is_last_inclusion_for_symbol_name = 331 | targets_bitmask & LAST_INCLUSION_FOR_SYMBOL_NAME_BITMASK != 0; 332 | if is_last_inclusion_for_symbol_name { 333 | targets_bitmask &= !LAST_INCLUSION_FOR_SYMBOL_NAME_BITMASK; 334 | } 335 | 336 | if targets_bitmask & disallowed_target_triple_bitmask != 0 { 337 | return Err(eyre!( 338 | "target triple bitmask of {} is outside of parsed range of {} targets", 339 | this_elem_desc!(), 340 | targets.len(), 341 | )); 342 | } 343 | 344 | let data = entry_parser.parse(reader).wrap_err_with(|| { 345 | eyre!( 346 | "failed to parse data specific to {}", 347 | entry_parser.type_desc_singular(), 348 | ) 349 | })?; 350 | 351 | let library_idx = reader.read_le::().wrap_err_with(|| { 352 | eyre!("failed to read library index of {}", this_elem_desc!()) 353 | })?; 354 | 355 | let library = lib_names 356 | .get(usize::from(library_idx)) 357 | .ok_or_else(|| { 358 | eyre!( 359 | "invalid library index {library_idx} of {}; expected index < {}", 360 | this_elem_desc!(), 361 | lib_names.len(), 362 | ) 363 | })? 364 | .clone(); 365 | 366 | let mut inclusion_versions = Vec::new(); 367 | let mut version_idx_idx = 0; 368 | loop { 369 | const LAST_VERSION_FOR_INCLUSION_BITMASK: u8 = !(!0 >> 1); 370 | let mut version_idx: u8 = reader.read_le().wrap_err_with(|| { 371 | eyre!( 372 | "failed to read version mapping element {version_idx_idx} of {}", 373 | this_elem_desc!() 374 | ) 375 | })?; 376 | 377 | let is_last_version_in_inclusion = 378 | version_idx & LAST_VERSION_FOR_INCLUSION_BITMASK != 0; 379 | if is_last_version_in_inclusion { 380 | version_idx &= !LAST_VERSION_FOR_INCLUSION_BITMASK; 381 | } 382 | 383 | let version = versions 384 | .get(usize::from(version_idx)) 385 | .ok_or_else(|| { 386 | eyre!( 387 | "invalid version index {version_idx} of {}; expected index < {}", 388 | this_elem_desc!(), 389 | versions.len(), 390 | ) 391 | })? 392 | .clone(); 393 | 394 | inclusion_versions.push(version); 395 | 396 | if is_last_version_in_inclusion { 397 | break; 398 | } 399 | version_idx_idx += 1; 400 | } 401 | 402 | inclusions.push(Inclusion { 403 | targets_bitmask, 404 | data, 405 | library, 406 | versions: inclusion_versions.into_iter().collect(), 407 | }); 408 | num_inclusions_found += 1; 409 | } 410 | 411 | let ret = Ok(Some(SymbolInclusions { 412 | symbol_name, 413 | inclusions, 414 | })); 415 | num_symbols_found += 1; 416 | 417 | if num_inclusions_found > num_expected_inclusions { 418 | return Err(eyre!( 419 | "number of inclusions ({num_inclusions_found}) exceeds expected \ 420 | ({num_expected_inclusions})" 421 | )); 422 | } 423 | 424 | ret 425 | } else { 426 | return Ok(None); 427 | } 428 | }; 429 | Ok(iter::from_fn(move || next_res().transpose())) 430 | } 431 | } 432 | 433 | trait InclusionDataParser { 434 | type Data; 435 | 436 | fn type_desc_singular(&self) -> &str; 437 | fn type_desc_plural(&self) -> &str; 438 | fn parse(&mut self, reader: &mut dyn BufRead) -> Result; 439 | } 440 | 441 | #[derive(Debug)] 442 | struct Inclusion { 443 | targets_bitmask: u32, 444 | data: T, 445 | library: ArcStr, 446 | versions: Vec, // OPT: the buffer upstream is set to size `50`, maybe useful? 447 | } 448 | 449 | pub struct GlibcFunction<'a> { 450 | targets: &'a [ArcStr], 451 | func: &'a Function, 452 | } 453 | 454 | impl<'a> GlibcFunction<'a> { 455 | pub fn symbol_name(&self) -> &str { 456 | let Self { 457 | func: Function(SymbolInclusions { symbol_name, .. }), 458 | .. 459 | } = self; 460 | symbol_name 461 | } 462 | 463 | pub fn inclusions(&self) -> impl Iterator> + '_ { 464 | let Self { 465 | func: Function(SymbolInclusions { inclusions, .. }), 466 | .. 467 | } = self; 468 | inclusions.iter().map(|inclusion| GlibcFunctionInclusion { 469 | targets: self.targets, 470 | inclusion, 471 | }) 472 | } 473 | } 474 | 475 | pub struct GlibcFunctionInclusion<'a> { 476 | targets: &'a [ArcStr], 477 | inclusion: &'a Inclusion<()>, 478 | } 479 | 480 | impl<'a> GlibcFunctionInclusion<'a> { 481 | pub fn library(&self) -> &str { 482 | &self.inclusion.library 483 | } 484 | 485 | pub fn versions(&self) -> &[GlibcVersion] { 486 | &self.inclusion.versions 487 | } 488 | 489 | pub fn targets(&self) -> impl Iterator { 490 | (0..self.targets.len()) 491 | .filter(|target_idx| self.inclusion.targets_bitmask & (1 << target_idx) != 0) 492 | .map(|target_idx| &self.targets[target_idx]) 493 | } 494 | } 495 | 496 | pub struct GlibcObject<'a> { 497 | targets: &'a [ArcStr], 498 | func: &'a Object, 499 | } 500 | 501 | impl<'a> GlibcObject<'a> { 502 | pub fn symbol_name(&self) -> &str { 503 | let Self { 504 | func: Object(SymbolInclusions { symbol_name, .. }), 505 | .. 506 | } = self; 507 | symbol_name 508 | } 509 | 510 | pub fn inclusions(&self) -> impl Iterator> + '_ { 511 | let Self { 512 | func: Object(SymbolInclusions { inclusions, .. }), 513 | .. 514 | } = self; 515 | inclusions.iter().map(|inclusion| GlibcObjectInclusion { 516 | targets: self.targets, 517 | inclusion, 518 | }) 519 | } 520 | } 521 | 522 | pub struct GlibcObjectInclusion<'a> { 523 | targets: &'a [ArcStr], 524 | inclusion: &'a Inclusion, 525 | } 526 | 527 | impl<'a> GlibcObjectInclusion<'a> { 528 | pub fn library(&self) -> &str { 529 | &self.inclusion.library 530 | } 531 | 532 | pub fn versions(&self) -> &[GlibcVersion] { 533 | &self.inclusion.versions 534 | } 535 | 536 | pub fn size(&self) -> u16 { 537 | self.inclusion.data.size 538 | } 539 | 540 | pub fn targets(&self) -> impl Iterator { 541 | (0..self.targets.len()) 542 | .filter(|target_idx| self.inclusion.targets_bitmask & (1 << target_idx) != 0) 543 | .map(|target_idx| &self.targets[target_idx]) 544 | } 545 | } 546 | 547 | fn parse_nul_term_ascii( 548 | file: &mut dyn BufRead, 549 | what: &dyn Display, 550 | ) -> Result { 551 | let mut buf = Vec::new(); 552 | file.read_until(0, &mut buf) 553 | .wrap_err_with(|| eyre!("failed to read {what}"))?; 554 | let err = |reason| eyre!("unexpected end of file while parsing {what}; {reason}"); 555 | match buf.last() { 556 | Some(0) => { 557 | buf.pop(); 558 | } 559 | Some(byte) => { 560 | return Err(err(format_args!( 561 | "expected null terminator, found {byte:02X}" 562 | ))) 563 | } 564 | None => return Err(err(format_args!("no more bytes"))), 565 | }; 566 | log::trace!("attempting to parse symbol name from {:X?}", buf); 567 | AsciiString::from_ascii(buf).wrap_err_with(|| eyre!("failed to parse {what} as ASCII")) 568 | } 569 | --------------------------------------------------------------------------------