@@ -172,7 +172,204 @@ static char **swift::getUnsafeArgvArgc(int *outArgLen) {
172172
173173template <typename F>
174174static 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__)
176373static char **swift::getUnsafeArgvArgc (int *outArgLen) {
177374 return nullptr ;
178375}
0 commit comments