Is it possible to access the arguments to main
outside of main
(namely in a shared library constructor) on Linux other than by parsing /proc/self/cmdline
?
Asked
Active
Viewed 4,328 times
10

Jonathan Leffler
- 730,956
- 141
- 904
- 1,278

Petr Skocik
- 58,047
- 6
- 95
- 142
-
1C does not define any way to access the command-line arguments other than via the parameters of the `main()` function. That function can pass those parameters to other functions, but no other function can obtain them autonomously (via any facility defined by the language). – John Bollinger May 21 '16 at 00:00
-
2The question this was marked as a duplicate of is how to pass the arguments of `main()` to another function. This question asks how to access the command-line arguments of a process *before `main()` is even called*. That's completely different. There's probably a dupe out there, but the one selected isn't it. – Andrew Henle May 21 '16 at 00:01
-
Previous duplicate was [Process argc and argv outside of main()](https://stackoverflow.com/questions/12862798/process-argc-and-argv-outside-of-main). – Jonathan Leffler May 21 '16 at 02:02
1 Answers
24
You can do this by putting the constructor in the .init_array
section. Functions in the .init_array
(unlike .init
) are called with the same arguments main
will be called with: argc
, argv
and env
.
Here's a simple example. I used LD_PRELOAD
simply to avoid complicating the example with code which actually links and uses a shared library, but it would work in a more normal scenario as well.
file: printargs.c
#include <stdio.h>
static int printargs(int argc, char** argv, char** env) {
puts("In printargs:");
for (int i = 0; i < argc; ++i)
printf(" Arg %d (%p) '%s'\n", i, (void*)argv[i], argv[i]);
return 0;
}
/* Put the function into the init_array */
__attribute__((section(".init_array"))) static void *ctr = &printargs;
Build and use the shared library
(If you use -Wall
, you will see a warning, because ctr
is unused.)
$ gcc -o printargs.so -std=c11 -shared -fpic printargs.c
$ LD_PRELOAD=./printargs.so /bin/echo Hello, world.
In printargs:
Arg 0 (0x7ffc7617102f) '/bin/echo'
Arg 1 (0x7ffc76171039) 'Hello,'
Arg 2 (0x7ffc76171040) 'world.'
Hello, world.
This solution comes from a suggestion by Mike Frysinger in the libc-help mailing list and there is an even more laconic version of this answer here on SO.
-
-
Note that if the application modifies `argv[n]` before the library is loaded, you will get modified command line this way, while you would get original command line from `/proc/self/cmdline`. – Employed Russian May 23 '16 at 01:33
-
@EmployedRussian: That depends on what you mean by "modifies argv[n]". If the application changes the value of argv[n], then that will not be reflected in /proc/self/cmdline. If it changes the string that argv[n] points at (say, changing its case), then the change will be visible in /proc/self/cmdline. In the vast majority of cases, though, no changes are made and if you really wanted to ensure that you saw the original values, you should either use a wrapper around main or LD_PRELOAD. – rici May 23 '16 at 02:34
-
@emloyed: also, as I was just reminded, `/proc/self/cmdline` is truncated at 4096 bytes, so it might only give you part of the arguments. – rici May 23 '16 at 16:51
-
When using compiler/linker optimizations, the compiler/linker may remove the `ctr` variable and the `.init_array` section. You can add `__attribute__((used))` to the definition of `ctr` to avoid this. – rveerd Jun 27 '18 at 09:36