Upstream macCatalyst support in debugserver and the macOS dynamic loader

plugin.

Unfortunately the test is currently XFAILed because of missing changes
to the clang driver.

Differential Revision: https://reviews.llvm.org/D67124

llvm-svn: 370931
This commit is contained in:
Adrian Prantl 2019-09-04 17:23:15 +00:00
parent 40fe351cf6
commit 2461061168
21 changed files with 259 additions and 48 deletions

View File

@ -895,6 +895,9 @@ public:
bool RemapSourceFile(llvm::StringRef path, std::string &new_path) const;
bool RemapSourceFile(const char *, std::string &) const = delete;
/// Update the ArchSpec to a more specific variant.
bool MergeArchitecture(const ArchSpec &arch_spec);
/// \class LookupInfo Module.h "lldb/Core/Module.h"
/// A class that encapsulates name lookup information.
///

View File

@ -27,6 +27,7 @@ private:
public:
static llvm::VersionTuple GetOSVersion();
static llvm::VersionTuple GetMacCatalystVersion();
static bool GetOSBuildString(std::string &s);
static bool GetOSKernelDescription(std::string &s);
static FileSpec GetProgramFileSpec();

View File

@ -1196,6 +1196,9 @@ public:
/// VersionTuple is returner.
virtual llvm::VersionTuple GetHostOSVersion() { return llvm::VersionTuple(); }
/// \return the macCatalyst version of the host OS.
virtual llvm::VersionTuple GetHostMacCatalystVersion() { return {}; }
/// Get the target object pointer for this module.
///
/// \return

View File

@ -0,0 +1,18 @@
LEVEL = ../../make
C_SOURCES := main.c
LD_EXTRAS := -L. -lfoo
TRIPLE := x86_64-apple-ios13.0-macabi
CFLAGS_EXTRAS := -target $(TRIPLE)
all: libfoo.dylib a.out
lib%.dylib: %.c
$(MAKE) MAKE_DSYM=YES CC=$(CC) \
ARCH=$(ARCH) DSYMUTIL=$(DSYMUTIL) \
BASENAME=$(shell basename $< .c) \
TRIPLE=x86_64-apple-macosx10.15 SDKROOT=$(SDKROOT) \
VPATH=$(SRCDIR) -I $(SRCDIR) -f $(SRCDIR)/dylib.mk all
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,28 @@
# TestMacABImacOSFramework.py
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
import os
import unittest2
class TestMacABImacOSFramework(TestBase):
mydir = TestBase.compute_mydir(__file__)
@skipIf(macos_version=["<", "10.15"])
@skipUnlessDarwin
@skipIfDarwinEmbedded
# There is a Clang driver change missing on llvm.org.
@expectedFailureAll(bugnumber="rdar://problem/54986190>")
def test_macabi(self):
"""Test the x86_64-apple-ios-macabi target linked against a macos dylib"""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here",
lldb.SBFileSpec('main.c'))
self.expect("image list -t -b",
patterns=["x86_64.*-apple-ios.*-macabi a\.out",
"x86_64.*-apple-macosx.* libfoo.dylib[^(]"])
self.expect("fr v s", "Hello MacABI")
self.expect("p s", "Hello MacABI")

View File

@ -0,0 +1,6 @@
LEVEL = ../../make
DYLIB_ONLY := YES
DYLIB_NAME := $(BASENAME)
DYLIB_C_SOURCES := $(DYLIB_NAME).c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,8 @@
#include "foo.h"
void stop() {}
int foo() {
stop();
return 0;
}

View File

@ -0,0 +1 @@
int foo();

View File

@ -0,0 +1,5 @@
#include "foo.h"
int main() {
const char *s = "Hello MacABI!";
return foo(); // break here
}

View File

