1 /*
2 * Copyright (c) 2021 Andrew G. Morgan <morgan@kernel.org>
3 *
4 * Some header magic to help make a shared object run-able as a stand
5 * alone executable binary.
6 *
7 * This is a slightly more sophisticated implementation than the
8 * answer I posted here:
9 *
10 * https://stackoverflow.com/a/68339111/14760867
11 *
12 * Compile your shared library with:
13 *
14 * -DSHARED_LOADER="\"ld-linux...\"" (loader for your target system)
15 * ...
16 * --entry=__so_start
17 */
18 #include <stdlib.h>
19 #include <string.h>
20
21 #ifdef __EXECABLE_H
22 #error "only include execable.h once"
23 #endif
24 #define __EXECABLE_H
25
26 const char __execable_dl_loader[] __attribute((section(".interp"))) =
27 SHARED_LOADER ;
28
__execable_parse_args(int * argc_p,char *** argv_p)29 static void __execable_parse_args(int *argc_p, char ***argv_p)
30 {
31 int argc = 0;
32 char **argv = NULL;
33 FILE *f = fopen("/proc/self/cmdline", "rb");
34 if (f != NULL) {
35 char *mem = NULL, *p;
36 size_t size = 32, offset;
37 for (offset=0; ; size *= 2) {
38 char *new_mem = realloc(mem, size+1);
39 if (new_mem == NULL) {
40 perror("unable to parse arguments");
41 if (mem != NULL) {
42 free(mem);
43 }
44 exit(1);
45 }
46 mem = new_mem;
47 offset += fread(mem+offset, 1, size-offset, f);
48 if (offset < size) {
49 size = offset;
50 mem[size] = '\0';
51 break;
52 }
53 }
54 fclose(f);
55 for (argc=1, p=mem+size-2; p >= mem; p--) {
56 argc += (*p == '\0');
57 }
58 argv = calloc(argc+1, sizeof(char *));
59 if (argv == NULL) {
60 perror("failed to allocate memory for argv");
61 free(mem);
62 exit(1);
63 }
64 for (p=mem, argc=0, offset=0; offset < size; argc++) {
65 argv[argc] = mem+offset;
66 offset += strlen(mem+offset)+1;
67 }
68 }
69 *argc_p = argc;
70 *argv_p = argv;
71 }
72
73 /*
74 * Note, to avoid any runtime confusion, SO_MAIN is a void static
75 * function.
76 */
77
78 #define SO_MAIN \
79 static void __execable_main(int, char**); \
80 extern void __so_start(void); \
81 void __so_start(void) \
82 { \
83 int argc; \
84 char **argv; \
85 __execable_parse_args(&argc, &argv); \
86 __execable_main(argc, argv); \
87 if (argc != 0) { \
88 free(argv[0]); \
89 free(argv); \
90 } \
91 exit(0); \
92 } \
93 static void __execable_main
94