Skip to content

Commit c1d797b

Browse files
committed
[lldb][DWARFASTParserClang] Eagerly search definitions for Objective-C classes
This patch essentially reverts the definition DIE delay changes (in llvm#98361) for Objective-C. The problem we've been seeing is as follows: 1. An Objetive-C class extension is represented in DWARF as: ``` DW_TAG_structure_type DW_AT_declaration (true) DW_AT_name ("ExtendedClass") DW_TAG_subprogram DW_AT_name ("extensionMethod") ... ``` I.e., it is a forward declaration of the extended class, but that forward declaration has children methods. 2. When we set a breakpoint in one of those methods we parse the subprogram DIE and try to create an `ObjCMethodDecl` for it (and attach it to the context). 3. When parsing the subprogram DIE, we first try to complete the DeclContext. Because `ExtendedClass` is only a forward declaration and we don't try to fetch the definition DIE eagerly anymore, LLDB has no idea that we're dealing with an Objective-C type. So it goes ahead and constructs it as a `CXXRecordDecl`. This confuses `DWARFASTParserClang::ParseObjCMethod` because it expects the context to be an `clang::ObjCObjectOrInterfaceType`. So it bails and we end up crashing because we try to attach a `FunctionDecl` to an incomplete `CXXRecordDecl` (which wasn't even forcefully completed). Since there's no good way to tell whether a forward declaration is an Objective-C type, the only solution I can really see here is to eagerly fetch the definition for Objective-C types.
1 parent 1274e90 commit c1d797b

File tree

3 files changed

+111
-57
lines changed

3 files changed

+111
-57
lines changed

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 72 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "lldb/Utility/LLDBAssert.h"
4141
#include "lldb/Utility/Log.h"
4242
#include "lldb/Utility/StreamString.h"
43+
#include "lldb/lldb-enumerations.h"
4344

4445
#include "clang/AST/CXXInheritance.h"
4546
#include "clang/AST/Decl.h"
@@ -302,6 +303,22 @@ static void PrepareContextToReceiveMembers(TypeSystemClang &ast,
302303
}
303304
}
304305

