diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
commit | 9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch) | |
tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /source/Host/macosx | |
parent | 3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff) |
Notes
Diffstat (limited to 'source/Host/macosx')
-rw-r--r-- | source/Host/macosx/Host.mm | 1587 | ||||
-rw-r--r-- | source/Host/macosx/HostInfoMacOSX.mm | 372 | ||||
-rw-r--r-- | source/Host/macosx/HostThreadMacOSX.mm | 102 | ||||
-rw-r--r-- | source/Host/macosx/Symbols.cpp | 559 | ||||
-rw-r--r-- | source/Host/macosx/ThisThread.cpp | 39 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCBundle.cpp | 99 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCBundle.h | 50 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCData.cpp | 82 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCData.h | 35 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableArray.cpp | 166 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableArray.h | 39 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableDictionary.cpp | 529 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableDictionary.h | 79 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableSet.cpp | 114 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableSet.h | 53 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCReleaser.h | 158 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCString.cpp | 195 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CFCString.h | 41 | ||||
-rw-r--r-- | source/Host/macosx/cfcpp/CoreFoundationCPP.h | 30 |
19 files changed, 4329 insertions, 0 deletions
diff --git a/source/Host/macosx/Host.mm b/source/Host/macosx/Host.mm new file mode 100644 index 0000000000000..2db27a276e4ab --- /dev/null +++ b/source/Host/macosx/Host.mm @@ -0,0 +1,1587 @@ +//===-- Host.mm -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Host.h" + +#include <AvailabilityMacros.h> + +#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 +#define NO_XPC_SERVICES 1 +#endif + +#if !defined(NO_XPC_SERVICES) +#define __XPC_PRIVATE_H__ +#include <xpc/xpc.h> + +#define LaunchUsingXPCRightName "com.apple.dt.Xcode.RootDebuggingXPCService" + +// These XPC messaging keys are used for communication between Host.mm and the XPC service. +#define LauncherXPCServiceAuthKey "auth-key" +#define LauncherXPCServiceArgPrefxKey "arg" +#define LauncherXPCServiceEnvPrefxKey "env" +#define LauncherXPCServiceCPUTypeKey "cpuType" +#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags" +#define LauncherXPCServiceStdInPathKeyKey "stdInPath" +#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath" +#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath" +#define LauncherXPCServiceChildPIDKey "childPID" +#define LauncherXPCServiceErrorTypeKey "errorType" +#define LauncherXPCServiceCodeTypeKey "errorCode" + +#endif + +#include "llvm/Support/Host.h" + +#include <asl.h> +#include <crt_externs.h> +#include <grp.h> +#include <libproc.h> +#include <pwd.h> +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/proc.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <unistd.h> + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/CleanUp.h" +#include "lldb/Utility/NameMatches.h" + +#include "cfcpp/CFCBundle.h" +#include "cfcpp/CFCMutableArray.h" +#include "cfcpp/CFCMutableDictionary.h" +#include "cfcpp/CFCReleaser.h" +#include "cfcpp/CFCString.h" + + +#include <objc/objc-auto.h> + +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> + +#ifndef _POSIX_SPAWN_DISABLE_ASLR +#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 +#endif + +extern "C" +{ + int __pthread_chdir(const char *path); + int __pthread_fchdir (int fildes); +} + +using namespace lldb; +using namespace lldb_private; + +bool +Host::GetBundleDirectory (const FileSpec &file, FileSpec &bundle_directory) +{ +#if defined (__APPLE__) + if (file.GetFileType () == FileSpec::eFileTypeDirectory) + { + char path[PATH_MAX]; + if (file.GetPath(path, sizeof(path))) + { + CFCBundle bundle (path); + if (bundle.GetPath (path, sizeof(path))) + { + bundle_directory.SetFile (path, false); + return true; + } + } + } +#endif + bundle_directory.Clear(); + return false; +} + + +bool +Host::ResolveExecutableInBundle (FileSpec &file) +{ +#if defined (__APPLE__) + if (file.GetFileType () == FileSpec::eFileTypeDirectory) + { + char path[PATH_MAX]; + if (file.GetPath(path, sizeof(path))) + { + CFCBundle bundle (path); + CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL ()); + if (url.get()) + { + if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path))) + { + file.SetFile(path, false); + return true; + } + } + } + } +#endif + return false; +} + +static void * +AcceptPIDFromInferior (void *arg) +{ + const char *connect_url = (const char *)arg; + ConnectionFileDescriptor file_conn; + Error error; + if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess) + { + char pid_str[256]; + ::memset (pid_str, 0, sizeof(pid_str)); + ConnectionStatus status; + const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), 0, status, NULL); + if (pid_str_len > 0) + { + int pid = atoi (pid_str); + return (void *)(intptr_t)pid; + } + } + return NULL; +} + +static bool +WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) +{ + const int time_delta_usecs = 100000; + const int num_retries = timeout_in_seconds/time_delta_usecs; + for (int i=0; i<num_retries; i++) + { + struct proc_bsdinfo bsd_info; + int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO, + (uint64_t) 0, + &bsd_info, + PROC_PIDTBSDINFO_SIZE); + + switch (error) + { + case EINVAL: + case ENOTSUP: + case ESRCH: + case EPERM: + return false; + + default: + break; + + case 0: + if (bsd_info.pbi_status == SSTOP) + return true; + } + ::usleep (time_delta_usecs); + } + return false; +} +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + +//static lldb::pid_t +//LaunchInNewTerminalWithCommandFile +//( +// const char **argv, +// const char **envp, +// const char *working_dir, +// const ArchSpec *arch_spec, +// bool stop_at_entry, +// bool disable_aslr +//) +//{ +// if (!argv || !argv[0]) +// return LLDB_INVALID_PROCESS_ID; +// +// OSStatus error = 0; +// +// FileSpec program (argv[0], false); +// +// +// std::string unix_socket_name; +// +// char temp_file_path[PATH_MAX]; +// const char *tmpdir = ::getenv ("TMPDIR"); +// if (tmpdir == NULL) +// tmpdir = "/tmp/"; +// ::snprintf (temp_file_path, sizeof(temp_file_path), "%s%s-XXXXXX", tmpdir, program.GetFilename().AsCString()); +// +// if (::mktemp (temp_file_path) == NULL) +// return LLDB_INVALID_PROCESS_ID; +// +// unix_socket_name.assign (temp_file_path); +// +// ::strlcat (temp_file_path, ".command", sizeof (temp_file_path)); +// +// StreamFile command_file; +// command_file.GetFile().Open (temp_file_path, +// File::eOpenOptionWrite | File::eOpenOptionCanCreate, +// lldb::eFilePermissionsDefault); +// +// if (!command_file.GetFile().IsValid()) +// return LLDB_INVALID_PROCESS_ID; +// +// FileSpec darwin_debug_file_spec; +// if (!HostInfo::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec)) +// return LLDB_INVALID_PROCESS_ID; +// darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); +// +// if (!darwin_debug_file_spec.Exists()) +// return LLDB_INVALID_PROCESS_ID; +// +// char launcher_path[PATH_MAX]; +// darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); +// command_file.Printf("\"%s\" ", launcher_path); +// +// command_file.Printf("--unix-socket=%s ", unix_socket_name.c_str()); +// +// if (arch_spec && arch_spec->IsValid()) +// { +// command_file.Printf("--arch=%s ", arch_spec->GetArchitectureName()); +// } +// +// if (disable_aslr) +// { +// command_file.PutCString("--disable-aslr "); +// } +// +// command_file.PutCString("-- "); +// +// if (argv) +// { +// for (size_t i=0; argv[i] != NULL; ++i) +// { +// command_file.Printf("\"%s\" ", argv[i]); +// } +// } +// command_file.PutCString("\necho Process exited with status $?\n"); +// command_file.GetFile().Close(); +// if (::chmod (temp_file_path, S_IRWXU | S_IRWXG) != 0) +// return LLDB_INVALID_PROCESS_ID; +// +// CFCMutableDictionary cf_env_dict; +// +// const bool can_create = true; +// if (envp) +// { +// for (size_t i=0; envp[i] != NULL; ++i) +// { +// const char *env_entry = envp[i]; +// const char *equal_pos = strchr(env_entry, '='); +// if (equal_pos) +// { +// std::string env_key (env_entry, equal_pos); +// std::string env_val (equal_pos + 1); +// CFCString cf_env_key (env_key.c_str(), kCFStringEncodingUTF8); +// CFCString cf_env_val (env_val.c_str(), kCFStringEncodingUTF8); +// cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), can_create); +// } +// } +// } +// +// LSApplicationParameters app_params; +// ::memset (&app_params, 0, sizeof (app_params)); +// app_params.flags = kLSLaunchDontAddToRecents | kLSLaunchAsync; +// app_params.argv = NULL; +// app_params.environment = (CFDictionaryRef)cf_env_dict.get(); +// +// CFCReleaser<CFURLRef> command_file_url (::CFURLCreateFromFileSystemRepresentation (NULL, +// (const UInt8 *)temp_file_path, +// strlen(temp_file_path), +// false)); +// +// CFCMutableArray urls; +// +// // Terminal.app will open the ".command" file we have created +// // and run our process inside it which will wait at the entry point +// // for us to attach. +// urls.AppendValue(command_file_url.get()); +// +// +// lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; +// +// Error lldb_error; +// // Sleep and wait a bit for debugserver to start to listen... +// char connect_url[128]; +// ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str()); +// +// // Spawn a new thread to accept incoming connection on the connect_url +// // so we can grab the pid from the inferior +// lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(), +// AcceptPIDFromInferior, +// connect_url, +// &lldb_error); +// +// ProcessSerialNumber psn; +// error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, &psn, 1); +// if (error == noErr) +// { +// thread_result_t accept_thread_result = NULL; +// if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error)) +// { +// if (accept_thread_result) +// { +// pid = (intptr_t)accept_thread_result; +// +// // Wait for process to be stopped the entry point by watching +// // for the process status to be set to SSTOP which indicates it it +// // SIGSTOP'ed at the entry point +// WaitForProcessToSIGSTOP (pid, 5); +// } +// } +// } +// else +// { +// Host::ThreadCancel (accept_thread, &lldb_error); +// } +// +// return pid; +//} + +const char *applscript_in_new_tty = +"tell application \"Terminal\"\n" +" do script \"%s\"\n" +"end tell\n"; + + +const char *applscript_in_existing_tty = "\ +set the_shell_script to \"%s\"\n\ +tell application \"Terminal\"\n\ + repeat with the_window in (get windows)\n\ + repeat with the_tab in tabs of the_window\n\ + set the_tty to tty in the_tab\n\ + if the_tty contains \"%s\" then\n\ + if the_tab is not busy then\n\ + set selected of the_tab to true\n\ + set frontmost of the_window to true\n\ + do script the_shell_script in the_tab\n\ + return\n\ + end if\n\ + end if\n\ + end repeat\n\ + end repeat\n\ + do script the_shell_script\n\ +end tell\n"; + + +static Error +LaunchInNewTerminalWithAppleScript (const char *exe_path, ProcessLaunchInfo &launch_info) +{ + Error error; + char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; + if (::mktemp (unix_socket_name) == NULL) + { + error.SetErrorString ("failed to make temporary path for a unix socket"); + return error; + } + + StreamString command; + FileSpec darwin_debug_file_spec; + if (!HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, darwin_debug_file_spec)) + { + error.SetErrorString ("can't locate the 'darwin-debug' executable"); + return error; + } + + darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); + + if (!darwin_debug_file_spec.Exists()) + { + error.SetErrorStringWithFormat ("the 'darwin-debug' executable doesn't exists at '%s'", + darwin_debug_file_spec.GetPath().c_str()); + return error; + } + + char launcher_path[PATH_MAX]; + darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); + + const ArchSpec &arch_spec = launch_info.GetArchitecture(); + // Only set the architecture if it is valid and if it isn't Haswell (x86_64h). + if (arch_spec.IsValid() && arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h) + command.Printf("arch -arch %s ", arch_spec.GetArchitectureName()); + + command.Printf("'%s' --unix-socket=%s", launcher_path, unix_socket_name); + + if (arch_spec.IsValid()) + command.Printf(" --arch=%s", arch_spec.GetArchitectureName()); + + FileSpec working_dir{launch_info.GetWorkingDirectory()}; + if (working_dir) + command.Printf(" --working-dir '%s'", working_dir.GetCString()); + else + { + char cwd[PATH_MAX]; + if (getcwd(cwd, PATH_MAX)) + command.Printf(" --working-dir '%s'", cwd); + } + + if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)) + command.PutCString(" --disable-aslr"); + + // We are launching on this host in a terminal. So compare the environment on the host + // to what is supplied in the launch_info. Any items that aren't in the host environment + // need to be sent to darwin-debug. If we send all environment entries, we might blow the + // max command line length, so we only send user modified entries. + const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector (); + + StringList host_env; + const size_t host_env_count = Host::GetEnvironment (host_env); + + if (envp && envp[0]) + { + const char *env_entry; + for (size_t env_idx = 0; (env_entry = envp[env_idx]) != NULL; ++env_idx) + { + bool add_entry = true; + for (size_t i=0; i<host_env_count; ++i) + { + const char *host_env_entry = host_env.GetStringAtIndex(i); + if (strcmp(env_entry, host_env_entry) == 0) + { + add_entry = false; + break; + } + } + if (add_entry) + { + command.Printf(" --env='%s'", env_entry); + } + } + } + + command.PutCString(" -- "); + + const char **argv = launch_info.GetArguments().GetConstArgumentVector (); + if (argv) + { + for (size_t i=0; argv[i] != NULL; ++i) + { + if (i==0) + command.Printf(" '%s'", exe_path); + else + command.Printf(" '%s'", argv[i]); + } + } + else + { + command.Printf(" '%s'", exe_path); + } + command.PutCString (" ; echo Process exited with status $?"); + if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit)) + command.PutCString (" ; exit"); + + StreamString applescript_source; + + const char *tty_command = command.GetString().c_str(); +// if (tty_name && tty_name[0]) +// { +// applescript_source.Printf (applscript_in_existing_tty, +// tty_command, +// tty_name); +// } +// else +// { + applescript_source.Printf (applscript_in_new_tty, + tty_command); +// } + + + + const char *script_source = applescript_source.GetString().c_str(); + //puts (script_source); + NSAppleScript* applescript = [[NSAppleScript alloc] initWithSource:[NSString stringWithCString:script_source encoding:NSUTF8StringEncoding]]; + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + Error lldb_error; + // Sleep and wait a bit for debugserver to start to listen... + ConnectionFileDescriptor file_conn; + char connect_url[128]; + ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name); + + // Spawn a new thread to accept incoming connection on the connect_url + // so we can grab the pid from the inferior. We have to do this because we + // are sending an AppleScript that will launch a process in Terminal.app, + // in a shell and the shell will fork/exec a couple of times before we get + // to the process that we wanted to launch. So when our process actually + // gets launched, we will handshake with it and get the process ID for it. + HostThread accept_thread = ThreadLauncher::LaunchThread(unix_socket_name, AcceptPIDFromInferior, connect_url, &lldb_error); + + [applescript executeAndReturnError:nil]; + + thread_result_t accept_thread_result = NULL; + lldb_error = accept_thread.Join(&accept_thread_result); + if (lldb_error.Success() && accept_thread_result) + { + pid = (intptr_t)accept_thread_result; + + // Wait for process to be stopped at the entry point by watching + // for the process status to be set to SSTOP which indicates it it + // SIGSTOP'ed at the entry point + WaitForProcessToSIGSTOP(pid, 5); + } + + FileSystem::Unlink(FileSpec{unix_socket_name, false}); + [applescript release]; + if (pid != LLDB_INVALID_PROCESS_ID) + launch_info.SetProcessID (pid); + return error; +} + +#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + + +// On MacOSX CrashReporter will display a string for each shared library if +// the shared library has an exported symbol named "__crashreporter_info__". + +static Mutex& +GetCrashReporterMutex () +{ + static Mutex g_mutex; + return g_mutex; +} + +extern "C" { + const char *__crashreporter_info__ = NULL; +} + +asm(".desc ___crashreporter_info__, 0x10"); + +void +Host::SetCrashDescriptionWithFormat (const char *format, ...) +{ + static StreamString g_crash_description; + Mutex::Locker locker (GetCrashReporterMutex ()); + + if (format) + { + va_list args; + va_start (args, format); + g_crash_description.GetString().clear(); + g_crash_description.PrintfVarArg(format, args); + va_end (args); + __crashreporter_info__ = g_crash_description.GetData(); + } + else + { + __crashreporter_info__ = NULL; + } +} + +void +Host::SetCrashDescription (const char *cstr) +{ + Mutex::Locker locker (GetCrashReporterMutex ()); + static std::string g_crash_description; + if (cstr) + { + g_crash_description.assign (cstr); + __crashreporter_info__ = g_crash_description.c_str(); + } + else + { + __crashreporter_info__ = NULL; + } +} + +bool +Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no) +{ +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + return false; +#else + // We attach this to an 'odoc' event to specify a particular selection + typedef struct { + int16_t reserved0; // must be zero + int16_t fLineNumber; + int32_t fSelStart; + int32_t fSelEnd; + uint32_t reserved1; // must be zero + uint32_t reserved2; // must be zero + } BabelAESelInfo; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST)); + char file_path[PATH_MAX]; + file_spec.GetPath(file_path, PATH_MAX); + CFCString file_cfstr (file_path, kCFStringEncodingUTF8); + CFCReleaser<CFURLRef> file_URL (::CFURLCreateWithFileSystemPath (NULL, + file_cfstr.get(), + kCFURLPOSIXPathStyle, + false)); + + if (log) + log->Printf("Sending source file: \"%s\" and line: %d to external editor.\n", file_path, line_no); + + long error; + BabelAESelInfo file_and_line_info = + { + 0, // reserved0 + (int16_t)(line_no - 1), // fLineNumber (zero based line number) + 1, // fSelStart + 1024, // fSelEnd + 0, // reserved1 + 0 // reserved2 + }; + + AEKeyDesc file_and_line_desc; + + error = ::AECreateDesc (typeUTF8Text, + &file_and_line_info, + sizeof (file_and_line_info), + &(file_and_line_desc.descContent)); + + if (error != noErr) + { + if (log) + log->Printf("Error creating AEDesc: %ld.\n", error); + return false; + } + + file_and_line_desc.descKey = keyAEPosition; + + static std::string g_app_name; + static FSRef g_app_fsref; + + LSApplicationParameters app_params; + ::memset (&app_params, 0, sizeof (app_params)); + app_params.flags = kLSLaunchDefaults | + kLSLaunchDontAddToRecents | + kLSLaunchDontSwitch; + + char *external_editor = ::getenv ("LLDB_EXTERNAL_EDITOR"); + + if (external_editor) + { + if (log) + log->Printf("Looking for external editor \"%s\".\n", external_editor); + + if (g_app_name.empty() || strcmp (g_app_name.c_str(), external_editor) != 0) + { + CFCString editor_name (external_editor, kCFStringEncodingUTF8); + error = ::LSFindApplicationForInfo (kLSUnknownCreator, + NULL, + editor_name.get(), + &g_app_fsref, + NULL); + + // If we found the app, then store away the name so we don't have to re-look it up. + if (error != noErr) + { + if (log) + log->Printf("Could not find External Editor application, error: %ld.\n", error); + return false; + } + + } + app_params.application = &g_app_fsref; + } + + ProcessSerialNumber psn; + CFCReleaser<CFArrayRef> file_array(CFArrayCreate (NULL, (const void **) file_URL.ptr_address(false), 1, NULL)); + error = ::LSOpenURLsWithRole (file_array.get(), + kLSRolesAll, + &file_and_line_desc, + &app_params, + &psn, + 1); + + AEDisposeDesc (&(file_and_line_desc.descContent)); + + if (error != noErr) + { + if (log) + log->Printf("LSOpenURLsWithRole failed, error: %ld.\n", error); + + return false; + } + + return true; +#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) +} + +size_t +Host::GetEnvironment (StringList &env) +{ + char **host_env = *_NSGetEnviron(); + char *env_entry; + size_t i; + for (i=0; (env_entry = host_env[i]) != NULL; ++i) + env.AppendString(env_entry); + return i; + +} + +static bool +GetMacOSXProcessCPUType (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + // Make a new mib to stay thread safe + int mib[CTL_MAXNAME]={0,}; + size_t mib_len = CTL_MAXNAME; + if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len)) + return false; + + mib[mib_len] = process_info.GetProcessID(); + mib_len++; + + cpu_type_t cpu, sub = 0; + size_t len = sizeof(cpu); + if (::sysctl (mib, mib_len, &cpu, &len, 0, 0) == 0) + { + switch (cpu) + { + case CPU_TYPE_I386: sub = CPU_SUBTYPE_I386_ALL; break; + case CPU_TYPE_X86_64: sub = CPU_SUBTYPE_X86_64_ALL; break; + +#if defined (CPU_TYPE_ARM64) && defined (CPU_SUBTYPE_ARM64_ALL) + case CPU_TYPE_ARM64: sub = CPU_SUBTYPE_ARM64_ALL; break; +#endif + + case CPU_TYPE_ARM: + { + // Note that we fetched the cpu type from the PROCESS but we can't get a cpusubtype of the + // process -- we can only get the host's cpu subtype. + uint32_t cpusubtype = 0; + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + sub = cpusubtype; + + bool host_cpu_is_64bit; + uint32_t is64bit_capable; + size_t is64bit_capable_len = sizeof (is64bit_capable); + if (sysctlbyname("hw.cpu64bit_capable", &is64bit_capable, &is64bit_capable_len, NULL, 0) == 0) + host_cpu_is_64bit = true; + else + host_cpu_is_64bit = false; + + // if the host is an armv8 device, its cpusubtype will be in CPU_SUBTYPE_ARM64 numbering + // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value instead. + + if (host_cpu_is_64bit) + { + sub = CPU_SUBTYPE_ARM_V7; + } + } + break; + + default: + break; + } + process_info.GetArchitecture ().SetArchitecture (eArchTypeMachO, cpu, sub); + return true; + } + } + process_info.GetArchitecture().Clear(); + return false; +} + +static bool +GetMacOSXProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, + ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + int proc_args_mib[3] = { CTL_KERN, KERN_PROCARGS2, (int)process_info.GetProcessID() }; + + size_t arg_data_size = 0; + if (::sysctl (proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) || arg_data_size == 0) + arg_data_size = 8192; + + // Add a few bytes to the calculated length, I know we need to add at least one byte + // to this number otherwise we get junk back, so add 128 just in case... + DataBufferHeap arg_data(arg_data_size+128, 0); + arg_data_size = arg_data.GetByteSize(); + if (::sysctl (proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size , NULL, 0) == 0) + { + DataExtractor data (arg_data.GetBytes(), arg_data_size, endian::InlHostByteOrder(), sizeof(void *)); + lldb::offset_t offset = 0; + uint32_t argc = data.GetU32 (&offset); + llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); + const llvm::Triple::ArchType triple_arch = triple.getArch(); + const bool check_for_ios_simulator = (triple_arch == llvm::Triple::x86 || triple_arch == llvm::Triple::x86_64); + const char *cstr = data.GetCStr (&offset); + if (cstr) + { + process_info.GetExecutableFile().SetFile(cstr, false); + + if (match_info_ptr == NULL || + NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(), + match_info_ptr->GetNameMatchType(), + match_info_ptr->GetProcessInfo().GetName())) + { + // Skip NULLs + while (1) + { + const uint8_t *p = data.PeekData(offset, 1); + if ((p == NULL) || (*p != '\0')) + break; + ++offset; + } + // Now extract all arguments + Args &proc_args = process_info.GetArguments(); + for (int i=0; i<static_cast<int>(argc); ++i) + { + cstr = data.GetCStr(&offset); + if (cstr) + proc_args.AppendArgument(cstr); + } + + Args &proc_env = process_info.GetEnvironmentEntries (); + while ((cstr = data.GetCStr(&offset))) + { + if (cstr[0] == '\0') + break; + + if (check_for_ios_simulator) + { + if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 0) + process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::IOS); + else + process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::MacOSX); + } + + proc_env.AppendArgument(cstr); + } + return true; + } + } + } + } + return false; +} + +static bool +GetMacOSXProcessUserAndGroup (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = process_info.GetProcessID(); + struct kinfo_proc proc_kinfo; + size_t proc_kinfo_size = sizeof(struct kinfo_proc); + + if (::sysctl (mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) + { + if (proc_kinfo_size > 0) + { + process_info.SetParentProcessID (proc_kinfo.kp_eproc.e_ppid); + process_info.SetUserID (proc_kinfo.kp_eproc.e_pcred.p_ruid); + process_info.SetGroupID (proc_kinfo.kp_eproc.e_pcred.p_rgid); + process_info.SetEffectiveUserID (proc_kinfo.kp_eproc.e_ucred.cr_uid); + if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) + process_info.SetEffectiveGroupID (proc_kinfo.kp_eproc.e_ucred.cr_groups[0]); + else + process_info.SetEffectiveGroupID (UINT32_MAX); + return true; + } + } + } + process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID); + process_info.SetUserID (UINT32_MAX); + process_info.SetGroupID (UINT32_MAX); + process_info.SetEffectiveUserID (UINT32_MAX); + process_info.SetEffectiveGroupID (UINT32_MAX); + return false; +} + + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + std::vector<struct kinfo_proc> kinfos; + + int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + + size_t pid_data_size = 0; + if (::sysctl (mib, 4, NULL, &pid_data_size, NULL, 0) != 0) + return 0; + + // Add a few extra in case a few more show up + const size_t estimated_pid_count = (pid_data_size / sizeof(struct kinfo_proc)) + 10; + + kinfos.resize (estimated_pid_count); + pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); + + if (::sysctl (mib, 4, &kinfos[0], &pid_data_size, NULL, 0) != 0) + return 0; + + const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); + + bool all_users = match_info.GetMatchAllUsers(); + const lldb::pid_t our_pid = getpid(); + const uid_t our_uid = getuid(); + for (size_t i = 0; i < actual_pid_count; i++) + { + const struct kinfo_proc &kinfo = kinfos[i]; + + bool kinfo_user_matches = false; + if (all_users) + kinfo_user_matches = true; + else + kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid; + + // Special case, if lldb is being run as root we can attach to anything. + if (our_uid == 0) + kinfo_user_matches = true; + + if (kinfo_user_matches == false || // Make sure the user is acceptable + static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) == our_pid || // Skip this process + kinfo.kp_proc.p_pid == 0 || // Skip kernel (kernel pid is zero) + kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains... + kinfo.kp_proc.p_flag & P_TRACED || // Being debugged? + kinfo.kp_proc.p_flag & P_WEXIT || // Working on exiting? + kinfo.kp_proc.p_flag & P_TRANSLATED) // Skip translated ppc (Rosetta) + continue; + + ProcessInstanceInfo process_info; + process_info.SetProcessID (kinfo.kp_proc.p_pid); + process_info.SetParentProcessID (kinfo.kp_eproc.e_ppid); + process_info.SetUserID (kinfo.kp_eproc.e_pcred.p_ruid); + process_info.SetGroupID (kinfo.kp_eproc.e_pcred.p_rgid); + process_info.SetEffectiveUserID (kinfo.kp_eproc.e_ucred.cr_uid); + if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0) + process_info.SetEffectiveGroupID (kinfo.kp_eproc.e_ucred.cr_groups[0]); + else + process_info.SetEffectiveGroupID (UINT32_MAX); + + // Make sure our info matches before we go fetch the name and cpu type + if (match_info.Matches (process_info)) + { + // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 + if (GetMacOSXProcessCPUType (process_info)) + { + if (GetMacOSXProcessArgs (&match_info, process_info)) + { + if (match_info.Matches (process_info)) + process_infos.Append (process_info); + } + } + } + } + return process_infos.GetSize(); +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.SetProcessID(pid); + bool success = false; + + // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 + if (GetMacOSXProcessCPUType (process_info)) + success = true; + + if (GetMacOSXProcessArgs (NULL, process_info)) + success = true; + + if (GetMacOSXProcessUserAndGroup (process_info)) + success = true; + + if (success) + return true; + + process_info.Clear(); + return false; +} + +#if !NO_XPC_SERVICES +static void +PackageXPCArguments (xpc_object_t message, const char *prefix, const Args& args) +{ + size_t count = args.GetArgumentCount(); + char buf[50]; // long enough for 'argXXX' + memset(buf, 0, 50); + sprintf(buf, "%sCount", prefix); + xpc_dictionary_set_int64(message, buf, count); + for (size_t i=0; i<count; i++) { + memset(buf, 0, 50); + sprintf(buf, "%s%zi", prefix, i); + xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i)); + } +} + +/* + A valid authorizationRef means that + - there is the LaunchUsingXPCRightName rights in the /etc/authorization + - we have successfully copied the rights to be send over the XPC wire + Once obtained, it will be valid for as long as the process lives. + */ +static AuthorizationRef authorizationRef = NULL; +static Error +getXPCAuthorization (ProcessLaunchInfo &launch_info) +{ + Error error; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + if ((launch_info.GetUserID() == 0) && !authorizationRef) + { + OSStatus createStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); + if (createStatus != errAuthorizationSuccess) + { + error.SetError(1, eErrorTypeGeneric); + error.SetErrorString("Can't create authorizationRef."); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + return error; + } + + OSStatus rightsStatus = AuthorizationRightGet(LaunchUsingXPCRightName, NULL); + if (rightsStatus != errAuthorizationSuccess) + { + // No rights in the security database, Create it with the right prompt. + CFStringRef prompt = CFSTR("Xcode is trying to take control of a root process."); + CFStringRef keys[] = { CFSTR("en") }; + CFTypeRef values[] = { prompt }; + CFDictionaryRef promptDict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFStringRef keys1[] = { CFSTR("class"), CFSTR("group"), CFSTR("comment"), CFSTR("default-prompt"), CFSTR("shared") }; + CFTypeRef values1[] = { CFSTR("user"), CFSTR("admin"), CFSTR(LaunchUsingXPCRightName), promptDict, kCFBooleanFalse }; + CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + rightsStatus = AuthorizationRightSet(authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL); + CFRelease(promptDict); + CFRelease(dict); + } + + OSStatus copyRightStatus = errAuthorizationDenied; + if (rightsStatus == errAuthorizationSuccess) + { + AuthorizationItem item1 = { LaunchUsingXPCRightName, 0, NULL, 0 }; + AuthorizationItem items[] = {item1}; + AuthorizationRights requestedRights = {1, items }; + AuthorizationFlags authorizationFlags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; + copyRightStatus = AuthorizationCopyRights(authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment, authorizationFlags, NULL); + } + + if (copyRightStatus != errAuthorizationSuccess) + { + // Eventually when the commandline supports running as root and the user is not + // logged in in the current audit session, we will need the trick in gdb where + // we ask the user to type in the root passwd in the terminal. + error.SetError(2, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Launching as root needs root authorization."); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + + if (authorizationRef) + { + AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); + authorizationRef = NULL; + } + } + } + + return error; +} +#endif + +static Error +LaunchProcessXPC(const char *exe_path, ProcessLaunchInfo &launch_info, lldb::pid_t &pid) +{ +#if !NO_XPC_SERVICES + Error error = getXPCAuthorization(launch_info); + if (error.Fail()) + return error; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + uid_t requested_uid = launch_info.GetUserID(); + const char *xpc_service = nil; + bool send_auth = false; + AuthorizationExternalForm extForm; + if (requested_uid == 0) + { + if (AuthorizationMakeExternalForm(authorizationRef, &extForm) == errAuthorizationSuccess) + { + send_auth = true; + } + else + { + error.SetError(3, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Launching root via XPC needs to externalize authorization reference."); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + return error; + } + xpc_service = LaunchUsingXPCRightName; + } + else + { + error.SetError(4, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Launching via XPC is only currently available for root."); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + return error; + } + + xpc_connection_t conn = xpc_connection_create(xpc_service, NULL); + + xpc_connection_set_event_handler(conn, ^(xpc_object_t event) { + xpc_type_t type = xpc_get_type(event); + + if (type == XPC_TYPE_ERROR) { + if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { + // The service has either canceled itself, crashed, or been terminated. + // The XPC connection is still valid and sending a message to it will re-launch the service. + // If the service is state-full, this is the time to initialize the new service. + return; + } else if (event == XPC_ERROR_CONNECTION_INVALID) { + // The service is invalid. Either the service name supplied to xpc_connection_create() is incorrect + // or we (this process) have canceled the service; we can do any cleanup of application state at this point. + // printf("Service disconnected"); + return; + } else { + // printf("Unexpected error from service: %s", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); + } + + } else { + // printf("Received unexpected event in handler"); + } + }); + + xpc_connection_set_finalizer_f (conn, xpc_finalizer_t(xpc_release)); + xpc_connection_resume (conn); + xpc_object_t message = xpc_dictionary_create (nil, nil, 0); + + if (send_auth) + { + xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes, sizeof(AuthorizationExternalForm)); + } + + PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey, launch_info.GetArguments()); + PackageXPCArguments(message, LauncherXPCServiceEnvPrefxKey, launch_info.GetEnvironmentEntries()); + + // Posix spawn stuff. + xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, launch_info.GetArchitecture().GetMachOCPUType()); + xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, Host::GetPosixspawnFlags(launch_info)); + const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); + if (file_action && file_action->GetPath()) + { + xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, file_action->GetPath()); + } + file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); + if (file_action && file_action->GetPath()) + { + xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, file_action->GetPath()); + } + file_action = launch_info.GetFileActionForFD(STDERR_FILENO); + if (file_action && file_action->GetPath()) + { + xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, file_action->GetPath()); + } + + xpc_object_t reply = xpc_connection_send_message_with_reply_sync(conn, message); + xpc_type_t returnType = xpc_get_type(reply); + if (returnType == XPC_TYPE_DICTIONARY) + { + pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey); + if (pid == 0) + { + int errorType = xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey); + int errorCode = xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey); + + error.SetError(errorCode, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Problems with launching via XPC. Error type : %i, code : %i", errorType, errorCode); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + + if (authorizationRef) + { + AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); + authorizationRef = NULL; + } + } + } + else if (returnType == XPC_TYPE_ERROR) + { + error.SetError(5, eErrorTypeGeneric); + error.SetErrorStringWithFormat("Problems with launching via XPC. XPC error : %s", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); + if (log) + { + error.PutToLog(log, "%s", error.AsCString()); + } + } + + return error; +#else + Error error; + return error; +#endif +} + +static bool +ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) +{ + bool result = false; + +#if !NO_XPC_SERVICES + bool launchingAsRoot = launch_info.GetUserID() == 0; + bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0; + + if (launchingAsRoot && !currentUserIsRoot) + { + // If current user is already root, we don't need XPC's help. + result = true; + } +#endif + + return result; +} + +Error +Host::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + char exe_path[PATH_MAX]; + PlatformSP host_platform_sp (Platform::GetHostPlatform ()); + + ModuleSpec exe_module_spec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()); + + FileSpec::FileType file_type = exe_module_spec.GetFileSpec().GetFileType(); + if (file_type != FileSpec::eFileTypeRegular) + { + lldb::ModuleSP exe_module_sp; + error = host_platform_sp->ResolveExecutable (exe_module_spec, + exe_module_sp, + NULL); + + if (error.Fail()) + return error; + + if (exe_module_sp) + exe_module_spec.GetFileSpec() = exe_module_sp->GetFileSpec(); + } + + if (exe_module_spec.GetFileSpec().Exists()) + { + exe_module_spec.GetFileSpec().GetPath (exe_path, sizeof(exe_path)); + } + else + { + launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path); + return error; + } + + if (launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) + { +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + return LaunchInNewTerminalWithAppleScript (exe_path, launch_info); +#else + error.SetErrorString ("launching a process in a new terminal is not supported on iOS devices"); + return error; +#endif + } + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + if (ShouldLaunchUsingXPC(launch_info)) + { + error = LaunchProcessXPC(exe_path, launch_info, pid); + } + else + { + error = LaunchProcessPosixSpawn(exe_path, launch_info, pid); + } + + if (pid != LLDB_INVALID_PROCESS_ID) + { + // If all went well, then set the process ID into the launch info + launch_info.SetProcessID(pid); + + // Make sure we reap any processes we spawn or we will have zombies. + if (!launch_info.MonitorProcess()) + { + const bool monitor_signals = false; + Host::MonitorChildProcessCallback callback = nullptr; + + if (!launch_info.GetFlags().Test(lldb::eLaunchFlagDontSetExitStatus)) + callback = Process::SetProcessExitStatus; + + StartMonitoringChildProcess (callback, + NULL, + pid, + monitor_signals); + } + } + else + { + // Invalid process ID, something didn't go well + if (error.Success()) + error.SetErrorString ("process launch failed for unknown reasons"); + } + return error; +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + Error error; + if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) + { + FileSpec expand_tool_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec)) + { + error.SetErrorString("could not get support executable directory for lldb-argdumper tool"); + return error; + } + expand_tool_spec.AppendPathComponent("lldb-argdumper"); + if (!expand_tool_spec.Exists()) + { + error.SetErrorStringWithFormat("could not find the lldb-argdumper tool: %s", expand_tool_spec.GetPath().c_str()); + return error; + } + + StreamString expand_tool_spec_stream; + expand_tool_spec_stream.Printf("\"%s\"",expand_tool_spec.GetPath().c_str()); + + Args expand_command(expand_tool_spec_stream.GetData()); + expand_command.AppendArguments (launch_info.GetArguments()); + + int status; + std::string output; + FileSpec cwd(launch_info.GetWorkingDirectory()); + if (!cwd.Exists()) + { + char *wd = getcwd(nullptr, 0); + if (wd == nullptr) + { + error.SetErrorStringWithFormat("cwd does not exist; cannot launch with shell argument expansion"); + return error; + } + else + { + FileSpec working_dir(wd, false); + free(wd); + launch_info.SetWorkingDirectory(working_dir); + + } + } + RunShellCommand(expand_command, cwd, &status, nullptr, &output, 10); + + if (status != 0) + { + error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", status); + return error; + } + + auto data_sp = StructuredData::ParseJSON(output); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto dict_sp = data_sp->GetAsDictionary(); + if (!data_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); + if (!args_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + auto args_array_sp = args_sp->GetAsArray(); + if (!args_array_sp) + { + error.SetErrorString("invalid JSON"); + return error; + } + + launch_info.GetArguments().Clear(); + + for (size_t i = 0; + i < args_array_sp->GetSize(); + i++) + { + auto item_sp = args_array_sp->GetItemAtIndex(i); + if (!item_sp) + continue; + auto str_sp = item_sp->GetAsString(); + if (!str_sp) + continue; + + launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str()); + } + } + + return error; +} + +HostThread +Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals) +{ + unsigned long mask = DISPATCH_PROC_EXIT; + if (monitor_signals) + mask |= DISPATCH_PROC_SIGNAL; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + + dispatch_source_t source = ::dispatch_source_create (DISPATCH_SOURCE_TYPE_PROC, + pid, + mask, + ::dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT,0)); + + if (log) + log->Printf ("Host::StartMonitoringChildProcess (callback=%p, baton=%p, pid=%i, monitor_signals=%i) source = %p\n", + callback, + callback_baton, + (int)pid, + monitor_signals, + source); + + if (source) + { + ::dispatch_source_set_cancel_handler (source, ^{ + ::dispatch_release (source); + }); + ::dispatch_source_set_event_handler (source, ^{ + + int status= 0; + int wait_pid = 0; + bool cancel = false; + bool exited = false; + do + { + wait_pid = ::waitpid (pid, &status, 0); + } while (wait_pid < 0 && errno == EINTR); + + if (wait_pid >= 0) + { + int signal = 0; + int exit_status = 0; + const char *status_cstr = NULL; + if (WIFSTOPPED(status)) + { + signal = WSTOPSIG(status); + status_cstr = "STOPPED"; + } + else if (WIFEXITED(status)) + { + exit_status = WEXITSTATUS(status); + status_cstr = "EXITED"; + exited = true; + } + else if (WIFSIGNALED(status)) + { + signal = WTERMSIG(status); + status_cstr = "SIGNALED"; + exited = true; + exit_status = -1; + } + else + { + status_cstr = "???"; + } + + if (log) + log->Printf ("::waitpid (pid = %llu, &status, 0) => pid = %i, status = 0x%8.8x (%s), signal = %i, exit_status = %i", + pid, + wait_pid, + status, + status_cstr, + signal, + exit_status); + + if (callback) + cancel = callback (callback_baton, pid, exited, signal, exit_status); + + if (exited || cancel) + { + ::dispatch_source_cancel(source); + } + } + }); + + ::dispatch_resume (source); + } + return HostThread(); +} + +//---------------------------------------------------------------------- +// Log to both stderr and to ASL Logging when running on MacOSX. +//---------------------------------------------------------------------- +void +Host::SystemLog (SystemLogType type, const char *format, va_list args) +{ + if (format && format[0]) + { + static aslmsg g_aslmsg = NULL; + if (g_aslmsg == NULL) + { + g_aslmsg = ::asl_new (ASL_TYPE_MSG); + char asl_key_sender[PATH_MAX]; + snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.LLDB.framework"); + ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); + } + + // Copy the va_list so we can log this message twice + va_list copy_args; + va_copy (copy_args, args); + // Log to stderr + ::vfprintf (stderr, format, copy_args); + va_end (copy_args); + + int asl_level; + switch (type) + { + case eSystemLogError: + asl_level = ASL_LEVEL_ERR; + break; + + case eSystemLogWarning: + asl_level = ASL_LEVEL_WARNING; + break; + } + + // Log to ASL + ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); + } +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + return lldb::DataBufferSP(); +} diff --git a/source/Host/macosx/HostInfoMacOSX.mm b/source/Host/macosx/HostInfoMacOSX.mm new file mode 100644 index 0000000000000..f5a0540e87749 --- /dev/null +++ b/source/Host/macosx/HostInfoMacOSX.mm @@ -0,0 +1,372 @@ +//===-- HostInfoMacOSX.mm ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if !defined(LLDB_DISABLE_PYTHON) +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#endif + +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/macosx/HostInfoMacOSX.h" +#include "lldb/Core/Log.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/SafeMachO.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +// C++ Includes +#include <string> + +// C inclues +#include <stdlib.h> +#include <sys/sysctl.h> +#include <sys/syslimits.h> +#include <sys/types.h> + +// Objective C/C++ includes +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> +#include <mach-o/dyld.h> +#include <objc/objc-auto.h> + +// These are needed when compiling on systems +// that do not yet have these definitions +#include <AvailabilityMacros.h> +#ifndef CPU_SUBTYPE_X86_64_H +#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) +#endif +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM|CPU_ARCH_ABI64) +#endif + +#include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH + +using namespace lldb_private; + +bool +HostInfoMacOSX::GetOSBuildString(std::string &s) +{ + int mib[2] = {CTL_KERN, KERN_OSVERSION}; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign(cstr, cstr_len); + return true; + } + + s.clear(); + return false; +} + +bool +HostInfoMacOSX::GetOSKernelDescription(std::string &s) +{ + int mib[2] = {CTL_KERN, KERN_VERSION}; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign(cstr, cstr_len); + return true; + } + s.clear(); + return false; +} + +bool +HostInfoMacOSX::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ + static uint32_t g_major = 0; + static uint32_t g_minor = 0; + static uint32_t g_update = 0; + + if (g_major == 0) + { + @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]; + if (version_str) + Args::StringToVersion(version_str, g_major, g_minor, g_update); + } + } + + if (g_major != 0) + { + major = g_major; + minor = g_minor; + update = g_update; + return true; + } + return false; +} + +FileSpec +HostInfoMacOSX::GetProgramFileSpec() +{ + static FileSpec g_program_filespec; + if (!g_program_filespec) + { + char program_fullpath[PATH_MAX]; + // If DST is NULL, then return the number of bytes needed. + uint32_t len = sizeof(program_fullpath); + int err = _NSGetExecutablePath(program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile(program_fullpath, false); + else if (err == -1) + { + char *large_program_fullpath = (char *)::malloc(len + 1); + + err = _NSGetExecutablePath(large_program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile(large_program_fullpath, false); + + ::free(large_program_fullpath); + } + } + return g_program_filespec; +} + +bool +HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + // Shallow bundle + raw_path.resize(framework_pos); +#else + // Normal bundle + raw_path.resize(framework_pos); + raw_path.append("/Resources"); +#endif + } + else + { + // Find the bin path relative to the lib path where the cmake-based + // OS X .dylib lives. This is not going to work if the bin and lib + // dir are not both in the same dir. + // + // It is not going to work to do it by the executable path either, + // as in the case of a python script, the executable is python, not + // the lldb driver. + raw_path.append("/../bin"); + FileSpec support_dir_spec(raw_path, true); + if (!support_dir_spec.Exists() || !support_dir_spec.IsDirectory()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoMacOSX::%s(): failed to find support directory", + __FUNCTION__); + return false; + } + + // Get normalization from support_dir_spec. Note the FileSpec resolve + // does not remove '..' in the path. + char *const dir_realpath = realpath(support_dir_spec.GetPath().c_str(), NULL); + if (dir_realpath) + { + raw_path = dir_realpath; + free(dir_realpath); + } + else + { + raw_path = support_dir_spec.GetPath(); + } + } + + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return (bool)file_spec.GetDirectory(); +} + +bool +HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!HostInfo::GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Headers"); + } + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +} + +bool +HostInfoMacOSX::ComputePythonDirectory(FileSpec &file_spec) +{ +#ifndef LLDB_DISABLE_PYTHON + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/Python"); + } + else + { + llvm::SmallString<256> python_version_dir; + llvm::raw_svector_ostream os(python_version_dir); + os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "/site-packages"; + + // We may get our string truncated. Should we protect this with an assert? + raw_path.append(python_version_dir.c_str()); + } + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +#else + return false; +#endif +} + +bool +HostInfoMacOSX::ComputeClangDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath (lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos != std::string::npos) + { + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/Clang"); + } + file_spec.SetFile (raw_path.c_str(), true); + return true; +} + +bool +HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) +{ + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + std::string raw_path = lldb_file_spec.GetPath(); + + size_t framework_pos = raw_path.find("LLDB.framework"); + if (framework_pos == std::string::npos) + return false; + + framework_pos += strlen("LLDB.framework"); + raw_path.resize(framework_pos); + raw_path.append("/Resources/PlugIns"); + file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); + return true; +} + +bool +HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) +{ + FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns", true); + file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); + return true; +} + +void +HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) +{ + // All apple systems support 32 bit execution. + uint32_t cputype, cpusubtype; + uint32_t is_64_bit_capable = false; + size_t len = sizeof(cputype); + ArchSpec host_arch; + // These will tell us about the kernel architecture, which even on a 64 + // bit machine can be 32 bit... + if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) + { + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) + cpusubtype = CPU_TYPE_ANY; + + len = sizeof(is_64_bit_capable); + ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); + + if (is_64_bit_capable) + { + if (cputype & CPU_ARCH_ABI64) + { + // We have a 64 bit kernel on a 64 bit system + arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); + } + else + { + // We have a 64 bit kernel that is returning a 32 bit cputype, the + // cpusubtype will be correct as if it were for a 64 bit architecture + arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, cpusubtype); + } + + // Now we need modify the cpusubtype for the 32 bit slices. + uint32_t cpusubtype32 = cpusubtype; +#if defined(__i386__) || defined(__x86_64__) + if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) + cpusubtype32 = CPU_SUBTYPE_I386_ALL; +#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) + cpusubtype32 = CPU_SUBTYPE_ARM_V7S; +#endif + arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), cpusubtype32); + + if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) + { + // When running on a watch or tv, report the host os correctly +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 + arch_32.GetTriple().setOS(llvm::Triple::TvOS); + arch_64.GetTriple().setOS(llvm::Triple::TvOS); +#else + arch_32.GetTriple().setOS(llvm::Triple::IOS); + arch_64.GetTriple().setOS(llvm::Triple::IOS); +#endif + } + else + { + arch_32.GetTriple().setOS(llvm::Triple::MacOSX); + arch_64.GetTriple().setOS(llvm::Triple::MacOSX); + } + } + else + { + // We have a 32 bit kernel on a 32 bit system + arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + arch_32.GetTriple().setOS(llvm::Triple::WatchOS); +#else + arch_32.GetTriple().setOS(llvm::Triple::IOS); +#endif + arch_64.Clear(); + } + } +} diff --git a/source/Host/macosx/HostThreadMacOSX.mm b/source/Host/macosx/HostThreadMacOSX.mm new file mode 100644 index 0000000000000..c84a78efd9e6c --- /dev/null +++ b/source/Host/macosx/HostThreadMacOSX.mm @@ -0,0 +1,102 @@ +//===-- HostThreadMacOSX.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/macosx/HostThreadMacOSX.h" +#include "lldb/Host/Host.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> + +#include <objc/objc-auto.h> +#include <pthread.h> + +using namespace lldb_private; + +namespace +{ + +pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; +pthread_key_t g_thread_create_key = 0; + +class MacOSXDarwinThread +{ + public: + MacOSXDarwinThread() + : m_pool(nil) + { + // Register our thread with the collector if garbage collection is enabled. + if (objc_collectingEnabled()) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 + // On Leopard and earlier there is no way objc_registerThreadWithCollector + // function, so we do it manually. + auto_zone_register_thread(auto_zone()); +#else + // On SnowLeopard and later we just call the thread registration function. + objc_registerThreadWithCollector(); +#endif + } + else + { + m_pool = [[NSAutoreleasePool alloc] init]; + } + } + + ~MacOSXDarwinThread() + { + if (m_pool) + { + [m_pool drain]; + m_pool = nil; + } + } + + static void + PThreadDestructor(void *v) + { + if (v) + delete static_cast<MacOSXDarwinThread *>(v); + ::pthread_setspecific(g_thread_create_key, NULL); + } + + protected: + NSAutoreleasePool *m_pool; + + private: + DISALLOW_COPY_AND_ASSIGN(MacOSXDarwinThread); +}; + +void +InitThreadCreated() +{ + ::pthread_key_create(&g_thread_create_key, MacOSXDarwinThread::PThreadDestructor); +} +} // namespace + +HostThreadMacOSX::HostThreadMacOSX() + : HostThreadPosix() +{ +} + +HostThreadMacOSX::HostThreadMacOSX(lldb::thread_t thread) + : HostThreadPosix(thread) +{ +} + +lldb::thread_result_t +HostThreadMacOSX::ThreadCreateTrampoline(lldb::thread_arg_t arg) +{ + ::pthread_once(&g_thread_create_once, InitThreadCreated); + if (g_thread_create_key) + { + ::pthread_setspecific(g_thread_create_key, new MacOSXDarwinThread()); + } + + return HostThreadPosix::ThreadCreateTrampoline(arg); +} diff --git a/source/Host/macosx/Symbols.cpp b/source/Host/macosx/Symbols.cpp new file mode 100644 index 0000000000000..f6a18febe6da9 --- /dev/null +++ b/source/Host/macosx/Symbols.cpp @@ -0,0 +1,559 @@ +//===-- Symbols.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Symbols.h" + +// C Includes +#include <dirent.h> +#include <pwd.h> +#include "lldb/Utility/SafeMachO.h" + +// C++ Includes +// Other libraries and framework includes +#include <CoreFoundation/CoreFoundation.h> + +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/CleanUp.h" +#include "Host/macosx/cfcpp/CFCBundle.h" +#include "Host/macosx/cfcpp/CFCData.h" +#include "Host/macosx/cfcpp/CFCReleaser.h" +#include "Host/macosx/cfcpp/CFCString.h" +#include "mach/machine.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::MachO; + +#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices +extern "C" { + +CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url); +CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url); + +} +#endif + +int +LocateMacOSXFilesUsingDebugSymbols +( + const ModuleSpec &module_spec, + ModuleSpec &return_module_spec +) +{ + return_module_spec = module_spec; + return_module_spec.GetFileSpec().Clear(); + return_module_spec.GetSymbolFileSpec().Clear(); + + int items_found = 0; + +#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices + + const UUID *uuid = module_spec.GetUUIDPtr(); + const ArchSpec *arch = module_spec.GetArchitecturePtr(); + + if (uuid && uuid->IsValid()) + { + // Try and locate the dSYM file using DebugSymbols first + const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes(); + if (module_uuid != NULL) + { + CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes (NULL, + module_uuid[0], + module_uuid[1], + module_uuid[2], + module_uuid[3], + module_uuid[4], + module_uuid[5], + module_uuid[6], + module_uuid[7], + module_uuid[8], + module_uuid[9], + module_uuid[10], + module_uuid[11], + module_uuid[12], + module_uuid[13], + module_uuid[14], + module_uuid[15])); + + if (module_uuid_ref.get()) + { + CFCReleaser<CFURLRef> exec_url; + const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (exec_fspec) + { + char exec_cf_path[PATH_MAX]; + if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) + exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL, + (const UInt8 *)exec_cf_path, + strlen(exec_cf_path), + FALSE)); + } + + CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get())); + char path[PATH_MAX]; + + if (dsym_url.get()) + { + if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + if (log) + { + log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for the dSYM", path, uuid->GetAsString().c_str()); + } + FileSpec dsym_filespec(path, path[0] == '~'); + + if (dsym_filespec.GetFileType () == FileSpec::eFileTypeDirectory) + { + dsym_filespec = Symbols::FindSymbolFileInBundle (dsym_filespec, uuid, arch); + ++items_found; + } + else + { + ++items_found; + } + return_module_spec.GetSymbolFileSpec() = dsym_filespec; + } + + bool success = false; + if (log) + { + if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for an exec file", path, uuid->GetAsString().c_str()); + } + + } + + CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get())); + CFDictionaryRef uuid_dict = NULL; + if (dict.get()) + { + CFCString uuid_cfstr (uuid->GetAsString().c_str()); + uuid_dict = static_cast<CFDictionaryRef>(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get())); + } + if (uuid_dict) + { + CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable"))); + if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) + { + if (log) + { + log->Printf ("plist bundle has exec path of %s for UUID %s", path, uuid->GetAsString().c_str()); + } + ++items_found; + FileSpec exec_filespec (path, path[0] == '~'); + if (exec_filespec.Exists()) + { + success = true; + return_module_spec.GetFileSpec() = exec_filespec; + } + } + } + + if (!success) + { + // No dictionary, check near the dSYM bundle for an executable that matches... + if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + char *dsym_extension_pos = ::strstr (path, ".dSYM"); + if (dsym_extension_pos) + { + *dsym_extension_pos = '\0'; + if (log) + { + log->Printf ("Looking for executable binary next to dSYM bundle with name with name %s", path); + } + FileSpec file_spec (path, true); + ModuleSpecList module_specs; + ModuleSpec matched_module_spec; + switch (file_spec.GetFileType()) + { + case FileSpec::eFileTypeDirectory: // Bundle directory? + { + CFCBundle bundle (path); + CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ()); + if (bundle_exe_url.get()) + { + if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + FileSpec bundle_exe_file_spec (path, true); + if (ObjectFile::GetModuleSpecifications(bundle_exe_file_spec, 0, 0, module_specs) && + module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + + { + ++items_found; + return_module_spec.GetFileSpec() = bundle_exe_file_spec; + if (log) + { + log->Printf ("Executable binary %s next to dSYM is compatible; using", path); + } + } + } + } + } + break; + + case FileSpec::eFileTypePipe: // Forget pipes + case FileSpec::eFileTypeSocket: // We can't process socket files + case FileSpec::eFileTypeInvalid: // File doesn't exist... + break; + + case FileSpec::eFileTypeUnknown: + case FileSpec::eFileTypeRegular: + case FileSpec::eFileTypeSymbolicLink: + case FileSpec::eFileTypeOther: + if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, module_specs) && + module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + + { + ++items_found; + return_module_spec.GetFileSpec() = file_spec; + if (log) + { + log->Printf ("Executable binary %s next to dSYM is compatible; using", path); + } + } + break; + } + } + } + } + } + } + } + } +#endif // #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) + + return items_found; +} + +FileSpec +Symbols::FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec, + const lldb_private::UUID *uuid, + const ArchSpec *arch) +{ + char path[PATH_MAX]; + + FileSpec dsym_fspec; + + if (dsym_bundle_fspec.GetPath(path, sizeof(path))) + { + ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1); + + lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir); + if (dirp.is_valid()) + { + dsym_fspec.GetDirectory().SetCString(path); + struct dirent* dp; + while ((dp = readdir(dirp.get())) != NULL) + { + // Only search directories + if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) + { + if (dp->d_namlen == 1 && dp->d_name[0] == '.') + continue; + + if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') + continue; + } + + if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) + { + dsym_fspec.GetFilename().SetCString(dp->d_name); + ModuleSpecList module_specs; + if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) + { + ModuleSpec spec; + for (size_t i = 0; i < module_specs.GetSize(); ++i) + { + assert(module_specs.GetModuleSpecAtIndex(i, spec)); + if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && + (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch)))) + { + return dsym_fspec; + } + } + } + } + } + } + } + dsym_fspec.Clear(); + return dsym_fspec; +} + +static bool +GetModuleSpecInfoFromUUIDDictionary (CFDictionaryRef uuid_dict, ModuleSpec &module_spec) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + bool success = false; + if (uuid_dict != NULL && CFGetTypeID (uuid_dict) == CFDictionaryGetTypeID ()) + { + std::string str; + CFStringRef cf_str; + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSymbolRichExecutable")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + if (CFCString::FileSystemRepresentation(cf_str, str)) + { + module_spec.GetFileSpec().SetFile (str.c_str(), true); + if (log) + { + log->Printf ("From dsymForUUID plist: Symbol rich executable is at '%s'", str.c_str()); + } + } + } + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGDSYMPath")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + if (CFCString::FileSystemRepresentation(cf_str, str)) + { + module_spec.GetSymbolFileSpec().SetFile (str.c_str(), true); + success = true; + if (log) + { + log->Printf ("From dsymForUUID plist: dSYM is at '%s'", str.c_str()); + } + } + } + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGArchitecture")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + if (CFCString::FileSystemRepresentation(cf_str, str)) + module_spec.GetArchitecture().SetTriple(str.c_str()); + } + + std::string DBGBuildSourcePath; + std::string DBGSourcePath; + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGBuildSourcePath")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath); + } + + cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSourcePath")); + if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) + { + CFCString::FileSystemRepresentation(cf_str, DBGSourcePath); + } + + if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) + { + module_spec.GetSourceMappingList().Append (ConstString(DBGBuildSourcePath.c_str()), ConstString(DBGSourcePath.c_str()), true); + } + } + return success; +} + + +bool +Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup) +{ + bool success = false; + const UUID *uuid_ptr = module_spec.GetUUIDPtr(); + const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); + + // It's expensive to check for the DBGShellCommands defaults setting, only do it once per + // lldb run and cache the result. + static bool g_have_checked_for_dbgshell_command = false; + static const char *g_dbgshell_command = NULL; + if (g_have_checked_for_dbgshell_command == false) + { + g_have_checked_for_dbgshell_command = true; + CFTypeRef defaults_setting = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols")); + if (defaults_setting && CFGetTypeID (defaults_setting) == CFStringGetTypeID()) + { + char cstr_buf[PATH_MAX]; + if (CFStringGetCString ((CFStringRef) defaults_setting, cstr_buf, sizeof (cstr_buf), kCFStringEncodingUTF8)) + { + g_dbgshell_command = strdup (cstr_buf); // this malloc'ed memory will never be freed + } + } + if (defaults_setting) + { + CFRelease (defaults_setting); + } + } + + // When g_dbgshell_command is NULL, the user has not enabled the use of an external program + // to find the symbols, don't run it for them. + if (force_lookup == false && g_dbgshell_command == NULL) + { + return false; + } + + if (uuid_ptr || (file_spec_ptr && file_spec_ptr->Exists())) + { + static bool g_located_dsym_for_uuid_exe = false; + static bool g_dsym_for_uuid_exe_exists = false; + static char g_dsym_for_uuid_exe_path[PATH_MAX]; + if (!g_located_dsym_for_uuid_exe) + { + g_located_dsym_for_uuid_exe = true; + const char *dsym_for_uuid_exe_path_cstr = getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE"); + FileSpec dsym_for_uuid_exe_spec; + if (dsym_for_uuid_exe_path_cstr) + { + dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, true); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + } + + if (!g_dsym_for_uuid_exe_exists) + { + dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", false); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + if (!g_dsym_for_uuid_exe_exists) + { + long bufsize; + if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) + { + char buffer[bufsize]; + struct passwd pwd; + struct passwd *tilde_rc = NULL; + // we are a library so we need to use the reentrant version of getpwnam() + if (getpwnam_r ("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 + && tilde_rc + && tilde_rc->pw_dir) + { + std::string dsymforuuid_path(tilde_rc->pw_dir); + dsymforuuid_path += "/bin/dsymForUUID"; + dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), false); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + } + } + } + } + if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) + { + dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, true); + g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); + } + + if (g_dsym_for_uuid_exe_exists) + dsym_for_uuid_exe_spec.GetPath (g_dsym_for_uuid_exe_path, sizeof(g_dsym_for_uuid_exe_path)); + } + if (g_dsym_for_uuid_exe_exists) + { + std::string uuid_str; + char file_path[PATH_MAX]; + file_path[0] = '\0'; + + if (uuid_ptr) + uuid_str = uuid_ptr->GetAsString(); + + if (file_spec_ptr) + file_spec_ptr->GetPath(file_path, sizeof(file_path)); + + StreamString command; + if (!uuid_str.empty()) + command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, uuid_str.c_str()); + else if (file_path[0] != '\0') + command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, file_path); + + if (!command.GetString().empty()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + int exit_status = -1; + int signo = -1; + std::string command_output; + if (log) + { + if (!uuid_str.empty()) + log->Printf("Calling %s with UUID %s to find dSYM", g_dsym_for_uuid_exe_path, uuid_str.c_str()); + else if (file_path[0] != '\0') + log->Printf("Calling %s with file %s to find dSYM", g_dsym_for_uuid_exe_path, file_path); + } + Error error = Host::RunShellCommand (command.GetData(), + NULL, // current working directory + &exit_status, // Exit status + &signo, // Signal int * + &command_output, // Command output + 30, // Large timeout to allow for long dsym download times + false); // Don't run in a shell (we don't need shell expansion) + if (error.Success() && exit_status == 0 && !command_output.empty()) + { + CFCData data (CFDataCreateWithBytesNoCopy (NULL, + (const UInt8 *)command_output.data(), + command_output.size(), + kCFAllocatorNull)); + + CFCReleaser<CFDictionaryRef> plist((CFDictionaryRef)::CFPropertyListCreateFromXMLData (NULL, data.get(), kCFPropertyListImmutable, NULL)); + + if (plist.get() && CFGetTypeID (plist.get()) == CFDictionaryGetTypeID ()) + { + if (!uuid_str.empty()) + { + CFCString uuid_cfstr(uuid_str.c_str()); + CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue (plist.get(), uuid_cfstr.get()); + success = GetModuleSpecInfoFromUUIDDictionary (uuid_dict, module_spec); + } + else + { + const CFIndex num_values = ::CFDictionaryGetCount(plist.get()); + if (num_values > 0) + { + std::vector<CFStringRef> keys (num_values, NULL); + std::vector<CFDictionaryRef> values (num_values, NULL); + ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]); + if (num_values == 1) + { + return GetModuleSpecInfoFromUUIDDictionary (values[0], module_spec); + } + else + { + for (CFIndex i=0; i<num_values; ++i) + { + ModuleSpec curr_module_spec; + if (GetModuleSpecInfoFromUUIDDictionary (values[i], curr_module_spec)) + { + if (module_spec.GetArchitecture().IsCompatibleMatch(curr_module_spec.GetArchitecture())) + { + module_spec = curr_module_spec; + return true; + } + } + } + } + } + } + } + } + else + { + if (log) + { + if (!uuid_str.empty()) + log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, uuid_str.c_str()); + else if (file_path[0] != '\0') + log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, file_path); + } + } + } + } + } + return success; +} + diff --git a/source/Host/macosx/ThisThread.cpp b/source/Host/macosx/ThisThread.cpp new file mode 100644 index 0000000000000..95c7f2bf1e38b --- /dev/null +++ b/source/Host/macosx/ThisThread.cpp @@ -0,0 +1,39 @@ +//===-- ThisThread.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/ThisThread.h" + +#include <pthread.h> + +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name) +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + ::pthread_setname_np(name); +#endif +} + +void +ThisThread::GetName(llvm::SmallVectorImpl<char> &name) +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + char pthread_name[1024]; + dispatch_queue_t current_queue = ::dispatch_get_current_queue(); + if (current_queue != NULL) + { + const char *queue_name = dispatch_queue_get_label(current_queue); + if (queue_name && queue_name[0]) + { + name = queue_name; + } + } +#endif +} diff --git a/source/Host/macosx/cfcpp/CFCBundle.cpp b/source/Host/macosx/cfcpp/CFCBundle.cpp new file mode 100644 index 0000000000000..71b074993661d --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCBundle.cpp @@ -0,0 +1,99 @@ +//===-- CFCBundle.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCBundle.h" +#include "CFCString.h" + +//---------------------------------------------------------------------- +// CFCBundle constructor +//---------------------------------------------------------------------- +CFCBundle::CFCBundle(const char *path) : + CFCReleaser<CFBundleRef>() +{ + if (path && path[0]) + SetPath(path); +} + +CFCBundle::CFCBundle(CFURLRef url) : + CFCReleaser<CFBundleRef>(url ? CFBundleCreate(NULL, url) : NULL) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCBundle::~CFCBundle() +{ +} + +//---------------------------------------------------------------------- +// Set the path for a bundle by supplying a +//---------------------------------------------------------------------- +bool +CFCBundle::SetPath (const char *path) +{ + CFAllocatorRef alloc = kCFAllocatorDefault; + // Release our old bundle and URL + reset(); + + // Make a CFStringRef from the supplied path + CFCString cf_path; + cf_path.SetFileSystemRepresentation(path); + if (cf_path.get()) + { + // Make our Bundle URL + CFCReleaser<CFURLRef> bundle_url (::CFURLCreateWithFileSystemPath (alloc, cf_path.get(), kCFURLPOSIXPathStyle, true)); + if (bundle_url.get()) + reset (::CFBundleCreate (alloc, bundle_url.get())); + } + return get() != NULL; +} + +bool +CFCBundle::GetPath (char *dst, size_t dst_len) +{ + CFBundleRef bundle = get(); + if (bundle) + { + CFCReleaser<CFURLRef> bundle_url (CFBundleCopyBundleURL (bundle)); + if (bundle_url.get()) + { + Boolean resolveAgainstBase = 0; + return ::CFURLGetFileSystemRepresentation (bundle_url.get(), resolveAgainstBase, (UInt8 *)dst, dst_len) != 0; + } + } + return false; +} + +CFStringRef +CFCBundle::GetIdentifier () const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return ::CFBundleGetIdentifier (bundle); + return NULL; +} + +CFTypeRef +CFCBundle::GetValueForInfoDictionaryKey(CFStringRef key) const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return ::CFBundleGetValueForInfoDictionaryKey(bundle, key); + return NULL; +} + +CFURLRef +CFCBundle::CopyExecutableURL () const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return CFBundleCopyExecutableURL(bundle); + return NULL; +} diff --git a/source/Host/macosx/cfcpp/CFCBundle.h b/source/Host/macosx/cfcpp/CFCBundle.h new file mode 100644 index 0000000000000..1cd1b681af840 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCBundle.h @@ -0,0 +1,50 @@ +//===-- CFCBundle.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFBundle_h_ +#define CoreFoundationCPP_CFBundle_h_ + +#include "CFCReleaser.h" + +class CFCBundle : public CFCReleaser<CFBundleRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCBundle (const char *path = NULL); + CFCBundle (CFURLRef url); + + virtual + ~CFCBundle(); + + CFURLRef + CopyExecutableURL () const; + + CFStringRef + GetIdentifier () const; + + CFTypeRef + GetValueForInfoDictionaryKey(CFStringRef key) const; + + bool + GetPath (char *dst, size_t dst_len); + + bool + SetPath (const char *path); + +private: + // Disallow copy and assignment constructors + CFCBundle(const CFCBundle&); + + const CFCBundle& + operator=(const CFCBundle&); +}; + +#endif // #ifndef CoreFoundationCPP_CFBundle_h_ diff --git a/source/Host/macosx/cfcpp/CFCData.cpp b/source/Host/macosx/cfcpp/CFCData.cpp new file mode 100644 index 0000000000000..4f49368ad8adf --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCData.cpp @@ -0,0 +1,82 @@ +//===-- CFCData.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCData.h" + +//---------------------------------------------------------------------- +// CFCData constructor +//---------------------------------------------------------------------- +CFCData::CFCData(CFDataRef data) : + CFCReleaser<CFDataRef>(data) +{ + +} + +//---------------------------------------------------------------------- +// CFCData copy constructor +//---------------------------------------------------------------------- +CFCData::CFCData(const CFCData& rhs) : + CFCReleaser<CFDataRef>(rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFCData copy constructor +//---------------------------------------------------------------------- +CFCData& +CFCData::operator=(const CFCData& rhs) + +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCData::~CFCData() +{ +} + + +CFIndex +CFCData::GetLength() const +{ + CFDataRef data = get(); + if (data) + return CFDataGetLength (data); + return 0; +} + + +const uint8_t* +CFCData::GetBytePtr() const +{ + CFDataRef data = get(); + if (data) + return CFDataGetBytePtr (data); + return NULL; +} + +CFDataRef +CFCData::Serialize(CFPropertyListRef plist, CFPropertyListFormat format) +{ + CFAllocatorRef alloc = kCFAllocatorDefault; + reset(); + CFCReleaser<CFWriteStreamRef> stream (::CFWriteStreamCreateWithAllocatedBuffers (alloc, alloc)); + ::CFWriteStreamOpen (stream.get()); + CFIndex len = ::CFPropertyListWriteToStream (plist, stream.get(), format, NULL); + if (len > 0) + reset((CFDataRef)::CFWriteStreamCopyProperty (stream.get(), kCFStreamPropertyDataWritten)); + ::CFWriteStreamClose (stream.get()); + return get(); +} + diff --git a/source/Host/macosx/cfcpp/CFCData.h b/source/Host/macosx/cfcpp/CFCData.h new file mode 100644 index 0000000000000..6a718f54c0555 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCData.h @@ -0,0 +1,35 @@ +//===-- CFCData.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFData_h_ +#define CoreFoundationCPP_CFData_h_ + +#include "CFCReleaser.h" + +class CFCData : public CFCReleaser<CFDataRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCData(CFDataRef data = NULL); + CFCData(const CFCData& rhs); + CFCData& operator=(const CFCData& rhs); + virtual ~CFCData(); + + CFDataRef Serialize(CFPropertyListRef plist, CFPropertyListFormat format); + const uint8_t* GetBytePtr () const; + CFIndex GetLength () const; +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFCData can see and modify these + //------------------------------------------------------------------ +}; + +#endif // #ifndef CoreFoundationCPP_CFData_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableArray.cpp b/source/Host/macosx/cfcpp/CFCMutableArray.cpp new file mode 100644 index 0000000000000..c3c0a11193a7c --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableArray.cpp @@ -0,0 +1,166 @@ +//===-- CFCMutableArray.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableArray.h" +#include "CFCString.h" + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableArray::CFCMutableArray(CFMutableArrayRef s) : + CFCReleaser<CFMutableArrayRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableArray copy constructor +//---------------------------------------------------------------------- +CFCMutableArray::CFCMutableArray(const CFCMutableArray& rhs) : + CFCReleaser<CFMutableArrayRef> (rhs) // NOTE: this won't make a copy of the array, just add a new reference to it +{ +} + +//---------------------------------------------------------------------- +// CFCMutableArray copy constructor +//---------------------------------------------------------------------- +CFCMutableArray& +CFCMutableArray::operator=(const CFCMutableArray& rhs) +{ + if (this != &rhs) + *this = rhs; // NOTE: this operator won't make a copy of the array, just add a new reference to it + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableArray::~CFCMutableArray() +{ +} + + +CFIndex +CFCMutableArray::GetCount() const +{ + CFMutableArrayRef array = get(); + if (array) + return ::CFArrayGetCount (array); + return 0; +} + +CFIndex +CFCMutableArray::GetCountOfValue(CFRange range, const void *value) const +{ + CFMutableArrayRef array = get(); + if (array) + return ::CFArrayGetCountOfValue (array, range, value); + return 0; +} + +CFIndex +CFCMutableArray::GetCountOfValue(const void *value) const +{ + CFMutableArrayRef array = get(); + if (array) + return ::CFArrayGetCountOfValue (array, CFRangeMake(0, GetCount()), value); + return 0; +} + +const void * +CFCMutableArray::GetValueAtIndex(CFIndex idx) const +{ + CFMutableArrayRef array = get(); + if (array) + { + const CFIndex num_array_items = ::CFArrayGetCount (array); + if (0 <= idx && idx < num_array_items) + { + return ::CFArrayGetValueAtIndex (array, idx); + } + } + return NULL; +} + +bool +CFCMutableArray::SetValueAtIndex(CFIndex idx, const void *value) +{ + CFMutableArrayRef array = get(); + if (array != NULL) + { + const CFIndex num_array_items = ::CFArrayGetCount (array); + if (0 <= idx && idx < num_array_items) + { + ::CFArraySetValueAtIndex (array, idx, value); + return true; + } + } + return false; +} + + +bool +CFCMutableArray::AppendValue(const void *value, bool can_create) +{ + CFMutableArrayRef array = get(); + if (array == NULL) + { + if (can_create == false) + return false; + array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + reset ( array ); + } + if (array != NULL) + { + ::CFArrayAppendValue(array, value); + return true; + } + return false; +} + + +bool +CFCMutableArray::AppendCStringAsCFString (const char *s, CFStringEncoding encoding, bool can_create) +{ + CFMutableArrayRef array = get(); + if (array == NULL) + { + if (can_create == false) + return false; + array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + reset ( array ); + } + if (array != NULL) + { + CFCString cf_str (s, encoding); + ::CFArrayAppendValue (array, cf_str.get()); + return true; + } + return false; +} + +bool +CFCMutableArray::AppendFileSystemRepresentationAsCFString (const char *s, bool can_create) +{ + CFMutableArrayRef array = get(); + if (array == NULL) + { + if (can_create == false) + return false; + array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + reset ( array ); + } + if (array != NULL) + { + CFCString cf_path; + cf_path.SetFileSystemRepresentation(s); + ::CFArrayAppendValue (array, cf_path.get()); + return true; + } + return false; +} diff --git a/source/Host/macosx/cfcpp/CFCMutableArray.h b/source/Host/macosx/cfcpp/CFCMutableArray.h new file mode 100644 index 0000000000000..f78cd92ffab16 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableArray.h @@ -0,0 +1,39 @@ +//===-- CFCMutableArray.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableArray_h_ +#define CoreFoundationCPP_CFMutableArray_h_ + +#include "CFCReleaser.h" + +class CFCMutableArray : public CFCReleaser<CFMutableArrayRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCMutableArray(CFMutableArrayRef array = NULL); + CFCMutableArray(const CFCMutableArray& rhs); // This will copy the array contents into a new array + CFCMutableArray& operator=(const CFCMutableArray& rhs); // This will re-use the same array and just bump the ref count + virtual ~CFCMutableArray(); + + CFIndex GetCount() const; + CFIndex GetCountOfValue(const void *value) const; + CFIndex GetCountOfValue(CFRange range, const void *value) const; + const void * GetValueAtIndex(CFIndex idx) const; + bool SetValueAtIndex(CFIndex idx, const void *value); + bool AppendValue(const void *value, bool can_create = true); // Appends value and optionally creates a CFCMutableArray if this class doesn't contain one + bool AppendCStringAsCFString (const char *cstr, + CFStringEncoding encoding = kCFStringEncodingUTF8, + bool can_create = true); + bool AppendFileSystemRepresentationAsCFString (const char *s, + bool can_create = true); +}; + +#endif // #ifndef CoreFoundationCPP_CFMutableArray_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp new file mode 100644 index 0000000000000..bce023bfd616e --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp @@ -0,0 +1,529 @@ +//===-- CFCMutableDictionary.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableDictionary.h" +#include "CFCString.h" +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableDictionary::CFCMutableDictionary(CFMutableDictionaryRef s) : + CFCReleaser<CFMutableDictionaryRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableDictionary copy constructor +//---------------------------------------------------------------------- +CFCMutableDictionary::CFCMutableDictionary(const CFCMutableDictionary& rhs) : + CFCReleaser<CFMutableDictionaryRef> (rhs) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableDictionary copy constructor +//---------------------------------------------------------------------- +const CFCMutableDictionary& +CFCMutableDictionary::operator=(const CFCMutableDictionary& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableDictionary::~CFCMutableDictionary() +{ +} + + +CFIndex +CFCMutableDictionary::GetCount() const +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetCount (dict); + return 0; +} + +CFIndex +CFCMutableDictionary::GetCountOfKey(const void *key) const + +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetCountOfKey (dict, key); + return 0; +} + +CFIndex +CFCMutableDictionary::GetCountOfValue(const void *value) const + +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetCountOfValue (dict, value); + return 0; +} + +void +CFCMutableDictionary::GetKeysAndValues(const void **keys, const void **values) const +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryGetKeysAndValues (dict, keys, values); +} + + +const void * +CFCMutableDictionary::GetValue(const void *key) const + +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetValue (dict, key); + return NULL; +} + +Boolean +CFCMutableDictionary::GetValueIfPresent(const void *key, const void **value_handle) const +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetValueIfPresent (dict, key, value_handle); + return false; +} + + +CFMutableDictionaryRef +CFCMutableDictionary::Dictionary(bool can_create) +{ + CFMutableDictionaryRef dict = get(); + if (can_create && dict == NULL) + { + dict = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + reset ( dict ); + } + return dict; +} + +bool +CFCMutableDictionary::AddValue(CFStringRef key, const void *value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, value); + return true; + } + return false; +} + +bool +CFCMutableDictionary::SetValue(CFStringRef key, const void *value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, value); + return true; + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt8(CFStringRef key, int8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt8(CFStringRef key, int8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt16(CFStringRef key, int16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt16(CFStringRef key, int16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt32(CFStringRef key, int32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt32(CFStringRef key, int32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt64(CFStringRef key, int64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt64(CFStringRef key, int64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueUInt8(CFStringRef key, uint8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int16_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueUInt8(CFStringRef key, uint8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int16_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + + +bool +CFCMutableDictionary::AddValueUInt16(CFStringRef key, uint16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int32_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueUInt16(CFStringRef key, uint16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int32_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueUInt32(CFStringRef key, uint32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int64_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueUInt32(CFStringRef key, uint32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int64_t sval = value; + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + + +bool +CFCMutableDictionary::AddValueUInt64(CFStringRef key, uint64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + + +bool +CFCMutableDictionary::SetValueUInt64(CFStringRef key, uint64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueDouble(CFStringRef key, double value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueDouble(CFStringRef key, double value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueCString(CFStringRef key, const char *cstr, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCString cf_str(cstr, kCFStringEncodingUTF8); + if (cf_str.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_str.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueCString(CFStringRef key, const char *cstr, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCString cf_str(cstr, kCFStringEncodingUTF8); + if (cf_str.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_str.get()); + return true; + } + } + return false; +} + + +void +CFCMutableDictionary::RemoveAllValues() +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryRemoveAllValues(dict); +} + +void +CFCMutableDictionary::RemoveValue(const void *value) +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryRemoveValue(dict, value); +} +void +CFCMutableDictionary::ReplaceValue(const void *key, const void *value) +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryReplaceValue (dict, key, value); +} + diff --git a/source/Host/macosx/cfcpp/CFCMutableDictionary.h b/source/Host/macosx/cfcpp/CFCMutableDictionary.h new file mode 100644 index 0000000000000..a1cfb68f569e1 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.h @@ -0,0 +1,79 @@ +//===-- CFCMutableDictionary.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableDictionary_h_ +#define CoreFoundationCPP_CFMutableDictionary_h_ + +#include "CFCReleaser.h" + +class CFCMutableDictionary : public CFCReleaser<CFMutableDictionaryRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCMutableDictionary(CFMutableDictionaryRef s = NULL); + CFCMutableDictionary(const CFCMutableDictionary& rhs); + virtual ~CFCMutableDictionary(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const CFCMutableDictionary& + operator=(const CFCMutableDictionary& rhs); + + + CFIndex GetCount() const; + CFIndex GetCountOfKey(const void *value) const; + CFIndex GetCountOfValue(const void *value) const; + void GetKeysAndValues(const void **keys, const void **values) const; + const void * GetValue(const void *key) const; + Boolean GetValueIfPresent(const void *key, const void **value_handle) const; + bool AddValue(CFStringRef key, const void *value, bool can_create = false); + bool SetValue(CFStringRef key, const void *value, bool can_create = false); + bool AddValueSInt8(CFStringRef key, int8_t value, bool can_create = false); + bool SetValueSInt8(CFStringRef key, int8_t value, bool can_create = false); + bool AddValueSInt16(CFStringRef key, int16_t value, bool can_create = false); + bool SetValueSInt16(CFStringRef key, int16_t value, bool can_create = false); + bool AddValueSInt32(CFStringRef key, int32_t value, bool can_create = false); + bool SetValueSInt32(CFStringRef key, int32_t value, bool can_create = false); + bool AddValueSInt64(CFStringRef key, int64_t value, bool can_create = false); + bool SetValueSInt64(CFStringRef key, int64_t value, bool can_create = false); + bool AddValueUInt8(CFStringRef key, uint8_t value, bool can_create = false); + bool SetValueUInt8(CFStringRef key, uint8_t value, bool can_create = false); + bool AddValueUInt16(CFStringRef key, uint16_t value, bool can_create = false); + bool SetValueUInt16(CFStringRef key, uint16_t value, bool can_create = false); + bool AddValueUInt32(CFStringRef key, uint32_t value, bool can_create = false); + bool SetValueUInt32(CFStringRef key, uint32_t value, bool can_create = false); + bool AddValueUInt64(CFStringRef key, uint64_t value, bool can_create = false); + bool SetValueUInt64(CFStringRef key, uint64_t value, bool can_create = false); + bool AddValueDouble(CFStringRef key, double value, bool can_create = false); + bool SetValueDouble(CFStringRef key, double value, bool can_create = false); + bool AddValueCString(CFStringRef key, const char *cstr, bool can_create = false); + bool SetValueCString(CFStringRef key, const char *cstr, bool can_create = false); + void RemoveValue(const void *value); + void ReplaceValue(const void *key, const void *value); + void RemoveAllValues(); + CFMutableDictionaryRef Dictionary(bool can_create); + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFCMutableDictionary can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For CFCMutableDictionary only + //------------------------------------------------------------------ + +}; + + +#endif // CoreFoundationCPP_CFMutableDictionary_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableSet.cpp b/source/Host/macosx/cfcpp/CFCMutableSet.cpp new file mode 100644 index 0000000000000..afc09e180b6b6 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableSet.cpp @@ -0,0 +1,114 @@ +//===-- CFCMutableSet.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableSet.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableSet::CFCMutableSet(CFMutableSetRef s) : + CFCReleaser<CFMutableSetRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableSet copy constructor +//---------------------------------------------------------------------- +CFCMutableSet::CFCMutableSet(const CFCMutableSet& rhs) : + CFCReleaser<CFMutableSetRef> (rhs) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableSet copy constructor +//---------------------------------------------------------------------- +const CFCMutableSet& +CFCMutableSet::operator=(const CFCMutableSet& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableSet::~CFCMutableSet() +{ +} + + +CFIndex +CFCMutableSet::GetCount() const +{ + CFMutableSetRef set = get(); + if (set) + return ::CFSetGetCount (set); + return 0; +} + +CFIndex +CFCMutableSet::GetCountOfValue(const void *value) const +{ + CFMutableSetRef set = get(); + if (set) + return ::CFSetGetCountOfValue (set, value); + return 0; +} + +const void * +CFCMutableSet::GetValue(const void *value) const +{ + CFMutableSetRef set = get(); + if (set) + return ::CFSetGetValue(set, value); + return NULL; +} + + +const void * +CFCMutableSet::AddValue(const void *value, bool can_create) +{ + CFMutableSetRef set = get(); + if (set == NULL) + { + if (can_create == false) + return NULL; + set = ::CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); + reset ( set ); + } + if (set != NULL) + { + ::CFSetAddValue(set, value); + return value; + } + return NULL; +} + +void +CFCMutableSet::RemoveValue(const void *value) +{ + CFMutableSetRef set = get(); + if (set) + ::CFSetRemoveValue(set, value); +} + +void +CFCMutableSet::RemoveAllValues() +{ + CFMutableSetRef set = get(); + if (set) + ::CFSetRemoveAllValues(set); +} + diff --git a/source/Host/macosx/cfcpp/CFCMutableSet.h b/source/Host/macosx/cfcpp/CFCMutableSet.h new file mode 100644 index 0000000000000..78f7a8be81d20 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableSet.h @@ -0,0 +1,53 @@ +//===-- CFCMutableSet.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableSet_h_ +#define CoreFoundationCPP_CFMutableSet_h_ + +#include "CFCReleaser.h" + +class CFCMutableSet : public CFCReleaser<CFMutableSetRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCMutableSet(CFMutableSetRef s = NULL); + CFCMutableSet(const CFCMutableSet& rhs); + virtual ~CFCMutableSet(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const CFCMutableSet& + operator=(const CFCMutableSet& rhs); + + + CFIndex GetCount() const; + CFIndex GetCountOfValue(const void *value) const; + const void * GetValue(const void *value) const; + const void * AddValue(const void *value, bool can_create); + void RemoveValue(const void *value); + void RemoveAllValues(); + + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFCMutableSet can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For CFCMutableSet only + //------------------------------------------------------------------ + +}; + +#endif // CoreFoundationCPP_CFMutableSet_h_ diff --git a/source/Host/macosx/cfcpp/CFCReleaser.h b/source/Host/macosx/cfcpp/CFCReleaser.h new file mode 100644 index 0000000000000..67dd2ead57990 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCReleaser.h @@ -0,0 +1,158 @@ +//===-- CFCReleaser.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFReleaser_h_ +#define CoreFoundationCPP_CFReleaser_h_ + +#include <CoreFoundation/CoreFoundation.h> + +#ifdef __cplusplus + +#include <assert.h> + +//---------------------------------------------------------------------- +// Templatized CF helper class that can own any CF pointer and will +// call CFRelease() on any valid pointer it owns unless that pointer is +// explicitly released using the release() member function. This class +// is designed to mimic the std::auto_ptr<T> class and has all of the +// same functions. The one thing to watch out for is the +// CFCReleaser<T>::release() function won't actually CFRelease any owned +// pointer, it is designed to relinquish ownership of the pointer just +// like std:auto_ptr<T>::release() does. +//---------------------------------------------------------------------- +template <class T> +class CFCReleaser +{ +public: + //---------------------------------------------------------- + // Constructor that takes a pointer to a CF object that is + // to be released when this object goes out of scope + //---------------------------------------------------------- + CFCReleaser(T ptr = NULL) : + _ptr(ptr) + { + } + + //---------------------------------------------------------- + // Copy constructor + // + // Note that copying a CFCReleaser will not transfer + // ownership of the contained pointer, but it will bump its + // reference count. This is where this class differs from + // std::auto_ptr. + //---------------------------------------------------------- + CFCReleaser(const CFCReleaser& rhs) : + _ptr(rhs.get()) + { + if (get()) + ::CFRetain(get()); + } + + + //---------------------------------------------------------- + // The destructor will release the pointer that it contains + // if it has a valid pointer. + //---------------------------------------------------------- + virtual ~CFCReleaser() + { + reset(); + } + + //---------------------------------------------------------- + // Assignment operator. + // + // Note that assigning one CFCReleaser to another will + // not transfer ownership of the contained pointer, but it + // will bump its reference count. This is where this class + // differs from std::auto_ptr. + //---------------------------------------------------------- + CFCReleaser& + operator= (const CFCReleaser<T>& rhs) + { + if (this != &rhs) + { + // Replace our owned pointer with the new one + reset(rhs.get()); + // Retain the current pointer that we own + if (get()) + ::CFRetain(get()); + } + return *this; + } + + //---------------------------------------------------------- + // Get the address of the contained type in case it needs + // to be passed to a function that will fill in a pointer + // value. The function currently will assert if _ptr is not + // NULL because the only time this method should be used is + // if another function will modify the contents, and we + // could leak a pointer if this is not NULL. If the + // assertion fires, check the offending code, or call + // reset() prior to using the "ptr_address()" member to make + // sure any owned objects has CFRelease called on it. + // I had to add the "enforce_null" bool here because some + // API's require the pointer address even though they don't change it. + //---------------------------------------------------------- + T* + ptr_address(bool enforce_null = true) + { + if (enforce_null) + assert (_ptr == NULL); + return &_ptr; + } + + //---------------------------------------------------------- + // Access the pointer itself + //---------------------------------------------------------- + T + get() + { + return _ptr; + } + + const T + get() const + { + return _ptr; + } + + + //---------------------------------------------------------- + // Set a new value for the pointer and CFRelease our old + // value if we had a valid one. + //---------------------------------------------------------- + void + reset(T ptr = NULL) + { + if ((_ptr != NULL) && (ptr != _ptr)) + ::CFRelease(_ptr); + _ptr = ptr; + } + + //---------------------------------------------------------- + // Release ownership without calling CFRelease. This class + // is designed to mimic std::auto_ptr<T>, so the release + // method releases ownership of the contained pointer + // and does NOT call CFRelease. + //---------------------------------------------------------- + T + release() + { + T tmp = _ptr; + _ptr = NULL; + return tmp; + } + +private: + T _ptr; +}; + +#endif // #ifdef __cplusplus +#endif // #ifndef CoreFoundationCPP_CFReleaser_h_ + diff --git a/source/Host/macosx/cfcpp/CFCString.cpp b/source/Host/macosx/cfcpp/CFCString.cpp new file mode 100644 index 0000000000000..81a96b824999c --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCString.cpp @@ -0,0 +1,195 @@ +//===-- CFCString.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCString.h" +#include <string> +#include <glob.h> + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCString::CFCString(CFStringRef s) : + CFCReleaser<CFStringRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCString copy constructor +//---------------------------------------------------------------------- +CFCString::CFCString(const CFCString& rhs) : + CFCReleaser<CFStringRef> (rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFCString copy constructor +//---------------------------------------------------------------------- +CFCString& +CFCString::operator=(const CFCString& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +CFCString::CFCString (const char *cstr, CFStringEncoding cstr_encoding) : + CFCReleaser<CFStringRef> () +{ + if (cstr && cstr[0]) + { + reset(::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding)); + } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCString::~CFCString() +{ +} + +const char * +CFCString::GetFileSystemRepresentation(std::string& s) +{ + return CFCString::FileSystemRepresentation(get(), s); +} + +CFStringRef +CFCString::SetFileSystemRepresentation (const char *path) +{ + CFStringRef new_value = NULL; + if (path && path[0]) + new_value = ::CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, path); + reset(new_value); + return get(); +} + + +CFStringRef +CFCString::SetFileSystemRepresentationFromCFType (CFTypeRef cf_type) +{ + CFStringRef new_value = NULL; + if (cf_type != NULL) + { + CFTypeID cf_type_id = ::CFGetTypeID(cf_type); + + if (cf_type_id == ::CFStringGetTypeID()) + { + // Retain since we are using the existing object + new_value = (CFStringRef)::CFRetain(cf_type); + } + else if (cf_type_id == ::CFURLGetTypeID()) + { + new_value = ::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle); + } + } + reset(new_value); + return get(); +} + +CFStringRef +CFCString::SetFileSystemRepresentationAndExpandTilde (const char *path) +{ + std::string expanded_path; + if (CFCString::ExpandTildeInPath(path, expanded_path)) + SetFileSystemRepresentation(expanded_path.c_str()); + else + reset(); + return get(); +} + +const char * +CFCString::UTF8(std::string& str) +{ + return CFCString::UTF8(get(), str); +} + +// Static function that puts a copy of the UTF8 contents of CF_STR into STR +// and returns the C string pointer that is contained in STR when successful, else +// NULL is returned. This allows the std::string parameter to own the extracted string, +// and also allows that string to be returned as a C string pointer that can be used. + +const char * +CFCString::UTF8 (CFStringRef cf_str, std::string& str) +{ + if (cf_str) + { + const CFStringEncoding encoding = kCFStringEncodingUTF8; + CFIndex max_utf8_str_len = CFStringGetLength (cf_str); + max_utf8_str_len = CFStringGetMaximumSizeForEncoding (max_utf8_str_len, encoding); + if (max_utf8_str_len > 0) + { + str.resize(max_utf8_str_len); + if (!str.empty()) + { + if (CFStringGetCString (cf_str, &str[0], str.size(), encoding)) + { + str.resize(strlen(str.c_str())); + return str.c_str(); + } + } + } + } + return NULL; +} + +const char* +CFCString::ExpandTildeInPath(const char* path, std::string &expanded_path) +{ + glob_t globbuf; + if (::glob (path, GLOB_TILDE, NULL, &globbuf) == 0) + { + expanded_path = globbuf.gl_pathv[0]; + ::globfree (&globbuf); + } + else + expanded_path.clear(); + + return expanded_path.c_str(); +} + +// Static function that puts a copy of the file system representation of CF_STR +// into STR and returns the C string pointer that is contained in STR when +// successful, else NULL is returned. This allows the std::string parameter +// to own the extracted string, and also allows that string to be returned as +// a C string pointer that can be used. + +const char * +CFCString::FileSystemRepresentation (CFStringRef cf_str, std::string& str) +{ + if (cf_str) + { + CFIndex max_length = ::CFStringGetMaximumSizeOfFileSystemRepresentation (cf_str); + if (max_length > 0) + { + str.resize(max_length); + if (!str.empty()) + { + if (::CFStringGetFileSystemRepresentation (cf_str, &str[0], str.size())) + { + str.erase(::strlen(str.c_str())); + return str.c_str(); + } + } + } + } + str.erase(); + return NULL; +} + + +CFIndex +CFCString::GetLength() const +{ + CFStringRef str = get(); + if (str) + return CFStringGetLength (str); + return 0; +} diff --git a/source/Host/macosx/cfcpp/CFCString.h b/source/Host/macosx/cfcpp/CFCString.h new file mode 100644 index 0000000000000..27c090313ece6 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCString.h @@ -0,0 +1,41 @@ +//===-- CFCString.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFString_h_ +#define CoreFoundationCPP_CFString_h_ + +#include <iosfwd> + +#include "CFCReleaser.h" + +class CFCString : public CFCReleaser<CFStringRef> +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCString (CFStringRef cf_str = NULL); + CFCString (const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8); + CFCString (const CFCString& rhs); + CFCString& operator= (const CFCString& rhs); + virtual ~CFCString (); + + const char * GetFileSystemRepresentation (std::string& str); + CFStringRef SetFileSystemRepresentation (const char *path); + CFStringRef SetFileSystemRepresentationFromCFType (CFTypeRef cf_type); + CFStringRef SetFileSystemRepresentationAndExpandTilde (const char *path); + const char * UTF8 (std::string& str); + CFIndex GetLength() const; + static const char *UTF8 (CFStringRef cf_str, std::string& str); + static const char *FileSystemRepresentation (CFStringRef cf_str, std::string& str); + static const char *ExpandTildeInPath(const char* path, std::string &expanded_path); + +}; + +#endif // #ifndef CoreFoundationCPP_CFString_h_ diff --git a/source/Host/macosx/cfcpp/CoreFoundationCPP.h b/source/Host/macosx/cfcpp/CoreFoundationCPP.h new file mode 100644 index 0000000000000..6843e2649cdab --- /dev/null +++ b/source/Host/macosx/cfcpp/CoreFoundationCPP.h @@ -0,0 +1,30 @@ +//===-- CoreFoundationCPP.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// CoreFoundationCPP.h +// CoreFoundationCPP +// +// Created by Greg Clayton on 4/23/09. +// +// +//---------------------------------------------------------------------- + +#ifndef CoreFoundationCPP_CoreFoundationCPP_H_ +#define CoreFoundationCPP_CoreFoundationCPP_H_ + +#include <CoreFoundationCPP/CFCBundle.h> +#include <CoreFoundationCPP/CFCData.h> +#include <CoreFoundationCPP/CFCReleaser.h> +#include <CoreFoundationCPP/CFCMutableArray.h> +#include <CoreFoundationCPP/CFCMutableDictionary.h> +#include <CoreFoundationCPP/CFCMutableSet.h> +#include <CoreFoundationCPP/CFCString.h> + +#endif // CoreFoundationCPP_CoreFoundationCPP_H_ |