Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[lldb] Avoid duplicate vdso modules when opening core files
When opening core files (and also in some other situations) we could end up with two vdso modules. This could happen because the vdso module is very special, and over the years, we have accumulated various ways to load it. In D10800, we added one mechanism for loading it, which took the form of a generic load-from-memory capability. Unfortunately loading an elf file from memory is not possible (because the loader never loads the entire file), and our attempts to do so were causing crashes. So, in D34352, we partially reverted D10800 and implemented a custom mechanism specific to the vdso. Unfortunately, enough of D10800 remained such that, under the right circumstances, it could end up loading a second (non-functional) copy of the vdso module. This happened when the process plugin did not support the extended MemoryRegionInfo query (added in D22219, to workaround a different bug), which meant that the loader plugin was not able to recognise that the linux-vdso.so.1 module (this is how the loader calls it) is in fact the same as the [vdso] module (the name used in /proc/$PID/maps) we loaded before. This typically happened in a core file, as they don't store this kind of information. This patch fixes the issue by completing the revert of D10800 -- the memory loading code is removed completely. It also reduces the scope of the hackaround introduced in D22219 -- it isn't completely sound and is only relevant for fairly old (but still supported) versions of android. I added the memory loading logic to the wasm dynamic loader, which has since appeared and is relying on this feature (it even has a test). As far as I can tell loading wasm modules from memory is possible and reliable. MachO memory loading is not affected by this patch, as it uses a completely different code path. Since the scenarios/patches I described came without test cases, I have created two new gdb-client tests cases for them. They're not particularly readable, but right now, this is the best way we can simulate the behavior (bugs) of a particular dynamic linker. Differential Revision: https://reviews.llvm.org/D122660
- Loading branch information
Showing
10 changed files
with
268 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
lldb/test/API/functionalities/gdb_remote_client/TestGdbClientModuleLoad.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import lldb | ||
from lldbsuite.test.lldbtest import * | ||
from lldbsuite.test.decorators import * | ||
from lldbsuite.test.gdbclientutils import * | ||
from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase | ||
from lldbsuite.support import seven | ||
|
||
class MyResponder(MockGDBServerResponder): | ||
""" | ||
A responder which simulates a process with a single shared library loaded. | ||
Its parameters allow configuration of various properties of the library. | ||
""" | ||
|
||
def __init__(self, testcase, triple, library_name, auxv_entry, region_info): | ||
MockGDBServerResponder.__init__(self) | ||
self.testcase = testcase | ||
self._triple = triple | ||
self._library_name = library_name | ||
self._auxv_entry = auxv_entry | ||
self._region_info = region_info | ||
|
||
def qSupported(self, client_supported): | ||
return (super().qSupported(client_supported) + | ||
";qXfer:auxv:read+;qXfer:libraries-svr4:read+") | ||
|
||
def qXferRead(self, obj, annex, offset, length): | ||
if obj == "features" and annex == "target.xml": | ||
return """<?xml version="1.0"?> | ||
<target version="1.0"> | ||
<architecture>i386:x86-64</architecture> | ||
<feature name="org.gnu.gdb.i386.core"> | ||
<reg name="rip" bitsize="64" regnum="0" type="code_ptr" group="general"/> | ||
</feature> | ||
</target>""", False | ||
elif obj == "auxv": | ||
# 0x09 = AT_ENTRY, which lldb uses to compute the load bias of the | ||
# main binary. | ||
return hex_decode_bytes(self._auxv_entry + | ||
"09000000000000000000ee000000000000000000000000000000000000000000"), False | ||
elif obj == "libraries-svr4": | ||
return """<?xml version="1.0"?> | ||
<library-list-svr4 version="1.0"> | ||
<library name="%s" lm="0xdeadbeef" l_addr="0xef0000" l_ld="0xdeadbeef"/> | ||
</library-list-svr4>""" % self._library_name, False | ||
else: | ||
return None, False | ||
|
||
def qfThreadInfo(self): | ||
return "m47" | ||
|
||
def qsThreadInfo(self): | ||
return "l" | ||
|
||
def qProcessInfo(self): | ||
return "pid:47;ptrsize:8;endian:little;triple:%s;" % hex_encode_bytes(self._triple) | ||
|
||
def setBreakpoint(self, packet): | ||
return "OK" | ||
|
||
def readMemory(self, addr, length): | ||
if addr == 0xee1000: | ||
return "00"*0x30 + "0020ee0000000000" | ||
elif addr == 0xee2000: | ||
return "01000000000000000030ee0000000000dead00000000000000000000000000000000000000000000" | ||
elif addr == 0xef0000: | ||
with open(self.testcase.getBuildArtifact("libmodule_load.so"), "rb") as f: | ||
contents = f.read(-1) | ||
return hex_encode_bytes(seven.bitcast_to_string(contents)) | ||
return ("baadf00d00"*1000)[0:length*2] | ||
|
||
def qMemoryRegionInfo(self, addr): | ||
if addr < 0xee0000: | ||
return "start:0;size:ee0000;" | ||
elif addr < 0xef0000: | ||
return "start:ee0000;size:10000;" | ||
elif addr < 0xf00000: | ||
return "start:ef0000;size:1000;permissions:rx;" + self._region_info | ||
else: | ||
return "start:ef1000;size:ffffffffff10f000" | ||
|
||
class TestGdbClientModuleLoad(GDBRemoteTestBase): | ||
|
||
mydir = TestBase.compute_mydir(__file__) | ||
|
||
@skipIfXmlSupportMissing | ||
def test_android_app_process(self): | ||
""" | ||
This test simulates the scenario where the (android) dynamic linker | ||
reports incorrect file name of the main executable. Lldb uses | ||
qMemoryRegionInfo to get the correct value. | ||
""" | ||
region_info = "name:%s;" % ( | ||
hex_encode_bytes(self.getBuildArtifact("libmodule_load.so"))) | ||
self.server.responder = MyResponder(self, "x86_64-pc-linux-android", | ||
"bogus-name", "", region_info) | ||
self.yaml2obj("module_load.yaml", self.getBuildArtifact("libmodule_load.so")) | ||
target = self.createTarget("module_load.yaml") | ||
|
||
process = self.connect(target) | ||
self.assertTrue(process.IsValid(), "Process is valid") | ||
|
||
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, | ||
[lldb.eStateStopped]) | ||
|
||
self.filecheck("image list", __file__, "-check-prefix=ANDROID") | ||
# ANDROID: [ 0] {{.*}} 0x0000000000ee0000 {{.*}}module_load | ||
# ANDROID: [ 1] {{.*}} 0x0000000000ef0000 {{.*}}libmodule_load.so | ||
|
||
@skipIfXmlSupportMissing | ||
def test_vdso(self): | ||
""" | ||
This test checks vdso loading in the situation where the process does | ||
not have memory region information about the vdso address. This can | ||
happen in core files, as they don't store this data. | ||
We want to check that the vdso is loaded exactly once. | ||
""" | ||
# vdso address | ||
AT_SYSINFO_EHDR = "21000000000000000000ef0000000000" | ||
self.server.responder = MyResponder(self, "x86_64-pc-linux", | ||
"linux-vdso.so.1", AT_SYSINFO_EHDR, "") | ||
self.yaml2obj("module_load.yaml", self.getBuildArtifact("libmodule_load.so")) | ||
target = self.createTarget("module_load.yaml") | ||
|
||
process = self.connect(target) | ||
self.assertTrue(process.IsValid(), "Process is valid") | ||
|
||
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, | ||
[lldb.eStateStopped]) | ||
|
||
self.filecheck("image list", __file__, "-check-prefix=VDSO") | ||
# VDSO: [ 0] {{.*}} 0x0000000000ee0000 {{.*}}module_load | ||
# VDSO: [ 1] {{.*}} 0x0000000000ef0000 {{.*}}[vdso] | ||
self.assertEquals(self.target().GetNumModules(), 2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.