Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support global ifuncs #61

Merged
merged 1 commit into from
Feb 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/iterators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub fn get_global_aliases(module: LLVMModuleRef) -> impl Iterator<Item = LLVMVal
GlobalAliasIterator::new(module)
}

pub fn get_global_ifuncs(module: LLVMModuleRef) -> impl Iterator<Item = LLVMValueRef> {
GlobalIFuncIterator::new(module)
}

pub fn get_parameters(func: LLVMValueRef) -> impl Iterator<Item = LLVMValueRef> {
ParamIterator::new(func)
}
Expand Down Expand Up @@ -82,6 +86,13 @@ iterator!(
LLVMGetFirstGlobalAlias,
LLVMGetNextGlobalAlias
);
iterator!(
GlobalIFuncIterator,
LLVMModuleRef,
LLVMValueRef,
LLVMGetFirstGlobalIFunc,
LLVMGetNextGlobalIFunc
);
iterator!(
ParamIterator,
LLVMValueRef,
Expand Down
49 changes: 46 additions & 3 deletions src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub struct Module {
pub global_vars: Vec<GlobalVariable>,
/// See [LLVM 14 docs on Global Aliases](https://releases.llvm.org/14.0.0/docs/LangRef.html#aliases)
pub global_aliases: Vec<GlobalAlias>,
/// See [LLVM 14 docs on Global IFuncs](https://releases.llvm.org/14.0.0/docs/LangRef.html#ifuncs)
pub global_ifuncs: Vec<GlobalIFunc>,
// --TODO not yet implemented-- pub function_attribute_groups: Vec<FunctionAttributeGroup>,
/// See [LLVM 14 docs on Module-Level Inline Assembly](https://releases.llvm.org/14.0.0/docs/LangRef.html#moduleasm)
pub inline_assembly: String,
Expand Down Expand Up @@ -74,6 +76,11 @@ impl Module {
self.global_aliases.iter().find(|global| global.name == *name)
}

/// Get the `GlobalIFunc` having the given `Name` (if any).
pub fn get_global_ifunc_by_name(&self, name: &Name) -> Option<&GlobalIFunc> {
self.global_ifuncs.iter().find(|global| global.name == *name)
}

/// Parse the LLVM bitcode (.bc) file at the given path to create a `Module`
pub fn from_bc_path(path: impl AsRef<Path>) -> Result<Self, String> {
unsafe fn parse_bc(
Expand Down Expand Up @@ -214,6 +221,22 @@ impl Typed for GlobalAlias {
}
}

/// See [LLVM 14 docs on Global IFuncs](https://releases.llvm.org/14.0.0/docs/LangRef.html#ifuncs)
#[derive(PartialEq, Clone, Debug)]
pub struct GlobalIFunc {
pub name: Name,
pub linkage: Linkage,
pub visibility: Visibility,
pub ty: TypeRef,
pub resolver_fn: ConstantRef,
}

impl Typed for GlobalIFunc {
fn get_type(&self, _types: &Types) -> TypeRef {
self.ty.clone()
}
}

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum UnnamedAddr {
Local,
Expand Down Expand Up @@ -593,9 +616,9 @@ impl Module {
let mut global_ctr = 0; // this ctr is used to number global objects that aren't named

// Modules require two passes over their contents.
// First we make a pass just to map global objects -- in particular, Functions,
// GlobalVariables, and GlobalAliases -- to Names; then we do the actual
// detailed pass.
// First we make a pass just to map global objects -- in particular,
// Functions, GlobalVariables, GlobalAliases, and GlobalIFuncs -- to
// Names; then we do the actual detailed pass.
// This is necessary because these structures may reference each other in a
// circular fashion, and we need to be able to fill in the Name of the
// referenced object from having only its `LLVMValueRef`.
Expand All @@ -605,6 +628,7 @@ impl Module {
.chain(get_declared_functions(module))
.chain(get_globals(module))
.chain(get_global_aliases(module))
.chain(get_global_ifuncs(module))
.map(|g| {
(
g,
Expand Down Expand Up @@ -633,6 +657,9 @@ impl Module {
global_aliases: get_global_aliases(module)
.map(|g| GlobalAlias::from_llvm_ref(g, &mut global_ctr, &mut ctx))
.collect(),
global_ifuncs: get_global_ifuncs(module)
.map(|g| GlobalIFunc::from_llvm_ref(g, &mut global_ctr, &mut ctx))
.collect(),
// function_attribute_groups: unimplemented!("function_attribute_groups"), // llvm-hs collects these in the decoder monad or something
inline_assembly: unsafe { get_module_inline_asm(module) },
// metadata_nodes: unimplemented!("metadata_nodes"),
Expand Down Expand Up @@ -719,6 +746,22 @@ impl GlobalAlias {
}
}

impl GlobalIFunc {
pub(crate) fn from_llvm_ref(
ifunc: LLVMValueRef,
ctr: &mut usize,
ctx: &mut ModuleContext,
) -> Self {
Self {
name: Name::name_or_num(unsafe { get_value_name(ifunc) }, ctr),
linkage: Linkage::from_llvm(unsafe { LLVMGetLinkage(ifunc) }),
visibility: Visibility::from_llvm(unsafe { LLVMGetVisibility(ifunc) }),
ty: ctx.types.type_from_llvm_ref(unsafe { LLVMTypeOf(ifunc) }),
resolver_fn: Constant::from_llvm_ref(unsafe { LLVMGetGlobalIFuncResolver(ifunc) }, ctx),
}
}
}

/* --TODO not yet implemented: metadata
impl NamedMetadata {
pub(crate) fn from_llvm_ref(nm: LLVMNamedMDNodeRef) -> Self {
Expand Down
7 changes: 7 additions & 0 deletions tests/basic_bc/ifunc_minimal.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@strstr = alias ptr (ptr, ptr), ptr @__libc_strstr

@__libc_strstr = ifunc ptr (ptr, ptr), ptr @__libc_strstr_ifunc

define internal ptr @__libc_strstr_ifunc() {
ret ptr null
}
Binary file added tests/basic_bc/strstr.o.bc
Binary file not shown.
22 changes: 22 additions & 0 deletions tests/basic_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1708,6 +1708,28 @@ fn issue42() {
// just check the module parses without errors
}

/// This test checks for regression on issue 57
#[cfg(feature = "llvm-16-or-greater")] // although the issue probably affects all of our supported versions (IFunc has existed since at least LLVM 8), the provided bitcode was produced with LLVM 16
#[test]
fn issue57() {
init_logging();
let path = Path::new(BC_DIR).join("ifunc_minimal.ll");
let module = Module::from_ir_path(&path).expect("Failed to parse module");
let ifunc = module.get_global_ifunc_by_name(&Name::from("__libc_strstr")).expect("failed to find global ifunc");
assert_eq!(ifunc.ty, module.types.pointer());
match ifunc.resolver_fn.as_ref() {
Constant::GlobalReference { name, ty } => {
assert_eq!(name, &Name::from("__libc_strstr_ifunc"));
assert_eq!(ty, &module.types.func_type(module.types.pointer(), vec![], false));
}
_ => panic!("expected a GlobalReference"),
}

let path = Path::new(BC_DIR).join("strstr.o.bc");
let _ = Module::from_bc_path(&path).expect("Failed to parse module");
// just check the module parses without errors
}

#[test]
fn rustbc() {
// This tests against the checked-in rust.bc, which was generated from the checked-in rust.rs with rustc 1.39.0
Expand Down
Loading