forked from OSchip/llvm-project
Make sure the interpreter module was loaded before making checks against it
This issue was introduced in https://reviews.llvm.org/D92187.
The guard I'm changing were is supposed to act when linux is loading the linker for the second time (due to differences in paths like symlinks).
This is done by checking `module_sp != m_interpreter_module.lock()` however this will be true when `m_interpreter_module` wasn't initialized, making linux unload the linker module (the most visible result here is that lldb will stop getting notified about new modules loaded by the process, because it can't set the rendezvous breakpoint again after the stepping over it once).
The `m_interpreter_module` is not getting initialize when it goes through this path: dbfdb139f7/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp (L332)
, which happens when lldb was able to read the address from the dynamic section of the executable.
What I'm not sure about though, is if when we go through this path if we still load the linker twice on linux. If that's the case then it means we need to somehow set the m_interpreter_module instead of the fix I provide here. I've only tested this on Android.
Differential Revision: https://reviews.llvm.org/D96637
This commit is contained in:
parent
7c706aa0d8
commit
a83a825e99
|
@ -622,6 +622,10 @@ def skipUnlessDarwin(func):
|
|||
"""Decorate the item to skip tests that should be skipped on any non Darwin platform."""
|
||||
return skipUnlessPlatform(lldbplatformutil.getDarwinOSTriples())(func)
|
||||
|
||||
def skipUnlessLinux(func):
|
||||
"""Decorate the item to skip tests that should be skipped on any non-Linux platform."""
|
||||
return skipUnlessPlatform(["linux"])(func)
|
||||
|
||||
def skipUnlessTargetAndroid(func):
|
||||
return unittest2.skipUnless(lldbplatformutil.target_is_android(),
|
||||
"requires target to be Android")(func)
|
||||
|
|
|
@ -441,6 +441,7 @@ void DynamicLoaderPOSIXDYLD::RefreshModules() {
|
|||
if (module_sp.get()) {
|
||||
if (module_sp->GetObjectFile()->GetBaseAddress().GetLoadAddress(
|
||||
&m_process->GetTarget()) == m_interpreter_base &&
|
||||
m_interpreter_module.lock() &&
|
||||
module_sp != m_interpreter_module.lock()) {
|
||||
// If this is a duplicate instance of ld.so, unload it. We may end up
|
||||
// with it if we load it via a different path than before (symlink
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
C_SOURCES := main.c
|
||||
LD_EXTRAS := -Wl,-rpath "-Wl,$(shell pwd)"
|
||||
USE_LIBDL := 1
|
||||
|
||||
feature:
|
||||
$(MAKE) -f $(MAKEFILE_RULES) \
|
||||
DYLIB_ONLY=YES DYLIB_NAME=feature DYLIB_C_SOURCES=feature.c
|
||||
all: feature
|
||||
|
||||
include Makefile.rules
|
|
@ -0,0 +1,49 @@
|
|||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
class TestCase(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
def build_launch_and_attach(self):
|
||||
self.build()
|
||||
# launch
|
||||
exe = self.getBuildArtifact("a.out")
|
||||
popen = self.spawnSubprocess(exe)
|
||||
# attach
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
listener = lldb.SBListener("my.attach.listener")
|
||||
error = lldb.SBError()
|
||||
process = target.AttachToProcessWithID(listener, popen.pid, error)
|
||||
self.assertTrue(error.Success() and process, PROCESS_IS_VALID)
|
||||
return process
|
||||
|
||||
def assertModuleIsLoaded(self, module_name):
|
||||
feature_module = self.dbg.GetSelectedTarget().FindModule(lldb.SBFileSpec(module_name))
|
||||
self.assertTrue(feature_module.IsValid(), f"Module {module_name} should be loaded")
|
||||
|
||||
def assertModuleIsNotLoaded(self, module_name):
|
||||
feature_module = self.dbg.GetSelectedTarget().FindModule(lldb.SBFileSpec(module_name))
|
||||
self.assertFalse(feature_module.IsValid(), f"Module {module_name} should not be loaded")
|
||||
|
||||
@skipIfRemote
|
||||
@skipUnlessLinux
|
||||
@no_debug_info_test
|
||||
def test(self):
|
||||
'''
|
||||
This test makes sure that after attach lldb still gets notifications
|
||||
about new modules being loaded by the process
|
||||
'''
|
||||
process = self.build_launch_and_attach()
|
||||
thread = process.GetSelectedThread()
|
||||
self.assertModuleIsNotLoaded("libfeature.so")
|
||||
thread.GetSelectedFrame().EvaluateExpression("flip_to_1_to_continue = 1")
|
||||
# Continue so that dlopen is called.
|
||||
breakpoint = self.target().BreakpointCreateBySourceRegex(
|
||||
"// break after dlopen", lldb.SBFileSpec("main.c"))
|
||||
self.assertNotEqual(breakpoint.GetNumResolvedLocations(), 0)
|
||||
stopped_threads = lldbutil.continue_to_breakpoint(self.process(), breakpoint)
|
||||
self.assertModuleIsLoaded("libfeature.so")
|
|
@ -0,0 +1 @@
|
|||
extern void feature() {}
|
|
@ -0,0 +1,15 @@
|
|||
#include <dlfcn.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
||||
volatile int flip_to_1_to_continue = 0;
|
||||
|
||||
int main() {
|
||||
lldb_enable_attach();
|
||||
while (! flip_to_1_to_continue) // Wait for debugger to attach
|
||||
sleep(1);
|
||||
// dlopen the feature
|
||||
void *feature = dlopen("libfeature.so", RTLD_NOW);
|
||||
assert(feature && "dlopen failed?");
|
||||
return 0; // break after dlopen
|
||||
}
|
Loading…
Reference in New Issue