blob: de9b270a18fde1dba270423ef1621f212be3ff69 [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/types.h>
34#include <sys/socket.h>
35#include <sys/un.h>
36
37#include <string>
Greg Claytondd36def2010-10-17 22:03:32 +000038
39#ifndef _POSIX_SPAWN_DISABLE_ASLR
40#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
41#endif
42
43#define streq(a,b) strcmp(a,b) == 0
44
45static struct option g_long_options[] =
46{
47 { "arch", required_argument, NULL, 'a' },
48 { "disable-aslr", no_argument, NULL, 'd' },
49 { "no-env", no_argument, NULL, 'e' },
50 { "help", no_argument, NULL, 'h' },
51 { "setsid", no_argument, NULL, 's' },
Greg Clayton3fcbed62010-10-19 03:25:40 +000052 { "unix-socket", required_argument, NULL, 'u' },
Greg Claytondd36def2010-10-17 22:03:32 +000053 { NULL, 0, NULL, 0 }
54};
55
56static void
57usage()
58{
59 puts (
60"NAME\n"
61" darwin-debug -- posix spawn a process that is stopped at the entry point\n"
62" for debugging.\n"
63"\n"
64"SYNOPSIS\n"
Greg Clayton3fcbed62010-10-19 03:25:40 +000065" darwin-debug --unix-socket=<SOCKET> [--arch=<ARCH>] [--disable-aslr] [--no-env] [--setsid] [--help] -- <PROGRAM> [<PROGRAM-ARG> <PROGRAM-ARG> ....]\n"
Greg Claytondd36def2010-10-17 22:03:32 +000066"\n"
67"DESCRIPTION\n"
68" darwin-debug will exec itself into a child process <PROGRAM> that is\n"
69" halted for debugging. It does this by using posix_spawn() along with\n"
70" darwin specific posix_spawn flags that allows exec only (no fork), and\n"
71" stop at the program entry point. Any program arguments <PROGRAM-ARG> are\n"
72" passed on to the exec as the arguments for the new process. The current\n"
73" environment will be passed to the new process unless the \"--no-env\"\n"
Greg Clayton3fcbed62010-10-19 03:25:40 +000074" option is used. A unix socket must be supplied using the\n"
75" --unix-socket=<SOCKET> option so the calling program can handshake with\n"
76" this process and get its process id.\n"
Greg Claytondd36def2010-10-17 22:03:32 +000077"\n"
78"EXAMPLE\n"
79" darwin-debug --arch=i386 -- /bin/ls -al /tmp\n"
80);
81 exit (1);
82}
83
84static void
85exit_with_errno (int err, const char *prefix)
86{
87 if (err)
88 {
89 fprintf (stderr,
90 "%s%s",
91 prefix ? prefix : "",
92 strerror(err));
93 exit (err);
94 }
95}
96
97pid_t
Greg Clayton58d1c9a2010-10-18 04:14:23 +000098posix_spawn_for_debug (char *const *argv, char *const *envp, cpu_type_t cpu_type, int disable_aslr)
Greg Claytondd36def2010-10-17 22:03:32 +000099{
100 pid_t pid = 0;
101
Greg Clayton58d1c9a2010-10-18 04:14:23 +0000102 const char *path = argv[0];
103
Greg Claytondd36def2010-10-17 22:03:32 +0000104 posix_spawnattr_t attr;
105
106 exit_with_errno (::posix_spawnattr_init (&attr), "::posix_spawnattr_init (&attr) error: ");
107
108 // Here we are using a darwin specific feature that allows us to exec only
109 // since we want this program to turn into the program we want to debug,
110 // and also have the new program start suspended (right at __dyld_start)
111 // so we can debug it
112 short flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETEXEC;
113
114 // Disable ASLR if we were asked to
115 if (disable_aslr)
116 flags |= _POSIX_SPAWN_DISABLE_ASLR;
117
118 // Set the flags we just made into our posix spawn attributes
119 exit_with_errno (::posix_spawnattr_setflags (&attr, flags), "::posix_spawnattr_setflags (&attr, flags) error: ");
120
121
122 // Another darwin specific thing here where we can select the architecture
123 // of the binary we want to re-exec as.
124 if (cpu_type != 0)
125 {
126 size_t ocount = 0;
127 exit_with_errno (::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), "posix_spawnattr_setbinpref_np () error: ");
128 }
129
130 exit_with_errno (::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), "posix_spawn() error: ");
131
132 // This code will only be reached if the posix_spawn exec failed...
133 ::posix_spawnattr_destroy (&attr);
134
135 return pid;
136}
137
138
139int main (int argc, char *const *argv, char *const *envp, const char **apple)
140{
141 const char *program_name = strrchr(apple[0], '/');
142
143 if (program_name)
144 program_name++; // Skip the last slash..
145 else
146 program_name = apple[0];
147
148#if defined (DEBUG_LLDB_LAUNCHER)
149 printf("%s called with:\n", program_name);
150 for (int i=0; i<argc; ++i)
151 printf("argv[%u] = '%s'\n", i, argv[i]);
152#endif
153
154 cpu_type_t cpu_type = 0;
155 bool show_usage = false;
156 char ch;
157 int disable_aslr = 0; // By default we disable ASLR
158 int pass_env = 1;
Greg Clayton3fcbed62010-10-19 03:25:40 +0000159 std::string unix_socket_name;
160 while ((ch = getopt_long(argc, argv, "a:dehsu:?", g_long_options, NULL)) != -1)
Greg Claytondd36def2010-10-17 22:03:32 +0000161 {
162 switch (ch)
163 {
164 case 0:
165 break;
166
167 case 'a': // "-a i386" or "--arch=i386"
168 if (optarg)
169 {
170 if (streq (optarg, "i386"))
171 cpu_type = CPU_TYPE_I386;
172 else if (streq (optarg, "x86_64"))
173 cpu_type = CPU_TYPE_X86_64;
174 else if (strstr (optarg, "arm") == optarg)
175 cpu_type = CPU_TYPE_ARM;
176 else
177 {
178 ::fprintf (stderr, "error: unsupported cpu type '%s'\n", optarg);
179 ::exit (1);
180 }
181 }
182 break;
183
184 case 'd':
185 disable_aslr = 1;
186 break;
187
188 case 'e':
189 pass_env = 0;
190 break;
191
192 case 's':
193 // Create a new session to avoid having control-C presses kill our current
194 // terminal session when this program is launched from a .command file
195 ::setsid();
196 break;
197
Greg Clayton3fcbed62010-10-19 03:25:40 +0000198 case 'u':
199 unix_socket_name.assign (optarg);
200 break;
201
Greg Claytondd36def2010-10-17 22:03:32 +0000202 case 'h':
203 case '?':
204 default:
205 show_usage = true;
206 break;
207 }
208 }
209 argc -= optind;
210 argv += optind;
211
Greg Clayton3fcbed62010-10-19 03:25:40 +0000212 if (show_usage || argc <= 0 || unix_socket_name.empty())
Greg Claytondd36def2010-10-17 22:03:32 +0000213 usage();
214
215#if defined (DEBUG_LLDB_LAUNCHER)
216 printf ("\n%s post options:\n", program_name);
217 for (int i=0; i<argc; ++i)
218 printf ("argv[%u] = '%s'\n", i, argv[i]);
219#endif
220
Greg Clayton3fcbed62010-10-19 03:25:40 +0000221 // Open the socket that was passed in as an argument
222 struct sockaddr_un saddr_un;
223 int s = ::socket (AF_UNIX, SOCK_STREAM, 0);
224 if (s < 0)
225 {
226 perror("error: socket (AF_UNIX, SOCK_STREAM, 0)");
227 exit(1);
228 }
229
230 saddr_un.sun_family = AF_UNIX;
231 ::strncpy(saddr_un.sun_path, unix_socket_name.c_str(), sizeof(saddr_un.sun_path) - 1);
232 saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
233 saddr_un.sun_len = SUN_LEN (&saddr_un);
234
235 if (::connect (s, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0)
236 {
237 perror("error: connect (socket, &saddr_un, saddr_un_len)");
238 exit(1);
239 }
240
241 // We were able to connect to the socket, now write our PID so whomever
242 // launched us will know this process's ID
243 char pid_str[64];
244 const int pid_str_len = ::snprintf (pid_str, sizeof(pid_str), "%i", ::getpid());
245 const int bytes_sent = ::send (s, pid_str, pid_str_len, 0);
246
247 if (pid_str_len != bytes_sent)
248 {
249 perror("error: send (s, pid_str, pid_str_len, 0)");
250 exit (1);
251 }
252
253 // We are done with the socket
254 close (s);
255
Greg Clayton6162f972010-10-19 18:15:50 +0000256 system("clear");
257 printf ("Launching '%s' for debug with %u arguments:\n", argv[0], argc);
258 for (int i=0; i<argc; ++i)
259 printf ("argv[%u] = '%s'\n", i, argv[i]);
260
Greg Clayton3fcbed62010-10-19 03:25:40 +0000261 // Now we posix spawn to exec this process into the inferior that we want
262 // to debug.
Greg Clayton58d1c9a2010-10-18 04:14:23 +0000263 posix_spawn_for_debug (argv,
Greg Claytondd36def2010-10-17 22:03:32 +0000264 pass_env ? envp : NULL,
265 cpu_type,
266 disable_aslr);
267
268 return 0;
269}
270
271#endif // #if defined (__APPLE__)
272