Advanced guessing of rendezvous breakpoint

When rendezvous structure is not initialized we need to set up
rendezvous breakpoint anyway. In this case the code will locate
dynamic loader (interpreter) and look for known function names.

Bug: https://bugs.llvm.org/show_bug.cgi?id=25806
Differential Revision: https://reviews.llvm.org/D41533

llvm-svn: 322209
This commit is contained in:
Eugene Zemtsov 2018-01-10 19:04:36 +00:00
parent 90d96aa107
commit 4c3ea8029e
4 changed files with 147 additions and 64 deletions

View File

@ -17,23 +17,23 @@ class TestBreakpointInGlobalConstructors(TestBase):
mydir = TestBase.compute_mydir(__file__)
NO_DEBUG_INFO_TESTCASE = True
def setUp(self):
TestBase.setUp(self)
def test(self):
self.build()
self.line_foo = line_number('foo.cpp', '// !BR_foo')
self.line_main = line_number('main.cpp', '// !BR_main')
@expectedFailureAll(bugnumber="llvm.org/pr35480", oslist=["linux"])
def test(self):
self.build()
exe = os.path.join(os.getcwd(), "a.out")
self.runCmd("file %s" % exe)
target = self.dbg.CreateTarget("a.out")
self.assertTrue(target, VALID_TARGET)
env= self.registerSharedLibrariesWithTarget(target, ["foo"])
bp_main = lldbutil.run_break_set_by_file_and_line(
self, 'main.cpp', self.line_main)
bp_foo = lldbutil.run_break_set_by_file_and_line(
self, 'foo.cpp', self.line_foo)
self.runCmd("run")
process = target.LaunchSimple(
None, env, self.get_process_working_directory())
self.assertIsNotNone(
lldbutil.get_one_thread_stopped_at_breakpoint_id(

View File

@ -368,7 +368,6 @@ class LoadUnloadTestCase(TestBase):
@skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
@skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently
@unittest2.expectedFailure("llvm.org/pr25806")
def test_static_init_during_load(self):
"""Test that we can set breakpoints correctly in static initializers"""
@ -392,16 +391,6 @@ class LoadUnloadTestCase(TestBase):
'd_init',
'stop reason = breakpoint %d' % d_init_bp_num])
self.runCmd("continue")
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped',
'a_init',
'stop reason = breakpoint %d' % a_init_bp_num])
self.expect("thread backtrace",
substrs=['a_init',
'dlopen',
'main'])
self.runCmd("continue")
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped',
@ -411,3 +400,13 @@ class LoadUnloadTestCase(TestBase):
substrs=['b_init',
'dlopen',
'main'])
self.runCmd("continue")
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped',
'a_init',
'stop reason = breakpoint %d' % a_init_bp_num])
self.expect("thread backtrace",
substrs=['a_init',
'dlopen',
'main'])

View File

