Skip to content

Commit 5ba3702

Browse files
committed
fix: strip leading underscore for __cdecl functions on x86
1 parent c2cad1e commit 5ba3702

File tree

6 files changed

+145
-1
lines changed

6 files changed

+145
-1
lines changed

lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,3 +1118,21 @@ size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id,
11181118
}
11191119
return 0;
11201120
}
1121+
1122+
llvm::StringRef lldb_private::npdb::StripCDeclPrefix(llvm::StringRef mangled) {
1123+
// See
1124+
// https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names#FormatC
1125+
if (!mangled.starts_with('_'))
1126+
return mangled;
1127+
1128+
// make sure this isn't __stdcall (`_{name}@{sizeof(params)}`) or __vectorcall
1129+
// (`{name}@@{sizeof(params)}`).
1130+
size_t last_at_pos = mangled.find_last_of('@');
1131+
if (last_at_pos != llvm::StringRef::npos &&
1132+
last_at_pos < mangled.size() - 1 &&
1133+
llvm::all_of(mangled.slice(last_at_pos + 1, mangled.size()),
1134+
llvm::isDigit))
1135+
return mangled;
1136+
1137+
return mangled.drop_front();
1138+
}

lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,18 @@ PdbTypeSymId GetBestPossibleDecl(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
161161

162162
size_t GetSizeOfType(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
163163

164+
/// Strips the leading underscore of mangled __cdecl functions.
165+
///
166+
/// If the name comes from another calling convention, it is returned as-is.
167+
///
168+
/// \pre \c mangled must not be from a 64-bit environment as __cdecl names
169+
/// aren't mangled there.
170+
///
171+
/// \param[in] mangled A mangled symbol name
172+
/// \returns The stripped name if this name is a mangled __cdecl one. Otherwise,
173+
/// the input is returned.
174+
llvm::StringRef StripCDeclPrefix(llvm::StringRef mangled);
175+
164176
} // namespace npdb
165177
} // namespace lldb_private
166178

lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2677,6 +2677,7 @@ SymbolFileNativePDB::FindMangledFunctionName(PdbCompilandSymId func_id) {
26772677

26782678
ProcSym proc(static_cast<SymbolRecordKind>(sym_record.kind()));
26792679
cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym_record, proc));
2680+
26802681
return FindMangledSymbol(SegmentOffset(proc.Segment, proc.CodeOffset));
26812682
}
26822683

@@ -2686,7 +2687,21 @@ SymbolFileNativePDB::FindMangledSymbol(SegmentOffset so) {
26862687
so.segment, so.offset);
26872688
if (!symbol)
26882689
return std::nullopt;
2689-
return symbol->first.Name;
2690+
2691+
llvm::StringRef name = symbol->first.Name;
2692+
// "In non-64 bit environments" (on x86 in pactice), __cdecl functions get
2693+
// prefixed with an underscore. For compilers using LLVM, this happens in LLVM
2694+
// (as opposed to the compiler frontend). Because of this, DWARF doesn't
2695+
// contain the "full" mangled name in DW_AT_linkage_name for these functions.
2696+
// We strip the mangling here for compatibility with DWARF. See
2697+
// llvm.org/pr161676 and
2698+
// https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names#FormatC
2699+
if ((symbol->first.Flags & PublicSymFlags::Function) !=
2700+
PublicSymFlags::None &&
2701+
m_index->dbi().getMachineType() == PDB_Machine::x86)
2702+
name = StripCDeclPrefix(name);
2703+
2704+
return name;
26902705
}
26912706

