summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Rowe <mark@vector35.com>2025-11-18 15:45:15 -0800
committerMark Rowe <mark@vector35.com>2026-01-14 09:34:32 -0800
commit6e50ceda4e65e5952e59449fad4953ea6c5aaf37 (patch)
tree3f9defd37d0b2d1b7725b0f876fa474b6301d056
parent56010d289afa142ebd206db8fc3b22f89d7a4ec0 (diff)
Allow controlling which address is used for instructions created when inlining during analysis
Previously the address of the instruction in the function being inlined was used as the new instruction's address when copying it during inlining. Now there is an additional option: use the address of the call instruction that is being replaced as the new instruction's address. This new mode is useful when inlining thunks or stub functions, but care must be taken if using it beyond that. The benefit is that it ensures that when a function contains multiple calls to the same stub function, each inlined copy ends up with distinct addresses. This ensures that call type adjustments and other overrides that are stored on the function and keyed by address can be applied independently to each callsite that was inlined. The trade-off is that if the function being inlined contains non-trivial logic, all of the inlined instructions sharing an address will limit what type of adjustments can be applied to them. The Objective-C and shared cache workflows are updated to take advantage of this new mode when they enable inlining of stub functions. This will make it possible for multiple calls to the same runtime function within a single function to have separate call type adjustments applied in the future.
-rw-r--r--binaryninjaapi.h28
-rw-r--r--binaryninjacore.h32
-rw-r--r--function.cpp27
-rw-r--r--plugins/workflow_objc/src/activities/inline_stubs.rs4
-rw-r--r--python/function.py41
-rw-r--r--python/types.py35
-rw-r--r--rust/src/confidence.rs21
-rw-r--r--rust/src/function.rs9
-rw-r--r--view/sharedcache/workflow/SharedCacheWorkflow.cpp2
9 files changed, 157 insertions, 42 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index db54a9a8..e28ebfb1 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -13139,6 +13139,8 @@ namespace BinaryNinja {
bool GetInstructionContainingAddress(Architecture* arch, uint64_t addr, uint64_t* start);
Confidence<bool> IsInlinedDuringAnalysis();
+ Confidence<BNInlineDuringAnalysis> GetInlinedDuringAnalysis();
+
/*! Set whether the function should be inlined during analysis.
This will take effect if the new confidence level is higher than the confidence
@@ -13148,8 +13150,30 @@ namespace BinaryNinja {
\param inlined Whether the function should be inlined.
*/
- void SetAutoInlinedDuringAnalysis(Confidence<bool> inlined);
- void SetUserInlinedDuringAnalysis(Confidence<bool> inlined);
+ void SetAutoInlinedDuringAnalysis(Confidence<BNInlineDuringAnalysis> inlined);
+ void SetUserInlinedDuringAnalysis(Confidence<BNInlineDuringAnalysis> inlined);
+
+ // These overloads are needed to disambiguate calls that pass enum values directly.
+ void SetAutoInlinedDuringAnalysis(BNInlineDuringAnalysis inlined) { SetAutoInlinedDuringAnalysis(Confidence(inlined)); }
+ void SetUserInlinedDuringAnalysis(BNInlineDuringAnalysis inlined) { SetUserInlinedDuringAnalysis(Confidence(inlined)); }
+
+ /*!
+ \deprecated Use the overload that takes `BNInlineDuringAnalysis`.
+ */
+ void SetAutoInlinedDuringAnalysis(Confidence<bool> inlined)
+ {
+ BNInlineDuringAnalysis value = inlined.GetValue() ? InlinePreservingTargetInstructionAddresses : DoNotInlineCall;
+ SetAutoInlinedDuringAnalysis(Confidence(value, inlined.GetConfidence()));
+ };
+
+ /*!
+ \deprecated Use the overload that takes `BNInlineDuringAnalysis`.
+ */
+ void SetUserInlinedDuringAnalysis(Confidence<bool> inlined)
+ {
+ BNInlineDuringAnalysis value = inlined.GetValue() ? InlinePreservingTargetInstructionAddresses : DoNotInlineCall;
+ SetUserInlinedDuringAnalysis(Confidence(value, inlined.GetConfidence()));
+ };
// TODO: Documentation
bool IsInstructionCollapsed(const HighLevelILInstruction& instr, uint64_t designator = 0) const;
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 8144ed7e..1e346bb1 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -1131,6 +1131,33 @@ extern "C"
MemoryIntrinsicClass
};
+ BN_ENUM(uint8_t, BNInlineDuringAnalysis)
+ {
+ // The called function should not be inlined.
+ DoNotInlineCall,
+
+ // The called function should be inlined, with the inlined
+ // instructions preserving their original addresses.
+ // This was the only behavior available up through Binary Ninja 5.2.
+ InlinePreservingTargetInstructionAddresses,
+
+ // The called function should be inlined, with the inlined
+ // instructions using the call site address as their address.
+ // This ensures that when the function is inlined into a caller
+ // multiple times, each occurrence can have different adjustments
+ // applied to it. The trade-off is that the instructions inlined
+ // at a given call site have the same address, which in turn
+ // prevents applying adjustments that depend on instruction
+ // address to only a subset of those instructions.
+ InlineUsingCallAddress,
+ };
+
+ typedef struct BNInlineDuringAnalysisWithConfidence
+ {
+ BNInlineDuringAnalysis value;
+ uint8_t confidence;
+ } BNInlineDuringAnalysisWithConfidence;
+
typedef struct BNLowLevelILInstruction
{
BNLowLevelILOperation operation;
@@ -5175,8 +5202,9 @@ extern "C"
BINARYNINJACOREAPI BNRegisterValueWithConfidence BNGetFunctionRegisterValueAtExit(BNFunction* func, uint32_t reg);
BINARYNINJACOREAPI BNBoolWithConfidence BNIsFunctionInlinedDuringAnalysis(BNFunction* func);
- BINARYNINJACOREAPI void BNSetAutoFunctionInlinedDuringAnalysis(BNFunction* func, BNBoolWithConfidence inlined);
- BINARYNINJACOREAPI void BNSetUserFunctionInlinedDuringAnalysis(BNFunction* func, BNBoolWithConfidence inlined);
+ BINARYNINJACOREAPI BNInlineDuringAnalysisWithConfidence BNGetFunctionInlinedDuringAnalysis(BNFunction* func);
+ BINARYNINJACOREAPI void BNSetAutoFunctionInlinedDuringAnalysis(BNFunction* func, BNInlineDuringAnalysisWithConfidence inlined);
+ BINARYNINJACOREAPI void BNSetUserFunctionInlinedDuringAnalysis(BNFunction* func, BNInlineDuringAnalysisWithConfidence inlined);
BINARYNINJACOREAPI bool BNGetInstructionContainingAddress(
BNFunction* func, BNArchitecture* arch, uint64_t addr, uint64_t* start);
diff --git a/function.cpp b/function.cpp
index d5e692de..3f5f5b2e 100644
--- a/function.cpp
+++ b/function.cpp
@@ -3230,21 +3230,28 @@ Confidence<bool> Function::IsInlinedDuringAnalysis()
}
-void Function::SetAutoInlinedDuringAnalysis(Confidence<bool> inlined)
+Confidence<BNInlineDuringAnalysis> Function::GetInlinedDuringAnalysis()
{
- BNBoolWithConfidence bc;
- bc.value = inlined.GetValue();
- bc.confidence = inlined.GetConfidence();
- BNSetAutoFunctionInlinedDuringAnalysis(m_object, bc);
+ BNInlineDuringAnalysisWithConfidence value = BNGetFunctionInlinedDuringAnalysis(m_object);
+ return Confidence(value.value, value.confidence);
}
-void Function::SetUserInlinedDuringAnalysis(Confidence<bool> inlined)
+void Function::SetAutoInlinedDuringAnalysis(Confidence<BNInlineDuringAnalysis> inlined)
{
- BNBoolWithConfidence bc;
- bc.value = inlined.GetValue();
- bc.confidence = inlined.GetConfidence();
- BNSetUserFunctionInlinedDuringAnalysis(m_object, bc);
+ BNInlineDuringAnalysisWithConfidence value;
+ value.value = inlined.GetValue();
+ value.confidence = inlined.GetConfidence();
+ BNSetAutoFunctionInlinedDuringAnalysis(m_object, value);
+}
+
+
+void Function::SetUserInlinedDuringAnalysis(Confidence<BNInlineDuringAnalysis> inlined)
+{
+ BNInlineDuringAnalysisWithConfidence value;
+ value.value = inlined.GetValue();
+ value.confidence = inlined.GetConfidence();
+ BNSetUserFunctionInlinedDuringAnalysis(m_object, value);
}
diff --git a/plugins/workflow_objc/src/activities/inline_stubs.rs b/plugins/workflow_objc/src/activities/inline_stubs.rs
index daae9baa..8ddb2fa3 100644
--- a/plugins/workflow_objc/src/activities/inline_stubs.rs
+++ b/plugins/workflow_objc/src/activities/inline_stubs.rs
@@ -1,4 +1,4 @@
-use binaryninja::workflow::AnalysisContext;
+use binaryninja::{function::InlineDuringAnalysis, workflow::AnalysisContext};
use crate::{metadata::GlobalState, Error};
@@ -19,7 +19,7 @@ pub fn process(ac: &AnalysisContext) -> Result<(), Error> {
};
if objc_stubs.contains(&func.start()) {
- func.set_auto_inline_during_analysis(true);
+ func.set_auto_inline_during_analysis(InlineDuringAnalysis::InlineUsingCallAddress);
}
Ok(())
diff --git a/python/function.py b/python/function.py
index c328ca77..29527f4d 100644
--- a/python/function.py
+++ b/python/function.py
@@ -1667,13 +1667,13 @@ class Function:
return self.view.get_function_parent_components(self)
@property
- def inline_during_analysis(self) -> 'types.BoolWithConfidence':
+ def inline_during_analysis(self) -> 'types.InlineDuringAnalysisWithConfidence':
"""Whether the function's IL should be inlined into all callers' IL"""
- result = core.BNIsFunctionInlinedDuringAnalysis(self.handle)
- return types.BoolWithConfidence(result.value, confidence=result.confidence)
+ result = core.BNGetFunctionInlinedDuringAnalysis(self.handle)
+ return types.InlineDuringAnalysisWithConfidence(result.value, confidence=result.confidence)
@inline_during_analysis.setter
- def inline_during_analysis(self, value: Union[bool, 'types.BoolWithConfidence']):
+ def inline_during_analysis(self, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']):
self.set_user_inline_during_analysis(value)
def mark_recent_use(self) -> None:
@@ -3553,23 +3553,26 @@ class Function:
"""
core.BNUnsplitVariable(self.handle, var.to_BNVariable())
- def set_auto_inline_during_analysis(self, value: Union[bool, 'types.BoolWithConfidence']):
- bc = core.BNBoolWithConfidence()
- bc.value = bool(value)
- if isinstance(value, types.BoolWithConfidence):
- bc.confidence = value.confidence
- else:
- bc.confidence = core.max_confidence
- core.BNSetAutoFunctionInlinedDuringAnalysis(self.handle, bc)
+ @classmethod
+ def _inline_during_analysis_with_confidence(cls, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']) -> 'types.InlineDuringAnalysisWithConfidence':
+ if isinstance(value, types.InlineDuringAnalysisWithConfidence):
+ return value
- def set_user_inline_during_analysis(self, value: Union[bool, 'types.BoolWithConfidence']):
- bc = core.BNBoolWithConfidence()
- bc.value = bool(value)
if isinstance(value, types.BoolWithConfidence):
- bc.confidence = value.confidence
- else:
- bc.confidence = core.max_confidence
- core.BNSetUserFunctionInlinedDuringAnalysis(self.handle, bc)
+ return core.BNInlineDuringAnalysisWithConfidence(int(value.value), value.confidence)
+
+ if isinstance(value, bool):
+ return core.BNInlineDuringAnalysisWithConfidence(int(value), core.max_confidence)
+
+ return core.BNInlineDuringAnalysisWithConfidence(value, core.max_confidence)
+
+ def set_auto_inline_during_analysis(self, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']):
+ value = self._inline_during_analysis_with_confidence(value)
+ core.BNSetAutoFunctionInlinedDuringAnalysis(self.handle, value)
+
+ def set_user_inline_during_analysis(self, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']):
+ value = self._inline_during_analysis_with_confidence(value)
+ core.BNSetUserFunctionInlinedDuringAnalysis(self.handle, value)
def toggle_region(self, hash):
"""
diff --git a/python/types.py b/python/types.py
index a47f531d..44583c4d 100644
--- a/python/types.py
+++ b/python/types.py
@@ -27,7 +27,7 @@ import uuid
# Binary Ninja components
from . import _binaryninjacore as core
from .enums import (
- StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass,
+ InlineDuringAnalysis, StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass,
ReferenceType, VariableSourceType,
TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType,
TokenEscapingType,
@@ -528,6 +528,39 @@ class BoolWithConfidence:
return BoolWithConfidence(value, confidence)._to_core_struct()
+@dataclass(frozen=True)
+class InlineDuringAnalysisWithConfidence:
+ """Represents an InlineDuringAnalysis value with an associated confidence level."""
+ value: InlineDuringAnalysis
+ confidence: int = core.max_confidence
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ # For backward compatibility, allow comparison with bool
+ if isinstance(other, bool):
+ return bool(self.value) == other
+ # Allow comparison with enum value directly
+ return self.value == other
+ else:
+ return (self.value, self.confidence) == (other.value, other.confidence)
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __bool__(self):
+ return bool(self.value)
+
+ def _to_core_struct(self) -> core.BNInlineDuringAnalysisWithConfidence:
+ result = core.BNInlineDuringAnalysisWithConfidence()
+ result.value = self.value
+ result.confidence = self.confidence
+ return result
+
+ @classmethod
+ def from_core_struct(cls, core_struct: core.BNInlineDuringAnalysisWithConfidence) -> 'InlineDuringAnalysisWithConfidence':
+ return cls(InlineDuringAnalysis(core_struct.value), core_struct.confidence)
+
+
@dataclass
class MutableTypeBuilder(Generic[TB]):
type: TB
diff --git a/rust/src/confidence.rs b/rust/src/confidence.rs
index fd0b5532..f8745180 100644
--- a/rust/src/confidence.rs
+++ b/rust/src/confidence.rs
@@ -6,7 +6,8 @@ use crate::rc::{Ref, RefCountable};
use crate::types::Type;
use binaryninjacore_sys::{
BNBoolWithConfidence, BNCallingConventionWithConfidence, BNGetCallingConventionArchitecture,
- BNOffsetWithConfidence, BNTypeWithConfidence,
+ BNInlineDuringAnalysis, BNInlineDuringAnalysisWithConfidence, BNOffsetWithConfidence,
+ BNTypeWithConfidence,
};
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
@@ -280,6 +281,15 @@ impl From<BNOffsetWithConfidence> for Conf<i64> {
}
}
+impl From<BNInlineDuringAnalysisWithConfidence> for Conf<BNInlineDuringAnalysis> {
+ fn from(inline_with_confidence: BNInlineDuringAnalysisWithConfidence) -> Self {
+ Self::new(
+ inline_with_confidence.value,
+ inline_with_confidence.confidence,
+ )
+ }
+}
+
impl From<Conf<bool>> for BNBoolWithConfidence {
fn from(conf: Conf<bool>) -> Self {
Self {
@@ -297,3 +307,12 @@ impl From<Conf<i64>> for BNOffsetWithConfidence {
}
}
}
+
+impl From<Conf<BNInlineDuringAnalysis>> for BNInlineDuringAnalysisWithConfidence {
+ fn from(conf: Conf<BNInlineDuringAnalysis>) -> Self {
+ Self {
+ value: conf.contents,
+ confidence: conf.confidence,
+ }
+ }
+}
diff --git a/rust/src/function.rs b/rust/src/function.rs
index e5516d14..c19e2107 100644
--- a/rust/src/function.rs
+++ b/rust/src/function.rs
@@ -36,6 +36,7 @@ pub use binaryninjacore_sys::BNBuiltinType as BuiltinType;
pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisSkipOverride;
pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType;
pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor;
+pub use binaryninjacore_sys::BNInlineDuringAnalysis as InlineDuringAnalysis;
use crate::architecture::{IndirectBranchInfo, RegisterId};
use crate::binary_view::AddressRange;
@@ -1127,17 +1128,17 @@ impl Function {
pub fn set_auto_inline_during_analysis<C>(&self, value: C)
where
- C: Into<Conf<bool>>,
+ C: Into<Conf<InlineDuringAnalysis>>,
{
- let value: Conf<bool> = value.into();
+ let value: Conf<InlineDuringAnalysis> = value.into();
unsafe { BNSetAutoFunctionInlinedDuringAnalysis(self.handle, value.into()) }
}
pub fn set_user_inline_during_analysis<C>(&self, value: C)
where
- C: Into<Conf<bool>>,
+ C: Into<Conf<InlineDuringAnalysis>>,
{
- let value: Conf<bool> = value.into();
+ let value: Conf<InlineDuringAnalysis> = value.into();
unsafe { BNSetUserFunctionInlinedDuringAnalysis(self.handle, value.into()) }
}
diff --git a/view/sharedcache/workflow/SharedCacheWorkflow.cpp b/view/sharedcache/workflow/SharedCacheWorkflow.cpp
index dcdf6c23..2b9cd28b 100644
--- a/view/sharedcache/workflow/SharedCacheWorkflow.cpp
+++ b/view/sharedcache/workflow/SharedCacheWorkflow.cpp
@@ -199,7 +199,7 @@ void AnalyzeStubFunction(Ref<Function> func, Ref<MediumLevelILFunction> mlil, Sh
{
case MLIL_CONST_PTR:
// NOTE: This runs every single function update.
- func->SetAutoInlinedDuringAnalysis(true);
+ func->SetAutoInlinedDuringAnalysis(InlineUsingCallAddress);
break;
default:
break;