Merge from vendor branch LESS:
[dragonfly.git] / usr.bin / symorder / symorder.c
1 /*
2  * Copyright (c) 1980 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1980 The Regents of the University of California. All rights reserved.
34  * @(#)symorder.c       5.8 (Berkeley) 4/1/91
35  * $FreeBSD: src/usr.bin/symorder/symorder.c,v 1.15 1999/08/28 01:05:57 peter Exp $
36  * $DragonFly: src/usr.bin/symorder/symorder.c,v 1.3 2003/10/04 20:36:51 hmp Exp $
37  */
38
39 /*
40  * symorder - reorder symbol table
41  */
42
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <a.out.h>
46 #include <ctype.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54
55 #define SPACE           500
56
57 #define OKEXIT          0
58 #define NOTFOUNDEXIT    1
59 #define ERREXIT         2
60
61 char    *exclude[SPACE];
62 struct  nlist order[SPACE];
63
64 struct  exec exec;
65 struct  stat stb;
66 struct  nlist *newtab, *symtab;
67 off_t   sa;
68 int     nexclude, nsym, strtabsize, symfound, symkept, small, missing, clean;
69 char    *kfile, *newstrings, *strings, asym[BUFSIZ];
70
71 void badfmt(char *);
72 int excluded(struct nlist *);
73 int inlist(struct nlist *);
74 void reorder(struct nlist *, struct nlist *, int);
75 int savesymb(struct nlist *);
76 static void usage(void);
77
78 int
79 main(int argc, char **argv)
80 {
81         register struct nlist *p, *symp;
82         register FILE *f, *xfile;
83         register int i;
84         register char *start, *t, *xfilename;
85         int ch, n, o;
86
87         xfilename = NULL;
88         while ((ch = getopt(argc, argv, "cmtx:")) != -1)
89                 switch(ch) {
90                 case 'c':
91                         clean = 1;
92                         break;
93                 case 'm':
94                         missing = 1;
95                         break;
96                 case 't':
97                         small = 1;
98                         break;
99                 case 'x':
100                         if (xfilename != NULL)
101                                 usage();
102                         xfilename = optarg;
103                         break;
104                 case '?':
105                 default:
106                         usage();
107                 }
108         argc -= optind;
109         argv += optind;
110
111         if (argc != 2)
112                 usage();
113
114         if ((f = fopen(argv[0], "r")) == NULL)
115                 err(ERREXIT, "%s", argv[0]);
116
117         for (p = order; fgets(asym, sizeof(asym), f) != NULL;) {
118                 for (t = asym; isspace(*t); ++t);
119                 if (!*(start = t))
120                         continue;
121                 while (*++t);
122                 if (*--t == '\n')
123                         *t = '\0';
124                 p->n_un.n_name = strdup(start);
125                 ++p;
126                 if (++nsym >= sizeof order / sizeof order[0])
127                         break;
128         }
129         (void)fclose(f);
130
131         if (xfilename != NULL) {
132                 if ((xfile = fopen(xfilename, "r")) == NULL)
133                         err(ERREXIT, "%s", xfilename);
134                 for (; fgets(asym, sizeof(asym), xfile) != NULL;) {
135                         for (t = asym; isspace(*t); ++t);
136                         if (!*(start = t))
137                                 continue;
138                         while (*++t);
139                         if (*--t == '\n')
140                                 *t = '\0';
141                         exclude[nexclude] = strdup(start);
142                         if (++nexclude >= sizeof exclude / sizeof exclude[0])
143                                 break;
144                 }
145                 (void)fclose(xfile);
146         }
147
148         kfile = argv[1];
149         if ((f = fopen(kfile, "r")) == NULL)
150                 err(ERREXIT, "%s", kfile);
151         if ((o = open(kfile, O_WRONLY)) < 0)
152                 err(ERREXIT, "%s", kfile);
153
154         /* read exec header */
155         if ((fread(&exec, sizeof(exec), 1, f)) != 1)
156                 badfmt("no exec header");
157         if (N_BADMAG(exec))
158                 badfmt("bad magic number");
159         if (exec.a_syms == 0)
160                 badfmt("stripped");
161         (void)fstat(fileno(f), &stb);
162         if (stb.st_size < N_STROFF(exec) + sizeof(off_t))
163                 badfmt("no string table");
164
165         /* seek to and read the symbol table */
166         sa = N_SYMOFF(exec);
167         (void)fseek(f, sa, SEEK_SET);
168         n = exec.a_syms;
169         if (!(symtab = (struct nlist *)malloc(n)))
170                 err(ERREXIT, NULL);
171         if (fread((void *)symtab, 1, n, f) != n)
172                 badfmt("corrupted symbol table");
173
174         /* read string table size and string table */
175         if (fread((void *)&strtabsize, sizeof(int), 1, f) != 1 ||
176             strtabsize <= 0)
177                 badfmt("corrupted string table");
178         strings = malloc(strtabsize);
179         if (strings == NULL)
180                 err(ERREXIT, NULL);
181         /*
182          * Subtract four from strtabsize since strtabsize includes itself,
183          * and we've already read it.
184          */
185         if (fread(strings, 1, strtabsize - sizeof(int), f) !=
186             strtabsize - sizeof(int))
187                 badfmt("corrupted string table");
188
189         i = n / sizeof(struct nlist);
190         if (!clean) {
191                 newtab = (struct nlist *)malloc(n);
192                 if (newtab == (struct nlist *)NULL)
193                         err(ERREXIT, NULL);
194                 memset(newtab, 0, n);
195
196                 reorder(symtab, newtab, i);
197                 free((void *)symtab);
198                 symtab = newtab;
199         } else {
200                 symkept = i;
201         }
202
203         newstrings = malloc(strtabsize);
204         if (newstrings == NULL)
205                 err(ERREXIT, NULL);
206         t = newstrings;
207         for (symp = symtab; --i >= 0; symp++) {
208                 if (symp->n_un.n_strx == 0)
209                         continue;
210                 if (inlist(symp) < 0) {
211                         if (small)
212                                 continue;
213                         if (clean && !savesymb(symp))
214                                 symp->n_type &= ~N_EXT;
215                 } else if (clean)
216                         symfound++;
217                 symp->n_un.n_strx -= sizeof(int);
218                 (void)strcpy(t, &strings[symp->n_un.n_strx]);
219                 symp->n_un.n_strx = (t - newstrings) + sizeof(int);
220                 t += strlen(t) + 1;
221         }
222
223         /* update shrunk sizes */
224         strtabsize = t - newstrings + sizeof(int);
225         n = symkept * sizeof(struct nlist);
226
227         /* fix exec sym size */
228         (void)lseek(o, (off_t)0, SEEK_SET);
229         exec.a_syms = n;
230         if (write(o, (void *)&exec, sizeof(exec)) != sizeof(exec))
231                 err(ERREXIT, "%s", kfile);
232
233         (void)lseek(o, sa, SEEK_SET);
234         if (write(o, (void *)symtab, n) != n)
235                 err(ERREXIT, "%s", kfile);
236         if (write(o, (void *)&strtabsize, sizeof(int)) != sizeof(int))
237                 err(ERREXIT, "%s", kfile);
238         if (write(o, newstrings, strtabsize - sizeof(int)) !=
239             strtabsize - sizeof(int))
240                 err(ERREXIT, "%s", kfile);
241
242         ftruncate(o, lseek(o, (off_t)0, SEEK_CUR));
243
244         if ((i = nsym - symfound) > 0) {
245                 (void)printf("symorder: %d symbol%s not found:\n",
246                     i, i == 1 ? "" : "s");
247                 for (i = 0; i < nsym; i++)
248                         if (order[i].n_value == 0)
249                                 printf("%s\n", order[i].n_un.n_name);
250                 if (!missing)
251                         exit(NOTFOUNDEXIT);
252         }
253         exit(OKEXIT);
254 }
255
256 int
257 savesymb(register struct nlist *s)
258 {
259         if ((s->n_type & N_EXT) != N_EXT)
260                 return 0;
261         switch (s->n_type & N_TYPE) {
262                 case N_TEXT:
263                 case N_DATA:    
264                         return 0;
265                 default:        
266                         return 1;
267         }
268 }
269
270 void
271 reorder(register struct nlist *st1, register struct nlist *st2, int entries)
272 {
273         register struct nlist *p;
274         register int i, n;
275
276         for (p = st1, n = entries; --n >= 0; ++p)
277                 if (inlist(p) != -1)
278                         ++symfound;
279         for (p = st2 + symfound, n = entries; --n >= 0; ++st1) {
280                 if (excluded(st1))
281                         continue;
282                 i = inlist(st1);
283                 if (i == -1)
284                         *p++ = *st1;
285                 else
286                         st2[i] = *st1;
287                 ++symkept;
288         }
289 }
290
291 int
292 inlist(register struct nlist *p)
293 {
294         register char *nam;
295         register struct nlist *op;
296
297         if (p->n_type & N_STAB || p->n_un.n_strx == 0)
298                 return (-1);
299         if (p->n_un.n_strx < sizeof(int) || p->n_un.n_strx >= strtabsize)
300                 badfmt("corrupted symbol table");
301         nam = &strings[p->n_un.n_strx - sizeof(int)];
302         for (op = &order[nsym]; --op >= order; ) {
303                 if (strcmp(op->n_un.n_name, nam) != 0)
304                         continue;
305                 op->n_value = 1;
306                 return (op - order);
307         }
308         return (-1);
309 }
310
311 int
312 excluded(register struct nlist *p)
313 {
314         register char *nam;
315         register int x;
316
317         if (p->n_type & N_STAB || p->n_un.n_strx == 0)
318                 return (0);
319         if (p->n_un.n_strx < sizeof(int) || p->n_un.n_strx >= strtabsize)
320                 badfmt("corrupted symbol table");
321         nam = &strings[p->n_un.n_strx - sizeof(int)];
322         for (x = nexclude; --x >= 0; )
323                 if (strcmp(nam, exclude[x]) == 0)
324                         return (1);
325         return (0);
326 }
327
328 void
329 badfmt(char *why)
330 {
331         errx(ERREXIT, "%s: %s: %s", kfile, why, strerror(EFTYPE));
332 }
333
334 static void
335 usage(void)
336 {
337         (void)fprintf(stderr,
338             "usage: symorder [-c] [-m] [-t] [-x excludelist] symlist file\n");
339         exit(ERREXIT);
340 }