Skip to content

Commit e7a1b0d

Browse files
committed
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.
1 parent 6138978 commit e7a1b0d

File tree

9 files changed

+157
-42
lines changed

9 files changed

+157
-42
lines changed

binaryninjaapi.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13139,6 +13139,8 @@ namespace BinaryNinja {
1313913139
bool GetInstructionContainingAddress(Architecture* arch, uint64_t addr, uint64_t* start);
1314013140

1314113141
Confidence<bool> IsInlinedDuringAnalysis();
13142+
Confidence<BNInlineDuringAnalysis> GetInlinedDuringAnalysis();
13143+
1314213144
/*! Set whether the function should be inlined during analysis.
1314313145

1314413146
This will take effect if the new confidence level is higher than the confidence
@@ -13148,8 +13150,30 @@ namespace BinaryNinja {
1314813150
\param inlined Whether the function should be inlined.
1314913151

1315013152
*/
13151-
void SetAutoInlinedDuringAnalysis(Confidence<bool> inlined);
13152-
void SetUserInlinedDuringAnalysis(Confidence<bool> inlined);
13153+
void SetAutoInlinedDuringAnalysis(Confidence<BNInlineDuringAnalysis> inlined);
13154+
void SetUserInlinedDuringAnalysis(Confidence<BNInlineDuringAnalysis> inlined);
13155+
13156+
// These overloads are needed to disambiguate calls that pass enum values directly.
13157+
void SetAutoInlinedDuringAnalysis(BNInlineDuringAnalysis inlined) { SetAutoInlinedDuringAnalysis(Confidence(inlined)); }
13158+
void SetUserInlinedDuringAnalysis(BNInlineDuringAnalysis inlined) { SetUserInlinedDuringAnalysis(Confidence(inlined)); }
13159+
13160+
/*!
13161+
\deprecated Use the overload that takes `BNInlineDuringAnalysis`.
13162+
*/
13163+
void SetAutoInlinedDuringAnalysis(Confidence<bool> inlined)
13164+
{
13165+
BNInlineDuringAnalysis value = inlined.GetValue() ? InlinePreservingTargetInstructionAddresses : DoNotInlineCall;
13166+
SetAutoInlinedDuringAnalysis(Confidence(value, inlined.GetConfidence()));
13167+
};
13168+
13169+
/*!
13170+
\deprecated Use the overload that takes `BNInlineDuringAnalysis`.
13171+
*/
13172+
void SetUserInlinedDuringAnalysis(Confidence<bool> inlined)
13173+
{
13174+
BNInlineDuringAnalysis value = inlined.GetValue() ? InlinePreservingTargetInstructionAddresses : DoNotInlineCall;
13175+
SetUserInlinedDuringAnalysis(Confidence(value, inlined.GetConfidence()));
13176+
};
1315313177

1315413178
// TODO: Documentation
1315513179
bool IsInstructionCollapsed(const HighLevelILInstruction& instr, uint64_t designator = 0) const;

binaryninjacore.h

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,33 @@ extern "C"
11311131
MemoryIntrinsicClass
11321132
};
11331133

