Skip to content

Commit

Permalink
Advanced guessing of rendezvous breakpoint
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Djuffin committed Jan 10, 2018
1 parent 90d96aa commit 4c3ea80
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 57 deletions.
Expand Up @@ -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(
Expand Down
Expand Up @@ -368,7 +368,6 @@ def test_step_over_load(self):

@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"""

Expand All @@ -395,19 +394,19 @@ def test_static_init_during_load(self):
self.runCmd("continue")
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped',
'a_init',
'stop reason = breakpoint %d' % a_init_bp_num])
'b_init',
'stop reason = breakpoint %d' % b_init_bp_num])
self.expect("thread backtrace",
substrs=['a_init',
substrs=['b_init',
'dlopen',
'main'])

self.runCmd("continue")
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped',
'b_init',
'stop reason = breakpoint %d' % b_init_bp_num])
'a_init',
'stop reason = breakpoint %d' % a_init_bp_num])
self.expect("thread backtrace",
substrs=['b_init',
substrs=['a_init',
'dlopen',
'main'])
149 changes: 112 additions & 37 deletions lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -329,38 +335,77 @@ bool DynamicLoaderPOSIXDYLD::EntryBreakpointHit(
return false; // Continue running.
}

void DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() {
bool DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() {
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
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;
}

addr_t break_addr = m_rendezvous.GetBreakAddress();
addr_t break_addr;
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();
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 {
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);
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;
}

// Make sure our breakpoint is at the right address.
assert(target.GetBreakpointByID(m_dyld_bid)
->FindLocationByAddress(break_addr)
->GetBreakpoint()
.GetID() == m_dyld_bid);
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(
Expand Down Expand Up @@ -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;

Expand All @@ -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",
Expand All @@ -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)
Expand All @@ -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));
Expand Down Expand Up @@ -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() {
Expand Down
Expand Up @@ -85,13 +85,17 @@ class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader {
/// 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.
Expand Down Expand Up @@ -138,7 +142,11 @@ class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader {
/// 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.
Expand All @@ -148,9 +156,10 @@ class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader {
/// 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);
Expand Down

0 comments on commit 4c3ea80

Please sign in to comment.