use haybale::{layout, Project};
use lazy_static::lazy_static;
use llvm_ir::Type;
use log::warn;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::sync::Mutex;
#[derive(PartialEq, Eq, Clone, Debug)]
#[allow(dead_code)]
pub(crate) enum CompleteAbstractData {
PublicValue { bits: usize, value: AbstractValue },
Secret { bits: usize },
Array { element_type: Box<Self>, num_elements: usize },
Struct { name: String, elements: Vec<Self> },
PublicPointerTo {
pointee: Box<Self>,
maybe_null: bool,
},
PublicPointerToFunction(String),
PublicPointerToHook(String),
PublicPointerToSelf,
PublicPointerToParentOr(Option<Box<CompleteAbstractData>>),
VoidOverride { llvm_struct_name: Option<String>, data: Box<Self> },
PointerOverride { llvm_struct_name: Option<String>, data: Box<Self> },
SameSizeOverride { data: Box<Self> },
WithWatchpoint { name: String, data: Box<Self> },
}
#[allow(dead_code)]
impl CompleteAbstractData {
pub fn pub_i8(value: AbstractValue) -> Self {
Self::PublicValue { bits: 8, value }
}
pub fn pub_i16(value: AbstractValue) -> Self {
Self::PublicValue { bits: 16, value }
}
pub fn pub_i32(value: AbstractValue) -> Self {
Self::PublicValue { bits: 32, value }
}
pub fn pub_i64(value: AbstractValue) -> Self {
Self::PublicValue { bits: 64, value }
}
pub fn pub_integer(bits: usize, value: AbstractValue) -> Self {
Self::PublicValue { bits, value }
}
pub fn sec_i8() -> Self {
Self::Secret { bits: 8 }
}
pub fn sec_i16() -> Self {
Self::Secret { bits: 16 }
}
pub fn sec_i32() -> Self {
Self::Secret { bits: 32 }
}
pub fn sec_i64() -> Self {
Self::Secret { bits: 64 }
}
pub fn sec_integer(bits: usize) -> Self {
Self::Secret { bits }
}
pub fn pub_pointer_to(data: Self) -> Self {
Self::PublicPointerTo { pointee: Box::new(data), maybe_null: false }
}
pub fn pub_maybe_null_pointer_to(data: Self) -> Self {
Self::PublicPointerTo { pointee: Box::new(data), maybe_null: true }
}
pub fn pub_pointer_to_func(funcname: impl Into<String>) -> Self {
Self::PublicPointerToFunction(funcname.into())
}
pub fn pub_pointer_to_hook(funcname: impl Into<String>) -> Self {
Self::PublicPointerToHook(funcname.into())
}
pub fn pub_pointer_to_self() -> Self {
Self::PublicPointerToSelf
}
pub fn pub_pointer_to_parent() -> Self {
Self::PublicPointerToParentOr(None)
}
pub fn pub_pointer_to_parent_or(data: Self) -> Self {
Self::PublicPointerToParentOr(Some(Box::new(data)))
}
pub fn array_of(element_type: Self, num_elements: usize) -> Self {
Self::Array { element_type: Box::new(element_type), num_elements }
}
pub fn _struct(name: impl Into<String>, elements: impl IntoIterator<Item = Self>) -> Self {
Self::Struct { name: name.into(), elements: elements.into_iter().collect() }
}
pub fn unconstrained_pointer() -> Self {
Self::PublicValue { bits: Self::POINTER_SIZE_BITS, value: AbstractValue::Unconstrained }
}
pub fn void_override(llvm_struct_name: Option<&str>, data: Self) -> Self {
Self::VoidOverride { llvm_struct_name: llvm_struct_name.map(Into::into), data: Box::new(data) }
}
pub fn pointer_override(llvm_struct_name: Option<&str>, data: Self) -> Self {
Self::PointerOverride { llvm_struct_name: llvm_struct_name.map(Into::into), data: Box::new(data) }
}
pub fn same_size_override(data: Self) -> Self {
Self::SameSizeOverride { data: Box::new(data) }
}
pub fn with_watchpoint(name: impl Into<String>, data: Self) -> Self {
Self::WithWatchpoint { name: name.into(), data: Box::new(data) }
}
}
#[allow(dead_code)]
impl CompleteAbstractData {
pub const POINTER_SIZE_BITS: usize = 64;
pub fn size_in_bits(&self) -> usize {
match self {
Self::PublicValue { bits, .. } => *bits,
Self::Array { element_type, num_elements } => element_type.size_in_bits() * num_elements,
Self::Struct { elements, .. } => elements.iter().map(Self::size_in_bits).sum(),
Self::PublicPointerTo { .. } => Self::POINTER_SIZE_BITS,
Self::PublicPointerToFunction(_) => Self::POINTER_SIZE_BITS,
Self::PublicPointerToHook(_) => Self::POINTER_SIZE_BITS,
Self::PublicPointerToSelf => Self::POINTER_SIZE_BITS,
Self::PublicPointerToParentOr(_) => Self::POINTER_SIZE_BITS,
Self::Secret { bits } => *bits,
Self::VoidOverride { data, .. } => data.size_in_bits(),
Self::PointerOverride { .. } => Self::POINTER_SIZE_BITS,
Self::SameSizeOverride { data, .. } => data.size_in_bits(),
Self::WithWatchpoint { data, .. } => data.size_in_bits(),
}
}
pub fn field_size_in_bits(&self, n: usize) -> usize {
match self {
Self::Struct { elements, .. } => Self::size_in_bits(&elements[n]),
Self::Array { element_type, .. } => Self::size_in_bits(element_type),
Self::VoidOverride { data, .. } => data.field_size_in_bits(n),
Self::SameSizeOverride { data, .. } => data.field_size_in_bits(n),
Self::WithWatchpoint { data, .. } => data.field_size_in_bits(n),
_ => panic!("field_size_in_bits called on {:?}", self),
}
}
pub fn offset_in_bits(&self, n: usize) -> usize {
match self {
Self::Struct { elements, .. } => elements.iter().take(n).map(Self::size_in_bits).sum(),
Self::Array { element_type, .. } => element_type.size_in_bits() * n,
Self::VoidOverride { data, .. } => data.offset_in_bits(n),
Self::SameSizeOverride { data, .. } => data.offset_in_bits(n),
Self::WithWatchpoint { data, .. } => data.offset_in_bits(n),
_ => panic!("offset_in_bits called on {:?}", self),
}
}
pub fn is_pointer(&self) -> bool {
match self {
Self::PublicValue { .. } => false,
Self::Secret { .. } => panic!("is_pointer on a Secret"),
Self::Array { .. } => false,
Self::Struct { .. } => false,
Self::PublicPointerTo { .. } => true,
Self::PublicPointerToFunction(_) => true,
Self::PublicPointerToHook(_) => true,
Self::PublicPointerToSelf => true,
Self::PublicPointerToParentOr(_) => true,
Self::VoidOverride { data, .. } => data.is_pointer(),
Self::PointerOverride { .. } => true,
Self::SameSizeOverride { data, .. } => data.is_pointer(),
Self::WithWatchpoint { data, .. } => data.is_pointer(),
}
}
pub fn pointee_size_in_bits(&self) -> usize {
match self {
Self::PublicValue { .. } => panic!("pointee_size_in_bits() on a non-pointer: {:?}", self),
Self::Array { .. } => panic!("pointee_size_in_bits() on a non-pointer: {:?}", self),
Self::Struct { .. } => panic!("pointee_size_in_bits() on a non-pointer: {:?}", self),
Self::PublicPointerTo { pointee, .. } => pointee.size_in_bits(),
Self::PublicPointerToFunction(_) => 64,
Self::PublicPointerToHook(_) => 64,
Self::PublicPointerToSelf => unimplemented!("pointee_size_in_bits() on PublicPointerToSelf"),
Self::PublicPointerToParentOr(None) => unimplemented!("pointee_size_in_bits() on PublicPointerToParent"),
Self::PublicPointerToParentOr(Some(data)) => data.size_in_bits(),
Self::Secret { .. } => panic!("pointee_size_in_bits() on a Secret"),
Self::VoidOverride { data, .. } => data.pointee_size_in_bits(),
Self::PointerOverride { data, .. } => data.size_in_bits(),
Self::SameSizeOverride { data, .. } => data.pointee_size_in_bits(),
Self::WithWatchpoint { data, .. } => data.pointee_size_in_bits(),
}
}
pub(crate) fn could_describe_a_struct_of_one_element(&self) -> bool {
match self {
Self::Struct { elements, .. } => elements.len() == 1,
Self::Secret { .. } => true,
Self::VoidOverride { .. } => true,
Self::PointerOverride { .. } => false,
Self::SameSizeOverride { .. } => true,
Self::WithWatchpoint { .. } => true,
_ => false,
}
}
}
impl fmt::Display for CompleteAbstractData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::PublicValue { bits, .. } => write!(f, "a {}-bit public value", bits),
Self::Secret { bits, .. } => write!(f, "a {}-bit secret value", bits),
Self::Array { num_elements, .. } => write!(f, "an array of {} elements", num_elements),
Self::Struct { name, elements } => write!(f, "a struct named {} with {} elements", name, elements.len()),
Self::PublicPointerTo { pointee, .. } => {
write!(f, "a pointer to ")?;
pointee.fmt(f)?;
Ok(())
},
Self::PublicPointerToFunction(funcname) => write!(f, "a pointer to a function named {}", funcname),
Self::PublicPointerToHook(funcname) => write!(f, "a pointer to the active hook for a function named {}", funcname),
Self::PublicPointerToSelf => write!(f, "a pointer to this struct itself"),
Self::PublicPointerToParentOr(opt) => match opt {
Some(_) => write!(f, "a pointer to this struct's parent, with a backup"),
None => write!(f, "a pointer to this struct's parent, with no backup"),
},
Self::VoidOverride { data, .. } => {
write!(f, "a void override containing ")?;
data.fmt(f)?;
Ok(())
},
Self::PointerOverride { data, .. } => {
write!(f, "a pointer override containing ")?;
data.fmt(f)?;
Ok(())
},
Self::SameSizeOverride { data, .. } => {
write!(f, "a same-size override containing ")?;
data.fmt(f)?;
Ok(())
},
Self::WithWatchpoint { name, data } => {
data.fmt(f)?;
write!(f, ", with a watchpoint named {}", name)?;
Ok(())
},
}
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct AbstractData(pub(crate) UnderspecifiedAbstractData);
#[derive(PartialEq, Eq, Clone, Debug)]
pub(crate) enum UnderspecifiedAbstractData {
Unspecified,
Unconstrained,
Secret,
Complete(CompleteAbstractData),
PublicPointerTo {
pointee: Box<AbstractData>,
maybe_null: bool,
},
PublicPointerToParentOr(Box<AbstractData>),
Array { element_type: Box<AbstractData>, num_elements: usize },
Struct { name: String, elements: Vec<AbstractData> },
DefaultForLLVMStructName { llvm_struct_name: String },
VoidOverride { llvm_struct_name: Option<String>, data: Box<AbstractData> },
PointerOverride { llvm_struct_name: Option<String>, data: Box<AbstractData> },
SameSizeOverride { data: Box<AbstractData> },
WithWatchpoint { name: String, data: Box<AbstractData> },
}
impl AbstractData {
pub fn pub_i8(value: AbstractValue) -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::pub_i8(value)))
}
pub fn pub_i16(value: AbstractValue) -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::pub_i16(value)))
}
pub fn pub_i32(value: AbstractValue) -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::pub_i32(value)))
}
pub fn pub_i64(value: AbstractValue) -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::pub_i64(value)))
}
pub fn pub_integer(bits: usize, value: AbstractValue) -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::pub_integer(bits, value)))
}
pub fn sec_i8() -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::sec_i8()))
}
pub fn sec_i16() -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::sec_i16()))
}
pub fn sec_i32() -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::sec_i32()))
}
pub fn sec_i64() -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::sec_i64()))
}
pub fn sec_integer(bits: usize) -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::sec_integer(bits)))
}
pub fn pub_pointer_to(data: Self) -> Self {
Self(UnderspecifiedAbstractData::PublicPointerTo { pointee: Box::new(data), maybe_null: false })
}
pub fn pub_maybe_null_pointer_to(data: Self) -> Self {
Self(UnderspecifiedAbstractData::PublicPointerTo { pointee: Box::new(data), maybe_null: true })
}
pub fn pub_pointer_to_func(funcname: impl Into<String>) -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::pub_pointer_to_func(funcname)))
}
pub fn pub_pointer_to_hook(funcname: impl Into<String>) -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::pub_pointer_to_hook(funcname)))
}
pub fn pub_pointer_to_self() -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::pub_pointer_to_self()))
}
pub fn pub_pointer_to_parent() -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::pub_pointer_to_parent()))
}
pub fn pub_pointer_to_parent_or(data: Self) -> Self {
Self(UnderspecifiedAbstractData::PublicPointerToParentOr(Box::new(data)))
}
pub fn array_of(element_type: Self, num_elements: usize) -> Self {
Self(UnderspecifiedAbstractData::Array { element_type: Box::new(element_type), num_elements })
}
pub fn _struct(name: impl Into<String>, elements: impl IntoIterator<Item = Self>) -> Self {
Self(UnderspecifiedAbstractData::Struct { name: name.into(), elements: elements.into_iter().collect() })
}
pub fn default() -> Self {
Self(UnderspecifiedAbstractData::Unspecified)
}
pub fn default_for_llvm_struct_name(llvm_struct_name: impl Into<String>) -> Self {
Self(UnderspecifiedAbstractData::DefaultForLLVMStructName { llvm_struct_name: llvm_struct_name.into() })
}
pub fn unconstrained_pointer() -> Self {
Self(UnderspecifiedAbstractData::Complete(CompleteAbstractData::unconstrained_pointer()))
}
pub fn unconstrained() -> Self {
Self(UnderspecifiedAbstractData::Unconstrained)
}
pub fn secret() -> Self {
Self(UnderspecifiedAbstractData::Secret)
}
pub fn void_override(llvm_struct_name: Option<&str>, data: AbstractData) -> Self {
Self(UnderspecifiedAbstractData::VoidOverride { llvm_struct_name: llvm_struct_name.map(Into::into), data: Box::new(data) })
}
pub fn pointer_override(llvm_struct_name: Option<&str>, data: Self) -> Self {
Self(UnderspecifiedAbstractData::PointerOverride { llvm_struct_name: llvm_struct_name.map(Into::into), data: Box::new(data) })
}
pub fn same_size_override(data: AbstractData) -> Self {
Self(UnderspecifiedAbstractData::SameSizeOverride { data: Box::new(data) })
}
pub fn with_watchpoint(name: impl Into<String>, data: Self) -> Self {
Self(UnderspecifiedAbstractData::WithWatchpoint { name: name.into(), data: Box::new(data) })
}
}
impl fmt::Display for AbstractData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for UnderspecifiedAbstractData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
UnderspecifiedAbstractData::Unspecified => write!(f, "an unspecified value"),
UnderspecifiedAbstractData::Unconstrained => write!(f, "an unconstrained value"),
UnderspecifiedAbstractData::Secret => write!(f, "a secret value"),
UnderspecifiedAbstractData::Complete(cad) => {
write!(f, "a complete value: ")?;
cad.fmt(f)?;
Ok(())
},
UnderspecifiedAbstractData::PublicPointerTo { pointee, .. } => {
write!(f, "a pointer to ")?;
pointee.fmt(f)?;
Ok(())
},
UnderspecifiedAbstractData::PublicPointerToParentOr(_) => write!(f, "a public pointer to parent, with a backup"),
UnderspecifiedAbstractData::Array { num_elements, .. } => write!(f, "an array of {} elements", num_elements),
UnderspecifiedAbstractData::Struct { name, elements } => write!(f, "a struct named {} with {} elements", name, elements.len()),
UnderspecifiedAbstractData::DefaultForLLVMStructName { llvm_struct_name } => write!(f, "the default for the LLVM struct {}", llvm_struct_name),
UnderspecifiedAbstractData::VoidOverride { data, .. } => {
write!(f, "a void override with data ")?;
data.fmt(f)?;
Ok(())
},
UnderspecifiedAbstractData::PointerOverride { data, .. } => {
write!(f, "a pointer override with data ")?;
data.fmt(f)?;
Ok(())
}
UnderspecifiedAbstractData::SameSizeOverride { data, .. } => {
write!(f, "a same-size override with data ")?;
data.fmt(f)?;
Ok(())
},
UnderspecifiedAbstractData::WithWatchpoint { name, data } => {
data.fmt(f)?;
write!(f, " with a watchpoint named {}", name)?;
Ok(())
},
}
}
}
pub type StructDescriptions = HashMap<String, AbstractData>;
impl AbstractData {
pub const DEFAULT_ARRAY_LENGTH: usize = 1024;
pub const POINTER_SIZE_BITS: usize = CompleteAbstractData::POINTER_SIZE_BITS;
pub const OPAQUE_STRUCT_SIZE_BYTES: usize = 1024 * 64;
pub(crate) fn to_complete(self, ty: &Type, proj: &Project, sd: &StructDescriptions) -> CompleteAbstractData {
self.0.to_complete(ty, proj, sd)
}
fn to_complete_rec<'a>(self, ty: Option<&'a Type>, ctx: ToCompleteContext<'a, '_>) -> CompleteAbstractData {
self.0.to_complete_rec(ty, ctx)
}
}
#[derive(Clone)]
struct ToCompleteContext<'a, 'p> {
proj: &'p Project,
sd: &'p StructDescriptions,
unspecified_named_structs: HashSet<&'a String>,
within_structs: Vec<String>,
}
impl<'a, 'p> ToCompleteContext<'a, 'p> {
fn new(proj: &'p Project, sd: &'p StructDescriptions) -> Self {
Self {
proj,
sd,
unspecified_named_structs: HashSet::new(),
within_structs: Vec::new(),
}
}
fn error_backtrace(&self) {
eprintln!();
for w in self.within_structs.iter() {
eprintln!("within struct {}:", w);
}
}
}
impl UnderspecifiedAbstractData {
pub(crate) fn could_describe_a_struct_of_one_element(&self) -> bool {
match self {
Self::Unspecified => true,
Self::Unconstrained => true,
Self::Secret => true,
Self::Struct { elements, .. } => elements.len() == 1,
Self::Complete(CompleteAbstractData::Struct { elements, .. }) => elements.len() == 1,
Self::VoidOverride { .. } => true,
Self::SameSizeOverride { .. } => true,
Self::WithWatchpoint { .. } => true,
_ => false,
}
}
pub(crate) fn to_complete(self, ty: &Type, proj: &Project, sd: &StructDescriptions) -> CompleteAbstractData {
self.to_complete_rec(Some(ty), ToCompleteContext::new(proj, sd))
}
fn to_complete_rec<'a>(self, ty: Option<&'a Type>, mut ctx: ToCompleteContext<'a, '_>) -> CompleteAbstractData {
lazy_static! {
static ref WARNED_STRUCTS: Mutex<HashSet<String>> = Mutex::new(HashSet::new());
}
match ty {
Some(Type::StructType { element_types, .. }) if element_types.len() == 1 => {
if !self.could_describe_a_struct_of_one_element() {
return self.to_complete_rec(Some(&element_types[0]), ctx);
}
},
Some(ty@Type::NamedStructType { .. }) => {
match ctx.proj.get_inner_struct_type_from_named(ty) {
None => {},
Some(arc) => {
let actual_ty: &Type = &arc.read().unwrap();
if let Type::StructType { element_types, .. } = actual_ty {
if element_types.len() == 1 {
if !self.could_describe_a_struct_of_one_element() {
return self.to_complete_rec(Some(&element_types[0]), ctx);
}
}
}
}
}
},
_ => {},
}
match self {
Self::Complete(abstractdata) => abstractdata,
Self::Unconstrained => match ty {
Some(ty) => CompleteAbstractData::PublicValue { bits: layout::size(ty), value: AbstractValue::Unconstrained },
None => {
ctx.error_backtrace();
panic!("Encountered an AbstractData::unconstrained() but don't have an LLVM type to use");
},
},
Self::Secret => match ty {
Some(ty) => CompleteAbstractData::Secret { bits: layout::size(ty) },
None => {
ctx.error_backtrace();
panic!("Encountered an AbstractData::secret() but don't have an LLVM type to use");
},
},
Self::WithWatchpoint { name, data } => CompleteAbstractData::with_watchpoint(name, data.to_complete_rec(ty, ctx)),
Self::VoidOverride { llvm_struct_name, data } => match llvm_struct_name {
None => CompleteAbstractData::void_override(None, data.to_complete_rec(None, ctx)),
Some(llvm_struct_name) => {
let (llvm_ty, _) = ctx.proj.get_named_struct_type_by_name(&llvm_struct_name)
.unwrap_or_else(|| { ctx.error_backtrace(); panic!("VoidOverride: llvm_struct_name {:?} not found in Project", llvm_struct_name) });
let arc = llvm_ty.as_ref().unwrap_or_else(|| { ctx.error_backtrace(); panic!("VoidOverride: llvm_struct_name {:?} is an opaque type", llvm_struct_name) });
let ty = &arc.read().unwrap();
CompleteAbstractData::void_override(Some(&llvm_struct_name), data.to_complete_rec(Some(ty), ctx))
},
},
Self::PointerOverride { llvm_struct_name, data } => match llvm_struct_name {
None => CompleteAbstractData::pointer_override(None, data.to_complete_rec(None, ctx)),
Some(llvm_struct_name) => {
let (llvm_ty, _) = ctx.proj.get_named_struct_type_by_name(&llvm_struct_name)
.unwrap_or_else(|| { ctx.error_backtrace(); panic!("PointerOverride: llvm_struct_name {:?} not found in Project", llvm_struct_name) });
let arc = llvm_ty.as_ref().unwrap_or_else(|| { ctx.error_backtrace(); panic!("PointerOverride: llvm_struct_name {:?} is an opaque type", llvm_struct_name) });
let ty = &arc.read().unwrap();
CompleteAbstractData::pointer_override(Some(&llvm_struct_name), data.to_complete_rec(Some(ty), ctx))
},
},
Self::SameSizeOverride { data } => CompleteAbstractData::same_size_override(data.to_complete_rec(None, ctx)),
Self::PublicPointerTo { pointee, maybe_null } => match ty {
Some(Type::PointerType { pointee_type, .. }) =>
CompleteAbstractData::PublicPointerTo { pointee: Box::new(match &pointee.0 {
Self::Array { num_elements, .. } => {
match &**pointee_type {
ty@Type::ArrayType { .. } | ty@Type::VectorType { .. } => {
pointee.to_complete_rec(Some(ty), ctx)
},
ty => {
let num_elements = *num_elements;
pointee.to_complete_rec(Some(&Type::ArrayType { element_type: Box::new(ty.clone()), num_elements }), ctx)
},
}
},
_ => {
pointee.to_complete_rec(Some(&**pointee_type), ctx)
},
}), maybe_null },
Some(Type::ArrayType { num_elements: 1, element_type }) | Some(Type::VectorType { num_elements: 1, element_type }) => {
Self::PublicPointerTo { pointee, maybe_null }.to_complete_rec(Some(&**element_type), ctx)
},
None => CompleteAbstractData::PublicPointerTo { pointee: Box::new(pointee.to_complete_rec(None, ctx)), maybe_null },
_ => {
ctx.error_backtrace();
panic!("Type mismatch: AbstractData::PublicPointerTo but LLVM type is {:?}", ty);
},
},
Self::PublicPointerToParentOr(ad) => {
let pointee_ty: Option<&Type> = match ty {
Some(Type::PointerType { pointee_type, .. }) => Some(pointee_type),
Some(ty) => {
ctx.error_backtrace();
panic!("Type mismatch: AbstractData::PublicPointerToParentOr but LLVM type is not a pointer: {:?}", ty);
},
None => None,
};
CompleteAbstractData::pub_pointer_to_parent_or(ad.to_complete_rec(pointee_ty, ctx))
},
Self::Array { element_type, num_elements } => match ty {
Some(Type::ArrayType { element_type: llvm_element_type, num_elements: llvm_num_elements })
| Some(Type::VectorType { element_type: llvm_element_type, num_elements: llvm_num_elements }) => {
if *llvm_num_elements != 0 && *llvm_num_elements != num_elements {
ctx.error_backtrace();
panic!("Type mismatch: AbstractData specifies an array with {} elements, but found an array with {} elements", num_elements, llvm_num_elements);
}
CompleteAbstractData::array_of(element_type.to_complete_rec(Some(&**llvm_element_type), ctx.clone()), num_elements)
},
None => CompleteAbstractData::array_of(element_type.to_complete_rec(None, ctx.clone()), num_elements),
_ => {
ctx.error_backtrace();
panic!("Type mismatch: AbstractData::Array with {} elements, but LLVM type is {:?}", num_elements, ty);
},
}
Self::Struct { elements, name } => match ty {
Some(ty@Type::NamedStructType { .. }) => {
match ctx.proj.get_inner_struct_type_from_named(ty) {
Some(arc) => {
let actual_ty: &Type = &arc.read().unwrap();
Self::Struct { elements, name }.to_complete_rec(Some(actual_ty), ctx)
},
None => Self::Struct { elements, name }.to_complete_rec(None, ctx),
}
},
Some(Type::StructType { element_types, .. }) => {
ctx.within_structs.push(name.clone());
if elements.len() != element_types.len() {
ctx.error_backtrace();
panic!("Type mismatch: AbstractData::Struct with {} elements, but LLVM type has {} elements: {:?}", elements.len(), element_types.len(), element_types);
}
CompleteAbstractData::_struct(name, elements
.into_iter()
.zip(element_types)
.map(|(el_data, el_type)| el_data.to_complete_rec(Some(el_type), ctx.clone()))
)
},
Some(Type::ArrayType { num_elements: 1, element_type }) | Some(Type::VectorType { num_elements: 1, element_type }) => {
Self::Struct { elements, name }.to_complete_rec(Some(&**element_type), ctx.clone())
},
None => {
ctx.within_structs.push(name.clone());
CompleteAbstractData::_struct(name, elements.into_iter().map(|el_data| el_data.to_complete_rec(None, ctx.clone())))
}
_ => {
ctx.error_backtrace();
panic!("Type mismatch: AbstractData::Struct {}, but LLVM type is {:?}", name, ty);
},
},
Self::DefaultForLLVMStructName { llvm_struct_name } => match ty {
Some(Type::NamedStructType { name, .. }) => {
if name == &llvm_struct_name {
Self::Unspecified.to_complete_rec(ty, ctx)
} else {
ctx.error_backtrace();
panic!("default_for_llvm_struct_name {:?}, but LLVM type is a struct named {:?}", llvm_struct_name, name)
}
},
Some(Type::StructType { .. }) => {
Self::Unspecified.to_complete_rec(ty, ctx)
},
Some(ty) => {
ctx.error_backtrace();
panic!("default_for_llvm_struct_name {:?}, but LLVM type is not a structure type: {:?}", llvm_struct_name, ty)
},
None => {
let (llvm_struct_ty_arc, _) = ctx.proj.get_named_struct_type_by_name(&llvm_struct_name)
.unwrap_or_else(|| { ctx.error_backtrace(); panic!("default_for_llvm_struct_name: struct name {:?} not found in the Project", llvm_struct_name); });
let llvm_struct_ty_arc = llvm_struct_ty_arc
.as_ref()
.unwrap_or_else(|| { ctx.error_backtrace(); panic!("default_for_llvm_struct_name: struct name {:?} is entirely opaque in this Project", llvm_struct_name); })
.clone();
let llvm_struct_ty: &Type = &llvm_struct_ty_arc.read().unwrap();
Self::Unspecified.to_complete_rec(Some(llvm_struct_ty), ctx)
},
},
Self::Unspecified => match ty {
None => {
ctx.error_backtrace();
panic!("Encountered an AbstractData::default() but don't have an LLVM type to use; this is either because:\n (1) either same_size_override or void_override with llvm_struct_name == None were used, but the specified AbstractData contained a default() somewhere; or\n (2) a struct in the StructDescriptions is opaque in this Project, but the specified AbstractData contained a default() somewhere");
},
Some(ty) => match ty {
ty@Type::IntegerType { .. } =>
CompleteAbstractData::pub_integer(layout::size(ty), AbstractValue::Unconstrained),
Type::PointerType { pointee_type, .. } => match &**pointee_type {
Type::FuncType { .. } =>
CompleteAbstractData::pub_pointer_to_hook("hook_uninitialized_function_pointer"),
Type::IntegerType { bits } =>
CompleteAbstractData::pub_pointer_to(CompleteAbstractData::array_of(
CompleteAbstractData::pub_integer(*bits as usize, AbstractValue::Unconstrained),
AbstractData::DEFAULT_ARRAY_LENGTH,
)),
Type::ArrayType { num_elements: 0, element_type } =>
CompleteAbstractData::pub_pointer_to(CompleteAbstractData::array_of(
Self::Unspecified.to_complete_rec(Some(element_type), ctx),
AbstractData::DEFAULT_ARRAY_LENGTH,
)),
ty => CompleteAbstractData::pub_pointer_to(Self::Unspecified.to_complete_rec(Some(ty), ctx)),
},
Type::VectorType { element_type, num_elements } | Type::ArrayType { element_type, num_elements } =>
CompleteAbstractData::array_of(
Self::Unspecified.to_complete_rec(Some(element_type), ctx),
*num_elements,
),
Type::NamedStructType { name, .. } => {
let arc = ctx.proj.get_inner_struct_type_from_named(ty);
if !ctx.unspecified_named_structs.insert(name) {
match arc {
Some(arc) => {
if WARNED_STRUCTS.lock().unwrap().insert(name.clone()) {
warn!("Setting the contents of a {:?} to unconstrained in order to avoid infinite recursion. We will not warn again for infinite recursion on a {:?}", name, name);
}
let inner_ty: &Type = &arc.read().unwrap();
return CompleteAbstractData::PublicValue { bits: layout::size(inner_ty), value: AbstractValue::Unconstrained };
},
None => {
ctx.error_backtrace();
panic!("Encountered infinite recursion in struct {:?}, which is opaque; this should be impossible");
},
}
}
match ctx.sd.get(name) {
Some(abstractdata) => {
ctx.within_structs.push(name.clone());
match arc {
Some(arc) => {
let inner_ty: &Type = &arc.read().unwrap();
abstractdata.clone().to_complete_rec(Some(inner_ty), ctx)
},
None => abstractdata.clone().to_complete_rec(None, ctx),
}
},
None => match arc {
Some(arc) => {
ctx.within_structs.push(name.clone());
let inner_ty: &Type = &arc.read().unwrap();
match self.to_complete_rec(Some(inner_ty), ctx) {
CompleteAbstractData::Struct { elements, .. } => CompleteAbstractData::_struct(name.clone(), elements),
cad => panic!("Expected to end up with a Struct from this call, but got {:?}", cad),
}
},
None => {
CompleteAbstractData::array_of(CompleteAbstractData::pub_i8(AbstractValue::Unconstrained), AbstractData::OPAQUE_STRUCT_SIZE_BYTES)
},
},
}
},
Type::StructType { element_types, .. } => CompleteAbstractData::_struct("unspecified_struct", element_types
.iter()
.map(|el_type| Self::Unspecified.to_complete_rec(Some(el_type), ctx.clone()))
),
_ => unimplemented!("AbstractData::to_complete with {:?}", ty),
},
},
}
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum AbstractValue {
ExactValue(u64),
Range(u64, u64),
Unconstrained,
Named {
name: String,
value: Box<AbstractValue>,
},
EqualTo(String),
SignedLessThan(String),
SignedGreaterThan(String),
UnsignedLessThan(String),
UnsignedGreaterThan(String),
}
impl AbstractValue {
pub fn named(name: &str, value: AbstractValue) -> Self {
Self::Named {
name: name.to_owned(),
value: Box::new(value),
}
}
}