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/types.h>
34 #include <sys/socket.h>
35 #include <sys/un.h>
36 
37 #include <string>
38 
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 
45 static 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'		},
52 	{ "unix-socket",    required_argument,  NULL,           'u'		},
53 	{ NULL,				0,					NULL,            0		}
54 };
55 
56 static void
57 usage()
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"
65 "    darwin-debug --unix-socket=<SOCKET> [--arch=<ARCH>] [--disable-aslr] [--no-env] [--setsid] [--help] -- <PROGRAM> [<PROGRAM-ARG> <PROGRAM-ARG> ....]\n"
66 "\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"
74 "    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"
77 "\n"
78 "EXAMPLE\n"
79 "   darwin-debug --arch=i386 -- /bin/ls -al /tmp\n"
80 );
81     exit (1);
82 }
83 
84 static void
85 exit_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 
97 pid_t
98 posix_spawn_for_debug (char *const *argv, char *const *envp, cpu_type_t cpu_type, int disable_aslr)
99 {
100     pid_t pid = 0;
101 
102     const char *path = argv[0];
103 
104     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 
139 int 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;
159     std::string unix_socket_name;
160 	while ((ch = getopt_long(argc, argv, "a:dehsu:?", g_long_options, NULL)) != -1)
161 	{
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 
198         case 'u':
199             unix_socket_name.assign (optarg);
200             break;
201 
202 		case 'h':
203 		case '?':
204 		default:
205 			show_usage = true;
206 			break;
207 		}
208 	}
209 	argc -= optind;
210 	argv += optind;
211 
212     if (show_usage || argc <= 0 || unix_socket_name.empty())
213         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 
221     // 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 
256     // Now we posix spawn to exec this process into the inferior that we want
257     // to debug.
258     posix_spawn_for_debug (argv,
259                            pass_env ? envp : NULL,
260                            cpu_type,
261                            disable_aslr);
262 
263 	return 0;
264 }
265 
266 #endif // #if defined (__APPLE__)
267 
268