diff options
author | Ron Yorston <rmy@pobox.com> | 2016-03-30 00:42:05 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2016-03-30 00:44:11 +0200 |
commit | 610c4c385b38280c7bde7a48d95ec019cbfe1ab4 (patch) | |
tree | 82ad4366ce9e927f64efa89d178c4dd2bfea1435 | |
parent | 9844d7e830a2c55421e27e8828d2067c50f57c23 (diff) | |
download | busybox-w32-610c4c385b38280c7bde7a48d95ec019cbfe1ab4.tar.gz busybox-w32-610c4c385b38280c7bde7a48d95ec019cbfe1ab4.tar.bz2 busybox-w32-610c4c385b38280c7bde7a48d95ec019cbfe1ab4.zip |
applet_tables: save space by removing applet name offsets
The array applet_nameofs consumes two bytes per applet. It encodes
nofork/noexec flags
suid flags
the offset of the applet name in the applet_name string
Change the applet_table build tool to store the flags in two separate
arrays (applet_flags and applet_suid). Replace applet_nameofs[] with a
smaller version that only stores a limited number of offsets.
This requires changes to the macros APPLET_IS_NOFORK, APPLET_IS_NOEXEC
and APPLET_SUID.
According to Valgrind the original find_applet_by_name required
353 cycles per call, averaged over all names. Adjusting the number
of known offsets allows space to be traded off against execution time:
KNOWN_OFFSETS cycles bytes (wrt KNOWN_OFFSETS = 0)
0 9057 -
2 4604 32
4 2407 75
8 1342 98
16 908 130
32 884 194
This patch uses KNOWN_OFFSETS = 8.
v2:
Remove some dead code from the applet_table tool;
Treat the applet in the middle of the table as a special case.
v3:
Use the middle applet to adjust the start of the linear search as
well as the last applet found.
v4:
Use an augmented linear search in find_applet_by_name.
Drop the special treatment of the middle name from get_applet_name:
most of the advantage now derives from the last stored value.
v5:
Don't store index in applet_nameofs, it can be calculated.
v6:
Tweaks by Denys
function old new delta
find_applet_by_name 25 125 +100
applet_suid - 92 +92
run_applet_no_and_exit 452 460 +8
run_applet_and_exit 695 697 +2
applet_name_compare 31 - -31
applet_nameofs 734 14 -720
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 3/1 up/down: 202/-751) Total: -549 bytes
text data bss dec hex filename
925464 906 17160 943530 e65aa busybox_old
924915 906 17160 942981 e6385 busybox_unstripped
Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | applets/applet_tables.c | 78 | ||||
-rw-r--r-- | include/busybox.h | 15 | ||||
-rw-r--r-- | libbb/appletlib.c | 82 | ||||
-rw-r--r-- | libbb/vfork_daemon_rexec.c | 3 |
4 files changed, 120 insertions, 58 deletions
diff --git a/applets/applet_tables.c b/applets/applet_tables.c index 92bf1e447..48544f08d 100644 --- a/applets/applet_tables.c +++ b/applets/applet_tables.c | |||
@@ -41,8 +41,6 @@ struct bb_applet { | |||
41 | 41 | ||
42 | enum { NUM_APPLETS = ARRAY_SIZE(applets) }; | 42 | enum { NUM_APPLETS = ARRAY_SIZE(applets) }; |
43 | 43 | ||
44 | static int offset[NUM_APPLETS]; | ||
45 | |||
46 | static int cmp_name(const void *a, const void *b) | 44 | static int cmp_name(const void *a, const void *b) |
47 | { | 45 | { |
48 | const struct bb_applet *aa = a; | 46 | const struct bb_applet *aa = a; |
@@ -60,22 +58,37 @@ static int str_isalnum_(const char *s) | |||
60 | return 1; | 58 | return 1; |
61 | } | 59 | } |
62 | 60 | ||
61 | // Before linear search, narrow it down by looking at N "equidistant" names: | ||
62 | // KNOWN_APPNAME_OFFSETS cycles code_size | ||
63 | // 0 9057 | ||
64 | // 2 4604 +32 | ||
65 | // 4 2407 +75 | ||
66 | // 8 1342 +98 | ||
67 | // 16 908 +130 | ||
68 | // 32 884 +194 | ||
69 | // With 8, applet_nameofs[] table has 7 elements. | ||
70 | #define KNOWN_APPNAME_OFFSETS 8 | ||
71 | |||
63 | int main(int argc, char **argv) | 72 | int main(int argc, char **argv) |
64 | { | 73 | { |
65 | int i; | 74 | int i, j; |
66 | int ofs; | 75 | int ofs, offset[KNOWN_APPNAME_OFFSETS], index[KNOWN_APPNAME_OFFSETS]; |
67 | // unsigned MAX_APPLET_NAME_LEN = 1; | 76 | // unsigned MAX_APPLET_NAME_LEN = 1; |
68 | 77 | ||
69 | qsort(applets, NUM_APPLETS, sizeof(applets[0]), cmp_name); | 78 | qsort(applets, NUM_APPLETS, sizeof(applets[0]), cmp_name); |
70 | 79 | ||
80 | for (i = 0; i < KNOWN_APPNAME_OFFSETS; i++) | ||
81 | index[i] = i * NUM_APPLETS / KNOWN_APPNAME_OFFSETS; | ||
82 | |||
71 | ofs = 0; | 83 | ofs = 0; |
72 | for (i = 0; i < NUM_APPLETS; i++) { | 84 | for (i = 0; i < NUM_APPLETS; i++) { |
73 | offset[i] = ofs; | 85 | for (j = 0; j < KNOWN_APPNAME_OFFSETS; j++) |
86 | if (i == index[j]) | ||
87 | offset[j] = ofs; | ||
74 | ofs += strlen(applets[i].name) + 1; | 88 | ofs += strlen(applets[i].name) + 1; |
75 | } | 89 | } |
76 | /* We reuse 4 high-order bits of offset array for other purposes, | 90 | /* If the list of names is too long refuse to proceed */ |
77 | * so if they are indeed needed, refuse to proceed */ | 91 | if (ofs > 0xffff) |
78 | if (ofs > 0xfff) | ||
79 | return 1; | 92 | return 1; |
80 | if (!argv[1]) | 93 | if (!argv[1]) |
81 | return 1; | 94 | return 1; |
@@ -94,7 +107,17 @@ int main(int argc, char **argv) | |||
94 | printf("#define SINGLE_APPLET_STR \"%s\"\n", applets[0].name); | 107 | printf("#define SINGLE_APPLET_STR \"%s\"\n", applets[0].name); |
95 | printf("#define SINGLE_APPLET_MAIN %s_main\n", applets[0].main); | 108 | printf("#define SINGLE_APPLET_MAIN %s_main\n", applets[0].main); |
96 | } | 109 | } |
97 | printf("\n"); | 110 | |
111 | if (KNOWN_APPNAME_OFFSETS > 0 && NUM_APPLETS > 2*KNOWN_APPNAME_OFFSETS) { | ||
112 | printf("#define KNOWN_APPNAME_OFFSETS %u\n\n", KNOWN_APPNAME_OFFSETS); | ||
113 | printf("const uint16_t applet_nameofs[] ALIGN2 = {\n"); | ||
114 | for (i = 1; i < KNOWN_APPNAME_OFFSETS; i++) | ||
115 | printf("%d,\n", offset[i]); | ||
116 | printf("};\n\n"); | ||
117 | } | ||
118 | else { | ||
119 | printf("#define KNOWN_APPNAME_OFFSETS 0\n\n"); | ||
120 | } | ||
98 | 121 | ||
99 | //printf("#ifndef SKIP_definitions\n"); | 122 | //printf("#ifndef SKIP_definitions\n"); |
100 | printf("const char applet_names[] ALIGN1 = \"\"\n"); | 123 | printf("const char applet_names[] ALIGN1 = \"\"\n"); |
@@ -119,20 +142,39 @@ int main(int argc, char **argv) | |||
119 | printf("};\n"); | 142 | printf("};\n"); |
120 | printf("#endif\n\n"); | 143 | printf("#endif\n\n"); |
121 | 144 | ||
122 | printf("const uint16_t applet_nameofs[] ALIGN2 = {\n"); | ||
123 | for (i = 0; i < NUM_APPLETS; i++) { | ||
124 | printf("0x%04x,\n", | ||
125 | offset[i] | ||
126 | #if ENABLE_FEATURE_PREFER_APPLETS | 145 | #if ENABLE_FEATURE_PREFER_APPLETS |
127 | + (applets[i].nofork << 12) | 146 | printf("const uint8_t applet_flags[] ALIGN1 = {\n"); |
128 | + (applets[i].noexec << 13) | 147 | i = 0; |
148 | while (i < NUM_APPLETS) { | ||
149 | int v = applets[i].nofork + (applets[i].noexec << 1); | ||
150 | if (++i < NUM_APPLETS) | ||
151 | v |= (applets[i].nofork + (applets[i].noexec << 1)) << 2; | ||
152 | if (++i < NUM_APPLETS) | ||
153 | v |= (applets[i].nofork + (applets[i].noexec << 1)) << 4; | ||
154 | if (++i < NUM_APPLETS) | ||
155 | v |= (applets[i].nofork + (applets[i].noexec << 1)) << 6; | ||
156 | printf("0x%02x,\n", v); | ||
157 | i++; | ||
158 | } | ||
159 | printf("};\n\n"); | ||
129 | #endif | 160 | #endif |
161 | |||
130 | #if ENABLE_FEATURE_SUID | 162 | #if ENABLE_FEATURE_SUID |
131 | + (applets[i].need_suid << 14) /* 2 bits */ | 163 | printf("const uint8_t applet_suid[] ALIGN1 = {\n"); |
132 | #endif | 164 | i = 0; |
133 | ); | 165 | while (i < NUM_APPLETS) { |
166 | int v = applets[i].need_suid; /* 2 bits */ | ||
167 | if (++i < NUM_APPLETS) | ||
168 | v |= applets[i].need_suid << 2; | ||
169 | if (++i < NUM_APPLETS) | ||
170 | v |= applets[i].need_suid << 4; | ||
171 | if (++i < NUM_APPLETS) | ||
172 | v |= applets[i].need_suid << 6; | ||
173 | printf("0x%02x,\n", v); | ||
174 | i++; | ||
134 | } | 175 | } |
135 | printf("};\n\n"); | 176 | printf("};\n\n"); |
177 | #endif | ||
136 | 178 | ||
137 | #if ENABLE_FEATURE_INSTALLER | 179 | #if ENABLE_FEATURE_INSTALLER |
138 | printf("const uint8_t applet_install_loc[] ALIGN1 = {\n"); | 180 | printf("const uint8_t applet_install_loc[] ALIGN1 = {\n"); |
diff --git a/include/busybox.h b/include/busybox.h index b1e31e5ee..737627bd0 100644 --- a/include/busybox.h +++ b/include/busybox.h | |||
@@ -15,25 +15,20 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | |||
15 | /* Keep in sync with applets/applet_tables.c! */ | 15 | /* Keep in sync with applets/applet_tables.c! */ |
16 | extern const char applet_names[] ALIGN1; | 16 | extern const char applet_names[] ALIGN1; |
17 | extern int (*const applet_main[])(int argc, char **argv); | 17 | extern int (*const applet_main[])(int argc, char **argv); |
18 | extern const uint16_t applet_nameofs[]; | 18 | extern const uint8_t applet_flags[] ALIGN1; |
19 | extern const uint8_t applet_suid[] ALIGN1; | ||
19 | extern const uint8_t applet_install_loc[] ALIGN1; | 20 | extern const uint8_t applet_install_loc[] ALIGN1; |
20 | 21 | ||
21 | #if ENABLE_FEATURE_SUID || ENABLE_FEATURE_PREFER_APPLETS | ||
22 | # define APPLET_NAME(i) (applet_names + (applet_nameofs[i] & 0x0fff)) | ||
23 | #else | ||
24 | # define APPLET_NAME(i) (applet_names + applet_nameofs[i]) | ||
25 | #endif | ||
26 | |||
27 | #if ENABLE_FEATURE_PREFER_APPLETS | 22 | #if ENABLE_FEATURE_PREFER_APPLETS |
28 | # define APPLET_IS_NOFORK(i) (applet_nameofs[i] & (1 << 12)) | 23 | # define APPLET_IS_NOFORK(i) (applet_flags[(i)/4] & (1 << (2 * ((i)%4)))) |
29 | # define APPLET_IS_NOEXEC(i) (applet_nameofs[i] & (1 << 13)) | 24 | # define APPLET_IS_NOEXEC(i) (applet_flags[(i)/4] & (1 << ((2 * ((i)%4))+1))) |
30 | #else | 25 | #else |
31 | # define APPLET_IS_NOFORK(i) 0 | 26 | # define APPLET_IS_NOFORK(i) 0 |
32 | # define APPLET_IS_NOEXEC(i) 0 | 27 | # define APPLET_IS_NOEXEC(i) 0 |
33 | #endif | 28 | #endif |
34 | 29 | ||
35 | #if ENABLE_FEATURE_SUID | 30 | #if ENABLE_FEATURE_SUID |
36 | # define APPLET_SUID(i) ((applet_nameofs[i] >> 14) & 0x3) | 31 | # define APPLET_SUID(i) ((applet_suid[(i)/4] >> (2 * ((i)%4)) & 3)) |
37 | #endif | 32 | #endif |
38 | 33 | ||
39 | #if ENABLE_FEATURE_INSTALLER | 34 | #if ENABLE_FEATURE_INSTALLER |
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 95e589e74..aeaf238f1 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
@@ -139,36 +139,56 @@ void FAST_FUNC bb_show_usage(void) | |||
139 | xfunc_die(); | 139 | xfunc_die(); |
140 | } | 140 | } |
141 | 141 | ||
142 | #if NUM_APPLETS > 8 | ||
143 | static int applet_name_compare(const void *name, const void *idx) | ||
144 | { | ||
145 | int i = (int)(ptrdiff_t)idx - 1; | ||
146 | return strcmp(name, APPLET_NAME(i)); | ||
147 | } | ||
148 | #endif | ||
149 | int FAST_FUNC find_applet_by_name(const char *name) | 142 | int FAST_FUNC find_applet_by_name(const char *name) |
150 | { | 143 | { |
151 | #if NUM_APPLETS > 8 | 144 | unsigned i, max; |
152 | /* Do a binary search to find the applet entry given the name. */ | 145 | int j; |
153 | const char *p; | 146 | const char *p; |
154 | p = bsearch(name, (void*)(ptrdiff_t)1, ARRAY_SIZE(applet_main), 1, applet_name_compare); | 147 | |
155 | /* | 148 | p = applet_names; |
156 | * if (!p) return -1; | 149 | i = 0; |
157 | * ^^^^^^^^^^^^^^^^^^ the code below will do this if p == NULL :) | 150 | #if KNOWN_APPNAME_OFFSETS <= 0 |
158 | */ | 151 | max = NUM_APPLETS; |
159 | return (int)(ptrdiff_t)p - 1; | ||
160 | #else | 152 | #else |
161 | /* A version which does not pull in bsearch */ | 153 | max = NUM_APPLETS * KNOWN_APPNAME_OFFSETS; |
162 | int i = 0; | 154 | for (j = ARRAY_SIZE(applet_nameofs)-1; j >= 0; j--) { |
163 | const char *p = applet_names; | 155 | const char *pp = applet_names + applet_nameofs[j]; |
164 | while (i < NUM_APPLETS) { | 156 | if (strcmp(name, pp) >= 0) { |
165 | if (strcmp(name, p) == 0) | 157 | //bb_error_msg("name:'%s' >= pp:'%s'", name, pp); |
166 | return i; | 158 | p = pp; |
167 | p += strlen(p) + 1; | 159 | i = max - NUM_APPLETS; |
160 | break; | ||
161 | } | ||
162 | max -= NUM_APPLETS; | ||
163 | } | ||
164 | max /= (unsigned)KNOWN_APPNAME_OFFSETS; | ||
165 | i /= (unsigned)KNOWN_APPNAME_OFFSETS; | ||
166 | //bb_error_msg("name:'%s' starting from:'%s' i:%u max:%u", name, p, i, max); | ||
167 | #endif | ||
168 | |||
169 | /* Open-coding without strcmp/strlen calls for speed */ | ||
170 | while (i < max) { | ||
171 | char ch; | ||
172 | j = 0; | ||
173 | /* Do we see "name\0" in applet_names[p] position? */ | ||
174 | while ((ch = *p) == name[j]) { | ||
175 | if (ch == '\0') { | ||
176 | //bb_error_msg("found:'%s' i:%u", name, i); | ||
177 | return i; /* yes */ | ||
178 | } | ||
179 | p++; | ||
180 | j++; | ||
181 | } | ||
182 | /* No. | ||
183 | * p => 1st non-matching char in applet_names[], | ||
184 | * skip to and including NUL. | ||
185 | */ | ||
186 | while (ch != '\0') | ||
187 | ch = *++p; | ||
188 | p++; | ||
168 | i++; | 189 | i++; |
169 | } | 190 | } |
170 | return -1; | 191 | return -1; |
171 | #endif | ||
172 | } | 192 | } |
173 | 193 | ||
174 | 194 | ||
@@ -583,6 +603,7 @@ static void install_links(const char *busybox, int use_symbolic_links, | |||
583 | * busybox.h::bb_install_loc_t, or else... */ | 603 | * busybox.h::bb_install_loc_t, or else... */ |
584 | int (*lf)(const char *, const char *); | 604 | int (*lf)(const char *, const char *); |
585 | char *fpc; | 605 | char *fpc; |
606 | const char *appname = applet_names; | ||
586 | unsigned i; | 607 | unsigned i; |
587 | int rc; | 608 | int rc; |
588 | 609 | ||
@@ -593,7 +614,7 @@ static void install_links(const char *busybox, int use_symbolic_links, | |||
593 | for (i = 0; i < ARRAY_SIZE(applet_main); i++) { | 614 | for (i = 0; i < ARRAY_SIZE(applet_main); i++) { |
594 | fpc = concat_path_file( | 615 | fpc = concat_path_file( |
595 | custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)], | 616 | custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)], |
596 | APPLET_NAME(i)); | 617 | appname); |
597 | // debug: bb_error_msg("%slinking %s to busybox", | 618 | // debug: bb_error_msg("%slinking %s to busybox", |
598 | // use_symbolic_links ? "sym" : "", fpc); | 619 | // use_symbolic_links ? "sym" : "", fpc); |
599 | rc = lf(busybox, fpc); | 620 | rc = lf(busybox, fpc); |
@@ -601,6 +622,8 @@ static void install_links(const char *busybox, int use_symbolic_links, | |||
601 | bb_simple_perror_msg(fpc); | 622 | bb_simple_perror_msg(fpc); |
602 | } | 623 | } |
603 | free(fpc); | 624 | free(fpc); |
625 | while (*appname++ != '\0') | ||
626 | continue; | ||
604 | } | 627 | } |
605 | } | 628 | } |
606 | # else | 629 | # else |
@@ -754,7 +777,7 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) | |||
754 | 777 | ||
755 | /* Reinit some shared global data */ | 778 | /* Reinit some shared global data */ |
756 | xfunc_error_retval = EXIT_FAILURE; | 779 | xfunc_error_retval = EXIT_FAILURE; |
757 | applet_name = APPLET_NAME(applet_no); | 780 | applet_name = bb_get_last_path_component_nostrip(argv[0]); |
758 | 781 | ||
759 | /* Special case. POSIX says "test --help" | 782 | /* Special case. POSIX says "test --help" |
760 | * should be no different from e.g. "test --foo". | 783 | * should be no different from e.g. "test --foo". |
@@ -785,11 +808,14 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) | |||
785 | 808 | ||
786 | void FAST_FUNC run_applet_and_exit(const char *name, char **argv) | 809 | void FAST_FUNC run_applet_and_exit(const char *name, char **argv) |
787 | { | 810 | { |
788 | int applet = find_applet_by_name(name); | 811 | int applet; |
789 | if (applet >= 0) | 812 | |
790 | run_applet_no_and_exit(applet, argv); | ||
791 | if (is_prefixed_with(name, "busybox")) | 813 | if (is_prefixed_with(name, "busybox")) |
792 | exit(busybox_main(argv)); | 814 | exit(busybox_main(argv)); |
815 | /* find_applet_by_name() search is more expensive, so goes second */ | ||
816 | applet = find_applet_by_name(name); | ||
817 | if (applet >= 0) | ||
818 | run_applet_no_and_exit(applet, argv); | ||
793 | } | 819 | } |
794 | 820 | ||
795 | #endif /* !defined(SINGLE_APPLET_MAIN) */ | 821 | #endif /* !defined(SINGLE_APPLET_MAIN) */ |
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index d6ca7b263..1adb5b3c4 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c | |||
@@ -116,8 +116,6 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv) | |||
116 | 116 | ||
117 | save_nofork_data(&old); | 117 | save_nofork_data(&old); |
118 | 118 | ||
119 | applet_name = APPLET_NAME(applet_no); | ||
120 | |||
121 | xfunc_error_retval = EXIT_FAILURE; | 119 | xfunc_error_retval = EXIT_FAILURE; |
122 | 120 | ||
123 | /* In case getopt() or getopt32() was already called: | 121 | /* In case getopt() or getopt32() was already called: |
@@ -157,6 +155,7 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv) | |||
157 | * need argv untouched because they free argv[i]! */ | 155 | * need argv untouched because they free argv[i]! */ |
158 | char *tmp_argv[argc+1]; | 156 | char *tmp_argv[argc+1]; |
159 | memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0])); | 157 | memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0])); |
158 | applet_name = tmp_argv[0]; | ||
160 | /* Finally we can call NOFORK applet's main() */ | 159 | /* Finally we can call NOFORK applet's main() */ |
161 | rc = applet_main[applet_no](argc, tmp_argv); | 160 | rc = applet_main[applet_no](argc, tmp_argv); |
162 | } else { | 161 | } else { |