1 //===-- Launcher.cpp --------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 //---------------------------------------------------------------------- 11 // Darwin launch helper 12 // 13 // This program was written to allow programs to be launched in a new 14 // Terminal.app window and have the application be stopped for debugging 15 // at the program entry point. 16 // 17 // Although it uses posix_spawn(), it uses Darwin specific posix spawn 18 // attribute flags to accomplish its task. It uses an "exec only" flag 19 // which avoids forking this process, and it uses a "stop at entry" 20 // flag to stop the program at the entry point. 21 // 22 // Since it uses darwin specific flags this code should not be compiled 23 // on other systems. 24 //---------------------------------------------------------------------- 25 #if defined (__APPLE__) 26 27 #include <getopt.h> 28 #include <mach/machine.h> 29 #include <spawn.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <sys/socket.h> 34 #include <sys/stat.h> 35 #include <sys/types.h> 36 #include <sys/un.h> 37 38 #include <string> 39 40 #ifndef _POSIX_SPAWN_DISABLE_ASLR 41 #define _POSIX_SPAWN_DISABLE_ASLR 0x0100 42 #endif 43 44 #define streq(a,b) strcmp(a,b) == 0 45 46 static struct option g_long_options[] = 47 { 48 { "arch", required_argument, NULL, 'a' }, 49 { "disable-aslr", no_argument, NULL, 'd' }, 50 { "no-env", no_argument, NULL, 'e' }, 51 { "help", no_argument, NULL, 'h' }, 52 { "setsid", no_argument, NULL, 's' }, 53 { "unix-socket", required_argument, NULL, 'u' }, 54 { "working-dir", required_argument, NULL, 'w' }, 55 { NULL, 0, NULL, 0 } 56 }; 57 58 static void 59 usage() 60 { 61 puts ( 62 "NAME\n" 63 " darwin-debug -- posix spawn a process that is stopped at the entry point\n" 64 " for debugging.\n" 65 "\n" 66 "SYNOPSIS\n" 67 " darwin-debug --unix-socket=<SOCKET> [--arch=<ARCH>] [--working-dir=<PATH>] [--disable-aslr] [--no-env] [--setsid] [--help] -- <PROGRAM> [<PROGRAM-ARG> <PROGRAM-ARG> ....]\n" 68 "\n" 69 "DESCRIPTION\n" 70 " darwin-debug will exec itself into a child process <PROGRAM> that is\n" 71 " halted for debugging. It does this by using posix_spawn() along with\n" 72 " darwin specific posix_spawn flags that allows exec only (no fork), and\n" 73 " stop at the program entry point. Any program arguments <PROGRAM-ARG> are\n" 74 " passed on to the exec as the arguments for the new process. The current\n" 75 " environment will be passed to the new process unless the \"--no-env\"\n" 76 " option is used. A unix socket must be supplied using the\n" 77 " --unix-socket=<SOCKET> option so the calling program can handshake with\n" 78 " this process and get its process id.\n" 79 "\n" 80 "EXAMPLE\n" 81 " darwin-debug --arch=i386 -- /bin/ls -al /tmp\n" 82 ); 83 exit (1); 84 } 85 86 static void 87 exit_with_errno (int err, const char *prefix) 88 { 89 if (err) 90 { 91 fprintf (stderr, 92 "%s%s", 93 prefix ? prefix : "", 94 strerror(err)); 95 exit (err); 96 } 97 } 98 99 pid_t 100 posix_spawn_for_debug 101 ( 102 char *const *argv, 103 char *const *envp, 104 const char *working_dir, 105 cpu_type_t cpu_type, 106 int disable_aslr) 107 { 108 pid_t pid = 0; 109 110 const char *path = argv[0]; 111 112 posix_spawnattr_t attr; 113 114 exit_with_errno (::posix_spawnattr_init (&attr), "::posix_spawnattr_init (&attr) error: "); 115 116 // Here we are using a darwin specific feature that allows us to exec only 117 // since we want this program to turn into the program we want to debug, 118 // and also have the new program start suspended (right at __dyld_start) 119 // so we can debug it 120 short flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETEXEC; 121 122 // Disable ASLR if we were asked to 123 if (disable_aslr) 124 flags |= _POSIX_SPAWN_DISABLE_ASLR; 125 126 // Set the flags we just made into our posix spawn attributes 127 exit_with_errno (::posix_spawnattr_setflags (&attr, flags), "::posix_spawnattr_setflags (&attr, flags) error: "); 128 129 130 // Another darwin specific thing here where we can select the architecture 131 // of the binary we want to re-exec as. 132 if (cpu_type != 0) 133 { 134 size_t ocount = 0; 135 exit_with_errno (::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), "posix_spawnattr_setbinpref_np () error: "); 136 } 137 138 // I wish there was a posix_spawn flag to change the working directory of 139 // the inferior process we will spawn, but there currently isn't. If there 140 // ever is a better way to do this, we should use it. I would rather not 141 // manually fork, chdir in the child process, and then posix_spawn with exec 142 // as the whole reason for doing posix_spawn is to not hose anything up 143 // after the fork and prior to the exec... 144 if (working_dir) 145 ::chdir (working_dir); 146 147 exit_with_errno (::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), "posix_spawn() error: "); 148 149 // This code will only be reached if the posix_spawn exec failed... 150 ::posix_spawnattr_destroy (&attr); 151 152 return pid; 153 } 154 155 156 int main (int argc, char *const *argv, char *const *envp, const char **apple) 157 { 158 const char *program_name = strrchr(apple[0], '/'); 159 160 if (program_name) 161 program_name++; // Skip the last slash.. 162 else 163 program_name = apple[0]; 164 165 #if defined (DEBUG_LLDB_LAUNCHER) 166 printf("%s called with:\n", program_name); 167 for (int i=0; i<argc; ++i) 168 printf("argv[%u] = '%s'\n", i, argv[i]); 169 #endif 170 171 cpu_type_t cpu_type = 0; 172 bool show_usage = false; 173 char ch; 174 int disable_aslr = 0; // By default we disable ASLR 175 int pass_env = 1; 176 std::string unix_socket_name; 177 std::string working_dir; 178 while ((ch = getopt_long(argc, argv, "a:dehsu:?", g_long_options, NULL)) != -1) 179 { 180 switch (ch) 181 { 182 case 0: 183 break; 184 185 case 'a': // "-a i386" or "--arch=i386" 186 if (optarg) 187 { 188 if (streq (optarg, "i386")) 189 cpu_type = CPU_TYPE_I386; 190 else if (streq (optarg, "x86_64")) 191 cpu_type = CPU_TYPE_X86_64; 192 else if (strstr (optarg, "arm") == optarg) 193 cpu_type = CPU_TYPE_ARM; 194 else 195 { 196 ::fprintf (stderr, "error: unsupported cpu type '%s'\n", optarg); 197 ::exit (1); 198 } 199 } 200 break; 201 202 case 'd': 203 disable_aslr = 1; 204 break; 205 206 case 'e': 207 pass_env = 0; 208 break; 209 210 case 's': 211 // Create a new session to avoid having control-C presses kill our current 212 // terminal session when this program is launched from a .command file 213 ::setsid(); 214 break; 215 216 case 'u': 217 unix_socket_name.assign (optarg); 218 break; 219 220 case 'w': 221 { 222 struct stat working_dir_stat; 223 if (stat (optarg, &working_dir_stat) == 0) 224 working_dir.assign (optarg); 225 else 226 ::fprintf(stderr, "warning: working directory doesn't exist: '%s'\n", optarg); 227 } 228 break; 229 230 case 'h': 231 case '?': 232 default: 233 show_usage = true; 234 break; 235 } 236 } 237 argc -= optind; 238 argv += optind; 239 240 if (show_usage || argc <= 0 || unix_socket_name.empty()) 241 usage(); 242 243 #if defined (DEBUG_LLDB_LAUNCHER) 244 printf ("\n%s post options:\n", program_name); 245 for (int i=0; i<argc; ++i) 246 printf ("argv[%u] = '%s'\n", i, argv[i]); 247 #endif 248 249 // Open the socket that was passed in as an option 250 struct sockaddr_un saddr_un; 251 int s = ::socket (AF_UNIX, SOCK_STREAM, 0); 252 if (s < 0) 253 { 254 perror("error: socket (AF_UNIX, SOCK_STREAM, 0)"); 255 exit(1); 256 } 257 258 saddr_un.sun_family = AF_UNIX; 259 ::strncpy(saddr_un.sun_path, unix_socket_name.c_str(), sizeof(saddr_un.sun_path) - 1); 260 saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; 261 saddr_un.sun_len = SUN_LEN (&saddr_un); 262 263 if (::connect (s, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0) 264 { 265 perror("error: connect (socket, &saddr_un, saddr_un_len)"); 266 exit(1); 267 } 268 269 // We were able to connect to the socket, now write our PID so whomever 270 // launched us will know this process's ID 271 char pid_str[64]; 272 const int pid_str_len = ::snprintf (pid_str, sizeof(pid_str), "%i", ::getpid()); 273 const int bytes_sent = ::send (s, pid_str, pid_str_len, 0); 274 275 if (pid_str_len != bytes_sent) 276 { 277 perror("error: send (s, pid_str, pid_str_len, 0)"); 278 exit (1); 279 } 280 281 // We are done with the socket 282 close (s); 283 284 system("clear"); 285 printf ("Launching: '%s'\n", argv[0]); 286 if (working_dir.empty()) 287 { 288 char cwd[PATH_MAX]; 289 const char *cwd_ptr = getcwd(cwd, sizeof(cwd)); 290 printf ("Working directory: '%s'\n", cwd_ptr); 291 } 292 else 293 { 294 printf ("Working directory: '%s'\n", working_dir.c_str()); 295 } 296 printf ("%i arguments:\n", argc); 297 298 for (int i=0; i<argc; ++i) 299 printf ("argv[%u] = '%s'\n", i, argv[i]); 300 301 // Now we posix spawn to exec this process into the inferior that we want 302 // to debug. 303 posix_spawn_for_debug (argv, 304 pass_env ? envp : NULL, 305 working_dir.empty() ? NULL : working_dir.c_str(), 306 cpu_type, 307 disable_aslr); 308 309 return 0; 310 } 311 312 #endif // #if defined (__APPLE__) 313 314