@@ -172,7 +172,151 @@ 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+ // We'll need this in a minute
225+ extern char **environ;
226+
227+ namespace {
228+
229+ struct ArgvGrabber {
230+ char **argv;
231+ int argc;
232+
233+ ArgvGrabber ();
234+
235+ private:
236+ struct stack {
237+ void *base;
238+ void *top;
239+
240+ stack () : base(nullptr ), top(nullptr ) {}
241+ stack (void *b, void *t) : base(b), top(t) {}
242+ };
243+
244+ stack findStack ();
245+ void findArgv (stack s);
246+ };
247+
248+ // Find the stack by looking at /proc/self/maps
249+ ArgvGrabber::stack ArgvGrabber::findStack (void ) {
250+ FILE *maps = fopen (" /proc/self/maps" , " r" );
251+ if (!maps)
252+ return stack ();
253+
254+ char line[256 ];
255+ void *base = NULL , *top = NULL ;
256+ bool found = false ;
257+ while (fgets (line, sizeof (line), maps)) {
258+ // line is on the stack, so we know we're looking at the right
259+ // region if line is between base and top.
260+ //
261+ // Note that we can't look for [stack], because Rosetta and qemu
262+ // set up a separate stack for the emulated code.
263+ if (sscanf (line, " %p-%p" , &base, &top) == 2
264+ && (void *)line >= base && (void *)line < top) {
265+ found = true ;
266+ break ;
267+ }
268+ }
269+
270+ fclose (maps);
271+
272+ if (!found)
273+ return stack ();
274+
275+ return stack (base, top);
276+ }
277+
278+ // Find argv by walking backwards from environ
279+ void ArgvGrabber::findArgv (ArgvGrabber::stack stack) {
280+ if (!stack.base )
281+ return ;
282+
283+ // Check that environ points to the stack
284+ char **envp = environ;
285+ if ((void *)envp < stack.base || (void *)envp >= stack.top )
286+ return ;
287+
288+ char **ptr = envp - 1 ;
289+
290+ // We're now pointing at the NULL that terminates argv. Keep going back
291+ // while we're seeing pointers (values greater than envp).
292+ while ((void *)(ptr - 1 ) > stack.base ) {
293+ --ptr;
294+
295+ // The first thing less than envp must be the argc value
296+ if ((void *)*ptr < (void *)envp) {
297+ argc = (int )(intptr_t )*ptr++;
298+ argv = ptr;
299+ return ;
300+ }
301+ }
302+ }
303+
304+ ArgvGrabber::ArgvGrabber () : argv(nullptr ), argc(0 ) {
305+ findArgv (findStack ());
306+ }
307+
308+ ArgvGrabber argvGrabber;
309+
310+ } // namespace
311+
312+ static char **swift::getUnsafeArgvArgc (int *outArgLen) {
313+ *outArgLen = argvGrabber.argc ;
314+ return argvGrabber.argv ;
315+ }
316+
317+ template <typename F>
318+ static void swift::enumerateUnsafeArgv (const F& body) { }
319+ #elif defined(__CYGWIN__)
176320static char **swift::getUnsafeArgvArgc (int *outArgLen) {
177321 return nullptr ;
178322}
0 commit comments