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

Fix crashing name mangler #1910

Merged
merged 2 commits into from
Feb 6, 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
125 changes: 125 additions & 0 deletions src/CppParser/ASTNameMangler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/************************************************************************
*
* CppSharp
* Licensed under the simplified BSD license. All rights reserved.
*
************************************************************************/

#include "ASTNameMangler.h"

#include <clang/AST/GlobalDecl.h>
#include <clang/AST/Mangle.h>
#include <clang/AST/VTableBuilder.h>
#include <clang/Basic/TargetInfo.h>
#include <llvm/IR/Mangler.h>

using namespace clang;
using namespace CppSharp::CppParser;

namespace {
enum ObjCKind {
ObjCClass,
ObjCMetaclass,
};

StringRef getClassSymbolPrefix(ObjCKind Kind, const ASTContext& Context) {
if (Context.getLangOpts().ObjCRuntime.isGNUFamily())
return Kind == ObjCMetaclass ? "_OBJC_METACLASS_" : "_OBJC_CLASS_";
return Kind == ObjCMetaclass ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_";
}

void WriteObjCClassName(const ObjCInterfaceDecl* D, raw_ostream& OS) {
OS << getClassSymbolPrefix(ObjCClass, D->getASTContext());
OS << D->getObjCRuntimeNameAsString();
}
}

ASTNameMangler::ASTNameMangler(ASTContext& Ctx)
: DL(Ctx.getTargetInfo().getDataLayoutString())
, MC(Ctx.createMangleContext())
{
}

std::string ASTNameMangler::GetName(const Decl* D) {
std::string Name;
{
llvm::raw_string_ostream OS(Name);
WriteName(D, OS);
}
return Name;
}

bool ASTNameMangler::WriteName(const Decl* D, raw_ostream& OS) {
// First apply frontend mangling.
if (auto* FD = dyn_cast<FunctionDecl>(D)) {
if (FD->isDependentContext())
return true;
if (WriteFuncOrVarName(FD, OS))
return true;
}
else if (auto* VD = dyn_cast<VarDecl>(D)) {
if (WriteFuncOrVarName(VD, OS))
return true;
}
else if (auto* MD = dyn_cast<ObjCMethodDecl>(D)) {
MC->mangleObjCMethodName(MD, OS, /*includePrefixByte=*/false,
/*includeCategoryNamespace=*/true);
return false;
}
else if (auto* ID = dyn_cast<ObjCInterfaceDecl>(D)) {
WriteObjCClassName(ID, OS);
}
else {
return true;
}

return false;
}

std::string ASTNameMangler::GetMangledStructor(const NamedDecl* ND, unsigned StructorType) {
std::string FrontendBuf;
llvm::raw_string_ostream FOS(FrontendBuf);

GlobalDecl GD;
if (const auto* CD = dyn_cast_or_null<CXXConstructorDecl>(ND))
GD = GlobalDecl(CD, static_cast<CXXCtorType>(StructorType));
else if (const auto* DD = dyn_cast_or_null<CXXDestructorDecl>(ND))
GD = GlobalDecl(DD, static_cast<CXXDtorType>(StructorType));
MC->mangleName(GD, FOS);

return FrontendBuf;
}

std::string ASTNameMangler::GetMangledThunk(const CXXMethodDecl* MD, const ThunkInfo& T, bool /*ElideOverrideInfo*/) {
std::string FrontendBuf;
llvm::raw_string_ostream FOS(FrontendBuf);

// TODO: Enable `ElideOverrideInfo` param if clang is updated to 19
MC->mangleThunk(MD, T, /*ElideOverrideInfo,*/ FOS);

return FrontendBuf;
}

bool ASTNameMangler::WriteFuncOrVarName(const NamedDecl* D, raw_ostream& OS) const
{
if (!MC->shouldMangleDeclName(D)) {
const IdentifierInfo* II = D->getIdentifier();
if (!II)
return true;
OS << II->getName();
return false;
}

GlobalDecl GD;
if (const auto* CtorD = dyn_cast<CXXConstructorDecl>(D))
GD = GlobalDecl(CtorD, Ctor_Base);
else if (const auto* DtorD = dyn_cast<CXXDestructorDecl>(D))
GD = GlobalDecl(DtorD, Dtor_Base);
else if (D->hasAttr<CUDAGlobalAttr>())
GD = GlobalDecl(cast<FunctionDecl>(D));
else
GD = GlobalDecl(D);

MC->mangleName(GD, OS);
return false;
}
50 changes: 50 additions & 0 deletions src/CppParser/ASTNameMangler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/************************************************************************
*
* CppSharp
* Licensed under the simplified BSD license. All rights reserved.
*
************************************************************************/

#pragma once

#include <clang/AST/ASTFwd.h>
#include <llvm/IR/DataLayout.h>

#include <string>

namespace clang
{
class ASTContext;
class MangleContext;
struct ThunkInfo;
}

namespace llvm
{
class raw_ostream;
}

namespace CppSharp::CppParser {

/// <summary>
/// Helper class for getting the mangled name of a declaration
/// </summary>
/// <remarks>Source adapted from https://clang.llvm.org/doxygen/Mangle_8cpp_source.html#l00394</remarks>
class ASTNameMangler
{
public:
explicit ASTNameMangler(clang::ASTContext& Ctx);

std::string GetName(const clang::Decl* D);
bool WriteName(const clang::Decl* D, llvm::raw_ostream& OS);

private:
std::string GetMangledStructor(const clang::NamedDecl* ND, unsigned StructorType);
std::string GetMangledThunk(const clang::CXXMethodDecl* MD, const clang::ThunkInfo& T, bool ElideOverrideInfo);
bool WriteFuncOrVarName(const clang::NamedDecl* D, llvm::raw_ostream& OS) const;

llvm::DataLayout DL;
std::unique_ptr<clang::MangleContext> MC;
};

}
82 changes: 35 additions & 47 deletions src/CppParser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
#include <Driver/ToolChains/Linux.h>
#include <Driver/ToolChains/MSVC.h>

