blob: e754ded474c4ebe84355ad0fe6ee1c39c99b3ed4 [file] [log] [blame]
Greg Claytondd36def2010-10-17 22:03:32 +00001//===-- 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.
Kate Stoneb9c1b512016-09-06 20:57:50 +000021//
Greg Claytondd36def2010-10-17 22:03:32 +000022// Since it uses darwin specific flags this code should not be compiled
23// on other systems.
24//----------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +000025#if defined(__APPLE__)
Greg Claytondd36def2010-10-17 22:03:32 +000026
Greg Claytondcff6712013-04-24 17:53:59 +000027#include <crt_externs.h> // for _NSGetEnviron()
Greg Claytondd36def2010-10-17 22:03:32 +000028#include <getopt.h>
Benjamin Kramer0f30a3e2012-02-22 16:56:26 +000029#include <limits.h>
Greg Claytondd36def2010-10-17 22:03:32 +000030#include <mach/machine.h>
Greg Clayton708c1ab2011-10-28 01:24:12 +000031#include <signal.h>
Greg Claytondd36def2010-10-17 22:03:32 +000032#include <spawn.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
Greg Clayton3fcbed62010-10-19 03:25:40 +000036#include <sys/socket.h>
Greg Claytonbd82a5d2011-01-23 05:56:20 +000037#include <sys/stat.h>
38#include <sys/types.h>
Greg Clayton3fcbed62010-10-19 03:25:40 +000039#include <sys/un.h>
40
41#include <string>
Greg Claytondd36def2010-10-17 22:03:32 +000042
43#ifndef _POSIX_SPAWN_DISABLE_ASLR
Kate Stoneb9c1b512016-09-06 20:57:50 +000044#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
Greg Claytondd36def2010-10-17 22:03:32 +000045#endif
46
Kate Stoneb9c1b512016-09-06 20:57:50 +000047#define streq(a, b) strcmp(a, b) == 0
Greg Claytondd36def2010-10-17 22:03:32 +000048
Kate Stoneb9c1b512016-09-06 20:57:50 +000049static struct option g_long_options[] = {
50 {"arch", required_argument, NULL, 'a'},
51 {"disable-aslr", no_argument, NULL, 'd'},
52 {"no-env", no_argument, NULL, 'e'},
53 {"help", no_argument, NULL, 'h'},
54 {"setsid", no_argument, NULL, 's'},
55 {"unix-socket", required_argument, NULL, 'u'},
56 {"working-dir", required_argument, NULL, 'w'},
57 {"env", required_argument, NULL, 'E'},
58 {NULL, 0, NULL, 0}};
Greg Claytondd36def2010-10-17 22:03:32 +000059
Kate Stoneb9c1b512016-09-06 20:57:50 +000060static void usage() {
61 puts("NAME\n"
62 " darwin-debug -- posix spawn a process that is stopped at the entry "
63 "point\n"
64 " for debugging.\n"
65 "\n"
66 "SYNOPSIS\n"
67 " darwin-debug --unix-socket=<SOCKET> [--arch=<ARCH>] "
68 "[--working-dir=<PATH>] [--disable-aslr] [--no-env] [--setsid] [--help] "
69 "-- <PROGRAM> [<PROGRAM-ARG> <PROGRAM-ARG> ....]\n"
70 "\n"
71 "DESCRIPTION\n"
72 " darwin-debug will exec itself into a child process <PROGRAM> that "
73 "is\n"
74 " halted for debugging. It does this by using posix_spawn() along "
75 "with\n"
76 " darwin specific posix_spawn flags that allows exec only (no fork), "
77 "and\n"
78 " stop at the program entry point. Any program arguments "
79 "<PROGRAM-ARG> are\n"
80 " passed on to the exec as the arguments for the new process. The "
81 "current\n"
82 " environment will be passed to the new process unless the "
83 "\"--no-env\"\n"
84 " option is used. A unix socket must be supplied using the\n"
85 " --unix-socket=<SOCKET> option so the calling program can handshake "
86 "with\n"
87 " this process and get its process id.\n"
88 "\n"
89 "EXAMPLE\n"
90 " darwin-debug --arch=i386 -- /bin/ls -al /tmp\n");
91 exit(1);
Greg Claytondd36def2010-10-17 22:03:32 +000092}
93
Kate Stoneb9c1b512016-09-06 20:57:50 +000094static void exit_with_errno(int err, const char *prefix) {
95 if (err) {
96 fprintf(stderr, "%s%s", prefix ? prefix : "", strerror(err));
97 exit(err);
98 }
Greg Claytondd36def2010-10-17 22:03:32 +000099}
100
Kate Stoneb9c1b512016-09-06 20:57:50 +0000101pid_t posix_spawn_for_debug(char *const *argv, char *const *envp,
102 const char *working_dir, cpu_type_t cpu_type,
103 int disable_aslr) {
104 pid_t pid = 0;
Greg Claytondd36def2010-10-17 22:03:32 +0000105
Kate Stoneb9c1b512016-09-06 20:57:50 +0000106 const char *path = argv[0];
Greg Clayton58d1c9a2010-10-18 04:14:23 +0000107
Kate Stoneb9c1b512016-09-06 20:57:50 +0000108 posix_spawnattr_t attr;
Greg Claytondd36def2010-10-17 22:03:32 +0000109
Kate Stoneb9c1b512016-09-06 20:57:50 +0000110 exit_with_errno(::posix_spawnattr_init(&attr),
111 "::posix_spawnattr_init (&attr) error: ");
Greg Claytondd36def2010-10-17 22:03:32 +0000112
Kate Stoneb9c1b512016-09-06 20:57:50 +0000113 // Here we are using a darwin specific feature that allows us to exec only
114 // since we want this program to turn into the program we want to debug,
115 // and also have the new program start suspended (right at __dyld_start)
116 // so we can debug it
117 short flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETEXEC |
118 POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
Greg Claytondd36def2010-10-17 22:03:32 +0000119
Kate Stoneb9c1b512016-09-06 20:57:50 +0000120 // Disable ASLR if we were asked to
121 if (disable_aslr)
122 flags |= _POSIX_SPAWN_DISABLE_ASLR;
Greg Claytondd36def2010-10-17 22:03:32 +0000123
Kate Stoneb9c1b512016-09-06 20:57:50 +0000124 sigset_t no_signals;
125 sigset_t all_signals;
126 sigemptyset(&no_signals);
127 sigfillset(&all_signals);
128 ::posix_spawnattr_setsigmask(&attr, &no_signals);
129 ::posix_spawnattr_setsigdefault(&attr, &all_signals);
Greg Claytondd36def2010-10-17 22:03:32 +0000130
Kate Stoneb9c1b512016-09-06 20:57:50 +0000131 // Set the flags we just made into our posix spawn attributes
132 exit_with_errno(::posix_spawnattr_setflags(&attr, flags),
133 "::posix_spawnattr_setflags (&attr, flags) error: ");
134
135 // Another darwin specific thing here where we can select the architecture
136 // of the binary we want to re-exec as.
137 if (cpu_type != 0) {
138 size_t ocount = 0;
139 exit_with_errno(
140 ::posix_spawnattr_setbinpref_np(&attr, 1, &cpu_type, &ocount),
141 "posix_spawnattr_setbinpref_np () error: ");
142 }
143
144 // I wish there was a posix_spawn flag to change the working directory of
145 // the inferior process we will spawn, but there currently isn't. If there
146 // ever is a better way to do this, we should use it. I would rather not
147 // manually fork, chdir in the child process, and then posix_spawn with exec
148 // as the whole reason for doing posix_spawn is to not hose anything up
149 // after the fork and prior to the exec...
150 if (working_dir)
151 ::chdir(working_dir);
152
153 exit_with_errno(::posix_spawnp(&pid, path, NULL, &attr, (char *const *)argv,
154 (char *const *)envp),
155 "posix_spawn() error: ");
156
157 // This code will only be reached if the posix_spawn exec failed...
158 ::posix_spawnattr_destroy(&attr);
159
160 return pid;
Greg Claytondd36def2010-10-17 22:03:32 +0000161}
162
Kate Stoneb9c1b512016-09-06 20:57:50 +0000163int main(int argc, char *const *argv, char *const *envp, const char **apple) {
164#if defined(DEBUG_LLDB_LAUNCHER)
165 const char *program_name = strrchr(apple[0], '/');
Greg Claytondd36def2010-10-17 22:03:32 +0000166
Kate Stoneb9c1b512016-09-06 20:57:50 +0000167 if (program_name)
168 program_name++; // Skip the last slash..
169 else
170 program_name = apple[0];
171
172 printf("%s called with:\n", program_name);
173 for (int i = 0; i < argc; ++i)
174 printf("argv[%u] = '%s'\n", i, argv[i]);
Greg Claytondd36def2010-10-17 22:03:32 +0000175#endif
176
Kate Stoneb9c1b512016-09-06 20:57:50 +0000177 cpu_type_t cpu_type = 0;
178 bool show_usage = false;
179 int ch;
180 int disable_aslr = 0; // By default we disable ASLR
181 bool pass_env = true;
182 std::string unix_socket_name;
183 std::string working_dir;
184
Greg Claytond4724cf2013-11-22 18:55:04 +0000185#if __GLIBC__
Kate Stoneb9c1b512016-09-06 20:57:50 +0000186 optind = 0;
Greg Claytond4724cf2013-11-22 18:55:04 +0000187#else
Kate Stoneb9c1b512016-09-06 20:57:50 +0000188 optreset = 1;
189 optind = 1;
Greg Claytond4724cf2013-11-22 18:55:04 +0000190#endif
191
Kate Stoneb9c1b512016-09-06 20:57:50 +0000192 while ((ch = getopt_long_only(argc, argv, "a:deE:hsu:?", g_long_options,
193 NULL)) != -1) {
194 switch (ch) {
195 case 0:
196 break;
Greg Claytondd36def2010-10-17 22:03:32 +0000197
Kate Stoneb9c1b512016-09-06 20:57:50 +0000198 case 'a': // "-a i386" or "--arch=i386"
199 if (optarg) {
200 if (streq(optarg, "i386"))
201 cpu_type = CPU_TYPE_I386;
202 else if (streq(optarg, "x86_64"))
203 cpu_type = CPU_TYPE_X86_64;
204 else if (streq(optarg, "x86_64h"))
205 cpu_type = 0; // Don't set CPU type when we have x86_64h
206 else if (strstr(optarg, "arm") == optarg)
207 cpu_type = CPU_TYPE_ARM;
208 else {
209 ::fprintf(stderr, "error: unsupported cpu type '%s'\n", optarg);
210 ::exit(1);
211 }
212 }
213 break;
Greg Claytondd36def2010-10-17 22:03:32 +0000214
Kate Stoneb9c1b512016-09-06 20:57:50 +0000215 case 'd':
216 disable_aslr = 1;
217 break;
Greg Claytondd36def2010-10-17 22:03:32 +0000218
Kate Stoneb9c1b512016-09-06 20:57:50 +0000219 case 'e':
220 pass_env = false;
221 break;
Greg Claytondd36def2010-10-17 22:03:32 +0000222
Kate Stoneb9c1b512016-09-06 20:57:50 +0000223 case 'E': {
224 // Since we will exec this program into our new program, we can just set
225 // environment
226 // variables in this process and they will make it into the child process.
227 std::string name;
228 std::string value;
229 const char *equal_pos = strchr(optarg, '=');
230 if (equal_pos) {
231 name.assign(optarg, equal_pos - optarg);
232 value.assign(equal_pos + 1);
233 } else {
234 name = optarg;
235 }
236 ::setenv(name.c_str(), value.c_str(), 1);
237 } break;
Greg Clayton3fcbed62010-10-19 03:25:40 +0000238
Kate Stoneb9c1b512016-09-06 20:57:50 +0000239 case 's':
240 // Create a new session to avoid having control-C presses kill our current
241 // terminal session when this program is launched from a .command file
242 ::setsid();
243 break;
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000244
Kate Stoneb9c1b512016-09-06 20:57:50 +0000245 case 'u':
246 unix_socket_name.assign(optarg);
247 break;
Greg Claytondd36def2010-10-17 22:03:32 +0000248
Kate Stoneb9c1b512016-09-06 20:57:50 +0000249 case 'w': {
250 struct stat working_dir_stat;
251 if (stat(optarg, &working_dir_stat) == 0)
252 working_dir.assign(optarg);
253 else
254 ::fprintf(stderr, "warning: working directory doesn't exist: '%s'\n",
255 optarg);
256 } break;
Greg Claytondd36def2010-10-17 22:03:32 +0000257
Kate Stoneb9c1b512016-09-06 20:57:50 +0000258 case 'h':
259 case '?':
260 default:
261 show_usage = true;
262 break;
263 }
264 }
265 argc -= optind;
266 argv += optind;
267
268 if (show_usage || argc <= 0 || unix_socket_name.empty())
269 usage();
270
271#if defined(DEBUG_LLDB_LAUNCHER)
272 printf("\n%s post options:\n", program_name);
273 for (int i = 0; i < argc; ++i)
274 printf("argv[%u] = '%s'\n", i, argv[i]);
Greg Claytondd36def2010-10-17 22:03:32 +0000275#endif
276
Kate Stoneb9c1b512016-09-06 20:57:50 +0000277 // Open the socket that was passed in as an option
278 struct sockaddr_un saddr_un;
279 int s = ::socket(AF_UNIX, SOCK_STREAM, 0);
280 if (s < 0) {
281 perror("error: socket (AF_UNIX, SOCK_STREAM, 0)");
282 exit(1);
283 }
Greg Clayton3fcbed62010-10-19 03:25:40 +0000284
Kate Stoneb9c1b512016-09-06 20:57:50 +0000285 saddr_un.sun_family = AF_UNIX;
286 ::strncpy(saddr_un.sun_path, unix_socket_name.c_str(),
287 sizeof(saddr_un.sun_path) - 1);
288 saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
289 saddr_un.sun_len = SUN_LEN(&saddr_un);
Greg Clayton3fcbed62010-10-19 03:25:40 +0000290
Kate Stoneb9c1b512016-09-06 20:57:50 +0000291 if (::connect(s, (struct sockaddr *)&saddr_un, SUN_LEN(&saddr_un)) < 0) {
292 perror("error: connect (socket, &saddr_un, saddr_un_len)");
293 exit(1);
294 }
Greg Clayton3fcbed62010-10-19 03:25:40 +0000295
Kate Stoneb9c1b512016-09-06 20:57:50 +0000296 // We were able to connect to the socket, now write our PID so whomever
297 // launched us will know this process's ID
298 char pid_str[64];
299 const int pid_str_len =
300 ::snprintf(pid_str, sizeof(pid_str), "%i", ::getpid());
301 const int bytes_sent = ::send(s, pid_str, pid_str_len, 0);
Greg Clayton3fcbed62010-10-19 03:25:40 +0000302
Kate Stoneb9c1b512016-09-06 20:57:50 +0000303 if (pid_str_len != bytes_sent) {
304 perror("error: send (s, pid_str, pid_str_len, 0)");
305 exit(1);
306 }
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000307
Kate Stoneb9c1b512016-09-06 20:57:50 +0000308 // We are done with the socket
309 close(s);
Greg Clayton6162f972010-10-19 18:15:50 +0000310
Kate Stoneb9c1b512016-09-06 20:57:50 +0000311 system("clear");
312 printf("Launching: '%s'\n", argv[0]);
313 if (working_dir.empty()) {
314 char cwd[PATH_MAX];
315 const char *cwd_ptr = getcwd(cwd, sizeof(cwd));
316 printf("Working directory: '%s'\n", cwd_ptr);
317 } else {
318 printf("Working directory: '%s'\n", working_dir.c_str());
319 }
320 printf("%i arguments:\n", argc);
321
322 for (int i = 0; i < argc; ++i)
323 printf("argv[%u] = '%s'\n", i, argv[i]);
324
325 // Now we posix spawn to exec this process into the inferior that we want
326 // to debug.
327 posix_spawn_for_debug(
328 argv,
329 pass_env ? *_NSGetEnviron() : NULL, // Pass current environment as we may
330 // have modified it if "--env" options
331 // was used, do NOT pass "envp" here
332 working_dir.empty() ? NULL : working_dir.c_str(), cpu_type, disable_aslr);
333
334 return 0;
Greg Claytondd36def2010-10-17 22:03:32 +0000335}
336
337#endif // #if defined (__APPLE__)