forked from OSchip/llvm-project
[debugserver] Fix LC_BUILD_VERSION load command handling.
Summary: In one of the 2 places the LC_BUILD_VERSION load command is handled, there is a bug preventing us from actually handling them (the address where to read the load command was not updated). This patch factors reading the deployment target load commands into a helper and adds testing for the 2 code paths calling the helper. The testing is a little bit complicated because the only times those load commands matter is when debugging a simulator process. I added a new decorator to check that a specific SDK is available. The actual testing was fairly easy once I knew how to run a simulated process. Reviewers: jasonmolenda, labath Subscribers: lldb-commits Differential Revision: https://reviews.llvm.org/D45298 llvm-svn: 329374
This commit is contained in:
parent
1e989deadd
commit
cd90f878d4
|
@ -344,6 +344,28 @@ def no_debug_info_test(func):
|
|||
wrapper.__no_debug_info_test__ = True
|
||||
return wrapper
|
||||
|
||||
def apple_simulator_test(platform):
|
||||
"""
|
||||
Decorate the test as a test requiring a simulator for a specific platform.
|
||||
|
||||
Consider that a simulator is available if you have the corresponding SDK installed.
|
||||
The SDK identifiers for simulators are iphonesimulator, appletvsimulator, watchsimulator
|
||||
"""
|
||||
def should_skip_simulator_test():
|
||||
if lldbplatformutil.getHostPlatform() != 'darwin':
|
||||
return "simulator tests are run only on darwin hosts"
|
||||
try:
|
||||
DEVNULL = open(os.devnull, 'w')
|
||||
output = subprocess.check_output(["xcodebuild", "-showsdks"], stderr=DEVNULL)
|
||||
if re.search('%ssimulator' % platform, output):
|
||||
return None
|
||||
else:
|
||||
return "%s simulator is not supported on this system." % platform
|
||||
except subprocess.CalledProcessError:
|
||||
return "%s is not supported on this system (xcodebuild failed)." % feature
|
||||
|
||||
return skipTestIfFn(should_skip_simulator_test)
|
||||
|
||||
|
||||
def debugserver_test(func):
|
||||
"""Decorate the item as a debugserver test."""
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
from __future__ import print_function
|
||||
|
||||
|
||||
import gdbremote_testcase
|
||||
import lldbgdbserverutils
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
import json
|
||||
|
||||
class TestAppleSimulatorOSType(gdbremote_testcase.GdbRemoteTestCaseBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
def check_simulator_ostype(self, sdk, platform_names, arch='x86_64'):
|
||||
sim_devices_str = subprocess.check_output(['xcrun', 'simctl', 'list',
|
||||
'-j', 'devices'])
|
||||
sim_devices = json.loads(sim_devices_str)['devices']
|
||||
# Find an available simulator for the requested platform
|
||||
deviceUDID = None
|
||||
for (runtime,devices) in sim_devices.items():
|
||||
if not any(p in runtime.lower() for p in platform_names):
|
||||
continue
|
||||
for device in devices:
|
||||
if device['availability'] != '(available)':
|
||||
continue
|
||||
deviceUDID = device['udid']
|
||||
break
|
||||
if deviceUDID != None:
|
||||
break
|
||||
|
||||
# Launch the process using simctl
|
||||
self.assertIsNotNone(deviceUDID)
|
||||
exe_name = 'test_simulator_platform_{}'.format(platform_names[0])
|
||||
sdkroot = subprocess.check_output(['xcrun', '--show-sdk-path', '--sdk',
|
||||
sdk])
|
||||
self.build(dictionary={ 'EXE': exe_name, 'SDKROOT': sdkroot.strip(),
|
||||
'ARCH': arch })
|
||||
exe_path = self.getBuildArtifact(exe_name)
|
||||
sim_launcher = subprocess.Popen(['xcrun', 'simctl', 'spawn',
|
||||
deviceUDID, exe_path,
|
||||
'print-pid', 'sleep:10'],
|
||||
stderr=subprocess.PIPE)
|
||||
# Get the PID from the process output
|
||||
pid = None
|
||||
while not pid:
|
||||
stderr = sim_launcher.stderr.readline()
|
||||
if stderr == '':
|
||||
continue
|
||||
m = re.match(r"PID: (.*)", stderr)
|
||||
self.assertIsNotNone(m)
|
||||
pid = int(m.group(1))
|
||||
|
||||
# Launch debug monitor attaching to the simulated process
|
||||
self.init_debugserver_test()
|
||||
server = self.connect_to_debug_monitor(attach_pid=pid)
|
||||
|
||||
# Setup packet sequences
|
||||
self.add_no_ack_remote_stream()
|
||||
self.add_process_info_collection_packets()
|
||||
self.test_sequence.add_log_lines(
|
||||
["read packet: " +
|
||||
"$jGetLoadedDynamicLibrariesInfos:{\"fetch_all_solibs\" : true}]#ce",
|
||||
{"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$",
|
||||
"capture": {1: "dylib_info_raw"}}],
|
||||
True)
|
||||
|
||||
# Run the stream
|
||||
context = self.expect_gdbremote_sequence()
|
||||
self.assertIsNotNone(context)
|
||||
|
||||
# Gather process info response
|
||||
process_info = self.parse_process_info_response(context)
|
||||
self.assertIsNotNone(process_info)
|
||||
|
||||
# Check that ostype is correct
|
||||
self.assertTrue(process_info['ostype'] in platform_names)
|
||||
|
||||
# Now for dylibs
|
||||
dylib_info_raw = context.get("dylib_info_raw")
|
||||
dylib_info = json.loads(self.decode_gdbremote_binary(dylib_info_raw))
|
||||
images = dylib_info['images']
|
||||
|
||||
image_info = None
|
||||
for image in images:
|
||||
if image['pathname'] != exe_path:
|
||||
continue
|
||||
image_info = image
|
||||
break
|
||||
|
||||
self.assertIsNotNone(image_info)
|
||||
self.assertTrue(image['min_version_os_name'] in platform_names)
|
||||
|
||||
|
||||
@apple_simulator_test('iphone')
|
||||
@debugserver_test
|
||||
def test_simulator_ostype_ios(self):
|
||||
self.check_simulator_ostype(sdk='iphonesimulator',
|
||||
platform_names=['iphoneos', 'ios'])
|
||||
|
||||
@apple_simulator_test('appletv')
|
||||
@debugserver_test
|
||||
def test_simulator_ostype_tvos(self):
|
||||
self.check_simulator_ostype(sdk='appletvsimulator',
|
||||
platform_names=['tvos'])
|
||||
|
||||
@apple_simulator_test('watch')
|
||||
@debugserver_test
|
||||
def test_simulator_ostype_watchos(self):
|
||||
self.check_simulator_ostype(sdk='watchsimulator',
|
||||
platform_names=['watchos'], arch='i386')
|
|
@ -48,6 +48,8 @@ static const char *const THREAD_COMMAND_NEW = "new";
|
|||
static const char *const THREAD_COMMAND_PRINT_IDS = "print-ids";
|
||||
static const char *const THREAD_COMMAND_SEGFAULT = "segfault";
|
||||
|
||||
static const char *const PRINT_PID_COMMAND = "print-pid";
|
||||
|
||||
static bool g_print_thread_ids = false;
|
||||
static pthread_mutex_t g_print_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool g_threads_do_segfault = false;
|
||||
|
@ -61,6 +63,10 @@ static char g_message[256];
|
|||
static volatile char g_c1 = '0';
|
||||
static volatile char g_c2 = '1';
|
||||
|
||||
static void print_pid() {
|
||||
fprintf(stderr, "PID: %d\n", getpid());
|
||||
}
|
||||
|
||||
static void print_thread_id() {
|
||||
// Put in the right magic here for your platform to spit out the thread id (tid)
|
||||
// that debugserver/lldb-gdbserver would see as a TID. Otherwise, let the else
|
||||
|
@ -349,6 +355,8 @@ int main(int argc, char **argv) {
|
|||
// At this point we don't do anything else with threads.
|
||||
// Later use thread index and send command to thread.
|
||||
}
|
||||
} else if (std::strstr(argv[i], PRINT_PID_COMMAND)) {
|
||||
print_pid();
|
||||
} else {
|
||||
// Treat the argument as text for stdout.
|
||||
printf("%s\n", argv[i]);
|
||||
|
|
|
@ -1426,6 +1426,21 @@ nub_bool_t DNBProcessSharedLibrariesUpdated(nub_process_t pid) {
|
|||
return false;
|
||||
}
|
||||
|
||||
const char *DNBGetDeploymentInfo(nub_process_t pid,
|
||||
const struct load_command& lc,
|
||||
uint64_t load_command_address,
|
||||
uint32_t& major_version,
|
||||
uint32_t& minor_version,
|
||||
uint32_t& patch_version) {
|
||||
MachProcessSP procSP;
|
||||
if (GetProcessSP(pid, procSP))
|
||||
return procSP->GetDeploymentInfo(lc, load_command_address,
|
||||
major_version, minor_version,
|
||||
patch_version);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Get the current shared library information for a process. Only return
|
||||
// the shared libraries that have changed since the last shared library
|
||||
|
|
|
@ -127,6 +127,12 @@ nub_bool_t DNBProcessSharedLibrariesUpdated(nub_process_t pid) DNB_EXPORT;
|
|||
nub_size_t
|
||||
DNBProcessGetSharedLibraryInfo(nub_process_t pid, nub_bool_t only_changed,
|
||||
DNBExecutableImageInfo **image_infos) DNB_EXPORT;
|
||||
const char *DNBGetDeploymentInfo(nub_process_t pid,
|
||||
const struct load_command& lc,
|
||||
uint64_t load_command_address,
|
||||
uint32_t& major_version,
|
||||
uint32_t& minor_version,
|
||||
uint32_t& patch_version);
|
||||
nub_bool_t DNBProcessSetNameToAddressCallback(nub_process_t pid,
|
||||
DNBCallbackNameToAddress callback,
|
||||
void *baton) DNB_EXPORT;
|
||||
|
|
|
@ -246,7 +246,10 @@ public:
|
|||
uint64_t plo_pthread_tsd_base_address_offset,
|
||||
uint64_t plo_pthread_tsd_base_offset,
|
||||
uint64_t plo_pthread_tsd_entry_size);
|
||||
|
||||
const char *
|
||||
GetDeploymentInfo(const struct load_command&, uint64_t load_command_address,
|
||||
uint32_t& major_version, uint32_t& minor_version,
|
||||
uint32_t& patch_version);
|
||||
bool GetMachOInformationFromMemory(nub_addr_t mach_o_header_addr,
|
||||
int wordsize,
|
||||
struct mach_o_information &inf);
|
||||
|
|
|
@ -572,6 +572,68 @@ nub_addr_t MachProcess::GetTSDAddressForThread(
|
|||
plo_pthread_tsd_entry_size);
|
||||
}
|
||||
|
||||
|
||||
const char *MachProcess::GetDeploymentInfo(const struct load_command& lc,
|
||||
uint64_t load_command_address,
|
||||
uint32_t& major_version,
|
||||
uint32_t& minor_version,
|
||||
uint32_t& patch_version) {
|
||||
uint32_t cmd = lc.cmd & ~LC_REQ_DYLD;
|
||||
bool lc_cmd_known =
|
||||
cmd == LC_VERSION_MIN_IPHONEOS || cmd == LC_VERSION_MIN_MACOSX ||
|
||||
cmd == LC_VERSION_MIN_TVOS || cmd == LC_VERSION_MIN_WATCHOS;
|
||||
|
||||
if (lc_cmd_known) {
|
||||
struct version_min_command vers_cmd;
|
||||
if (ReadMemory(load_command_address, sizeof(struct version_min_command),
|
||||
&vers_cmd) != sizeof(struct version_min_command)) {
|
||||
return nullptr;
|
||||
}
|
||||
major_version = vers_cmd.sdk >> 16;
|
||||
minor_version = (vers_cmd.sdk >> 8) & 0xffu;
|
||||
patch_version = vers_cmd.sdk & 0xffu;
|
||||
|
||||
switch (cmd) {
|
||||
case LC_VERSION_MIN_IPHONEOS:
|
||||
return "iphoneos";
|
||||
case LC_VERSION_MIN_MACOSX:
|
||||
return "macosx";
|
||||
case LC_VERSION_MIN_TVOS:
|
||||
return "tvos";
|
||||
case LC_VERSION_MIN_WATCHOS:
|
||||
return "watchos";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#if defined (LC_BUILD_VERSION)
|
||||
if (cmd == LC_BUILD_VERSION) {
|
||||
struct build_version_command build_vers;
|
||||
if (ReadMemory(load_command_address, sizeof(struct build_version_command),
|
||||
&build_vers) != sizeof(struct build_version_command)) {
|
||||
return nullptr;
|
||||
}
|
||||
major_version = build_vers.sdk >> 16;;
|
||||
minor_version = (build_vers.sdk >> 8) & 0xffu;
|
||||
patch_version = build_vers.sdk & 0xffu;
|
||||
|
||||
switch (build_vers.platform) {
|
||||
case PLATFORM_MACOS:
|
||||
return "macosx";
|
||||
case PLATFORM_IOS:
|
||||
return "iphoneos";
|
||||
case PLATFORM_TVOS:
|
||||
return "tvos";
|
||||
case PLATFORM_WATCHOS:
|
||||
return "watchos";
|
||||
case PLATFORM_BRIDGEOS:
|
||||
return "bridgeos";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Given an address, read the mach-o header and load commands out of memory to
|
||||
// fill in
|
||||
// the mach_o_information "inf" object.
|
||||
|
@ -670,91 +732,22 @@ bool MachProcess::GetMachOInformationFromMemory(
|
|||
sizeof(struct uuid_command))
|
||||
uuid_copy(inf.uuid, uuidcmd.uuid);
|
||||
}
|
||||
bool lc_cmd_known =
|
||||
lc.cmd == LC_VERSION_MIN_IPHONEOS || lc.cmd == LC_VERSION_MIN_MACOSX;
|
||||
#if defined(LC_VERSION_MIN_TVOS)
|
||||
lc_cmd_known |= lc.cmd == LC_VERSION_MIN_TVOS;
|
||||
#endif
|
||||
#if defined(LC_VERSION_MIN_WATCHOS)
|
||||
lc_cmd_known |= lc.cmd == LC_VERSION_MIN_WATCHOS;
|
||||
#endif
|
||||
if (lc_cmd_known) {
|
||||
struct version_min_command vers_cmd;
|
||||
if (ReadMemory(load_cmds_p, sizeof(struct version_min_command),
|
||||
&vers_cmd) != sizeof(struct version_min_command)) {
|
||||
return false;
|
||||
}
|
||||
switch (lc.cmd) {
|
||||
case LC_VERSION_MIN_IPHONEOS:
|
||||
inf.min_version_os_name = "iphoneos";
|
||||
break;
|
||||
case LC_VERSION_MIN_MACOSX:
|
||||
inf.min_version_os_name = "macosx";
|
||||
break;
|
||||
#if defined(LC_VERSION_MIN_TVOS)
|
||||
case LC_VERSION_MIN_TVOS:
|
||||
inf.min_version_os_name = "tvos";
|
||||
break;
|
||||
#endif
|
||||
#if defined(LC_VERSION_MIN_WATCHOS)
|
||||
case LC_VERSION_MIN_WATCHOS:
|
||||
inf.min_version_os_name = "watchos";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
uint32_t xxxx = vers_cmd.sdk >> 16;
|
||||
uint32_t yy = (vers_cmd.sdk >> 8) & 0xffu;
|
||||
uint32_t zz = vers_cmd.sdk & 0xffu;
|
||||
|
||||
uint32_t major_version, minor_version, patch_version;
|
||||
if (const char *platform = GetDeploymentInfo(lc, load_cmds_p,
|
||||
major_version, minor_version,
|
||||
patch_version)) {
|
||||
inf.min_version_os_name = platform;
|
||||
inf.min_version_os_version = "";
|
||||
inf.min_version_os_version += std::to_string(xxxx);
|
||||
inf.min_version_os_version += std::to_string(major_version);
|
||||
inf.min_version_os_version += ".";
|
||||
inf.min_version_os_version += std::to_string(yy);
|
||||
if (zz != 0) {
|
||||
inf.min_version_os_version += std::to_string(minor_version);
|
||||
if (patch_version != 0) {
|
||||
inf.min_version_os_version += ".";
|
||||
inf.min_version_os_version += std::to_string(zz);
|
||||
inf.min_version_os_version += std::to_string(patch_version);
|
||||
}
|
||||
}
|
||||
#if defined (LC_BUILD_VERSION)
|
||||
if (lc.cmd == LC_BUILD_VERSION)
|
||||
{
|
||||
struct build_version_command build_vers;
|
||||
if (ReadMemory(load_cmds_p, sizeof(struct build_version_command),
|
||||
&build_vers) != sizeof(struct build_version_command)) {
|
||||
return false;
|
||||
}
|
||||
switch (build_vers.platform)
|
||||
{
|
||||
case PLATFORM_MACOS:
|
||||
inf.min_version_os_name = "macosx";
|
||||
break;
|
||||
case PLATFORM_IOS:
|
||||
inf.min_version_os_name = "iphoneos";
|
||||
break;
|
||||
case PLATFORM_TVOS:
|
||||
inf.min_version_os_name = "tvos";
|
||||
break;
|
||||
case PLATFORM_WATCHOS:
|
||||
inf.min_version_os_name = "watchos";
|
||||
break;
|
||||
case PLATFORM_BRIDGEOS:
|
||||
inf.min_version_os_name = "bridgeos";
|
||||
break;
|
||||
}
|
||||
uint32_t xxxx = build_vers.sdk >> 16;;
|
||||
uint32_t yy = (build_vers.sdk >> 8) & 0xffu;
|
||||
uint32_t zz = build_vers.sdk & 0xffu;
|
||||
inf.min_version_os_version = "";
|
||||
inf.min_version_os_version += std::to_string(xxxx);
|
||||
inf.min_version_os_version += ".";
|
||||
inf.min_version_os_version += std::to_string(yy);
|
||||
if (zz != 0) {
|
||||
inf.min_version_os_version += ".";
|
||||
inf.min_version_os_version += std::to_string(zz);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
load_cmds_p += lc.cmdsize;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -6091,100 +6091,18 @@ rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) {
|
|||
for (uint32_t i = 0; i < mh.ncmds && !os_handled; ++i) {
|
||||
const nub_size_t bytes_read =
|
||||
DNBProcessMemoryRead(pid, load_command_addr, sizeof(lc), &lc);
|
||||
uint32_t raw_cmd = lc.cmd & ~LC_REQ_DYLD;
|
||||
if (bytes_read != sizeof(lc))
|
||||
break;
|
||||
switch (raw_cmd) {
|
||||
case LC_VERSION_MIN_IPHONEOS:
|
||||
os_handled = true;
|
||||
rep << "ostype:ios;";
|
||||
DNBLogThreadedIf(LOG_RNB_PROC,
|
||||
"LC_VERSION_MIN_IPHONEOS -> 'ostype:ios;'");
|
||||
break;
|
||||
|
||||
case LC_VERSION_MIN_MACOSX:
|
||||
uint32_t major_version, minor_version, patch_version;
|
||||
auto *platform = DNBGetDeploymentInfo(pid, lc, load_command_addr,
|
||||
major_version, minor_version,
|
||||
patch_version);
|
||||
if (platform) {
|
||||
os_handled = true;
|
||||
rep << "ostype:macosx;";
|
||||
DNBLogThreadedIf(LOG_RNB_PROC,
|
||||
"LC_VERSION_MIN_MACOSX -> 'ostype:macosx;'");
|
||||
break;
|
||||
|
||||
#if defined(LC_VERSION_MIN_TVOS)
|
||||
case LC_VERSION_MIN_TVOS:
|
||||
os_handled = true;
|
||||
rep << "ostype:tvos;";
|
||||
DNBLogThreadedIf(LOG_RNB_PROC,
|
||||
"LC_VERSION_MIN_TVOS -> 'ostype:tvos;'");
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(LC_VERSION_MIN_WATCHOS)
|
||||
case LC_VERSION_MIN_WATCHOS:
|
||||
os_handled = true;
|
||||
rep << "ostype:watchos;";
|
||||
DNBLogThreadedIf(LOG_RNB_PROC,
|
||||
"LC_VERSION_MIN_WATCHOS -> 'ostype:watchos;'");
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
rep << "ostype:" << platform << ";";
|
||||
break;
|
||||
}
|
||||
load_command_addr = load_command_addr + lc.cmdsize;
|
||||
}
|
||||
|
||||
// Test that the PLATFORM_* defines are available from mach-o/loader.h
|
||||
#if defined (PLATFORM_MACOS)
|
||||
for (uint32_t i = 0; i < mh.ncmds && !os_handled; ++i)
|
||||
{
|
||||
nub_size_t bytes_read =
|
||||
DNBProcessMemoryRead(pid, load_command_addr, sizeof(lc), &lc);
|
||||
uint32_t raw_cmd = lc.cmd & ~LC_REQ_DYLD;
|
||||
if (bytes_read != sizeof(lc))
|
||||
break;
|
||||
|
||||
if (raw_cmd == LC_BUILD_VERSION)
|
||||
{
|
||||
uint32_t platform; // first field of 'struct build_version_command'
|
||||
bytes_read = DNBProcessMemoryRead(pid, load_command_addr + 8, sizeof(platform), &platform);
|
||||
if (bytes_read != sizeof (platform))
|
||||
break;
|
||||
switch (platform)
|
||||
{
|
||||
case PLATFORM_MACOS:
|
||||
os_handled = true;
|
||||
rep << "ostype:macosx;";
|
||||
DNBLogThreadedIf(LOG_RNB_PROC,
|
||||
"LC_BUILD_VERSION PLATFORM_MACOS -> 'ostype:macosx;'");
|
||||
break;
|
||||
case PLATFORM_IOS:
|
||||
os_handled = true;
|
||||
rep << "ostype:ios;";
|
||||
DNBLogThreadedIf(LOG_RNB_PROC,
|
||||
"LC_BUILD_VERSION PLATFORM_IOS -> 'ostype:ios;'");
|
||||
break;
|
||||
case PLATFORM_TVOS:
|
||||
os_handled = true;
|
||||
rep << "ostype:tvos;";
|
||||
DNBLogThreadedIf(LOG_RNB_PROC,
|
||||
"LC_BUILD_VERSION PLATFORM_TVOS -> 'ostype:tvos;'");
|
||||
break;
|
||||
case PLATFORM_WATCHOS:
|
||||
os_handled = true;
|
||||
rep << "ostype:watchos;";
|
||||
DNBLogThreadedIf(LOG_RNB_PROC,
|
||||
"LC_BUILD_VERSION PLATFORM_WATCHOS -> 'ostype:watchos;'");
|
||||
break;
|
||||
case PLATFORM_BRIDGEOS:
|
||||
os_handled = true;
|
||||
rep << "ostype:bridgeos;";
|
||||
DNBLogThreadedIf(LOG_RNB_PROC,
|
||||
"LC_BUILD_VERSION PLATFORM_BRIDGEOS -> 'ostype:bridgeos;'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // PLATFORM_MACOS
|
||||
}
|
||||
#endif // when compiling this on x86 targets
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue