Skip to content

Commit 335c69c

Browse files
jiminghamJDevlieghere
authored andcommitted
Adapt the ObjC stepping algorithm to deal with "selector-stubs" in clang.
Clang is adding a feature to ObjC code generation, where instead of calling objc_msgSend directly with an object & selector, it generates a stub that gets passed only the object and the stub figures out the selector. This patch adds support for following that dispatch method into the implementation function. (cherry picked from commit 833882b)
1 parent 7e4441b commit 335c69c

File tree

8 files changed

+375
-296
lines changed

8 files changed

+375
-296
lines changed

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,7 +1567,7 @@ AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoUtilityFunctionImpl(
15671567
if (!utility_fn_or_error) {
15681568
LLDB_LOG_ERROR(
15691569
log, utility_fn_or_error.takeError(),
1570-
"Failed to get utility function for implementation lookup: {0}");
1570+
"Failed to get utility function for dynamic info extractor: {0}");
15711571
return {};
15721572
}
15731573

@@ -1697,7 +1697,7 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::
16971697
if (!utility_fn_or_error) {
16981698
LLDB_LOG_ERROR(
16991699
log, utility_fn_or_error.takeError(),
1700-
"Failed to get utility function for implementation lookup: {0}");
1700+
"Failed to get utility function for shared class info extractor: {0}");
17011701
return nullptr;
17021702
}
17031703

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp

Lines changed: 253 additions & 264 deletions
Large diffs are not rendered by default.

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ class AppleObjCTrampolineHandler {
3838
public:
3939
enum FixUpState { eFixUpNone, eFixUpFixed, eFixUpToFix };
4040

41-
const char *name;
42-
bool stret_return;
43-
bool is_super;
44-
bool is_super2;
45-
FixUpState fixedup;
41+
const char *name = nullptr;
42+
bool stret_return = false;
43+
bool is_super = false;
44+
bool is_super2 = false;
45+
FixUpState fixedup = eFixUpNone;
4646
};
4747

4848
lldb::addr_t SetupDispatchFunction(Thread &thread,
@@ -52,9 +52,19 @@ class AppleObjCTrampolineHandler {
5252
const DispatchFunction &)>);
5353

5454
private:
55+
/// These hold the code for the function that finds the implementation of
56+
/// an ObjC message send given the class & selector and the kind of dispatch.
57+
/// There are two variants depending on whether the platform uses a separate
58+
/// _stret passing convention (e.g. Intel) or not (e.g. ARM). The difference
59+
/// is only at the very end of the function, so the code is broken into the
60+
/// common prefix and the suffix, which get composed appropriately before
61+
/// the function gets compiled.
62+
/// \{
5563
static const char *g_lookup_implementation_function_name;
64+
static const char *g_lookup_implementation_function_common_code;
5665
static const char *g_lookup_implementation_with_stret_function_code;
5766
static const char *g_lookup_implementation_no_stret_function_code;
67+
/// \}
5868

5969
class AppleObjCVTables {
6070
public:
@@ -144,7 +154,7 @@ class AppleObjCTrampolineHandler {
144154
MsgsendMap m_opt_dispatch_map;
145155
lldb::ProcessWP m_process_wp;
146156
lldb::ModuleSP m_objc_module_sp;
147-
const char *m_lookup_implementation_function_code;
157+
std::string m_lookup_implementation_function_code;
148158
std::unique_ptr<UtilityFunction> m_impl_code;
149159
std::mutex m_impl_function_mutex;
150160
lldb::addr_t m_impl_fn_addr;

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ using namespace lldb_private;
3232
AppleThreadPlanStepThroughObjCTrampoline::
3333
AppleThreadPlanStepThroughObjCTrampoline(
3434
Thread &thread, AppleObjCTrampolineHandler &trampoline_handler,
35-
ValueList &input_values, lldb::addr_t isa_addr, lldb::addr_t sel_addr)
35+
ValueList &input_values, lldb::addr_t isa_addr, lldb::addr_t sel_addr,
36+
lldb::addr_t sel_str_addr, llvm::StringRef sel_str)
3637
: ThreadPlan(ThreadPlan::eKindGeneric,
3738
"MacOSX Step through ObjC Trampoline", thread, eVoteNoOpinion,
3839
eVoteNoOpinion),
3940
m_trampoline_handler(trampoline_handler),
4041
m_args_addr(LLDB_INVALID_ADDRESS), m_input_values(input_values),
41-
m_isa_addr(isa_addr), m_sel_addr(sel_addr), m_impl_function(nullptr) {}
42+
m_isa_addr(isa_addr), m_sel_addr(sel_addr), m_impl_function(nullptr),
43+
m_sel_str_addr(sel_str_addr), m_sel_str(sel_str) {}
4244

4345
// Destructor
4446
AppleThreadPlanStepThroughObjCTrampoline::
@@ -126,8 +128,10 @@ bool AppleThreadPlanStepThroughObjCTrampoline::ShouldStop(Event *event_ptr) {
126128
}
127129
}
128130

