use arcstr::ArcStr;
use arrayvec::ArrayVec;
use ascii::AsciiString;
use color_eyre::eyre::{eyre, Context};
use eio::ReadExt;
use std::{fmt::Display, io::BufRead, iter};
pub struct AbiList {
lib_names: ArrayVec<ArcStr, 8>,
versions: ArrayVec<GlibcVersion, 64>,
targets: ArrayVec<ArcStr, 32>,
functions: Vec<Function>,
objects: Vec<Object>,
}
pub type AbiListParseError = color_eyre::eyre::Error;
impl AbiList {
pub fn from_reader(mut reader: &mut dyn BufRead) -> Result<Self, AbiListParseError> {
let lib_names = {
let mut lib_names = ArrayVec::<ArcStr, 8>::new();
let num_lib_names: u8 = reader
.read_le()
.wrap_err("failed to read number of library names")?;
for idx in 0..num_lib_names {
macro_rules! this_elem_disp {
() => {
format_args!("element {idx} of library names array")
};
}
let lib_name = parse_nul_term_ascii(&mut reader, &this_elem_disp!())
.wrap_err_with(|| {
eyre!("failed to read element {idx} of library names array")
})?;
let lib_name: String = lib_name.into();
lib_names.push(ArcStr::from(lib_name));
}
lib_names
};
let versions = {
let mut versions = ArrayVec::<GlibcVersion, 64>::new();
let num_versions: u8 = reader
.read_le()
.wrap_err("failed to read number of supported `glibc` versions")?;
for idx in 0..num_versions {
let mut parse = |what| {
reader.read_le().wrap_err_with(|| {
eyre!(
"failed to read {what} field of element {idx} of supported `glibc` \
versions array"
)
})
};
let major = parse("major")?;
let minor = parse("minor")?;
let patch = parse("patch")?;
versions.push(GlibcVersion {
major,
minor,
patch,
});
}
versions
};
let targets = {
let mut targets = ArrayVec::<ArcStr, 32>::new();
let num_targets: u8 = reader
.read_le()
.wrap_err("failed to read number of target triples")?;
for idx in 0..num_targets {
macro_rules! this_elem_disp {
() => {
format_args!("element {idx} of target triples array")
};
}
let target = parse_nul_term_ascii(&mut reader, &this_elem_disp!())?;
let target: String = target.into();
let target = ArcStr::from(target);
targets.push(target);
}
targets
};
let functions = {
struct FunctionDataParser;
impl InclusionDataParser for FunctionDataParser {
type Data = ();
fn type_desc_singular(&self) -> &str {
"function"
}
fn type_desc_plural(&self) -> &str {
"functions"
}
fn parse(
&mut self,
_reader: &mut dyn BufRead,
) -> Result<Self::Data, AbiListParseError> {
Ok(())
}
}
SymbolInclusions::parse_set(
reader,
&targets,
&lib_names,
&versions,
FunctionDataParser,
)?
.map(|inc_res| inc_res.map(|inc| Function(inc)))
.collect::<Result<_, _>>()?
};
let objects = {
struct ObjectDataParser;
impl InclusionDataParser for ObjectDataParser {
type Data = ObjectData;
fn type_desc_singular(&self) -> &str {
"object"
}
fn type_desc_plural(&self) -> &str {
"objects"
}
fn parse(
&mut self,
mut reader: &mut dyn BufRead,
) -> Result<Self::Data, AbiListParseError> {
let size = reader.read_le().wrap_err("failed to read object size")?;
Ok(ObjectData { size })
}
}
SymbolInclusions::parse_set(reader, &targets, &lib_names, &versions, ObjectDataParser)?
.map(|inc_res| inc_res.map(|inc| Object(inc)))
.collect::<Result<_, _>>()?
};
Ok(AbiList {
functions,
lib_names,
objects,
targets,
versions,
})
}
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct GlibcVersion {
pub major: u8,
pub minor: u8,
pub patch: u8,
}
impl AbiList {
pub fn libraries(&self) -> &[ArcStr] {
&self.lib_names[..]
}
pub fn versions(&self) -> &[GlibcVersion] {
&self.versions[..]
}
pub fn targets(&self) -> &[ArcStr] {
&self.targets[..]
}
pub fn functions(&self) -> GlibcFunctions<'_> {
let Self {
targets, functions, ..
} = self;
GlibcFunctions { targets, functions }
}
pub fn objects(&self) -> GlibcObjects<'_> {
let Self {
targets, objects, ..
} = self;
GlibcObjects { targets, objects }
}
}
pub struct GlibcFunctions<'a> {
targets: &'a [ArcStr],
functions: &'a [Function],
}
impl<'a> GlibcFunctions<'a> {
pub fn len(&self) -> u16 {
self.functions
.len()
.try_into()
.expect("internal error: length out of `u16` bounds")
}
pub fn iter(&self) -> impl Iterator<Item = GlibcFunction<'a>> + '_ {
(0..self.len()).map(|idx| self.get(idx).unwrap())
}
pub fn get(&self, index: u16) -> Option<GlibcFunction<'a>> {
self.functions
.get(usize::from(index))
.map(|func| GlibcFunction {
targets: self.targets,
func,
})
}
}
#[derive(Debug)]
struct Function(SymbolInclusions<()>);
pub struct GlibcObjects<'a> {
targets: &'a [ArcStr],
objects: &'a [Object],
}
impl<'a> GlibcObjects<'a> {
pub fn len(&self) -> u16 {
self.objects
.len()
.try_into()
.expect("internal error: length out of `u16` bounds")
}
pub fn iter(&self) -> impl Iterator<Item = GlibcObject<'a>> + '_ {
(0..self.len()).map(|idx| self.get(idx).unwrap())
}
pub fn get(&self, index: u16) -> Option<GlibcObject<'a>> {
self.objects
.get(usize::from(index))
.map(|func| GlibcObject {
targets: self.targets,
func,
})
}
}
#[derive(Debug)]
struct Object(SymbolInclusions<ObjectData>);
#[derive(Debug)]
pub struct ObjectData {
size: u16,
}
#[derive(Debug)]
struct SymbolInclusions<T> {
symbol_name: ArcStr,
inclusions: Vec<Inclusion<T>>,
}
impl<T> SymbolInclusions<T> {
fn parse_set<'a, Parser>(
mut reader: &'a mut dyn BufRead,
targets: &'a [ArcStr],
lib_names: &'a [ArcStr],
versions: &'a [GlibcVersion],
mut entry_parser: Parser,
) -> Result<impl Iterator<Item = Result<Self, AbiListParseError>> + 'a, AbiListParseError>
where
Parser: InclusionDataParser<Data = T> + 'a,
{
let disallowed_target_triple_bitmask = (!0u32) << targets.len();
let mut num_inclusions_found = 0;
let mut num_symbols_found = 0;
let num_expected_inclusions = reader.read_le::<u16>().wrap_err_with(|| {
eyre!(
"failed to read expected number of {}",
entry_parser.type_desc_plural()
)
})?;
let mut next_res = move || {
if num_inclusions_found < num_expected_inclusions {
macro_rules! this_elem_disp {
() => {
format_args!(
"element {num_symbols_found} of {} array",
entry_parser.type_desc_singular(),
)
};
}
let symbol_name = parse_nul_term_ascii(
&mut reader,
&format_args!("symbol name of {}", this_elem_disp!()),
)?;
let symbol_name: String = symbol_name.into();
let symbol_name = ArcStr::from(symbol_name);
log::debug!(
"found {} symbol with name `{symbol_name}`",
entry_parser.type_desc_singular(),
);
macro_rules! this_elem_desc {
() => {
format_args!("`{symbol_name}` ({})", this_elem_disp!())
};
}
let mut inclusions = Vec::new();
let mut is_last_inclusion_for_symbol_name = false;
while !is_last_inclusion_for_symbol_name {
const LAST_INCLUSION_FOR_SYMBOL_NAME_BITMASK: u32 = !(!0 >> 1);
let mut targets_bitmask = reader.read_le::<u32>().wrap_err_with(|| {
eyre!("failed to read target bitmask of {}", this_elem_desc!())
})?;
is_last_inclusion_for_symbol_name =
targets_bitmask & LAST_INCLUSION_FOR_SYMBOL_NAME_BITMASK != 0;
if is_last_inclusion_for_symbol_name {
targets_bitmask &= !LAST_INCLUSION_FOR_SYMBOL_NAME_BITMASK;
}
if targets_bitmask & disallowed_target_triple_bitmask != 0 {
return Err(eyre!(
"target triple bitmask of {} is outside of parsed range of {} targets",
this_elem_desc!(),
targets.len(),
));
}
let data = entry_parser.parse(reader).wrap_err_with(|| {
eyre!(
"failed to parse data specific to {}",
entry_parser.type_desc_singular(),
)
})?;
let library_idx = reader.read_le::<u8>().wrap_err_with(|| {
eyre!("failed to read library index of {}", this_elem_desc!())
})?;
let library = lib_names
.get(usize::from(library_idx))
.ok_or_else(|| {
eyre!(
"invalid library index {library_idx} of {}; expected index < {}",
this_elem_desc!(),
lib_names.len(),
)
})?
.clone();
let mut inclusion_versions = Vec::new();
let mut version_idx_idx = 0;
loop {
const LAST_VERSION_FOR_INCLUSION_BITMASK: u8 = !(!0 >> 1);
let mut version_idx: u8 = reader.read_le().wrap_err_with(|| {
eyre!(
"failed to read version mapping element {version_idx_idx} of {}",
this_elem_desc!()
)
})?;
let is_last_version_in_inclusion =
version_idx & LAST_VERSION_FOR_INCLUSION_BITMASK != 0;
if is_last_version_in_inclusion {
version_idx &= !LAST_VERSION_FOR_INCLUSION_BITMASK;
}
let version = versions
.get(usize::from(version_idx))
.ok_or_else(|| {
eyre!(
"invalid version index {version_idx} of {}; expected index < {}",
this_elem_desc!(),
versions.len(),
)
})?
.clone();
inclusion_versions.push(version);
if is_last_version_in_inclusion {
break;
}
version_idx_idx += 1;
}
inclusions.push(Inclusion {
targets_bitmask,
data,
library,
versions: inclusion_versions.into_iter().collect(),
});
num_inclusions_found += 1;
}
let ret = Ok(Some(SymbolInclusions {
symbol_name,
inclusions,
}));
num_symbols_found += 1;
if num_inclusions_found > num_expected_inclusions {
return Err(eyre!(
"number of inclusions ({num_inclusions_found}) exceeds expected \
({num_expected_inclusions})"
));
}
ret
} else {
return Ok(None);
}
};
Ok(iter::from_fn(move || next_res().transpose()))
}
}
trait InclusionDataParser {
type Data;
fn type_desc_singular(&self) -> &str;
fn type_desc_plural(&self) -> &str;
fn parse(&mut self, reader: &mut dyn BufRead) -> Result<Self::Data, AbiListParseError>;
}
#[derive(Debug)]
struct Inclusion<T> {
targets_bitmask: u32,
data: T,
library: ArcStr,
versions: Vec<GlibcVersion>, }
pub struct GlibcFunction<'a> {
targets: &'a [ArcStr],
func: &'a Function,
}
impl<'a> GlibcFunction<'a> {
pub fn symbol_name(&self) -> &str {
let Self {
func: Function(SymbolInclusions { symbol_name, .. }),
..
} = self;
symbol_name
}
pub fn inclusions(&self) -> impl Iterator<Item = GlibcFunctionInclusion<'a>> + '_ {
let Self {
func: Function(SymbolInclusions { inclusions, .. }),
..
} = self;
inclusions.iter().map(|inclusion| GlibcFunctionInclusion {
targets: self.targets,
inclusion,
})
}
}
pub struct GlibcFunctionInclusion<'a> {
targets: &'a [ArcStr],
inclusion: &'a Inclusion<()>,
}
impl<'a> GlibcFunctionInclusion<'a> {
pub fn library(&self) -> &str {
&self.inclusion.library
}
pub fn versions(&self) -> &[GlibcVersion] {
&self.inclusion.versions
}
pub fn targets(&self) -> impl Iterator<Item = &ArcStr> {
(0..self.targets.len())
.filter(|target_idx| self.inclusion.targets_bitmask & (1 << target_idx) != 0)
.map(|target_idx| &self.targets[target_idx])
}
}
pub struct GlibcObject<'a> {
targets: &'a [ArcStr],
func: &'a Object,
}
impl<'a> GlibcObject<'a> {
pub fn symbol_name(&self) -> &str {
let Self {
func: Object(SymbolInclusions { symbol_name, .. }),
..
} = self;
symbol_name
}
pub fn inclusions(&self) -> impl Iterator<Item = GlibcObjectInclusion<'a>> + '_ {
let Self {
func: Object(SymbolInclusions { inclusions, .. }),
..
} = self;
inclusions.iter().map(|inclusion| GlibcObjectInclusion {
targets: self.targets,
inclusion,
})
}
}
pub struct GlibcObjectInclusion<'a> {
targets: &'a [ArcStr],
inclusion: &'a Inclusion<ObjectData>,
}
impl<'a> GlibcObjectInclusion<'a> {
pub fn library(&self) -> &str {
&self.inclusion.library
}
pub fn versions(&self) -> &[GlibcVersion] {
&self.inclusion.versions
}
pub fn size(&self) -> u16 {
self.inclusion.data.size
}
pub fn targets(&self) -> impl Iterator<Item = &ArcStr> {
(0..self.targets.len())
.filter(|target_idx| self.inclusion.targets_bitmask & (1 << target_idx) != 0)
.map(|target_idx| &self.targets[target_idx])
}
}
fn parse_nul_term_ascii(
file: &mut dyn BufRead,
what: &dyn Display,
) -> Result<AsciiString, AbiListParseError> {
let mut buf = Vec::new();
file.read_until(0, &mut buf)
.wrap_err_with(|| eyre!("failed to read {what}"))?;
let err = |reason| eyre!("unexpected end of file while parsing {what}; {reason}");
match buf.last() {
Some(0) => {
buf.pop();
}
Some(byte) => {
return Err(err(format_args!(
"expected null terminator, found {byte:02X}"
)))
}
None => return Err(err(format_args!("no more bytes"))),
};
log::trace!("attempting to parse symbol name from {:X?}", buf);
AsciiString::from_ascii(buf).wrap_err_with(|| eyre!("failed to parse {what} as ASCII"))
}