Skip to content

[Psuedo-ObjC] Prefer displaying id rather than objc_object* #6904

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

Merged
merged 4 commits into from
Jun 18, 2025
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
45 changes: 45 additions & 0 deletions lang/c/objctypes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "objctypes.h"
#include "binaryninjaapi.h"

using namespace BinaryNinja;

namespace {

bool IsPointerToObjCObject(const Ref<Type>& type)
{
if (!type || type->GetClass() != PointerTypeClass)
return false;

auto childType = type->GetChildType();
if (!childType || childType->GetClass() != NamedTypeReferenceClass)
return false;

auto namedType = childType->GetNamedTypeReference();
return namedType && namedType->GetName().GetString() == "objc_object";
}

} // unnamed namespace

PseudoObjCTypePrinter::PseudoObjCTypePrinter() : TypePrinter("Objective-C") {}

std::vector<InstructionTextToken> PseudoObjCTypePrinter::GetTypeTokensBeforeName(
Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType, BNTokenEscapingType escaping)
{
// It is idiomatic in Objective-C to use `id` rather than `objc_object*`.
if (IsPointerToObjCObject(type))
return {InstructionTextToken {baseConfidence, TypeNameToken, "id"}};

return TypePrinter::GetDefault()->GetTypeTokensBeforeName(type, platform, baseConfidence, parentType, escaping);
}

std::vector<InstructionTextToken> PseudoObjCTypePrinter::GetTypeTokensAfterName(
Ref<Type> type, Ref<Platform> platform, uint8_t baseConfidence, Ref<Type> parentType, BNTokenEscapingType escaping)
{
return TypePrinter::GetDefault()->GetTypeTokensAfterName(type, platform, baseConfidence, parentType, escaping);
}

std::vector<TypeDefinitionLine> PseudoObjCTypePrinter::GetTypeLines(Ref<Type> type, const TypeContainer& types,
const QualifiedName& name, int paddingCols, bool collapsed, BNTokenEscapingType escaping)
{
return TypePrinter::GetDefault()->GetTypeLines(type, types, name, paddingCols, collapsed, escaping);
}
24 changes: 24 additions & 0 deletions lang/c/objctypes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include "binaryninjaapi.h"

class PseudoObjCTypePrinter : public BinaryNinja::TypePrinter
{
public:
PseudoObjCTypePrinter();

std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensBeforeName(BinaryNinja::Ref<BinaryNinja::Type> type,
BinaryNinja::Ref<BinaryNinja::Platform> platform,
uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr,
BNTokenEscapingType escaping = NoTokenEscapingType) override;

std::vector<BinaryNinja::InstructionTextToken> GetTypeTokensAfterName(BinaryNinja::Ref<BinaryNinja::Type> type,
BinaryNinja::Ref<BinaryNinja::Platform> platform,
uint8_t baseConfidence = BN_FULL_CONFIDENCE, BinaryNinja::Ref<BinaryNinja::Type> parentType = nullptr,
BNTokenEscapingType escaping = NoTokenEscapingType) override;

std::vector<BinaryNinja::TypeDefinitionLine> GetTypeLines(BinaryNinja::Ref<BinaryNinja::Type> type,
const BinaryNinja::TypeContainer& types,
const BinaryNinja::QualifiedName& name, int paddingCols = 64, bool collapsed = false,
BNTokenEscapingType escaping = NoTokenEscapingType) override;
};
52 changes: 25 additions & 27 deletions lang/c/pseudoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ using namespace BinaryNinja;

PseudoCFunction::PseudoCFunction(LanguageRepresentationFunctionType* type, Architecture* arch, Function* owner,
HighLevelILFunction* highLevelILFunction) :
LanguageRepresentationFunction(type, arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction)
LanguageRepresentationFunction(type, arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction),
m_typePrinter(type->GetTypePrinter())
{
if (!m_typePrinter) {
m_typePrinter = TypePrinter::GetDefault();
}
}


Expand Down Expand Up @@ -177,11 +181,7 @@ BNSymbolDisplayResult PseudoCFunction::AppendPointerTextToken(const HighLevelILI

string PseudoCFunction::GetSizeToken(size_t size, bool isSigned)
{
return TypePrinter::GetDefault()->GetTypeString(
Type::IntegerType(size, isSigned),
nullptr,
QualifiedName()
);
return GetTypePrinter()->GetTypeString(Type::IntegerType(size, isSigned), nullptr, QualifiedName());
}


Expand Down Expand Up @@ -546,11 +546,8 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H
{
tokens.AppendOpenParen();
tokens.AppendOpenParen();
auto typeTokens = TypePrinter::GetDefault()->GetTypeTokens(
instr.GetType(),
GetArchitecture()->GetStandalonePlatform(),
QualifiedName()
);
auto typeTokens = GetTypePrinter()->GetTypeTokens(
instr.GetType(), GetArchitecture()->GetStandalonePlatform(), QualifiedName());
for (auto& token: typeTokens)
{
tokens.Append(token);
Expand Down Expand Up @@ -1004,14 +1001,12 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H

const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(destExpr);
const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
const auto prevTypeTokens =
variableType ?
TypePrinter::GetDefault()->GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) :
vector<InstructionTextToken>{};
const auto postTypeTokens =
variableType ?
TypePrinter::GetDefault()->GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) :
vector<InstructionTextToken>{};
const auto prevTypeTokens = variableType ?
GetTypePrinter()->GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) :
vector<InstructionTextToken> {};
const auto postTypeTokens = variableType ?
GetTypePrinter()->GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) :
vector<InstructionTextToken> {};

