Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / gensetdefs / gensetdefs.c
1 /*-
2  * Copyright (c) 1997 John D. Polstra.
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  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/usr.bin/gensetdefs/gensetdefs.c,v 1.4 1999/12/12 21:16:46 marcel Exp $
27  */
28
29 #include <sys/types.h>
30 #if defined(arch_i386)
31 #define __ELF_WORD_SIZE 32
32 #include <sys/elf32.h>
33 #elif defined(arch_alpha)
34 #define __ELF_WORD_SIZE 64
35 #include <sys/elf64.h>
36 #endif
37 #include <sys/elf_generic.h>
38
39 #include <err.h>
40 #include <stddef.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #define HASHSIZE        1009u   /* Number of hash chains. */
46 #define PREFIX          ".set." /* Section name prefix for linker sets. */
47
48 /* One entry in the hash table. */
49 typedef struct hashent {
50         struct hashent *next;   /* Next entry with the same hash. */
51         char *name;             /* Name of the linker set. */
52         size_t size;            /* Size in bytes. */
53 } hashent;
54
55 /* Allocate storage for "count" objects of type "type". */
56 #define NEW(type, count)        ((type *) xmalloc((count) * sizeof(type)))
57
58 static hashent *hashtab[HASHSIZE];      /* Hash chain heads. */
59
60 static void              enter(const char *, size_t);
61 static int               enter_sets(const char *);
62 static unsigned int      hash(const char *);
63 static hashent          *merge(void);
64 static int               my_byte_order(void);
65 static void             *xmalloc(size_t);
66 static char             *xstrdup(const char *);
67
68 /*
69  * This is a special-purpose program to generate the linker set definitions
70  * needed when building an ELF kernel.  Its arguments are the names of
71  * ELF object files.  It scans the section names of the object files,
72  * building a table of those that begin with ".set.", which represent
73  * linker sets.  Finally, for each set "foo" with "count" elements, it
74  * writes a line "DEFINE_SET(foo, count);" to the standard output.
75  */
76 int
77 main(int argc, char **argv)
78 {
79         int      i;
80         int      status = EXIT_SUCCESS;
81         hashent *list;
82         FILE    *fp;
83         char    *ptrop;
84         int      align;
85
86         for (i = 1;  i < argc;  i++)
87                 if (enter_sets(argv[i]) == -1)
88                         status = EXIT_FAILURE;
89
90         fp = fopen("setdefs.h", "w");
91         if (!fp)
92                 err(1, "setdefs.h");
93         list = merge();
94         while (list != NULL) {
95                 hashent *next;
96
97                 fprintf(fp, "DEFINE_SET(%s, %lu);\n", list->name,
98                         (unsigned long) (list->size / sizeof (void *)));
99                 next = list->next;
100                 free(list->name);
101                 free(list);
102                 list = next;
103         }
104         fclose(fp);
105
106 #if defined(arch_i386)
107         ptrop = "long";
108         align = 2;
109 #elif defined(arch_alpha)
110         ptrop = "quad";
111         align = 3;
112 #endif
113         if (!ptrop)
114                 errx(1, "unknown architecture");
115
116         fp = fopen("setdef0.c", "w");
117         if (!fp)
118                 err(1, "setdef0.c");
119
120         fprintf(fp, "/* THIS FILE IS GENERATED, DO NOT EDIT. */\n\n");
121         fprintf(fp, "\
122 #define DEFINE_SET(set, count)                          \\\n\
123 __asm__(\".section .set.\" #set \",\\\"aw\\\"\");       \\\n\
124 __asm__(\".globl \" #set);                      \\\n\
125 __asm__(\".type \" #set \",@object\");          \\\n\
126 __asm__(\".p2align %d\");                               \\\n\
127 __asm__(#set \":\");                            \\\n\
128 __asm__(\".%s \" #count);                       \\\n\
129 __asm__(\".previous\")\n\
130
131 #include \"setdefs.h\"          /* Contains a `DEFINE_SET' for each set */\n\
132 ", align, ptrop);
133
134         fclose(fp);
135
136         fp = fopen("setdef1.c", "w");
137         if (!fp)
138                 err(1, "setdef0.c");
139
140         fprintf(fp, "/* THIS FILE IS GENERATED, DO NOT EDIT. */\n\n");
141         fprintf(fp, "\
142 #define DEFINE_SET(set, count)                          \\\n\
143 __asm__(\".section .set.\" #set \",\\\"aw\\\"\");       \\\n\
144 __asm__(\".%s 0\");                     \\\n\
145 __asm__(\".previous\")\n\
146
147 #include \"setdefs.h\"          /* Contains a `DEFINE_SET' for each set */\n\
148 ", ptrop);
149
150         fclose(fp);
151
152         return (status);
153 }
154
155 /*
156  * Enter the given string into the hash table, if it is not already there.
157  * Each hash chain is kept sorted, so that it will be easy to merge the
158  * chains to get a single sorted list.
159  */
160 static void
161 enter(const char *name, size_t size)
162 {
163         int       c = 0;
164         hashent  *entp;
165         hashent **linkp;
166         hashent  *newp;
167
168         linkp = &hashtab[hash(name) % HASHSIZE];
169         while ((entp = *linkp) != NULL && (c = strcmp(name, entp->name)) > 0)
170                 linkp = &entp->next;
171
172         if (entp == NULL || c != 0) {   /* Not found; create a new entry. */
173                 newp = NEW(hashent, 1);
174                 newp->name = xstrdup(name);
175                 newp->size = 0;
176                 newp->next = entp;
177                 *linkp = newp;
178                 entp = newp;
179         }
180
181         entp->size += size;
182 }
183
184 /*
185  * Return a hash value for the given string.
186  */
187 static unsigned int
188 hash(const char *s)
189 {
190         unsigned char   ch;
191         unsigned int    h = 0;
192
193         while((ch = *s) != '\0') {
194                 h = 9*h +  ch;
195                 s++;
196         }
197         return (h);
198 }
199
200 /*
201  * Enter the linker sets from the given ELF object file.  Returns 0 on
202  * success, or -1 if an error occurred.
203  */
204 static int
205 enter_sets(const char *filename)
206 {
207         int              i;
208         FILE            *iop;
209         Elf_Shdr        *shdr;
210         char            *shstr;
211         Elf_Ehdr         ehdr;
212
213         if ((iop = fopen(filename, "rb")) == NULL) {
214                 warn("%s", filename);
215                 return (-1);
216         }
217         if (fread(&ehdr, sizeof ehdr, 1, iop) != 1 ||
218             ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
219             ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
220             ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
221             ehdr.e_ident[EI_MAG3] != ELFMAG3) {
222                 warnx("%s: not an ELF file", filename);
223                 fclose(iop);
224                 return (-1);
225         }
226         if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
227                 warnx("%s: unsupported ELF version", filename);
228                 fclose(iop);
229                 return (-1);
230         }
231         if (ehdr.e_ident[EI_DATA] != my_byte_order()) {
232                 warnx("%s: unsupported byte order", filename);
233                 fclose(iop);
234                 return (-1);
235         }
236         if (ehdr.e_shoff == 0) {
237                 warnx("%s: no section table", filename);
238                 fclose(iop);
239                 return (-1);
240         }
241         if (ehdr.e_shstrndx == SHN_UNDEF) {
242                 warnx("%s: no section name string table", filename);
243                 fclose(iop);
244                 return (-1);
245         }
246
247         shdr = NEW(Elf_Shdr, ehdr.e_shnum);
248         if (fseek(iop, ehdr.e_shoff, SEEK_SET) == -1) {
249                 warn("%s", filename);
250                 free(shdr);
251                 fclose(iop);
252                 return (-1);
253         }
254         if (fread(shdr, sizeof *shdr, ehdr.e_shnum, iop) != ehdr.e_shnum) {
255                 warnx("%s: truncated section table", filename);
256                 free(shdr);
257                 fclose(iop);
258                 return (-1);
259         }
260
261         shstr = NEW(char, shdr[ehdr.e_shstrndx].sh_size);
262         if (fseek(iop, shdr[ehdr.e_shstrndx].sh_offset, SEEK_SET) == -1) {
263                 warn("%s", filename);
264                 free(shstr);
265                 free(shdr);
266                 fclose(iop);
267                 return (-1);
268         }
269         if (fread(shstr, sizeof *shstr, shdr[ehdr.e_shstrndx].sh_size, iop) !=
270             shdr[ehdr.e_shstrndx].sh_size) {
271                 warnx("%s: truncated section name string table", filename);
272                 free(shstr);
273                 free(shdr);
274                 fclose(iop);
275                 return (-1);
276         }
277
278         for (i = 1;  i < ehdr.e_shnum;  i++) {
279                 const char *name = shstr + shdr[i].sh_name;
280
281                 if (strncmp(name, PREFIX, sizeof (PREFIX) - 1) == 0)
282                         enter(name + sizeof (PREFIX) - 1, shdr[i].sh_size);
283         }
284
285         free(shstr);
286         free(shdr);
287         fclose(iop);
288         return (0);
289 }
290
291 /*
292  * Destructively merge all the sorted hash chains into a single sorted
293  * list, and return a pointer to its first element.
294  */
295 static hashent *
296 merge(void)
297 {
298         unsigned int numchains = HASHSIZE;
299
300         while (numchains > 1) {         /* More merging to do. */
301                 unsigned int lo = 0;
302                 /*
303                  * Merge chains pairwise from the outside in, halving the
304                  * number of chains.
305                  */
306                 while (numchains - lo >= 2) {
307                         hashent **linkp = &hashtab[lo];
308                         hashent  *l1 = hashtab[lo++];
309                         hashent  *l2 = hashtab[--numchains];
310
311                         while (l1 != NULL && l2 != NULL) {
312                                 if (strcmp(l1->name, l2->name) < 0) {
313                                         *linkp = l1;
314                                         linkp = &l1->next;
315                                         l1 = l1->next;
316                                 } else {
317                                         *linkp = l2;
318                                         linkp = &l2->next;
319                                         l2 = l2->next;
320                                 }
321                         }
322                         *linkp = l1==NULL ? l2 : l1;
323                 }
324         }
325
326         return (hashtab[0]);
327 }
328
329 /*
330  * Determine the host byte order.
331  */
332 static int
333 my_byte_order(void)
334 {
335         static unsigned short   s = 0xbbaa;
336         int                     byte0;
337
338         byte0 = *(unsigned char *)&s;
339         if (byte0 == 0xaa)
340                 return (ELFDATA2LSB);
341         else if (byte0 == 0xbb)
342                 return (ELFDATA2MSB);
343         else
344                 return (ELFDATANONE);
345 }
346
347 /*
348  * Allocate a chunk of memory and return a pointer to it.  Die if the
349  * malloc fails.
350  */
351 static void *
352 xmalloc(size_t size)
353 {
354         void *p;
355
356         p = malloc(size);
357         if (p == NULL)
358                 err(EXIT_FAILURE, "malloc");
359         return (p);
360 }
361
362 /*
363  * Duplicate a string and return a pointer to the copy.  Die if there is
364  * not enough memory.
365  */
366 static char *
367 xstrdup(const char *s)
368 {
369         int size;
370
371         size = strlen(s) + 1;
372         return (memcpy(xmalloc(size), s, size));
373 }