Skip to content

Commit 833882b

Browse files
committed
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.
1 parent 2a2149c commit 833882b

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
@@ -1554,7 +1554,7 @@ AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoUtilityFunctionImpl(
15541554
if (!utility_fn_or_error) {
15551555
LLDB_LOG_ERROR(
15561556
log, utility_fn_or_error.takeError(),
1557-
"Failed to get utility function for implementation lookup: {0}");
1557+
"Failed to get utility function for dynamic info extractor: {0}");
15581558
return {};
15591559
}
15601560

@@ -1684,7 +1684,7 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::
16841684
if (!utility_fn_or_error) {
16851685
LLDB_LOG_ERROR(
16861686
log, utility_fn_or_error.takeError(),
1687-
"Failed to get utility function for implementation lookup: {0}");
1687+
"Failed to get utility function for shared class info extractor: {0}");
16881688
return nullptr;
16891689
}
16901690

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
@@ -57,7 +57,7 @@ def cleanup():
5757
' 21 key/value pairs'
5858
])
5959

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

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

0 commit comments

Comments
 (0)