Skip to content

Commit 8d5cd5a

Browse files
authored
Merge pull request #71885 from al45tair/eng/PR-117963394-5.10
[CommandLine][Linux] Don't read argv from /proc/self/cmdline.
2 parents ebaa8c6 + 103db6f commit 8d5cd5a

File tree

1 file changed

+198
-1
lines changed

1 file changed

+198
-1
lines changed

stdlib/public/CommandLineSupport/CommandLine.cpp

+198-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,204 @@ static char **swift::getUnsafeArgvArgc(int *outArgLen) {
172172

173173
template <typename F>
174174
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__)
176373
static char **swift::getUnsafeArgvArgc(int *outArgLen) {
177374
return nullptr;
178375
}

0 commit comments

Comments
 (0)