xref: /aosp_15_r20/external/libcap/libcap/execable.h (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1 /*
2  * Copyright (c) 2021 Andrew G. Morgan <[email protected]>
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  * Linux x86 ABI requires the stack be 16 byte aligned. Keep things
75  * simple and just force it.
76  */
77 #if defined(__i386__) || defined(__x86_64__)
78 #define __SO_FORCE_ARG_ALIGNMENT  __attribute__((force_align_arg_pointer))
79 #else
80 #define __SO_FORCE_ARG_ALIGNMENT
81 #endif /* def some x86 */
82 
83 /*
84  * Permit the compiler to override this one.
85  */
86 #ifndef EXECABLE_INITIALIZE
87 #define EXECABLE_INITIALIZE do { } while(0)
88 #endif /* ndef EXECABLE_INITIALIZE */
89 
90 /*
91  * Note, to avoid any runtime confusion, SO_MAIN is a void static
92  * function.
93  */
94 #define SO_MAIN							\
95 static void __execable_main(int, char**);			\
96 __attribute__((visibility ("hidden")))                          \
97 void __so_start(void);					        \
98 __SO_FORCE_ARG_ALIGNMENT					\
99 void __so_start(void)						\
100 {								\
101     int argc;							\
102     char **argv;						\
103     __execable_parse_args(&argc, &argv);			\
104     EXECABLE_INITIALIZE;                                        \
105     __execable_main(argc, argv);				\
106     if (argc != 0) {						\
107 	free(argv[0]);						\
108 	free(argv);						\
109     }								\
110     exit(0);							\
111 }								\
112 static void __execable_main
113