// Check to see if the variable appears live
bool appearsDead = false;
Expand Down Expand Up @@ -1070,14 +1065,12 @@ void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, H

const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(variable);
const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform();
const auto prevTypeTokens =
variableType ?
TypePrinter::GetDefault()->GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) :
vector<InstructionTextToken>{};
const auto postTypeTokens =
variableType ?
TypePrinter::GetDefault()->GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) :
vector<InstructionTextToken>{};
const auto prevTypeTokens = variableType ?
GetTypePrinter()->GetTypeTokensBeforeName(variableType, platform, variableType.GetConfidence()) :
vector<InstructionTextToken> {};
const auto postTypeTokens = variableType ?
GetTypePrinter()->GetTypeTokensAfterName(variableType, platform, variableType.GetConfidence()) :
vector<InstructionTextToken> {};

if (variableType)
{
Expand Down Expand Up @@ -2848,6 +2841,11 @@ string PseudoCFunction::GetAnnotationEndString() const
return " */";
}

TypePrinter* PseudoCFunction::GetTypePrinter() const
{
return m_typePrinter;
}


PseudoCFunctionType::PseudoCFunctionType(): LanguageRepresentationFunctionType("Pseudo C")
{
Expand Down
3 changes: 3 additions & 0 deletions lang/c/pseudoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
class PseudoCFunction: public BinaryNinja::LanguageRepresentationFunction
{
BinaryNinja::Ref<BinaryNinja::HighLevelILFunction> m_highLevelIL;
BinaryNinja::Ref<BinaryNinja::TypePrinter> m_typePrinter;

enum FieldDisplayType
{
Expand Down Expand Up @@ -52,6 +53,8 @@ class PseudoCFunction: public BinaryNinja::LanguageRepresentationFunction
void EndLines(
const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens) override;

BinaryNinja::TypePrinter* GetTypePrinter() const;

virtual void GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelILInstruction& instr,
BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
BNOperatorPrecedence precedence, bool statement);
Expand Down
69 changes: 68 additions & 1 deletion lang/c/pseudoobjc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

#include "binaryninjaapi.h"
#include "highlevelilinstruction.h"
#include "objctypes.h"
#include <optional>
#include <string>
#include <string_view>
#include <vector>

using namespace BinaryNinja;
Expand Down Expand Up @@ -111,6 +113,9 @@ struct RuntimeCall
Autorelease,
RetainAutorelease,
Class,
Self,
RespondsToSelector,
IsKindOfClass
};

Type type;
Expand All @@ -128,6 +133,9 @@ constexpr std::array RUNTIME_CALLS = {
std::make_pair("_objc_msgSendSuper2", RuntimeCall::MessageSendSuper),
std::make_pair("_objc_opt_class", RuntimeCall::Class),
std::make_pair("_objc_opt_new", RuntimeCall::New),
std::make_pair("_objc_opt_self", RuntimeCall::Self),
std::make_pair("_objc_opt_respondsToSelector", RuntimeCall::RespondsToSelector),
std::make_pair("_objc_opt_isKindOfClass", RuntimeCall::IsKindOfClass),
std::make_pair("_objc_release", RuntimeCall::Release),
std::make_pair("_objc_retain", RuntimeCall::Retain),
std::make_pair("_objc_retainAutoreleasedReturnValue", RuntimeCall::Retain),
Expand All @@ -142,6 +150,9 @@ constexpr std::array RUNTIME_CALLS = {
std::make_pair("j__objc_msgSendSuper2", RuntimeCall::MessageSendSuper),
std::make_pair("j__objc_opt_class", RuntimeCall::Class),
std::make_pair("j__objc_opt_new", RuntimeCall::New),
std::make_pair("j__objc_opt_self", RuntimeCall::Self),
std::make_pair("j__objc_opt_respondsToSelector", RuntimeCall::RespondsToSelector),
std::make_pair("j__objc_opt_isKindOfClass", RuntimeCall::IsKindOfClass),
std::make_pair("j__objc_release", RuntimeCall::Release),
std::make_pair("j__objc_retain", RuntimeCall::Retain),
std::make_pair("j__objc_retainAutoreleasedReturnValue", RuntimeCall::Retain),
Expand Down Expand Up @@ -243,7 +254,20 @@ void PseudoObjCFunction::GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelIL
case RuntimeCall::Class:
runtimeCallTokens = {"class"};
break;
default:
case RuntimeCall::Self:
runtimeCallTokens = {"self"};
break;
case RuntimeCall::RespondsToSelector:
case RuntimeCall::IsKindOfClass:
std::string_view selectorToken =
objCRuntimeCall->type == RuntimeCall::RespondsToSelector ? "respondsToSelector:" : "isKindOfClass:";
if (GetExpr_TwoParamObjCRuntimeCall(
objCRuntimeCall->address, instr, tokens, settings, parameterExprs, selectorToken))
{
if (statement)
tokens.AppendSemicolon();
return;
}
break;
}

Expand Down Expand Up @@ -331,6 +355,24 @@ bool PseudoObjCFunction::GetExpr_GenericObjCRuntimeCall(uint64_t address, const
return true;
}

bool PseudoObjCFunction::GetExpr_TwoParamObjCRuntimeCall(uint64_t address, const HighLevelILInstruction& instr,
HighLevelILTokenEmitter& tokens, DisassemblySettings* settings,
const std::vector<HighLevelILInstruction>& parameterExprs, std::string_view selectorToken)
{
if (parameterExprs.size() < 2)
return false;

tokens.AppendOpenBracket();

GetExprText(parameterExprs[0], tokens, settings);
tokens.Append(TextToken, " ");
tokens.Append(CodeSymbolToken, StringReferenceTokenContext, std::string(selectorToken), instr.address, address);
GetExprText(parameterExprs[1], tokens, settings, MemberAndFunctionOperatorPrecedence);
tokens.AppendCloseBracket();

return true;
}

void PseudoObjCFunction::GetExpr_CONST_PTR(const BinaryNinja::HighLevelILInstruction& instr,
BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
BNOperatorPrecedence precedence, bool statement)
Expand All @@ -350,6 +392,11 @@ void PseudoObjCFunction::GetExpr_CONST_PTR(const BinaryNinja::HighLevelILInstruc
return;
}

if (shortName.rfind("sel_", 0) == 0
&& GetExpr_Selector(
std::string_view(shortName).substr(4), constant, instr, tokens, settings, precedence, statement))
return;

DataVariable variable {};
auto hasVariable = GetFunction()->GetView()->GetDataVariableAtAddress(constant, variable);
if (!hasVariable)
Expand Down Expand Up @@ -393,6 +440,21 @@ bool PseudoObjCFunction::GetExpr_OBJC_CLASS(const Symbol& symbol, uint64_t const
return true;
}

bool PseudoObjCFunction::GetExpr_Selector(std::string_view selector, uint64_t constant,
const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement)
{
if (selector.empty())
return false;

tokens.Append(KeywordToken, "@selector");
tokens.AppendOpenParen();
tokens.Append(DataSymbolToken, ConstDataTokenContext, std::string(selector), instr.address, constant);
tokens.AppendCloseParen();

return true;
}

bool PseudoObjCFunction::GetExpr_NSConstantString(Ref<Type> type, uint64_t constant,
const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens,
BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement)
Expand Down Expand Up @@ -457,3 +519,8 @@ Ref<LanguageRepresentationFunction> PseudoObjCFunctionType::Create(
{
return new PseudoObjCFunction(this, arch, owner, highLevelILFunction);
}

Ref<TypePrinter> PseudoObjCFunctionType::GetTypePrinter()
{
return new PseudoObjCTypePrinter();
}
7 changes: 7 additions & 0 deletions lang/c/pseudoobjc.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,15 @@ class PseudoObjCFunction : public PseudoCFunction
bool GetExpr_GenericObjCRuntimeCall(uint64_t address, const BinaryNinja::HighLevelILInstruction& expr,
BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
const std::vector<BinaryNinja::HighLevelILInstruction>& parameterExprs, const std::vector<std::string_view>& selectorTokens);
bool GetExpr_TwoParamObjCRuntimeCall(uint64_t address, const BinaryNinja::HighLevelILInstruction& expr,
BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings,
const std::vector<BinaryNinja::HighLevelILInstruction>& parameterExprs, std::string_view selectorToken);
bool GetExpr_OBJC_CLASS(const BinaryNinja::Symbol& symbol, uint64_t constant,
const BinaryNinja::HighLevelILInstruction& expr, BinaryNinja::HighLevelILTokenEmitter& tokens,
BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement);
bool GetExpr_Selector(std::string_view selector, uint64_t constant,
const BinaryNinja::HighLevelILInstruction& expr, BinaryNinja::HighLevelILTokenEmitter& tokens,
BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement);
bool GetExpr_NSConstantString(BinaryNinja::Ref<BinaryNinja::Type> type, uint64_t constant,
const BinaryNinja::HighLevelILInstruction& expr, BinaryNinja::HighLevelILTokenEmitter& tokens,
BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement);
Expand All @@ -41,4 +47,5 @@ class PseudoObjCFunctionType : public PseudoCFunctionType {
PseudoObjCFunctionType();
BinaryNinja::Ref<BinaryNinja::LanguageRepresentationFunction> Create(BinaryNinja::Architecture* arch,
BinaryNinja::Function* owner, BinaryNinja::HighLevelILFunction* highLevelILFunction) override;
BinaryNinja::Ref<BinaryNinja::TypePrinter> GetTypePrinter() override;
};