26922707
void SymbolFileNativePDB::CacheUdtDeclarations() {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// clang-format off
2+
// REQUIRES: lld, x86
3+
4+
// RUN: %build --compiler=clang-cl --arch=32 --nodefaultlib --output=%t-32.exe %s
5+
// RUN: lldb-test symbols %t-32.exe | FileCheck --check-prefixes CHECK-32,CHECK-BOTH %s
6+
// RUN: %build --compiler=clang-cl --arch=64 --nodefaultlib --output=%t-64.exe %s
7+
// RUN: lldb-test symbols %t-64.exe | FileCheck --check-prefixes CHECK-64,CHECK-BOTH %s
8+
9+
extern "C" {
10+
int FuncCCall() { return 0; }
11+
int __stdcall FuncStdCall() { return 0; }
12+
int __fastcall FuncFastCall() { return 0; }
13+
int __vectorcall FuncVectorCall() { return 0; }
14+
15+
int __cdecl _underscoreCdecl() { return 0; }
16+
int __stdcall _underscoreStdcall() { return 0; }
17+
int __fastcall _underscoreFastcall() { return 0; }
18+
int __vectorcall _underscoreVectorcall() { return 0; }
19+
}
20+
21+
int main() {
22+
FuncCCall();
23+
FuncStdCall();
24+
FuncFastCall();
25+
FuncVectorCall();
26+
_underscoreCdecl();
27+
_underscoreStdcall();
28+
_underscoreFastcall();
29+
_underscoreVectorcall();
30+
return 0;
31+
}
32+
33+
// CHECK-BOTH-DAG: Function{{.*}}, demangled = FuncCCall,
34+
// CHECK-BOTH-DAG: Function{{.*}}, demangled = FuncVectorCall@@0,
35+
// CHECK-BOTH-DAG: Function{{.*}}, demangled = _underscoreCdecl,
36+
// CHECK-BOTH-DAG: Function{{.*}}, demangled = _underscoreVectorcall@@0,
37+
// CHECK-BOTH-DAG: Function{{.*}}, demangled = main,
38+
39+
// __stdcall and __fastcall aren't available on 64 bit
40+
41+
// CHECK-32-DAG: Function{{.*}}, demangled = _FuncStdCall@0,
42+
// CHECK-64-DAG: Function{{.*}}, demangled = FuncStdCall,
43+
44+
// CHECK-32-DAG: Function{{.*}}, demangled = @FuncFastCall@0,
45+
// CHECK-64-DAG: Function{{.*}}, demangled = FuncFastCall,
46+
47+
// CHECK-32-DAG: Function{{.*}}, demangled = __underscoreStdcall@0,
48+
// CHECK-64-DAG: Function{{.*}}, demangled = _underscoreStdcall,
49+
50+
// CHECK-32-DAG: Function{{.*}}, demangled = @_underscoreFastcall@0,
51+
// CHECK-64-DAG: Function{{.*}}, demangled = _underscoreFastcall,

lldb/unittests/SymbolFile/NativePDB/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_lldb_unittest(SymbolFileNativePDBTests
22
PdbFPOProgramToDWARFExpressionTests.cpp
3+
PdbUtilTests.cpp
34
UdtRecordCompleterTests.cpp
45

56
LINK_COMPONENTS
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "Plugins/SymbolFile/NativePDB/PdbUtil.h"
10+
#include "gtest/gtest.h"
11+
12+
using namespace lldb_private::npdb;
13+
14+
TEST(PdbUtil, StripCDeclPrefix) {
15+
ASSERT_EQ(StripCDeclPrefix("main"), "main");
16+
17+
// __cdecl
18+
ASSERT_EQ(StripCDeclPrefix("_main"), "main");
19+
ASSERT_EQ(StripCDeclPrefix("__main"), "_main");
20+
ASSERT_EQ(StripCDeclPrefix("_main@"), "main@");
21+
ASSERT_EQ(StripCDeclPrefix("_main@foo"), "main@foo");
22+
ASSERT_EQ(StripCDeclPrefix("_main@4@foo"), "main@4@foo");
23+
24+
// __stdcall
25+
ASSERT_EQ(StripCDeclPrefix("_main@4"), "_main@4");
26+
ASSERT_EQ(StripCDeclPrefix("_main@foo@4"), "_main@foo@4");
27+
ASSERT_EQ(StripCDeclPrefix("_main@4@5"), "_main@4@5");
28+
29+
// __fastcall
30+
ASSERT_EQ(StripCDeclPrefix("@main@4"), "@main@4");
31+
32+
// __vectorcall
33+
ASSERT_EQ(StripCDeclPrefix("main@@4"), "main@@4");
34+
ASSERT_EQ(StripCDeclPrefix("_main@@4"), "_main@@4");
35+
36+
// MS C++ mangling
37+
ASSERT_EQ(StripCDeclPrefix("?a@@YAHD@Z"), "?a@@YAHD@Z");
38+
// Itanium mangling (e.g. on MinGW)
39+
ASSERT_EQ(StripCDeclPrefix("__Z7recursei"), "_Z7recursei");
40+
41+
ASSERT_EQ(StripCDeclPrefix("_"), "");
42+
ASSERT_EQ(StripCDeclPrefix("_@"), "@");
43+
ASSERT_EQ(StripCDeclPrefix(""), "");
44+
ASSERT_EQ(StripCDeclPrefix("_@4"), "_@4");
45+
ASSERT_EQ(StripCDeclPrefix("@4"), "@4");
46+
ASSERT_EQ(StripCDeclPrefix("@"), "@");
47+
}

0 commit comments

Comments
 (0)