@ -88,6 +88,7 @@ ifneq "$(TRIPLE)" ""
triple_os_and_version =$(shell echo $(word 3, $(triple_space)) | sed 's/\([a-z]*\)\(.*\)/\1 \2/')
TRIPLE_OS =$(word 1, $(triple_os_and_version))
TRIPLE_VERSION =$(word 2, $(triple_os_and_version))
TRIPLE_ENV =$(word 4, $(triple_space))
ifeq "$(TRIPLE_VENDOR)" "apple"
ifeq "$(TRIPLE_OS)" "ios"
CODESIGN := codesign
@ -99,15 +100,23 @@ ifneq "$(TRIPLE)" ""
TRIPLE_VERSION =$(shell echo $(notdir $(SDKROOT)) | sed -e 's/.*\([0-9]\.[0-9]\).*/\1/')
endif
ARCH_CFLAGS :=-mios-version-min=$(TRIPLE_VERSION) -isysroot "$(SDKROOT)"
else
ifeq "$(TRIPLE_ENV)" "macabi"
SDKROOT = $(shell xcrun --sdk macosx --show-sdk-path)
else
SDKROOT = $(shell xcrun --sdk iphonesimulator --show-sdk-path)
endif
ifeq "$(TRIPLE_VERSION)" ""
TRIPLE_VERSION =$(shell echo $(notdir $(SDKROOT)) | sed -e 's/.*\([0-9]\.[0-9]\).*/\1/')
endif
ifeq "$(TRIPLE_ENV)" "macabi"
ARCH_CFLAGS :=-mios-version-min=$(TRIPLE_VERSION) -isysroot "$(SDKROOT)"
else
ARCH_CFLAGS :=-mios-simulator-version-min=$(TRIPLE_VERSION) -isysroot "$(SDKROOT)"
endif
endif
endif
endif
ifeq "$(TRIPLE_OS)" "watchos"
CODESIGN := codesign
ifeq "$(SDKROOT)" ""

View File

@ -24,6 +24,7 @@ class TestGdbRemoteHostInfo(GdbRemoteTestCaseBase):
"os_build",
"os_kernel",
"os_version",
"maccatalyst_version",
"ptrsize",
"triple",
"vendor",

View File