129-
// Second stage, if all went well with the function calling, then fetch the
130-
// target address, and queue up a "run to that address" plan.
131+
// Second stage, if all went well with the function calling, get the
132+
// implementation function address, and queue up a "run to that address" plan.
133+
Log *log = GetLog(LLDBLog::Step);
134+
131135
if (!m_run_to_sp) {
132136
Value target_addr_value;
133137
ExecutionContext exc_ctx;
@@ -142,7 +146,6 @@ bool AppleThreadPlanStepThroughObjCTrampoline::ShouldStop(Event *event_ptr) {
142146
}
143147
Address target_so_addr;
144148
target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr());
145-
Log *log = GetLog(LLDBLog::Step);
146149
if (target_addr == 0) {
147150
LLDB_LOGF(log, "Got target implementation of 0x0, stopping.");
148151
SetPlanComplete();
@@ -174,13 +177,25 @@ bool AppleThreadPlanStepThroughObjCTrampoline::ShouldStop(Event *event_ptr) {
174177
ObjCLanguageRuntime *objc_runtime =
175178
ObjCLanguageRuntime::Get(*GetThread().GetProcess());
176179
assert(objc_runtime != nullptr);
177-
objc_runtime->AddToMethodCache(m_isa_addr, m_sel_addr, target_addr);
178-
LLDB_LOGF(log,
179-
"Adding {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64
180-
"} = addr=0x%" PRIx64 " to cache.",
181-
m_isa_addr, m_sel_addr, target_addr);
182-
183-
// Extract the target address from the value:
180+
if (m_sel_str_addr != LLDB_INVALID_ADDRESS) {
181+
// Cache the string -> implementation and free the string in the target.
182+
Status dealloc_error =
183+
GetThread().GetProcess()->DeallocateMemory(m_sel_str_addr);
184+
// For now just log this:
185+
if (dealloc_error.Fail())
186+
LLDB_LOG(log, "Failed to deallocate the sel str at {0} - error: {1}",
187+
m_sel_str_addr, dealloc_error);
188+
objc_runtime->AddToMethodCache(m_isa_addr, m_sel_str, target_addr);
189+
LLDB_LOG(log,
190+
"Adding \\{isa-addr={0}, sel-addr={1}\\} = addr={2} to cache.",
191+
m_isa_addr, m_sel_str, target_addr);
192+
} else {
193+
objc_runtime->AddToMethodCache(m_isa_addr, m_sel_addr, target_addr);
194+
LLDB_LOGF(log,
195+
"Adding {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64
196+
"} = addr=0x%" PRIx64 " to cache.",
197+
m_isa_addr, m_sel_addr, target_addr);
198+
}
184199

185200
m_run_to_sp = std::make_shared<ThreadPlanRunToAddress>(
186201
GetThread(), target_so_addr, false);

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ class AppleThreadPlanStepThroughObjCTrampoline : public ThreadPlan {
2424
public:
2525
AppleThreadPlanStepThroughObjCTrampoline(
2626
Thread &thread, AppleObjCTrampolineHandler &trampoline_handler,
27-
ValueList &values, lldb::addr_t isa_addr, lldb::addr_t sel_addr);
27+
ValueList &values, lldb::addr_t isa_addr, lldb::addr_t sel_addr,
28+
lldb::addr_t sel_str_addr, llvm::StringRef sel_str);
2829

2930
~AppleThreadPlanStepThroughObjCTrampoline() override;
3031

@@ -70,6 +71,13 @@ class AppleThreadPlanStepThroughObjCTrampoline : public ThreadPlan {
7071
FunctionCaller *m_impl_function; /// This is a pointer to a impl function that
7172
/// is owned by the client that pushes this
7273
/// plan.
74+
lldb::addr_t m_sel_str_addr; /// If this is not LLDB_INVALID_ADDRESS then it
75+
/// is the address we wrote the selector string
76+
/// to. We need to deallocate it when the
77+
/// function call is done.
78+
std::string m_sel_str; /// This is the string we wrote to memory - we
79+
/// use it for caching, but only if
80+
/// m_sel_str_addr is non-null.
7381
};
7482

7583
class AppleThreadPlanStepThroughDirectDispatch: public ThreadPlanStepOut {

lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ char ObjCLanguageRuntime::ID = 0;
3737
ObjCLanguageRuntime::~ObjCLanguageRuntime() = default;
3838

3939
ObjCLanguageRuntime::ObjCLanguageRuntime(Process *process)
40-
: LanguageRuntime(process), m_impl_cache(),
40+
: LanguageRuntime(process), m_impl_cache(), m_impl_str_cache(),
4141
m_has_new_literals_and_indexing(eLazyBoolCalculate),
4242
m_isa_to_descriptor(), m_hash_to_isa_map(), m_type_size_cache(),
4343
m_isa_to_descriptor_stop_id(UINT32_MAX), m_complete_class_cache(),
@@ -75,6 +75,18 @@ void ObjCLanguageRuntime::AddToMethodCache(lldb::addr_t class_addr,
7575
ClassAndSel(class_addr, selector), impl_addr));
7676
}
7777

78+
void ObjCLanguageRuntime::AddToMethodCache(lldb::addr_t class_addr,
79+
llvm::StringRef sel_str,
80+
lldb::addr_t impl_addr) {
81+
Log *log = GetLog(LLDBLog::Step);
82+
83+
LLDB_LOG(log, "Caching: class {0} selector {1} implementation {2}.",
84+
class_addr, sel_str, impl_addr);
85+
86+
m_impl_str_cache.insert(std::pair<ClassAndSelStr, lldb::addr_t>(
87+
ClassAndSelStr(class_addr, sel_str), impl_addr));
88+
}
89+
7890
lldb::addr_t ObjCLanguageRuntime::LookupInMethodCache(lldb::addr_t class_addr,
7991
lldb::addr_t selector) {
8092
MsgImplMap::iterator pos, end = m_impl_cache.end();
@@ -84,6 +96,15 @@ lldb::addr_t ObjCLanguageRuntime::LookupInMethodCache(lldb::addr_t class_addr,
8496
return LLDB_INVALID_ADDRESS;
8597
}
8698

99+
lldb::addr_t ObjCLanguageRuntime::LookupInMethodCache(lldb::addr_t class_addr,
100+
llvm::StringRef sel_str) {
101+
MsgImplStrMap::iterator pos, end = m_impl_str_cache.end();
102+
pos = m_impl_str_cache.find(ClassAndSelStr(class_addr, sel_str));
103+
if (pos != end)
104+
return (*pos).second;
105+
return LLDB_INVALID_ADDRESS;
106+
}
107+
87108
lldb::TypeSP
88109
ObjCLanguageRuntime::LookupInCompleteClassCache(ConstString &name) {
89110
CompleteClassMap::iterator complete_class_iter =

lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "lldb/Symbol/CompilerType.h"
2323
#include "lldb/Symbol/Type.h"
2424
#include "lldb/Target/LanguageRuntime.h"
25+
#include "lldb/Utility/ConstString.h"
2526
#include "lldb/lldb-private.h"
2627

2728
class CommandObjectObjC_ClassTable_Dump;
@@ -242,11 +243,19 @@ class ObjCLanguageRuntime : public LanguageRuntime {
242243

243244
virtual bool HasReadObjCLibrary() = 0;
244245

246+
// These two methods actually use different caches. The only time we'll
247+
// cache a sel_str is if we found a "selector specific stub" for the selector
248+
// and conversely we only add to the SEL cache if we saw a regular dispatch.
245249
lldb::addr_t LookupInMethodCache(lldb::addr_t class_addr, lldb::addr_t sel);
250+
lldb::addr_t LookupInMethodCache(lldb::addr_t class_addr,
251+
llvm::StringRef sel_str);
246252

247253
void AddToMethodCache(lldb::addr_t class_addr, lldb::addr_t sel,
248254
lldb::addr_t impl_addr);
249255

256+
void AddToMethodCache(lldb::addr_t class_addr, llvm::StringRef sel_str,
257+
lldb::addr_t impl_addr);
258+
250259
TypeAndOrName LookupInClassNameCache(lldb::addr_t class_addr);
251260

252261
void AddToClassNameCache(lldb::addr_t class_addr, const char *name,
@@ -343,20 +352,22 @@ class ObjCLanguageRuntime : public LanguageRuntime {
343352
}
344353

345354
private:
346-
// We keep a map of <Class,Selector>->Implementation so we don't have to call
347-
// the resolver function over and over.
355+
// We keep two maps of <Class,Selector>->Implementation so we don't have
356+
// to call the resolver function over and over.
357+
// The first comes from regular obj_msgSend type dispatch, and maps the
358+
// class + uniqued SEL value to an implementation.
359+
// The second comes from the "selector-specific stubs", which are always
360+
// of the form _objc_msgSend$SelectorName, so we don't know the uniqued
361+
// selector, only the string name.
348362

349363
// FIXME: We need to watch for the loading of Protocols, and flush the cache
350364
// for any
351365
// class that we see so changed.
352366

353367
struct ClassAndSel {
354-
ClassAndSel() {
355-
sel_addr = LLDB_INVALID_ADDRESS;
356-
class_addr = LLDB_INVALID_ADDRESS;
357-
}
368+
ClassAndSel() = default;
358369

359-
ClassAndSel(lldb::addr_t in_sel_addr, lldb::addr_t in_class_addr)
370+
ClassAndSel(lldb::addr_t in_class_addr, lldb::addr_t in_sel_addr)
360371
: class_addr(in_class_addr), sel_addr(in_sel_addr) {}
361372

362373
bool operator==(const ClassAndSel &rhs) {
@@ -379,18 +390,43 @@ class ObjCLanguageRuntime : public LanguageRuntime {
379390
}
380391
}
381392

382-
lldb::addr_t class_addr;
383-
lldb::addr_t sel_addr;
393+
lldb::addr_t class_addr = LLDB_INVALID_ADDRESS;
394+
lldb::addr_t sel_addr = LLDB_INVALID_ADDRESS;
395+
};
396+
397+
struct ClassAndSelStr {
398+
ClassAndSelStr() = default;
399+
400+
ClassAndSelStr(lldb::addr_t in_class_addr, llvm::StringRef in_sel_name)
401+
: class_addr(in_class_addr), sel_name(in_sel_name) {}
402+
403+
bool operator==(const ClassAndSelStr &rhs) {
404+
return class_addr == rhs.class_addr && sel_name == rhs.sel_name;
405+
}
406+
407+
bool operator<(const ClassAndSelStr &rhs) const {
408+
if (class_addr < rhs.class_addr)
409+
return true;
410+
else if (class_addr > rhs.class_addr)
411+
return false;
412+
else
413+
return ConstString::Compare(sel_name, rhs.sel_name);
414+
}
415+
416+
lldb::addr_t class_addr = LLDB_INVALID_ADDRESS;
417+
ConstString sel_name;
384418
};
385419

386420
typedef std::map<ClassAndSel, lldb::addr_t> MsgImplMap;
421+
typedef std::map<ClassAndSelStr, lldb::addr_t> MsgImplStrMap;
387422
typedef std::map<ObjCISA, ClassDescriptorSP> ISAToDescriptorMap;
388423
typedef std::multimap<uint32_t, ObjCISA> HashToISAMap;
389424
typedef ISAToDescriptorMap::iterator ISAToDescriptorIterator;
390425
typedef HashToISAMap::iterator HashToISAIterator;
391426
typedef ThreadSafeDenseMap<void *, uint64_t> TypeSizeCache;
392427

393428
MsgImplMap m_impl_cache;
429+
MsgImplStrMap m_impl_str_cache;
394430
LazyBool m_has_new_literals_and_indexing;
395431
ISAToDescriptorMap m_isa_to_descriptor;
396432
HashToISAMap m_hash_to_isa_map;

lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCKVO.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def cleanup():
5656
' 21 key/value pairs'
5757
])
5858

59-
lldbutil.run_break_set_by_regexp(self, 'setAtoms')
59+
lldbutil.run_break_set_by_symbol(self, '-[Molecule setAtoms:]')
6060

6161
self.runCmd("continue")
6262
self.expect("frame variable _cmd", substrs=['setAtoms:'])

0 commit comments

Comments
 (0)