| import lldb |
| from lldbsuite.test.lldbtest import * |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.gdbclientutils import * |
| from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase |
| |
| |
| 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): |
| @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.assertEqual(self.target().GetNumModules(), 2) |