@ -1634,6 +1634,26 @@ bool Module::RemapSourceFile(llvm::StringRef path,
return m_source_mappings.RemapPath(path, new_path);
}
bool Module::MergeArchitecture(const ArchSpec &arch_spec) {
if (!arch_spec.IsValid())
return false;
LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_MODULES),
"module has arch %s, merging/replacing with arch %s",
m_arch.GetTriple().getTriple().c_str(),
arch_spec.GetTriple().getTriple().c_str());
if (!m_arch.IsCompatibleMatch(arch_spec)) {
// The new architecture is different, we just need to replace it.
return SetArchitecture(arch_spec);
}
// Merge bits from arch_spec into "merged_arch" and set our architecture.
ArchSpec merged_arch(m_arch);
merged_arch.MergeFrom(arch_spec);
// SetArchitecture() is a no-op if m_arch is already valid.
m_arch = ArchSpec();
return SetArchitecture(merged_arch);
}
llvm::VersionTuple Module::GetVersion() {
if (ObjectFile *obj_file = GetObjectFile())
return obj_file->GetVersion();

View File

@ -71,23 +71,32 @@ bool HostInfoMacOSX::GetOSKernelDescription(std::string &s) {
return false;
}
static void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) {
@autoreleasepool {
NSDictionary *version_info =
[NSDictionary dictionaryWithContentsOfFile:
@"/System/Library/CoreServices/SystemVersion.plist"];
NSString *version_value = [version_info objectForKey: Key];
const char *version_str = [version_value UTF8String];
version.tryParse(version_str);
}
}
llvm::VersionTuple HostInfoMacOSX::GetOSVersion() {
static llvm::VersionTuple g_version;
if (g_version.empty()) {
@autoreleasepool {
NSDictionary *version_info = [NSDictionary
dictionaryWithContentsOfFile:
@"/System/Library/CoreServices/SystemVersion.plist"];
NSString *version_value = [version_info objectForKey:@"ProductVersion"];
const char *version_str = [version_value UTF8String];
g_version.tryParse(version_str);
}
}
if (g_version.empty())
ParseOSVersion(g_version, @"ProductVersion");
return g_version;
}
llvm::VersionTuple HostInfoMacOSX::GetMacCatalystVersion() {
static llvm::VersionTuple g_version;
if (g_version.empty())
ParseOSVersion(g_version, @"iOSSupportVersion");
return g_version;
}
FileSpec HostInfoMacOSX::GetProgramFileSpec() {
static FileSpec g_program_filespec;
if (!g_program_filespec) {

View File

@ -100,6 +100,18 @@ ModuleSP DynamicLoaderDarwin::FindTargetModuleForImageInfo(
const ModuleList &target_images = target.GetImages();
ModuleSpec module_spec(image_info.file_spec);
module_spec.GetUUID() = image_info.uuid;
// macCatalyst support: Request matching os/environment.
{
auto &target_triple = target.GetArchitecture().GetTriple();
if (target_triple.getOS() == llvm::Triple::IOS &&
target_triple.getEnvironment() == llvm::Triple::MacABI) {
// Request the macCatalyst variant of frameworks that have both
// a PLATFORM_MACOS and a PLATFORM_MACCATALYST load command.
module_spec.GetArchitecture() = ArchSpec(target_triple);
}
}
ModuleSP module_sp(target_images.FindFirstModule(module_spec));
if (module_sp && !module_spec.GetUUID().IsValid() &&
@ -384,6 +396,10 @@ bool DynamicLoaderDarwin::JSONImageInformationIntoImageInfo(
image_infos[i].os_type = llvm::Triple::WatchOS;
// NEED_BRIDGEOS_TRIPLE else if (os_name == "bridgeos")
// NEED_BRIDGEOS_TRIPLE image_infos[i].os_type = llvm::Triple::BridgeOS;
else if (os_name == "maccatalyst") {
image_infos[i].os_type = llvm::Triple::IOS;
image_infos[i].os_env = llvm::Triple::MacABI;
}
}
if (image->HasKey("min_version_os_sdk")) {
image_infos[i].min_version_os_sdk =
@ -654,6 +670,20 @@ bool DynamicLoaderDarwin::AddModulesUsingImageInfos(
target_images.AppendIfNeeded(image_module_sp);
loaded_module_list.AppendIfNeeded(image_module_sp);
}
// macCataylst support:
// Update the module's platform with the DYLD info.
ArchSpec dyld_spec = image_infos[idx].GetArchitecture();
if (dyld_spec.GetTriple().getOS() == llvm::Triple::IOS &&
dyld_spec.GetTriple().getEnvironment() == llvm::Triple::MacABI) {
image_module_sp->MergeArchitecture(dyld_spec);
const auto &target_triple = target.GetArchitecture().GetTriple();
// If dyld reports the process as being loaded as MACCATALYST,
// force-update the target's architecture to MACCATALYST.
if (!(target_triple.getOS() == llvm::Triple::IOS &&
target_triple.getEnvironment() == llvm::Triple::MacABI))
target.SetArchitecture(dyld_spec);
}
}
}
@ -711,6 +741,20 @@ void DynamicLoaderDarwin::Segment::PutToLog(Log *log,
}
}
lldb_private::ArchSpec DynamicLoaderDarwin::ImageInfo::GetArchitecture() const {
// Update the module's platform with the DYLD info.
lldb_private::ArchSpec arch_spec(lldb_private::eArchTypeMachO, header.cputype,
header.cpusubtype);
if (os_type == llvm::Triple::IOS && os_env == llvm::Triple::MacABI) {
llvm::Triple triple(llvm::Twine("x86_64-apple-ios") + min_version_os_sdk +
"-macabi");
ArchSpec maccatalyst_spec(triple);
if (arch_spec.IsCompatibleMatch(maccatalyst_spec))
arch_spec.MergeFrom(maccatalyst_spec);
}
return arch_spec;
}
const DynamicLoaderDarwin::Segment *
DynamicLoaderDarwin::ImageInfo::FindSegment(ConstString name) const {
const size_t num_segments = segments.size();

View File

@ -95,25 +95,34 @@ protected:
};
struct ImageInfo {
lldb::addr_t address; // Address of mach header for this dylib
lldb::addr_t slide; // The amount to slide all segments by if there is a
// global slide.
lldb::addr_t mod_date; // Modification date for this dylib
lldb_private::FileSpec file_spec; // Resolved path for this dylib
lldb_private::UUID
uuid; // UUID for this dylib if it has one, else all zeros
llvm::MachO::mach_header header; // The mach header for this image
std::vector<Segment> segments; // All segment vmaddr and vmsize pairs for
// this executable (from memory of inferior)
uint32_t load_stop_id; // The process stop ID that the sections for this
// image were loaded
llvm::Triple::OSType os_type; // LC_VERSION_MIN_... load command os type
std::string min_version_os_sdk; // LC_VERSION_MIN_... sdk value
/// Address of mach header for this dylib.
lldb::addr_t address = LLDB_INVALID_ADDRESS;
/// The amount to slide all segments by if there is a global
/// slide.
lldb::addr_t slide = 0;
/// Modification date for this dylib.
lldb::addr_t mod_date = 0;
/// Resolved path for this dylib.
lldb_private::FileSpec file_spec;
/// UUID for this dylib if it has one, else all zeros.
lldb_private::UUID uuid;
/// The mach header for this image.
llvm::MachO::mach_header header;
/// All segment vmaddr and vmsize pairs for this executable (from
/// memory of inferior).
std::vector<Segment> segments;
/// The process stop ID that the sections for this image were
/// loaded.
uint32_t load_stop_id = 0;
/// LC_VERSION_MIN_... load command os type.
llvm::Triple::OSType os_type = llvm::Triple::OSType::UnknownOS;
/// LC_VERSION_MIN_... load command os environment.
llvm::Triple::EnvironmentType os_env =
llvm::Triple::EnvironmentType::UnknownEnvironment;
/// LC_VERSION_MIN_... SDK.
std::string min_version_os_sdk;
ImageInfo()
: address(LLDB_INVALID_ADDRESS), slide(0), mod_date(0), file_spec(),
uuid(), header(), segments(), load_stop_id(0),
os_type(llvm::Triple::OSType::UnknownOS), min_version_os_sdk() {}
ImageInfo() = default;
void Clear(bool load_cmd_data_only) {
if (!load_cmd_data_only) {
@ -127,6 +136,7 @@ protected:
segments.clear();
load_stop_id = 0;
os_type = llvm::Triple::OSType::UnknownOS;
os_env = llvm::Triple::EnvironmentType::UnknownEnvironment;
min_version_os_sdk.clear();
}
@ -135,7 +145,8 @@ protected:
mod_date == rhs.mod_date && file_spec == rhs.file_spec &&
uuid == rhs.uuid &&
memcmp(&header, &rhs.header, sizeof(header)) == 0 &&
segments == rhs.segments && os_type == rhs.os_type;
segments == rhs.segments && os_type == rhs.os_type &&
os_env == rhs.os_env;
}
bool UUIDValid() const { return uuid.IsValid(); }
@ -150,10 +161,7 @@ protected:
return 0;
}
lldb_private::ArchSpec GetArchitecture() const {
return lldb_private::ArchSpec(lldb_private::eArchTypeMachO,
header.cputype, header.cpusubtype);
}
lldb_private::ArchSpec GetArchitecture() const;
const Segment *FindSegment(lldb_private::ConstString name) const;

View File

@ -931,6 +931,11 @@ llvm::VersionTuple GDBRemoteCommunicationClient::GetOSVersion() {
return m_os_version;
}
llvm::VersionTuple GDBRemoteCommunicationClient::GetMacCatalystVersion() {
GetHostInfo();
return m_maccatalyst_version;
}
bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) {
if (GetHostInfo()) {
if (!m_os_build.empty()) {
@ -1133,6 +1138,7 @@ bool GDBRemoteCommunicationClient::GetHostInfo(bool force) {
uint32_t sub = 0;
std::string arch_name;
std::string os_name;
std::string environment;
std::string vendor_name;
std::string triple;
std::string distribution_id;
@ -1172,6 +1178,10 @@ bool GDBRemoteCommunicationClient::GetHostInfo(bool force) {
extractor.GetHexByteString(m_os_kernel);
++num_keys_decoded;
} else if (name.equals("ostype")) {
if (value.equals("maccatalyst")) {
os_name = "ios";
environment = "macabi";
} else
os_name = value;
++num_keys_decoded;
} else if (name.equals("vendor")) {
@ -1196,6 +1206,9 @@ bool GDBRemoteCommunicationClient::GetHostInfo(bool force) {
{
if (!m_os_version.tryParse(value))
++num_keys_decoded;
} else if (name.equals("maccatalyst_version")) {
if (!m_maccatalyst_version.tryParse(value))
++num_keys_decoded;
} else if (name.equals("watchpoint_exceptions_received")) {
m_watchpoints_trigger_after_instruction =
llvm::StringSwitch<LazyBool>(value)
@ -1233,6 +1246,8 @@ bool GDBRemoteCommunicationClient::GetHostInfo(bool force) {
llvm::StringRef(vendor_name));
if (!os_name.empty())
m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name));
if (!environment.empty())
m_host_arch.GetTriple().setEnvironmentName(environment);
}
} else {
std::string triple;
@ -1985,6 +2000,7 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
uint32_t sub = 0;
std::string arch_name;
std::string os_name;
std::string environment;
std::string vendor_name;
std::string triple;
std::string elf_abi;
@ -2005,6 +2021,10 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
extractor.GetHexByteString(triple);
++num_keys_decoded;
} else if (name.equals("ostype")) {
if (value.equals("maccatalyst")) {
os_name = "ios";
environment = "macabi";
} else
os_name = value;
++num_keys_decoded;
} else if (name.equals("vendor")) {
@ -2046,6 +2066,8 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
} else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() &&
!vendor_name.empty()) {
llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name);
if (!environment.empty())
triple.setEnvironmentName(environment);
assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat);
assert(triple.getObjectFormat() != llvm::Triple::Wasm);
@ -2077,8 +2099,10 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
}
m_process_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name));
m_process_arch.GetTriple().setOSName(llvm::StringRef(os_name));
m_process_arch.GetTriple().setEnvironmentName(llvm::StringRef(environment));
m_host_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name));
m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name));
m_host_arch.GetTriple().setEnvironmentName(llvm::StringRef(environment));
}
return true;
}

