blob: 9be325952460c2a6be8b121f358f64aa5d261867 [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.
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>
Greg Clayton3fcbed62010-10-19 03:25:40 +000033#include <sys/socket.h>
Greg Claytonbd82a5d2011-01-23 05:56:20 +000034#include <sys/stat.h>
35#include <sys/types.h>
Greg Clayton3fcbed62010-10-19 03:25:40 +000036#include <sys/un.h>
37
38#include <string>
Greg Claytondd36def2010-10-17 22:03:32 +000039
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
46static 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' },
Greg Clayton3fcbed62010-10-19 03:25:40 +000053 { "unix-socket", required_argument, NULL, 'u' },
Greg Claytonbd82a5d2011-01-23 05:56:20 +000054 { "working-dir", required_argument, NULL, 'w' },
Greg Claytondd36def2010-10-17 22:03:32 +000055 { NULL, 0, NULL, 0 }
56};
57
58static void
59usage()
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"
Greg Claytonbd82a5d2011-01-23 05:56:20 +000067" darwin-debug --unix-socket=<SOCKET> [--arch=<ARCH>] [--working-dir=<PATH>] [--disable-aslr] [--no-env] [--setsid] [--help] -- <PROGRAM> [<PROGRAM-ARG> <PROGRAM-ARG> ....]\n"
Greg Claytondd36def2010-10-17 22:03:32 +000068"\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"
Greg Clayton3fcbed62010-10-19 03:25:40 +000076" 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"
Greg Claytondd36def2010-10-17 22:03:32 +000079"\n"
80"EXAMPLE\n"
81" darwin-debug --arch=i386 -- /bin/ls -al /tmp\n"
82);
83 exit (1);
84}
85
86static void
87exit_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
99pid_t
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000100posix_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)
Greg Claytondd36def2010-10-17 22:03:32 +0000107{
108 pid_t pid = 0;
109
Greg Clayton58d1c9a2010-10-18 04:14:23 +0000110 const char *path = argv[0];
111
Greg Claytondd36def2010-10-17 22:03:32 +0000112 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 }
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000137
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);
Greg Claytondd36def2010-10-17 22:03:32 +0000146
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
156int 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;
Greg Clayton3fcbed62010-10-19 03:25:40 +0000176 std::string unix_socket_name;
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000177 std::string working_dir;
Greg Clayton3fcbed62010-10-19 03:25:40 +0000178 while ((ch = getopt_long(argc, argv, "a:dehsu:?", g_long_options, NULL)) != -1)
Greg Claytondd36def2010-10-17 22:03:32 +0000179 {
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
Greg Clayton3fcbed62010-10-19 03:25:40 +0000216 case 'u':
217 unix_socket_name.assign (optarg);
218 break;
219
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000220 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
Greg Claytondd36def2010-10-17 22:03:32 +0000230 case 'h':
231 case '?':
232 default:
233 show_usage = true;
234 break;
235 }
236 }
237 argc -= optind;
238 argv += optind;
239
Greg Clayton3fcbed62010-10-19 03:25:40 +0000240 if (show_usage || argc <= 0 || unix_socket_name.empty())
Greg Claytondd36def2010-10-17 22:03:32 +0000241 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
Greg Clayton8b82f082011-04-12 05:54:46 +0000249 // Open the socket that was passed in as an option
Greg Clayton3fcbed62010-10-19 03:25:40 +0000250 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
Greg Clayton6162f972010-10-19 18:15:50 +0000284 system("clear");
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000285 if (working_dir.empty())
286 {
287 char cwd[PATH_MAX];
288 const char *cwd_ptr = getcwd(cwd, sizeof(cwd));
289 printf ("Launching '%s' in '%s' for debug with %u arguments:\n", argv[0], cwd_ptr, argc);
290 }
291 else
292 printf ("Launching '%s' in '%s' for debug with %u arguments:\n", argv[0], working_dir.c_str(), argc);
293
Greg Clayton6162f972010-10-19 18:15:50 +0000294 for (int i=0; i<argc; ++i)
295 printf ("argv[%u] = '%s'\n", i, argv[i]);
296
Greg Clayton3fcbed62010-10-19 03:25:40 +0000297 // Now we posix spawn to exec this process into the inferior that we want
298 // to debug.
Greg Clayton58d1c9a2010-10-18 04:14:23 +0000299 posix_spawn_for_debug (argv,
Greg Claytondd36def2010-10-17 22:03:32 +0000300 pass_env ? envp : NULL,
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000301 working_dir.empty() ? NULL : working_dir.c_str(),
Greg Claytondd36def2010-10-17 22:03:32 +0000302 cpu_type,
303 disable_aslr);
304
305 return 0;
306}
307
308#endif // #if defined (__APPLE__)
309