@ -79,7 +79,8 @@ DynamicLoaderPOSIXDYLD::DynamicLoaderPOSIXDYLD(Process *process)
: DynamicLoader(process), m_rendezvous(process),
m_load_offset(LLDB_INVALID_ADDRESS), m_entry_point(LLDB_INVALID_ADDRESS),
m_auxv(), m_dyld_bid(LLDB_INVALID_BREAK_ID),
m_vdso_base(LLDB_INVALID_ADDRESS) {}
m_vdso_base(LLDB_INVALID_ADDRESS),
m_interpreter_base(LLDB_INVALID_ADDRESS) {}
DynamicLoaderPOSIXDYLD::~DynamicLoaderPOSIXDYLD() {
if (m_dyld_bid != LLDB_INVALID_BREAK_ID) {
@ -117,7 +118,7 @@ void DynamicLoaderPOSIXDYLD::DidAttach() {
: "<null executable>",
load_offset);
EvalVdsoStatus();
EvalSpecialModulesStatus();
// if we dont have a load address we cant re-base
bool rebase_exec = (load_offset == LLDB_INVALID_ADDRESS) ? false : true;
@ -207,7 +208,7 @@ void DynamicLoaderPOSIXDYLD::DidLaunch() {
executable = GetTargetExecutable();
load_offset = ComputeLoadOffset();
EvalVdsoStatus();
EvalSpecialModulesStatus();
if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) {
ModuleList module_list;
@ -217,7 +218,12 @@ void DynamicLoaderPOSIXDYLD::DidLaunch() {
if (log)
log->Printf("DynamicLoaderPOSIXDYLD::%s about to call ProbeEntry()",
__FUNCTION__);
ProbeEntry();
if (!SetRendezvousBreakpoint()) {
// If we cannot establish rendezvous breakpoint right now
// we'll try again at entry point.
ProbeEntry();
}
m_process->GetTarget().ModulesDidLoad(module_list);
}
@ -329,38 +335,77 @@ bool DynamicLoaderPOSIXDYLD::EntryBreakpointHit(
return false; // Continue running.
}
void DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() {
bool DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() {
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
addr_t break_addr = m_rendezvous.GetBreakAddress();
Target &target = m_process->GetTarget();
if (m_dyld_bid == LLDB_INVALID_BREAK_ID) {
if (log)
log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
" setting rendezvous break address at 0x%" PRIx64,
__FUNCTION__,
m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
break_addr);
Breakpoint *dyld_break =
target.CreateBreakpoint(break_addr, true, false).get();
dyld_break->SetCallback(RendezvousBreakpointHit, this, true);
dyld_break->SetBreakpointKind("shared-library-event");
m_dyld_bid = dyld_break->GetID();
} else {
if (log)
log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
" reusing break id %" PRIu32 ", address at 0x%" PRIx64,
__FUNCTION__,
m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
m_dyld_bid, break_addr);
if (m_dyld_bid != LLDB_INVALID_BREAK_ID) {
LLDB_LOG(log,
"Rendezvous breakpoint breakpoint id {0} for pid {1}"
"is already set.",
m_dyld_bid,
m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
return true;
}
// Make sure our breakpoint is at the right address.
assert(target.GetBreakpointByID(m_dyld_bid)
->FindLocationByAddress(break_addr)
->GetBreakpoint()
.GetID() == m_dyld_bid);
addr_t break_addr;
Target &target = m_process->GetTarget();
BreakpointSP dyld_break;
if (m_rendezvous.IsValid()) {
break_addr = m_rendezvous.GetBreakAddress();
LLDB_LOG(log, "Setting rendezvous break address for pid {0} at {1:x}",
m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
break_addr);
dyld_break = target.CreateBreakpoint(break_addr, true, false);
} else {
LLDB_LOG(log, "Rendezvous structure is not set up yet. "
"Trying to locate rendezvous breakpoint in the interpreter "
"by symbol name.");
ModuleSP interpreter = LoadInterpreterModule();
if (!interpreter) {
LLDB_LOG(log, "Can't find interpreter, rendezvous breakpoint isn't set.");
return false;
}
// Function names from different dynamic loaders that are known
// to be used as rendezvous between the loader and debuggers.
static std::vector<std::string> DebugStateCandidates{
"_dl_debug_state", "rtld_db_dlactivity", "__dl_rtld_db_dlactivity",
"r_debug_state", "_r_debug_state", "_rtld_debug_state",
};
FileSpecList containingModules;
containingModules.Append(interpreter->GetFileSpec());
dyld_break = target.CreateBreakpoint(
&containingModules, nullptr /* containingSourceFiles */,
DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC,
0, /* offset */
eLazyBoolNo, /* skip_prologue */
true, /* internal */
false /* request_hardware */);
}
if (dyld_break->GetNumResolvedLocations() != 1) {
LLDB_LOG(
log,
"Rendezvous breakpoint has abnormal number of"
" resolved locations ({0}) in pid {1}. It's supposed to be exactly 1.",
dyld_break->GetNumResolvedLocations(),
m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
target.RemoveBreakpointByID(dyld_break->GetID());
return false;
}
BreakpointLocationSP location = dyld_break->GetLocationAtIndex(0);
LLDB_LOG(log,
"Successfully set rendezvous breakpoint at address {0:x} "
"for pid {1}",
location->GetLoadAddress(),
m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
dyld_break->SetCallback(RendezvousBreakpointHit, this, true);
dyld_break->SetBreakpointKind("shared-library-event");
m_dyld_bid = dyld_break->GetID();
return true;
}
bool DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(
@ -485,7 +530,7 @@ DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread,
return thread_plan_sp;
}
void DynamicLoaderPOSIXDYLD::LoadVDSO(ModuleList &modules) {
void DynamicLoaderPOSIXDYLD::LoadVDSO() {
if (m_vdso_base == LLDB_INVALID_ADDRESS)
return;
@ -506,13 +551,38 @@ void DynamicLoaderPOSIXDYLD::LoadVDSO(ModuleList &modules) {
}
}
ModuleSP DynamicLoaderPOSIXDYLD::LoadInterpreterModule() {
if (m_interpreter_base == LLDB_INVALID_ADDRESS)
return nullptr;
MemoryRegionInfo info;
Target &target = m_process->GetTarget();
Status status = m_process->GetMemoryRegionInfo(m_interpreter_base, info);
if (status.Fail() || info.GetMapped() != MemoryRegionInfo::eYes ||
info.GetName().IsEmpty()) {
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
LLDB_LOG(log, "Failed to get interpreter region info: {0}", status);
return nullptr;
}
FileSpec file(info.GetName().GetCString(), false);
ModuleSpec module_spec(file, target.GetArchitecture());
if (ModuleSP module_sp = target.GetSharedModule(module_spec)) {
UpdateLoadedSections(module_sp, LLDB_INVALID_ADDRESS, m_interpreter_base,
false);
return module_sp;
}
return nullptr;
}
void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() {
DYLDRendezvous::iterator I;
DYLDRendezvous::iterator E;
ModuleList module_list;
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
if (!m_rendezvous.Resolve()) {
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
if (log)
log->Printf("DynamicLoaderPOSIXDYLD::%s unable to resolve POSIX DYLD "
"rendezvous address",
@ -524,7 +594,7 @@ void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() {
// that ourselves here.
ModuleSP executable = GetTargetExecutable();
m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress();
LoadVDSO(module_list);
LoadVDSO();
std::vector<FileSpec> module_names;
for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I)
@ -536,6 +606,8 @@ void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() {
ModuleSP module_sp =
LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true);
if (module_sp.get()) {
LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}",
I->file_spec.GetFilename());
module_list.Append(module_sp);
} else {
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
@ -575,11 +647,14 @@ addr_t DynamicLoaderPOSIXDYLD::ComputeLoadOffset() {
return m_load_offset;
}
void DynamicLoaderPOSIXDYLD::EvalVdsoStatus() {
AuxVector::iterator I = m_auxv->FindEntry(AuxVector::AUXV_AT_SYSINFO_EHDR);
if (I != m_auxv->end())
void DynamicLoaderPOSIXDYLD::EvalSpecialModulesStatus() {
auto I = m_auxv->FindEntry(AuxVector::AUXV_AT_SYSINFO_EHDR);
if (I != m_auxv->end() && I->value != 0)
m_vdso_base = I->value;
I = m_auxv->FindEntry(AuxVector::AUXV_AT_BASE);
if (I != m_auxv->end() && I->value != 0)
m_interpreter_base = I->value;
}
addr_t DynamicLoaderPOSIXDYLD::GetEntryPoint() {

View File

@ -85,13 +85,17 @@ protected:
/// mapped to the address space
lldb::addr_t m_vdso_base;
/// Contains AT_BASE, which means a dynamic loader has been
/// mapped to the address space
lldb::addr_t m_interpreter_base;
/// Loaded module list. (link map for each module)
std::map<lldb::ModuleWP, lldb::addr_t, std::owner_less<lldb::ModuleWP>>
m_loaded_modules;
/// Enables a breakpoint on a function called by the runtime
/// If possible sets a breakpoint on a function called by the runtime
/// linker each time a module is loaded or unloaded.
virtual void SetRendezvousBreakpoint();
bool SetRendezvousBreakpoint();
/// Callback routine which updates the current list of loaded modules based
/// on the information supplied by the runtime linker.
@ -138,7 +142,11 @@ protected:
/// of all dependent modules.
virtual void LoadAllCurrentModules();
void LoadVDSO(lldb_private::ModuleList &modules);
void LoadVDSO();
// Loading an interpreter module (if present) assumming m_interpreter_base
// already points to its base address.
lldb::ModuleSP LoadInterpreterModule();
/// Computes a value for m_load_offset returning the computed address on
/// success and LLDB_INVALID_ADDRESS on failure.
@ -148,9 +156,10 @@ protected:
/// success and LLDB_INVALID_ADDRESS on failure.
lldb::addr_t GetEntryPoint();
/// Evaluate if Aux vectors contain vDSO information
/// Evaluate if Aux vectors contain vDSO and LD information
/// in case they do, read and assign the address to m_vdso_base
void EvalVdsoStatus();
/// and m_interpreter_base.
void EvalSpecialModulesStatus();
/// Loads Module from inferior process.
void ResolveExecutableModule(lldb::ModuleSP &module_sp);