306+
static std::optional<TypeSP> TryEmplaceDIEToType(DWARFDIE const &def_die,
307+
SymbolFileDWARF &dwarf) {
308+
auto [it, inserted] =
309+
dwarf.GetDIEToType().try_emplace(def_die.GetDIE(), DIE_IS_BEING_PARSED);
310+
311+
// First time we're parsing this DIE, nothing to return.
312+
if (inserted)
313+
return std::nullopt;
314+
315+
// DIE is currently being parsed.
316+
if (it->getSecond() == nullptr || it->getSecond() == DIE_IS_BEING_PARSED)
317+
return nullptr;
318+
319+
return it->getSecond()->shared_from_this();
320+
}
321+
305322
void DWARFASTParserClang::RegisterDIE(DWARFDebugInfoEntry *die,
306323
CompilerType type) {
307324
if (clang::TagDecl *td = ClangUtil::GetAsTagDecl(type)) {
@@ -981,42 +998,18 @@ TypeSP DWARFASTParserClang::ParseEnum(const SymbolContext &sc,
981998
ParsedDWARFTypeAttributes &attrs) {
982999
Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
9831000
SymbolFileDWARF *dwarf = decl_die.GetDWARF();
984-
const dw_tag_t tag = decl_die.Tag();
9851001

9861002
DWARFDIE def_die;
9871003
if (attrs.is_forward_declaration) {
9881004
if (TypeSP type_sp = ParseTypeFromClangModule(sc, decl_die, log))
9891005
return type_sp;
9901006

991-
def_die = dwarf->FindDefinitionDIE(decl_die);
992-
993-
if (!def_die) {
994-
SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile();
995-
if (debug_map_symfile) {
996-
// We weren't able to find a full declaration in this DWARF,
997-
// see if we have a declaration anywhere else...
998-
def_die = debug_map_symfile->FindDefinitionDIE(decl_die);
999-
}
1000-
}
1001-
1002-
if (log) {
1003-
dwarf->GetObjectFile()->GetModule()->LogMessage(
1004-
log,
1005-
"SymbolFileDWARF({0:p}) - {1:x16}}: {2} ({3}) type \"{4}\" is a "
1006-
"forward declaration, complete DIE is {5}",
1007-
static_cast<void *>(this), decl_die.GetID(), DW_TAG_value_to_name(tag),
1008-
tag, attrs.name.GetCString(),
1009-
def_die ? llvm::utohexstr(def_die.GetID()) : "not found");
1010-
}
1007+
def_die = FindDefinitionDIE(decl_die, *dwarf);
10111008
}
10121009
if (def_die) {
1013-
if (auto [it, inserted] = dwarf->GetDIEToType().try_emplace(
1014-
def_die.GetDIE(), DIE_IS_BEING_PARSED);
1015-
!inserted) {
1016-
if (it->getSecond() == nullptr || it->getSecond() == DIE_IS_BEING_PARSED)
1017-
return nullptr;
1018-
return it->getSecond()->shared_from_this();
1019-
}
1010+
if (auto maybe_type = TryEmplaceDIEToType(def_die, *dwarf))
1011+
return *maybe_type;
1012+
10201013
attrs = ParsedDWARFTypeAttributes(def_die);
10211014
} else {
10221015
// No definition found. Proceed with the declaration die. We can use it to
@@ -1791,10 +1784,8 @@ static void adjustArgPassing(TypeSystemClang &ast,
17911784
}
17921785
}
17931786

1794-
TypeSP
1795-
DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
1796-
const DWARFDIE &die,
1797-
ParsedDWARFTypeAttributes &attrs) {
1787+
TypeSP DWARFASTParserClang::ParseStructureLikeDIE(
1788+
const SymbolContext &sc, DWARFDIE die, ParsedDWARFTypeAttributes &attrs) {
17981789
CompilerType clang_type;
17991790
const dw_tag_t tag = die.Tag();
18001791
SymbolFileDWARF *dwarf = die.GetDWARF();
@@ -1810,19 +1801,28 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
18101801
ConstString unique_typename(attrs.name);
18111802
Declaration unique_decl(attrs.decl);
18121803
uint64_t byte_size = attrs.byte_size.value_or(0);
1813-
if (attrs.byte_size && *attrs.byte_size == 0 && attrs.name &&
1814-
!die.HasChildren() && cu_language == eLanguageTypeObjC) {
1815-
// Work around an issue with clang at the moment where forward
1816-
// declarations for objective C classes are emitted as:
1817-
// DW_TAG_structure_type [2]
1818-
// DW_AT_name( "ForwardObjcClass" )
1819-
// DW_AT_byte_size( 0x00 )
1820-
// DW_AT_decl_file( "..." )
1821-
// DW_AT_decl_line( 1 )
1822-
//
1823-
// Note that there is no DW_AT_declaration and there are no children,
1824-
// and the byte size is zero.
1825-
attrs.is_forward_declaration = true;
1804+
1805+
if (attrs.is_forward_declaration) {
1806+
// See if the type comes from a Clang module and if so, track down
1807+
// that type.
1808+
if (TypeSP type_sp = ParseTypeFromClangModule(sc, die, log))
1809+
return type_sp;
1810+
1811+
// Objetive-C forward declared types are represented in the same way
1812+
// that they are in C++. Since we don't want to create a CXXRecordDecl
1813+
// for the Objective-C type here, we need to fetch the definition DIE,
1814+
// which will have a DW_AT_APPLE_runtime_class attribute indicating
1815+
// we're dealing with Objective-C.
1816+
if (cu_language == eLanguageTypeObjC_plus_plus ||
1817+
cu_language == eLanguageTypeObjC) {
1818+
if (DWARFDIE def_die = FindDefinitionDIE(die, *dwarf)) {
1819+
if (auto maybe_type = TryEmplaceDIEToType(def_die, *dwarf))
1820+
return *maybe_type;
1821+
1822+
attrs = ParsedDWARFTypeAttributes(def_die);
1823+
die = def_die;
1824+
}
1825+
}
18261826
}
18271827

18281828
if (attrs.name) {
@@ -1906,14 +1906,6 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
19061906
}
19071907
}
19081908

1909-
if (attrs.is_forward_declaration) {
1910-
// See if the type comes from a Clang module and if so, track down
1911-
// that type.
1912-
TypeSP type_sp = ParseTypeFromClangModule(sc, die, log);
1913-
if (type_sp)
1914-
return type_sp;
1915-
}
1916-
19171909
assert(tag_decl_kind != -1);
19181910
UNUSED_IF_ASSERT_DISABLED(tag_decl_kind);
19191911

@@ -4084,3 +4076,30 @@ void DWARFASTParserClang::ParseRustVariantPart(
40844076

40854077
layout_info.field_offsets.insert({inner_field, 0});
40864078
}
4079+
4080+
DWARFDIE DWARFASTParserClang::FindDefinitionDIE(DWARFDIE const &decl_die,
4081+
SymbolFileDWARF &dwarf) {
4082+
DWARFDIE def_die = dwarf.FindDefinitionDIE(decl_die);
4083+
4084+
if (!def_die) {
4085+
SymbolFileDWARFDebugMap *debug_map_symfile = dwarf.GetDebugMapSymfile();
4086+
if (debug_map_symfile) {
4087+
// We weren't able to find a full declaration in this DWARF,
4088+
// see if we have a declaration anywhere else...
4089+
def_die = debug_map_symfile->FindDefinitionDIE(decl_die);
4090+
}
4091+
}
4092+
4093+
if (auto *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups)) {
4094+
char const *name = decl_die.GetName();
4095+
dwarf.GetObjectFile()->GetModule()->LogMessage(
4096+
log,
4097+
"SymbolFileDWARF({0:p}) - {1:x16}}: {2} ({3}) type \"{4}\" is a "
4098+
"forward declaration, complete DIE is {5}",
4099+
static_cast<void *>(this), decl_die.GetID(),
4100+
DW_TAG_value_to_name(decl_die.Tag()), decl_die.Tag(), name ? name : "",
4101+
def_die ? llvm::utohexstr(def_die.GetID()) : "not found");
4102+
}
4103+
4104+
return def_die;
4105+
}

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,9 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
219219
const lldb_private::plugin::dwarf::DWARFDIE &parent_die);
220220

221221
/// Parse a structure, class, or union type DIE.
222-
lldb::TypeSP
223-
ParseStructureLikeDIE(const lldb_private::SymbolContext &sc,
224-
const lldb_private::plugin::dwarf::DWARFDIE &die,
225-
ParsedDWARFTypeAttributes &attrs);
222+
lldb::TypeSP ParseStructureLikeDIE(const lldb_private::SymbolContext &sc,
223+
lldb_private::plugin::dwarf::DWARFDIE die,
224+
ParsedDWARFTypeAttributes &attrs);
226225

227226
clang::Decl *
228227
GetClangDeclForDIE(const lldb_private::plugin::dwarf::DWARFDIE &die);
@@ -498,6 +497,9 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
498497
bool IsSwiftInteropType(const lldb_private::plugin::dwarf::DWARFDIE &die);
499498
// END SWIFT
500499

500+
lldb_private::plugin::dwarf::DWARFDIE
501+
FindDefinitionDIE(lldb_private::plugin::dwarf::DWARFDIE const &decl_die,
502+
lldb_private::plugin::dwarf::SymbolFileDWARF &dwarf);
501503
};
502504

503505
/// Parsed form of all attributes that are relevant for type reconstruction.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# REQUIRES: system-darwin
2+
3+
# Test that we can set a breakpoint in a method of a class extension.
4+
# This requires us to parse the method into an AST type, and the context
5+
# too (which in DWARF is just a forward declaration).
6+
#
7+
# RUN: split-file %s %t
8+
# RUN: %clangxx_host %t/lib.m -c -g -gmodules -fmodules -o %t/lib.o
9+
# RUN: %clangxx_host %t/main.m -g -gmodules -fmodules %t/lib.o -o %t/a.out -framework Foundation
10+
#
11+
# RUN: %lldb %t/a.out -o "breakpoint set -f lib.m -l 6" -o exit | FileCheck %s
12+
13+
# CHECK: (lldb) breakpoint set -f lib.m -l 6
14+
# CHECK: Breakpoint 1: where = a.out`-[NSObject(Foo) func]
15+
16+
#--- main.m
17+
int main() {
18+
return 0;
19+
}
20+
21+
#--- lib.m
22+
#import <Foundation/Foundation.h>
23+
24+
@implementation NSObject (Foo)
25+
- (NSError *)func {
26+
NSLog(@"Hello, World!");
27+
return 0;
28+
}
29+
@end
30+
31+
NSObject * func() {
32+
return 0;
33+
}

0 commit comments

Comments
 (0)