1134+
BN_ENUM(uint8_t, BNInlineDuringAnalysis)
1135+
{
1136+
// The called function should not be inlined.
1137+
DoNotInlineCall,
1138+
1139+
// The called function should be inlined, with the inlined
1140+
// instructions preserving their original addresses.
1141+
// This was the only behavior available up through Binary Ninja 5.2.
1142+
InlinePreservingTargetInstructionAddresses,
1143+
1144+
// The called function should be inlined, with the inlined
1145+
// instructions using the call site address as their address.
1146+
// This ensures that when the function is inlined into a caller
1147+
// multiple times, each occurrence can have different adjustments
1148+
// applied to it. The trade-off is that the instructions inlined
1149+
// at a given call site have the same address, which in turn
1150+
// prevents applying adjustments that depend on instruction
1151+
// address to only a subset of those instructions.
1152+
InlineUsingCallAddress,
1153+
};
1154+
1155+
typedef struct BNInlineDuringAnalysisWithConfidence
1156+
{
1157+
BNInlineDuringAnalysis value;
1158+
uint8_t confidence;
1159+
} BNInlineDuringAnalysisWithConfidence;
1160+
11341161
typedef struct BNLowLevelILInstruction
11351162
{
11361163
BNLowLevelILOperation operation;
@@ -5175,8 +5202,9 @@ extern "C"
51755202
BINARYNINJACOREAPI BNRegisterValueWithConfidence BNGetFunctionRegisterValueAtExit(BNFunction* func, uint32_t reg);
51765203

51775204
BINARYNINJACOREAPI BNBoolWithConfidence BNIsFunctionInlinedDuringAnalysis(BNFunction* func);
5178-
BINARYNINJACOREAPI void BNSetAutoFunctionInlinedDuringAnalysis(BNFunction* func, BNBoolWithConfidence inlined);
5179-
BINARYNINJACOREAPI void BNSetUserFunctionInlinedDuringAnalysis(BNFunction* func, BNBoolWithConfidence inlined);
5205+
BINARYNINJACOREAPI BNInlineDuringAnalysisWithConfidence BNGetFunctionInlinedDuringAnalysis(BNFunction* func);
5206+
BINARYNINJACOREAPI void BNSetAutoFunctionInlinedDuringAnalysis(BNFunction* func, BNInlineDuringAnalysisWithConfidence inlined);
5207+
BINARYNINJACOREAPI void BNSetUserFunctionInlinedDuringAnalysis(BNFunction* func, BNInlineDuringAnalysisWithConfidence inlined);
51805208

51815209
BINARYNINJACOREAPI bool BNGetInstructionContainingAddress(
51825210
BNFunction* func, BNArchitecture* arch, uint64_t addr, uint64_t* start);

function.cpp

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3230,21 +3230,28 @@ Confidence<bool> Function::IsInlinedDuringAnalysis()
32303230
}
32313231

32323232

3233-
void Function::SetAutoInlinedDuringAnalysis(Confidence<bool> inlined)
3233+
Confidence<BNInlineDuringAnalysis> Function::GetInlinedDuringAnalysis()
32343234
{
3235-
BNBoolWithConfidence bc;
3236-
bc.value = inlined.GetValue();
3237-
bc.confidence = inlined.GetConfidence();
3238-
BNSetAutoFunctionInlinedDuringAnalysis(m_object, bc);
3235+
BNInlineDuringAnalysisWithConfidence value = BNGetFunctionInlinedDuringAnalysis(m_object);
3236+
return Confidence(value.value, value.confidence);
32393237
}
32403238

32413239

3242-
void Function::SetUserInlinedDuringAnalysis(Confidence<bool> inlined)
3240+
void Function::SetAutoInlinedDuringAnalysis(Confidence<BNInlineDuringAnalysis> inlined)
32433241
{
3244-
BNBoolWithConfidence bc;
3245-
bc.value = inlined.GetValue();
3246-
bc.confidence = inlined.GetConfidence();
3247-
BNSetUserFunctionInlinedDuringAnalysis(m_object, bc);
3242+
BNInlineDuringAnalysisWithConfidence value;
3243+
value.value = inlined.GetValue();
3244+
value.confidence = inlined.GetConfidence();
3245+
BNSetAutoFunctionInlinedDuringAnalysis(m_object, value);
3246+
}
3247+
3248+
3249+
void Function::SetUserInlinedDuringAnalysis(Confidence<BNInlineDuringAnalysis> inlined)
3250+
{
3251+
BNInlineDuringAnalysisWithConfidence value;
3252+
value.value = inlined.GetValue();
3253+
value.confidence = inlined.GetConfidence();
3254+
BNSetUserFunctionInlinedDuringAnalysis(m_object, value);
32483255
}
32493256

32503257

plugins/workflow_objc/src/activities/inline_stubs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use binaryninja::workflow::AnalysisContext;
1+
use binaryninja::{function::InlineDuringAnalysis, workflow::AnalysisContext};
22

33
use crate::{metadata::GlobalState, Error};
44

@@ -19,7 +19,7 @@ pub fn process(ac: &AnalysisContext) -> Result<(), Error> {
1919
};
2020

2121
if objc_stubs.contains(&func.start()) {
22-
func.set_auto_inline_during_analysis(true);
22+
func.set_auto_inline_during_analysis(InlineDuringAnalysis::InlineUsingCallAddress);
2323
}
2424

2525
Ok(())

python/function.py

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,13 +1667,13 @@ def components(self):
16671667
return self.view.get_function_parent_components(self)
16681668