#include "ASTNameMangler.h"

#if defined(__APPLE__) || defined(__linux__)
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
Expand Down Expand Up @@ -468,69 +470,55 @@ void Parser::Setup(bool Compile)
PP.getLangOpts());

c->createASTContext();
NameMangler.reset(new ASTNameMangler(c->getASTContext()));
}

//-----------------------------------//

std::string Parser::GetDeclMangledName(const clang::Decl* D)
std::string Parser::GetDeclMangledName(const clang::Decl* D) const
{
using namespace clang;
// Source adapted from https://clang.llvm.org/doxygen/JSONNodeDumper_8cpp_source.html#l00845

if(!D || !isa<NamedDecl>(D))
return "";
using namespace clang;

bool CanMangle = isa<FunctionDecl>(D) || isa<VarDecl>(D)
|| isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D);
auto ND = dyn_cast_or_null<NamedDecl>(D);
if (!ND || !ND->getDeclName())
return {};

if (!CanMangle) return "";
// If the declaration is dependent or is in a dependent context, then the
// mangling is unlikely to be meaningful (and in some cases may cause
// "don't know how to mangle this" assertion failures.)
if (ND->isTemplated())
return {};

auto ND = cast<NamedDecl>(D);
std::unique_ptr<MangleContext> MC;

auto& AST = c->getASTContext();
auto targetABI = c->getTarget().getCXXABI().getKind();
switch(targetABI)
{
default:
MC.reset(ItaniumMangleContext::create(AST, AST.getDiagnostics()));
break;
case TargetCXXABI::Microsoft:
MC.reset(MicrosoftMangleContext::create(AST, AST.getDiagnostics()));
break;
}

if (!MC)
llvm_unreachable("Unknown mangling ABI");

std::string Mangled;
llvm::raw_string_ostream Out(Mangled);
/*bool CanMangle = isa<FunctionDecl>(D) || isa<VarDecl>(D)
|| isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D);

bool IsDependent = false;
if (const ValueDecl *VD = dyn_cast<ValueDecl>(ND))
IsDependent |= VD->getType()->isDependentType();
if (!CanMangle)
return {};*/

if (!IsDependent)
IsDependent |= ND->getDeclContext()->isDependentContext();
// FIXME: There are likely other contexts in which it makes no sense to ask
// for a mangled name.
if (isa<RequiresExprBodyDecl>(ND->getDeclContext()))
return {};

if (!MC->shouldMangleDeclName(ND) || IsDependent)
return ND->getDeclName().getAsString();
// Do not mangle template deduction guides.
if (isa<CXXDeductionGuideDecl>(ND))
return {};

GlobalDecl GD;
if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(ND))
GD = GlobalDecl(CD, Ctor_Base);
else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(ND))
GD = GlobalDecl(DD, Dtor_Base);
else
GD = GlobalDecl(ND);
MC->mangleName(GD, Out);
// Mangled names are not meaningful for locals, and may not be well-defined
// in the case of VLAs.
auto* VD = dyn_cast<VarDecl>(ND);
if (VD && VD->hasLocalStorage())
return {};

Out.flush();
std::string MangledName = NameMangler->GetName(ND);

// Strip away LLVM name marker.
if(!Mangled.empty() && Mangled[0] == '\01')
Mangled = Mangled.substr(1);
if (!MangledName.empty() && MangledName[0] == '\01')
MangledName = MangledName.substr(1);

return Mangled;
return MangledName;
}

//-----------------------------------//
Expand Down Expand Up @@ -632,7 +620,7 @@ static clang::SourceLocation GetDeclStartLocation(clang::CompilerInstance* C,
return GetDeclStartLocation(C, prevDecl);
}

std::string Parser::GetTypeName(const clang::Type* Type)
std::string Parser::GetTypeName(const clang::Type* Type) const
{
using namespace clang;

Expand Down
6 changes: 4 additions & 2 deletions src/CppParser/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ namespace clang {
#define Debug printf

namespace CppSharp { namespace CppParser {
class ASTNameMangler;

class Parser
{
Expand Down Expand Up @@ -133,8 +134,8 @@ class Parser
// Clang helpers
SourceLocationKind GetLocationKind(const clang::SourceLocation& Loc);
bool IsValidDeclaration(const clang::SourceLocation& Loc);
std::string GetDeclMangledName(const clang::Decl* D);
std::string GetTypeName(const clang::Type* Type);
std::string GetDeclMangledName(const clang::Decl* D) const;
std::string GetTypeName(const clang::Type* Type) const;
bool CanCheckCodeGenInfo(const clang::Type* Ty);
void CompleteIfSpecializationType(const clang::QualType& QualType);
Parameter* WalkParameter(const clang::ParmVarDecl* PVD,
Expand Down Expand Up @@ -181,6 +182,7 @@ class Parser
int index;
std::unique_ptr<clang::CompilerInstance> c;
llvm::LLVMContext LLVMCtx;
std::unique_ptr<ASTNameMangler> NameMangler;
std::unique_ptr<llvm::Module> LLVMModule;
std::unique_ptr<clang::CodeGen::CodeGenModule> CGM;
std::unique_ptr<clang::CodeGen::CodeGenTypes> codeGenTypes;
Expand Down