From 08bd04582d7d51c8b067b8e79bed01ea2ca64393 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Sat, 22 Jun 2013 23:01:00 +0200 Subject: [PATCH] Add support for running fruitstrap with LLDB. --- fruitstrap.c | 232 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 157 insertions(+), 75 deletions(-) diff --git a/fruitstrap.c b/fruitstrap.c index 5f5fd9c2..b22d1d56 100644 --- a/fruitstrap.c +++ b/fruitstrap.c @@ -3,42 +3,60 @@ #import #include #include +#include +#include #include #include #include #include +#include +#include #include "MobileDevice.h" #define FDVENDOR_PATH "/tmp/fruitstrap-remote-debugserver" -#define PREP_CMDS_PATH "/tmp/fruitstrap-gdb-prep-cmds" -#define GDB_SHELL "`xcode-select -print-path`/Platforms/iPhoneOS.platform/Developer/usr/libexec/gdb/gdb-arm-apple-darwin --arch armv7 -q -x " PREP_CMDS_PATH +#define PREP_CMDS_PATH "/tmp/fruitstrap-lldb-prep-cmds" +#define PYTHON_MODULE_PATH "/tmp/fruitstrap.py" +#define LLDB_SHELL "/usr/bin/lldb -s " PREP_CMDS_PATH #define PRINT(...) if (!quiet) printf(__VA_ARGS__) -// approximation of what Xcode does: -#define GDB_PREP_CMDS CFSTR("set mi-show-protections off\n\ - set auto-raise-load-levels 1\n\ - set shlib-path-substitutions /usr \"{ds_path}/Symbols/usr\" /System \"{ds_path}/Symbols/System\" \"{device_container}\" \"{disk_container}\" \"/private{device_container}\" \"{disk_container}\" /Developer \"{ds_path}/Symbols/Developer\"\n\ - set remote max-packet-size 1024\n\ - set sharedlibrary check-uuids on\n\ - set env NSUnbufferedIO YES\n\ - set minimal-signal-handling 1\n\ - set sharedlibrary load-rules \\\".*\\\" \\\".*\\\" container\n\ - set inferior-auto-start-dyld 0\n\ - file \"{disk_app}\"\n\ - set remote executable-directory {device_app}\n\ - set remote noack-mode 1\n\ - set trust-readonly-sections 1\n\ - target remote-mobile " FDVENDOR_PATH "\n\ - mem 0x1000 0x3fffffff cache\n\ - mem 0x40000000 0xffffffff none\n\ - mem 0x00000000 0x0fff none\n\ - run {args}\n\ - set minimal-signal-handling 0\n\ - set inferior-auto-start-cfm off\n\ - set sharedLibrary load-rules dyld \".*libobjc.*\" all dyld \".*CoreFoundation.*\" all dyld \".*Foundation.*\" all dyld \".*libSystem.*\" all dyld \".*AppKit.*\" all dyld \".*PBGDBIntrospectionSupport.*\" all dyld \".*/usr/lib/dyld.*\" all dyld \".*CarbonDataFormatters.*\" all dyld \".*libauto.*\" all dyld \".*CFDataFormatters.*\" all dyld \"/System/Library/Frameworks\\\\\\\\|/System/Library/PrivateFrameworks\\\\\\\\|/usr/lib\" extern dyld \".*\" all exec \".*\" all\n\ - sharedlibrary apply-load-rules all\n\ - set inferior-auto-start-dyld 1\n") +/* + * Startup script passed to lldb. + * To see how xcode interacts with lldb, put this into .lldbinit: + * log enable -v -f /Users/vargaz/lldb.log lldb all + * log enable -v -f /Users/vargaz/gdb-remote.log gdb-remote all + */ +#define LLDB_PREP_CMDS CFSTR("\ + script fruitstrap_device_app=\"{device_app}\"\n\ + script fruitstrap_connect_url=\"connect://127.0.0.1:12345\"\n\ + platform select remote-ios\n\ + target create \"{disk_app}\"\n\ + #settings set target.process.extra-startup-command \"QSetLogging:bitmask=LOG_ALL;\"\n \ + command script import \"" PYTHON_MODULE_PATH "\"\n\ +") + +/* + * Some things do not seem to work when using the normal commands like process connect/launch, so we invoke them + * through the python interface. Also, Launch () doesn't seem to work when ran from init_module (), so we add + * a command which can be used by the user to run it. + */ +#define LLDB_FRUITSTRAP_MODULE CFSTR("\ +import lldb\n\ +\n\ +def __lldb_init_module(debugger, internal_dict):\n\ + # These two are passed in by the script which loads us\n\ + device_app=internal_dict['fruitstrap_device_app']\n\ + connect_url=internal_dict['fruitstrap_connect_url']\n\ + lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))\n\ + lldb.debugger.HandleCommand (\"command script add -s asynchronous -f fruitstrap.fsrun_command run\")\n\ + error=lldb.SBError()\n\ + lldb.target.ConnectRemote(lldb.target.GetDebugger().GetListener(),connect_url,None,error)\n\ + print (\"Use 'run' to start the app.\")\n\ +\n\ +def fsrun_command(debugger, command, result, internal_dict):\n\ + error=lldb.SBError()\n\ + lldb.target.Launch(lldb.SBLaunchInfo(None),error)\n\ +") typedef enum { OP_NONE, @@ -296,41 +314,6 @@ void operation_callback(CFDictionaryRef dict, int arg) { PRINT("[%3d%%] %s\n", (percent / 2) + 50, CFStringGetCStringPtr(status, kCFStringEncodingMacRoman)); } -void fdvendor_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) { - CFSocketNativeHandle socket = (CFSocketNativeHandle)(*((CFSocketNativeHandle *)data)); - - struct msghdr message; - struct iovec iov[1]; - struct cmsghdr *control_message = NULL; - char ctrl_buf[CMSG_SPACE(sizeof(int))]; - char dummy_data[1]; - - memset(&message, 0, sizeof(struct msghdr)); - memset(ctrl_buf, 0, CMSG_SPACE(sizeof(int))); - - dummy_data[0] = ' '; - iov[0].iov_base = dummy_data; - iov[0].iov_len = sizeof(dummy_data); - - message.msg_name = NULL; - message.msg_namelen = 0; - message.msg_iov = iov; - message.msg_iovlen = 1; - message.msg_controllen = CMSG_SPACE(sizeof(int)); - message.msg_control = ctrl_buf; - - control_message = CMSG_FIRSTHDR(&message); - control_message->cmsg_level = SOL_SOCKET; - control_message->cmsg_type = SCM_RIGHTS; - control_message->cmsg_len = CMSG_LEN(sizeof(int)); - - *((int *) CMSG_DATA(control_message)) = gdbfd; - - sendmsg(socket, &message, 0); - CFSocketInvalidate(s); - CFRelease(s); -} - CFURLRef copy_device_app_url(AMDeviceRef device, CFStringRef identifier) { CFDictionaryRef result; assert(AMDeviceLookupApplications(device, 0, &result) == 0); @@ -341,6 +324,8 @@ CFURLRef copy_device_app_url(AMDeviceRef device, CFStringRef identifier) { CFStringRef app_path = CFDictionaryGetValue(app_dict, CFSTR("Path")); assert(app_path != NULL); +// printf ("AAA: %s\n", CFStringGetCStringPtr(app_path, CFStringGetSystemEncoding())); + CFURLRef url = CFURLCreateWithFileSystemPath(NULL, app_path, kCFURLPOSIXPathStyle, true); CFRelease(result); return url; @@ -361,8 +346,8 @@ CFStringRef copy_disk_app_identifier(CFURLRef disk_app_url) { return bundle_identifier; } -void write_gdb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) { - CFMutableStringRef cmds = CFStringCreateMutableCopy(NULL, 0, GDB_PREP_CMDS); +void write_lldb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) { + CFMutableStringRef cmds = CFStringCreateMutableCopy(NULL, 0, LLDB_PREP_CMDS); CFRange range = { 0, CFStringGetLength(cmds) }; CFStringRef ds_path = copy_device_support_path(device); @@ -406,6 +391,12 @@ void write_gdb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) { fwrite(CFDataGetBytePtr(cmds_data), CFDataGetLength(cmds_data), 1, out); fclose(out); + CFMutableStringRef pmodule = CFStringCreateMutableCopy(NULL, 0, LLDB_FRUITSTRAP_MODULE); + CFDataRef pmodule_data = CFStringCreateExternalRepresentation(NULL, pmodule, kCFStringEncodingASCII, 0); + out = fopen(PYTHON_MODULE_PATH, "w"); + fwrite(CFDataGetBytePtr(pmodule_data), CFDataGetLength(pmodule_data), 1, out); + fclose(out); + CFRelease(cmds); if (ds_path != NULL) CFRelease(ds_path); CFRelease(bundle_identifier); @@ -420,22 +411,112 @@ void write_gdb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) { CFRelease(cmds_data); } + +CFSocketRef server_socket; +CFSocketRef lldb_socket; +CFWriteStreamRef serverWriteStream = NULL; +CFWriteStreamRef lldbWriteStream = NULL; + +void +server_callback (CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) +{ + int res; + + //PRINT ("server: %s\n", CFDataGetBytePtr (data)); + + if (CFDataGetLength (data) == 0) { + // FIXME: Close the socket + //shutdown (CFSocketGetNative (lldb_socket), SHUT_RDWR); + //close (CFSocketGetNative (lldb_socket)); + return; + } + res = write (CFSocketGetNative (lldb_socket), CFDataGetBytePtr (data), CFDataGetLength (data)); +} + +void lldb_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) +{ + //PRINT ("lldb: %s\n", CFDataGetBytePtr (data)); + + if (CFDataGetLength (data) == 0) + return; + write (gdbfd, CFDataGetBytePtr (data), CFDataGetLength (data)); +} + +void fdvendor_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) { + CFSocketNativeHandle socket = (CFSocketNativeHandle)(*((CFSocketNativeHandle *)data)); + + assert (callbackType == kCFSocketAcceptCallBack); + //PRINT ("callback!\n"); + + lldb_socket = CFSocketCreateWithNative(NULL, socket, kCFSocketDataCallBack, &lldb_callback, NULL); + CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, lldb_socket, 0), kCFRunLoopCommonModes); +} + void start_remote_debug_server(AMDeviceRef device) { + char buf [256]; + int res, err, i; + char msg [256]; + int chsum, len; + struct stat s; + socklen_t buflen; + struct sockaddr name; + int namelen; + assert(AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL) == 0); + assert (gdbfd); + +#if 0 + //sprintf (msg, "$qC#00"); + sprintf (msg, "$QSetLogging:bitmask=LOG_ALL;#00"); + chsum = 0; + len = strlen (msg); + for (i = 1; i < len - 3; ++i) + chsum += (int)msg [i]; + chsum = chsum % 256; + sprintf (msg + len - 2, "%x", chsum); + PRINT ("%s", msg); + err = write (gdbfd, msg, strlen (msg)); + PRINT ("Z: %d\n", err); + PRINT("X: %d\n", gdbfd); + err = read (gdbfd, buf, 256); + PRINT("Y: %d\n", err); + if (err > 0) { + for (i = 0; i < err; ++i) + PRINT("%c", buf [i]); + PRINT ("\n"); + } + err = read (gdbfd, buf, 256); + PRINT("Y: %d\n", err); + if (err > 0) { + for (i = 0; i < err; ++i) + PRINT("%c", buf [i]); + PRINT ("\n"); + } +#endif + + /* + * The debugserver connection is through a fd handle, while lldb requires a host/port to connect, so create an intermediate + * socket to transfer data. + */ + server_socket = CFSocketCreateWithNative (NULL, gdbfd, kCFSocketDataCallBack, &server_callback, NULL); + CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, server_socket, 0), kCFRunLoopCommonModes); - CFSocketRef fdvendor = CFSocketCreate(NULL, AF_UNIX, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, NULL); + struct sockaddr_in addr4; + memset(&addr4, 0, sizeof(addr4)); + addr4.sin_len = sizeof(addr4); + addr4.sin_family = AF_INET; + addr4.sin_port = htons(12345); + addr4.sin_addr.s_addr = htonl(INADDR_ANY); + + CFSocketRef fdvendor = CFSocketCreate(NULL, PF_INET, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, NULL); int yes = 1; setsockopt(CFSocketGetNative(fdvendor), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + int flag = 1; + res = setsockopt(CFSocketGetNative(fdvendor), IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + assert (res == 0); - struct sockaddr_un address; - memset(&address, 0, sizeof(address)); - address.sun_family = AF_UNIX; - strcpy(address.sun_path, FDVENDOR_PATH); - address.sun_len = SUN_LEN(&address); - CFDataRef address_data = CFDataCreate(NULL, (const UInt8 *)&address, sizeof(address)); - - unlink(FDVENDOR_PATH); + CFDataRef address_data = CFDataCreate(NULL, (const UInt8 *)&addr4, sizeof(addr4)); CFSocketSetAddress(fdvendor, address_data); CFRelease(address_data); @@ -563,7 +644,7 @@ void handle_device(AMDeviceRef device) { mount_developer_image(device); // put debugserver on the device start_remote_debug_server(device); // start debugserver - write_gdb_prep_cmds(device, url); // dump the necessary gdb commands into a file + write_lldb_prep_cmds(device, url); // dump the necessary lldb commands into a file CFRelease(url); @@ -572,7 +653,7 @@ void handle_device(AMDeviceRef device) { if (wait_with_gdb) { printf ("You must now execute: \n"); - printf ("%s\n", GDB_SHELL); + printf ("%s\n", LLDB_SHELL); // Figure out when to exit } else { signal(SIGHUP, gdb_ready_handler); @@ -580,7 +661,7 @@ void handle_device(AMDeviceRef device) { pid_t parent = getpid(); int pid = fork(); if (pid == 0) { - system(GDB_SHELL); // launch gdb + system(LLDB_SHELL); // launch gdb kill(parent, SIGHUP); // "No. I am your father." _exit(EXIT_SUCCESS); } @@ -667,6 +748,7 @@ int main(int argc, char *argv[]) { no_mount = 1; break; default: + printf ("1\n"); usage(argv[0]); return 1; }