aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2016-03-30 00:42:05 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2016-03-30 00:44:11 +0200
commit610c4c385b38280c7bde7a48d95ec019cbfe1ab4 (patch)
tree82ad4366ce9e927f64efa89d178c4dd2bfea1435
parent9844d7e830a2c55421e27e8828d2067c50f57c23 (diff)
downloadbusybox-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.c78
-rw-r--r--include/busybox.h15
-rw-r--r--libbb/appletlib.c82
-rw-r--r--libbb/vfork_daemon_rexec.c3
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
42enum { NUM_APPLETS = ARRAY_SIZE(applets) }; 42enum { NUM_APPLETS = ARRAY_SIZE(applets) };
43 43
44static int offset[NUM_APPLETS];
45
46static int cmp_name(const void *a, const void *b) 44static 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
63int main(int argc, char **argv) 72int 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! */
16extern const char applet_names[] ALIGN1; 16extern const char applet_names[] ALIGN1;
17extern int (*const applet_main[])(int argc, char **argv); 17extern int (*const applet_main[])(int argc, char **argv);
18extern const uint16_t applet_nameofs[]; 18extern const uint8_t applet_flags[] ALIGN1;
19extern const uint8_t applet_suid[] ALIGN1;
19extern const uint8_t applet_install_loc[] ALIGN1; 20extern 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
143static 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
149int FAST_FUNC find_applet_by_name(const char *name) 142int 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
786void FAST_FUNC run_applet_and_exit(const char *name, char **argv) 809void 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 {