View File

@ -248,6 +248,8 @@ public:
llvm::VersionTuple GetOSVersion();
llvm::VersionTuple GetMacCatalystVersion();
bool GetOSBuildString(std::string &s);
bool GetOSKernelDescription(std::string &s);
@ -548,6 +550,7 @@ protected:
ArchSpec m_host_arch;
ArchSpec m_process_arch;
llvm::VersionTuple m_os_version;
llvm::VersionTuple m_maccatalyst_version;
std::string m_os_build;
std::string m_os_kernel;
std::string m_hostname;

View File

@ -260,6 +260,15 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo(
response.PutChar(';');
}
#if defined(__APPLE__)
llvm::VersionTuple maccatalyst_version = HostInfo::GetMacCatalystVersion();
if (!maccatalyst_version.empty()) {
response.Format("maccatalyst_version:{0}",
maccatalyst_version.getAsString());
response.PutChar(';');
}
#endif
std::string s;
if (HostInfo::GetOSBuildString(s)) {
response.PutCString("os_build:");

View File

@ -4320,6 +4320,10 @@ llvm::VersionTuple ProcessGDBRemote::GetHostOSVersion() {
return m_gdb_comm.GetOSVersion();
}
llvm::VersionTuple ProcessGDBRemote::GetHostMacCatalystVersion() {
return m_gdb_comm.GetMacCatalystVersion();
}
namespace {
typedef std::vector<std::string> stringVec;

View File

@ -199,6 +199,7 @@ public:
const llvm::Triple &triple) override;
llvm::VersionTuple GetHostOSVersion() override;
llvm::VersionTuple GetHostMacCatalystVersion() override;
llvm::Error LoadModules() override;

View File

@ -762,12 +762,16 @@ bool MachProcess::GetMachOInformationFromMemory(
uint32_t major_version, minor_version, patch_version;
if (const char *lc_platform = GetDeploymentInfo(
lc, load_cmds_p, major_version, minor_version, patch_version)) {
// APPLE INTERNAL: macCatalyst support
// macCatalyst support.
//
// This handles two special cases:
// 1. Zippered frameworks have two deployment info load commands.
// Make sure to select the requested one.
// 2. The xctest binary is a pure macOS binary but is launched with
// DYLD_FORCE_PLATFORM=6.
//
// 1. Frameworks that have both a PLATFORM_MACOS and a
// PLATFORM_MACCATALYST load command. Make sure to select
// the requested one.
//
// 2. The xctest binary is a pure macOS binary but is launched
// with DYLD_FORCE_PLATFORM=6.
if (dyld_platform == PLATFORM_MACCATALYST &&
inf.mach_header.filetype == MH_EXECUTE &&
inf.min_version_os_name.empty() &&
@ -782,10 +786,12 @@ bool MachProcess::GetMachOInformationFromMemory(
inf.min_version_os_version = GetMacCatalystVersionString();
} else if (dyld_platform != PLATFORM_MACCATALYST &&
inf.min_version_os_name == "macosx") {
// This is a zippered binary and the process is not running as
// PLATFORM_MACCATALYST. Stick with the macosx load command
// that we've already processed, ignore this one, which is
// presumed to be a PLATFORM_MACCATALYST one.
// This is a binary with both PLATFORM_MACOS and
// PLATFORM_MACCATALYST load commands and the process is not
// running as PLATFORM_MACCATALYST. Stick with the
// "macosx" load command that we've already processed,
// ignore this one, which is presumed to be a
// PLATFORM_MACCATALYST one.
} else {
inf.min_version_os_name = lc_platform;
inf.min_version_os_version = "";