@@ -172,7 +172,204 @@ static char **swift::getUnsafeArgvArgc(int *outArgLen) {
172
172
173
173
template <typename F>
174
174
static void swift::enumerateUnsafeArgv (const F& body) { }
175
- #elif defined(__linux__) || defined(__CYGWIN__)
175
+ #elif defined(__linux__)
176
+ // On Linux, there is no easy way to get the argument vector pointer outside
177
+ // of the main() function. However, the ABI specifications dictate the layout
178
+ // of the process's initial stack, which looks something like:
179
+ //
180
+ // stack top ----> ┌────────────────────────┐
181
+ // │ Unspecified │
182
+ // ┊ ┊
183
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
184
+ // │ Information block │
185
+ // │ (argument strings, │
186
+ // │ environment strings, │
187
+ // │ auxiliary information) │
188
+ // ┊ ┊
189
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
190
+ // │ Unspecified │
191
+ // ┊ ┊
192
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
193
+ // │ NULL │
194
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
195
+ // │ Auxiliary Vector │
196
+ // ┊ ┊
197
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
198
+ // │ NULL │
199
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
200
+ // │ Environment Pointers │
201
+ // ┊ ┊
202
+ // environ ------> ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
203
+ // │ NULL │
204
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
205
+ // │ Argument Pointers │
206
+ // ┊ ┊
207
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
208
+ // │ Argument Count │
209
+ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
210
+ // ┊ ┊
211
+ //
212
+ // See https://gitlab.com/x86-psABIs/x86-64-ABI,
213
+ // https://gitlab.com/x86-psABIs/i386-ABI
214
+ //
215
+ // The upshot is that if we can get hold of `environ` before anything has
216
+ // had a chance to change it, we can find the `argv` array and also the
217
+ // argument count, `argc`, by walking back up the stack.
218
+ //
219
+ // (Note that Linux uses this same layout for all platforms, not just x86-based
220
+ // ones. It also has a fixed layout for the data at the top of the stack, but
221
+ // we don't need to take advantage of that here and can stick to things that
222
+ // are defined in the ABI specs.)
223
+
224
+ #include < unistd.h>
225
+
226
+ #define DEBUG_ARGVGRABBER 0
227
+ #if DEBUG_ARGVGRABBER
228
+ #define ARGVDEBUG (...) fprintf(stderr, __VA_ARGS__)
229
+ #else
230
+ #define ARGVDEBUG (...)
231
+ #endif
232
+
233
+ namespace {
234
+
235
+ struct ArgvGrabber {
236
+ char **argv;
237
+ int argc;
238
+
239
+ ArgvGrabber ();
240
+
241
+ private:
242
+ struct stack {
243
+ void *base;
244
+ void *top;
245
+
246
+ stack () : base(nullptr ), top(nullptr ) {}
247
+ stack (void *b, void *t) : base(b), top(t) {}
248
+ };
249
+
250
+ stack findStack ();
251
+ void findArgv (stack s);
252
+ };
253
+
254
+ // Find the stack by looking at /proc/self/maps
255
+ ArgvGrabber::stack ArgvGrabber::findStack (void ) {
256
+ FILE *maps = fopen (" /proc/self/maps" , " r" );
257
+ if (!maps) {
258
+ ARGVDEBUG (" unable to open maps - %d\n " , errno);
259
+ return stack ();
260
+ }
261
+
262
+ char line[256 ];
263
+ void *base = NULL , *top = NULL ;
264
+ bool found = false ;
265
+ while (fgets (line, sizeof (line), maps)) {
266
+ // line is on the stack, so we know we're looking at the right
267
+ // region if line is between base and top.
268
+ //
269
+ // Note that we can't look for [stack], because Rosetta and qemu
270
+ // set up a separate stack for the emulated code.
271
+ //
272
+ // We also need to glom on extra VM ranges after the first one
273
+ // we find, because *sometimes* we end up with an extra range.
274
+ void *lo, *hi;
275
+ if (sscanf (line, " %p-%p" , &lo, &hi) == 2 ) {
276
+ if ((void *)line >= lo && (void *)line < hi) {
277
+ base = lo;
278
+ top = hi;
279
+ found = true ;
280
+ } else if (found && top == lo) {
281
+ top = hi;
282
+ }
283
+ }
284
+ }
285
+
286
+ fclose (maps);
287
+
288
+ if (!found) {
289
+ ARGVDEBUG (" stack not found in maps\n " );
290
+ return stack ();
291
+ }
292
+
293
+ return stack (base, top);
294
+ }
295
+
296
+ #if DEBUG_ARGVGRABBER
297
+ void printMaps () {
298
+ FILE *maps = fopen (" /proc/self/maps" , " r" );
299
+ if (!maps) {
300
+ fprintf (stderr, " unable to open maps - %d\n " , errno);
301
+ return ;
302
+ }
303
+
304
+ char line[256 ];
305
+ while (fgets (line, sizeof (line), maps)) {
306
+ fputs (line, stderr);
307
+ }
308
+
309
+ fclose (maps);
310
+ }
311
+ #endif
312
+
313
+ // Find argv by walking backwards from environ
314
+ void ArgvGrabber::findArgv (ArgvGrabber::stack stack) {
315
+ if (!stack.base ) {
316
+ ARGVDEBUG (" no stack\n " );
317
+ return ;
318
+ }
319
+
320
+ // Check that environ points to the stack
321
+ char **envp = environ;
322
+ if ((void *)envp < stack.base || (void *)envp >= stack.top ) {
323
+ ARGVDEBUG (" envp = %p, stack is from %p to %p\n " ,
324
+ envp, stack.base , stack.top );
325
+ #if DEBUG_ARGVGRABBER
326
+ printMaps ();
327
+ #endif
328
+ return ;
329
+ }
330
+
331
+ char **ptr = envp - 1 ;
332
+
333
+ // We're now pointing at the NULL that terminates argv. Keep going back
334
+ // while we're seeing pointers (values greater than envp).
335
+ while ((void *)(ptr - 1 ) > stack.base ) {
336
+ --ptr;
337
+
338
+ // The first thing less than envp must be the argc value
339
+ if ((void *)*ptr < (void *)envp) {
340
+ argc = (int )(intptr_t )*ptr++;
341
+ argv = ptr;
342
+ return ;
343
+ }
344
+ }
345
+
346
+ ARGVDEBUG (" didn't find argc\n " );
347
+ }
348
+
349
+ ArgvGrabber::ArgvGrabber () : argv(nullptr ), argc(0 ) {
350
+ ARGVDEBUG (" ***GRABBING ARGV for %d***\n " , getpid ());
351
+ findArgv (findStack ());
352
+ #if DEBUG_ARGVGRABBER
353
+ fprintf (stderr, " ARGV is at %p with count %d\n " , argv, argc);
354
+ for (int i = 0 ; i < argc; ++i) {
355
+ fprintf (stderr, " argv[%d] = \" %s\"\n " , i, argv[i]);
356
+ }
357
+ fprintf (stderr, " ***ARGV GRABBED***\n " );
358
+ #endif
359
+ }
360
+
361
+ ArgvGrabber argvGrabber;
362
+
363
+ } // namespace
364
+
365
+ static char **swift::getUnsafeArgvArgc (int *outArgLen) {
366
+ *outArgLen = argvGrabber.argc ;
367
+ return argvGrabber.argv ;
368
+ }
369
+
370
+ template <typename F>
371
+ static void swift::enumerateUnsafeArgv (const F& body) { }
372
+ #elif defined(__CYGWIN__)
176
373
static char **swift::getUnsafeArgvArgc (int *outArgLen) {
177
374
return nullptr ;
178
375
}
0 commit comments