Skip to content

jGetLoadedDynamicLibrariesInfos can inspect machos not yet loaded #5065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lldb/test/API/macosx/unregistered-macho/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
C_SOURCES = main.c

include Makefile.rules
47 changes: 47 additions & 0 deletions lldb/test/API/macosx/unregistered-macho/TestUnregisteredMacho.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Test that debugserver will parse a mach-o in inferior memory even if it's not loaded."""

import os
import re
import subprocess

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil

class TestUnregisteredMacho(TestBase):

# newer debugserver required for jGetLoadedDynamicLibrariesInfos
# to support this
@skipIfOutOfTreeDebugserver
@no_debug_info_test
@skipUnlessDarwin
def test(self):
self.build()
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
self, "// break here", lldb.SBFileSpec("main.c"))

frame = thread.GetFrameAtIndex(0)
macho_buf = frame.GetValueForVariablePath("macho_buf")
macho_addr = macho_buf.GetValueAsUnsigned()
invalid_macho_addr = macho_buf.GetValueAsUnsigned() + 4
gdb_packet = "process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{\"solib_addresses\":[%d]}]'" % macho_addr

# Send the jGetLoadedDynamicLibrariesInfos packet
# to debugserver, asking it to parse the mach-o binary
# at this address and give us the UUID etc, even though
# dyld doesn't think there is a binary at that address.
# We won't get a pathname for the binary (from dyld), but
# we will get to the LC_UUID and include that.
self.expect (gdb_packet, substrs=['"pathname":""', '"uuid":"1B4E28BA-2FA1-11D2-883F-B9A761BDE3FB"'])

no_macho_gdb_packet = "process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{\"solib_addresses\":[%d]}]'" % invalid_macho_addr
self.expect (no_macho_gdb_packet, substrs=['response: {"images":[]}'])

# Test that we get back the information for the properly
# formatted Mach-O binary in memory, but do not get an
# entry for the invalid Mach-O address.
both_gdb_packet = "process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{\"solib_addresses\":[%d,%d]}]'" % (macho_addr, invalid_macho_addr)
self.expect (both_gdb_packet, substrs=['"load_address":%d,' % macho_addr])
self.expect (both_gdb_packet, substrs=['"load_address":%d,' % invalid_macho_addr], matching=False)

63 changes: 63 additions & 0 deletions lldb/test/API/macosx/unregistered-macho/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include <mach-o/loader.h>
#include <mach/machine.h>
#include <stdlib.h>
#include <string.h>
#include <uuid/uuid.h>

int main() {
int size_of_load_cmds =
sizeof(struct segment_command_64) + sizeof(struct uuid_command);
uint8_t *macho_buf =
(uint8_t *)malloc(sizeof(struct mach_header_64) + size_of_load_cmds);
uint8_t *p = macho_buf;
struct mach_header_64 mh;
mh.magic = MH_MAGIC_64;
mh.cputype = CPU_TYPE_ARM64;
mh.cpusubtype = 0;
mh.filetype = MH_EXECUTE;
mh.ncmds = 2;
mh.sizeofcmds = size_of_load_cmds;
mh.flags = MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL | MH_PIE;

memcpy(p, &mh, sizeof(mh));
p += sizeof(mh);

struct segment_command_64 seg;
seg.cmd = LC_SEGMENT_64;
seg.cmdsize = sizeof(seg);
strcpy(seg.segname, "__TEXT");
seg.vmaddr = 0x5000;
seg.vmsize = 0x1000;
seg.fileoff = 0;
seg.filesize = 0;
seg.maxprot = 0;
seg.initprot = 0;
seg.nsects = 0;
seg.flags = 0;

memcpy(p, &seg, sizeof(seg));
p += sizeof(seg);

struct uuid_command uuid;
uuid.cmd = LC_UUID;
uuid.cmdsize = sizeof(uuid);
uuid_clear(uuid.uuid);
uuid_parse("1b4e28ba-2fa1-11d2-883f-b9a761bde3fb", uuid.uuid);

memcpy(p, &uuid, sizeof(uuid));
p += sizeof(uuid);

// If this needs to be debugged, the memory buffer can be written
// to a file with
// (lldb) mem rea -b -o /tmp/t -c `p - macho_buf` macho_buf
// (lldb) platform shell otool -hlv /tmp/t
// to verify that it is well formed.

// And inside lldb, it should be inspectable via
// (lldb) script print(lldb.frame.locals["macho_buf"][0].GetValueAsUnsigned())
// 105553162403968
// (lldb) process plugin packet send
// 'jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[105553162403968]}]'

return 0; // break here
}
4 changes: 3 additions & 1 deletion lldb/tools/debugserver/source/MacOSX/MachProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ class MachProcess {
uint64_t load_address;
uint64_t mod_date; // may not be available - 0 if so
struct mach_o_information macho_info;
bool is_valid_mach_header;

binary_image_information()
: filename(), load_address(INVALID_NUB_ADDRESS), mod_date(0) {}
: filename(), load_address(INVALID_NUB_ADDRESS), mod_date(0),
is_valid_mach_header(false) {}
};

// Child process control
Expand Down
Loading