16691669
@property
1670-
def inline_during_analysis(self) -> 'types.BoolWithConfidence':
1670+
def inline_during_analysis(self) -> 'types.InlineDuringAnalysisWithConfidence':
16711671
"""Whether the function's IL should be inlined into all callers' IL"""
1672-
result = core.BNIsFunctionInlinedDuringAnalysis(self.handle)
1673-
return types.BoolWithConfidence(result.value, confidence=result.confidence)
1672+
result = core.BNGetFunctionInlinedDuringAnalysis(self.handle)
1673+
return types.InlineDuringAnalysisWithConfidence(result.value, confidence=result.confidence)
16741674

16751675
@inline_during_analysis.setter
1676-
def inline_during_analysis(self, value: Union[bool, 'types.BoolWithConfidence']):
1676+
def inline_during_analysis(self, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']):
16771677
self.set_user_inline_during_analysis(value)
16781678

16791679
def mark_recent_use(self) -> None:
@@ -3553,23 +3553,26 @@ def unsplit_var(self, var: 'variable.Variable') -> None:
35533553
"""
35543554
core.BNUnsplitVariable(self.handle, var.to_BNVariable())
35553555

3556-
def set_auto_inline_during_analysis(self, value: Union[bool, 'types.BoolWithConfidence']):
3557-
bc = core.BNBoolWithConfidence()
3558-
bc.value = bool(value)
3559-
if isinstance(value, types.BoolWithConfidence):
3560-
bc.confidence = value.confidence
3561-
else:
3562-
bc.confidence = core.max_confidence
3563-
core.BNSetAutoFunctionInlinedDuringAnalysis(self.handle, bc)
3556+
@classmethod
3557+
def _inline_during_analysis_with_confidence(cls, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']) -> 'types.InlineDuringAnalysisWithConfidence':
3558+
if isinstance(value, types.InlineDuringAnalysisWithConfidence):
3559+
return value
35643560

3565-
def set_user_inline_during_analysis(self, value: Union[bool, 'types.BoolWithConfidence']):
3566-
bc = core.BNBoolWithConfidence()
3567-
bc.value = bool(value)
35683561
if isinstance(value, types.BoolWithConfidence):
3569-
bc.confidence = value.confidence
3570-
else:
3571-
bc.confidence = core.max_confidence
3572-
core.BNSetUserFunctionInlinedDuringAnalysis(self.handle, bc)
3562+
return core.BNInlineDuringAnalysisWithConfidence(int(value.value), value.confidence)
3563+
3564+
if isinstance(value, bool):
3565+
return core.BNInlineDuringAnalysisWithConfidence(int(value), core.max_confidence)
3566+
3567+
return core.BNInlineDuringAnalysisWithConfidence(value, core.max_confidence)
3568+
3569+
def set_auto_inline_during_analysis(self, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']):
3570+
value = self._inline_during_analysis_with_confidence(value)
3571+
core.BNSetAutoFunctionInlinedDuringAnalysis(self.handle, value)
3572+
3573+
def set_user_inline_during_analysis(self, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']):
3574+
value = self._inline_during_analysis_with_confidence(value)
3575+
core.BNSetUserFunctionInlinedDuringAnalysis(self.handle, value)
35733576

35743577
def toggle_region(self, hash):
35753578
"""

python/types.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
# Binary Ninja components
2828
from . import _binaryninjacore as core
2929
from .enums import (
30-
StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass,
30+
InlineDuringAnalysis, StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass,
3131
ReferenceType, VariableSourceType,
3232
TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType,
3333
TokenEscapingType,
@@ -528,6 +528,39 @@ def get_core_struct(value: Union[BoolWithConfidenceType, bool], confidence: int
528528
return BoolWithConfidence(value, confidence)._to_core_struct()
529529

530530

531+
@dataclass(frozen=True)
532+
class InlineDuringAnalysisWithConfidence:
533+
"""Represents an InlineDuringAnalysis value with an associated confidence level."""
534+
value: InlineDuringAnalysis
535+
confidence: int = core.max_confidence
536+
537+
def __eq__(self, other):
538+
if not isinstance(other, self.__class__):
539+
# For backward compatibility, allow comparison with bool
540+
if isinstance(other, bool):
541+
return bool(self.value) == other
542+
# Allow comparison with enum value directly
543+
return self.value == other
544+
else:
545+
return (self.value, self.confidence) == (other.value, other.confidence)
546+
547+
def __ne__(self, other):
548+
return not (self == other)
549+
550+
def __bool__(self):
551+
return bool(self.value)
552+
553+
def _to_core_struct(self) -> core.BNInlineDuringAnalysisWithConfidence:
554+
result = core.BNInlineDuringAnalysisWithConfidence()
555+
result.value = self.value
556+
result.confidence = self.confidence
557+
return result
558+
559+
@classmethod
560+
def from_core_struct(cls, core_struct: core.BNInlineDuringAnalysisWithConfidence) -> 'InlineDuringAnalysisWithConfidence':
561+
return cls(InlineDuringAnalysis(core_struct.value), core_struct.confidence)
562+
563+
531564
@dataclass
532565
class MutableTypeBuilder(Generic[TB]):
533566
type: TB

rust/src/confidence.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use crate::rc::{Ref, RefCountable};
66
use crate::types::Type;
77
use binaryninjacore_sys::{
88
BNBoolWithConfidence, BNCallingConventionWithConfidence, BNGetCallingConventionArchitecture,
9-
BNOffsetWithConfidence, BNTypeWithConfidence,
9+
BNInlineDuringAnalysis, BNInlineDuringAnalysisWithConfidence, BNOffsetWithConfidence,
10+
BNTypeWithConfidence,
1011
};
1112
use std::fmt;
1213
use std::fmt::{Debug, Display, Formatter};
@@ -280,6 +281,15 @@ impl From<BNOffsetWithConfidence> for Conf<i64> {
280281
}
281282
}
282283

284+
impl From<BNInlineDuringAnalysisWithConfidence> for Conf<BNInlineDuringAnalysis> {
285+
fn from(inline_with_confidence: BNInlineDuringAnalysisWithConfidence) -> Self {
286+
Self::new(
287+
inline_with_confidence.value,
288+
inline_with_confidence.confidence,
289+
)
290+
}
291+
}
292+
283293
impl From<Conf<bool>> for BNBoolWithConfidence {
284294
fn from(conf: Conf<bool>) -> Self {
285295
Self {
@@ -297,3 +307,12 @@ impl From<Conf<i64>> for BNOffsetWithConfidence {
297307
}
298308
}
299309
}
310+
311+
impl From<Conf<BNInlineDuringAnalysis>> for BNInlineDuringAnalysisWithConfidence {
312+
fn from(conf: Conf<BNInlineDuringAnalysis>) -> Self {
313+
Self {
314+
value: conf.contents,
315+
confidence: conf.confidence,
316+
}
317+
}
318+
}

rust/src/function.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub use binaryninjacore_sys::BNBuiltinType as BuiltinType;
3636
pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisSkipOverride;
3737
pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType;
3838
pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor;
39+
pub use binaryninjacore_sys::BNInlineDuringAnalysis as InlineDuringAnalysis;
3940

4041
use crate::architecture::{IndirectBranchInfo, RegisterId};
4142
use crate::binary_view::AddressRange;
@@ -1127,17 +1128,17 @@ impl Function {
11271128

11281129
pub fn set_auto_inline_during_analysis<C>(&self, value: C)
11291130
where
1130-
C: Into<Conf<bool>>,
1131+
C: Into<Conf<InlineDuringAnalysis>>,
11311132
{
1132-
let value: Conf<bool> = value.into();
1133+
let value: Conf<InlineDuringAnalysis> = value.into();
11331134
unsafe { BNSetAutoFunctionInlinedDuringAnalysis(self.handle, value.into()) }
11341135
}
11351136

11361137
pub fn set_user_inline_during_analysis<C>(&self, value: C)
11371138
where
1138-
C: Into<Conf<bool>>,
1139+
C: Into<Conf<InlineDuringAnalysis>>,
11391140
{
1140-
let value: Conf<bool> = value.into();
1141+
let value: Conf<InlineDuringAnalysis> = value.into();
11411142
unsafe { BNSetUserFunctionInlinedDuringAnalysis(self.handle, value.into()) }
11421143
}
11431144

view/sharedcache/workflow/SharedCacheWorkflow.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ void AnalyzeStubFunction(Ref<Function> func, Ref<MediumLevelILFunction> mlil, Sh
199199
{
200200
case MLIL_CONST_PTR:
201201
// NOTE: This runs every single function update.
202-
func->SetAutoInlinedDuringAnalysis(true);
202+
func->SetAutoInlinedDuringAnalysis(InlineUsingCallAddress);
203203
break;
204204
default:
205205
break;

0 commit comments

Comments
 (0)