OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / user / module-init-tools / modinfo.c
1 /* Extract module info: useful for both the curious and for scripts. */
2 #define _GNU_SOURCE /* asprintf rocks */
3 #include <elf.h>
4 #include <unistd.h>
5 #include <getopt.h>
6 #include <sys/types.h>
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/utsname.h>
14 #include <sys/mman.h>
15 #include "zlibsupport.h"
16 #include "backwards_compat.c"
17
18 #define streq(a,b) (strcmp((a),(b)) == 0)
19 #define strstarts(a,start) (strncmp((a),(start), strlen(start)) == 0)
20
21 #ifndef MODULE_DIR
22 #define MODULE_DIR "/lib/modules"
23 #endif
24
25 static int elf_endian;
26 static int my_endian;
27
28 static inline void __endian(const void *src, void *dest, unsigned int size)
29 {
30         unsigned int i;
31         for (i = 0; i < size; i++)
32                 ((unsigned char*)dest)[i] = ((unsigned char*)src)[size - i-1];
33 }
34
35 #define TO_NATIVE(x)                                                      \
36 ({                                                                        \
37         typeof(x) __x;                                                    \
38         if (elf_endian != my_endian) __endian(&(x), &(__x), sizeof(__x)); \
39         else __x = x;                                                     \
40         __x;                                                              \
41 })
42
43 static void *get_section32(void *file, unsigned long *size, const char *name)
44 {
45         Elf32_Ehdr *hdr = file;
46         Elf32_Shdr *sechdrs = file + TO_NATIVE(hdr->e_shoff);
47         const char *secnames;
48         unsigned int i;
49
50         secnames = file
51                 + TO_NATIVE(sechdrs[TO_NATIVE(hdr->e_shstrndx)].sh_offset);
52         for (i = 1; i < TO_NATIVE(hdr->e_shnum); i++)
53                 if (streq(secnames + TO_NATIVE(sechdrs[i].sh_name), name)) {
54                         *size = TO_NATIVE(sechdrs[i].sh_size);
55                         return file + TO_NATIVE(sechdrs[i].sh_offset);
56                 }
57         return NULL;
58 }
59
60 static void *get_section64(void *file, unsigned long *size, const char *name)
61 {
62         Elf64_Ehdr *hdr = file;
63         Elf64_Shdr *sechdrs = file + TO_NATIVE(hdr->e_shoff);
64         const char *secnames;
65         unsigned int i;
66
67         secnames = file
68                 + TO_NATIVE(sechdrs[TO_NATIVE(hdr->e_shstrndx)].sh_offset);
69         for (i = 1; i < TO_NATIVE(hdr->e_shnum); i++)
70                 if (streq(secnames + TO_NATIVE(sechdrs[i].sh_name), name)) {
71                         *size = TO_NATIVE(sechdrs[i].sh_size);
72                         return file + TO_NATIVE(sechdrs[i].sh_offset);
73                 }
74         return NULL;
75 }
76
77 static int elf_ident(void *mod, unsigned long size)
78 {
79         /* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */
80         char *ident = mod;
81
82         if (size < EI_CLASS || memcmp(mod, ELFMAG, SELFMAG) != 0)
83                 return ELFCLASSNONE;
84         elf_endian = ident[EI_DATA];
85         return ident[EI_CLASS];
86 }
87
88 static void *get_section(void *file, unsigned long filesize,
89                          unsigned long *size, const char *name)
90 {
91         switch (elf_ident(file, filesize)) {
92         case ELFCLASS32:
93                 return get_section32(file, size, name);
94         case ELFCLASS64:
95                 return get_section64(file, size, name);
96         default:
97                 return NULL;
98         }
99 }
100
101 static const char *next_string(const char *string, unsigned long *secsize)
102 {
103         /* Skip non-zero chars */
104         while (string[0]) {
105                 string++;
106                 if ((*secsize)-- <= 1)
107                         return NULL;
108         }
109
110         /* Skip any zero padding. */
111         while (!string[0]) {
112                 string++;
113                 if ((*secsize)-- <= 1)
114                         return NULL;
115         }
116         return string;
117 }
118
119 struct param
120 {
121         struct param *next;
122         const char *name;       /* Terminated by a colon */
123         const char *param;
124         const char *type;
125 };
126
127 static struct param *add_param(const char *name, struct param **list)
128 {
129         struct param *i;
130         unsigned int namelen = strcspn(name, ":") + 1;
131
132         for (i = *list; i; i = i->next)
133                 if (strncmp(i->name, name, namelen) == 0)
134                         return i;
135         i = malloc(sizeof(*i) + namelen+1);
136         strncpy((char *)(i + 1), name, namelen);
137         ((char *)(i + 1))[namelen] = '\0';
138         i->name = (char *)(i + 1);
139         i->param = NULL;
140         i->type = NULL;
141         i->next = *list;
142         *list = i;
143         return i;
144 }
145
146 static void print_tag(const char *tag, const char *info, unsigned long size,
147                       const char *filename, char sep)
148 {
149         unsigned int taglen = strlen(tag);
150
151         if (streq(tag, "filename")) {
152                 printf("%s%c", filename, sep);
153                 return;
154         }
155
156         for (; info; info = next_string(info, &size))
157                 if (strncmp(info, tag, taglen) == 0 && info[taglen] == '=')
158                         printf("%s%c", info + taglen + 1, sep);
159 }
160
161 static void print_all(const char *info, unsigned long size,
162                       const char *filename, char sep)
163 {
164         struct param *i, *params = NULL;
165
166         printf("%-16s%s%c", "filename:", filename, sep);
167         for (; info; info = next_string(info, &size)) {
168                 char *eq, *colon;
169
170                 /* We expect this in parm and parmtype. */
171                 colon = strchr(info, ':');
172
173                 /* We store these for handling at the end */
174                 if (strstarts(info, "parm=") && colon) {
175                         i = add_param(info + strlen("parm="), &params);
176                         i->param = colon + 1;
177                         continue;
178                 }
179                 if (strstarts(info, "parmtype=") && colon) {
180                         i = add_param(info + strlen("parmtype="), &params);
181                         i->type = colon + 1;
182                         continue;
183                 }
184
185                 if (!sep) {
186                         printf("%s%c", info, sep);
187                         continue;
188                 }
189
190                 eq = strchr(info, '=');
191                 /* Warn if no '=' maybe? */
192                 if (eq) {
193                         char tag[eq - info + 2];
194                         strncpy(tag, info, eq - info);
195                         tag[eq-info] = ':';
196                         tag[eq-info+1] = '\0';
197                         printf("%-16s%s%c", tag, eq+1, sep);
198                 }
199         }
200
201         /* Now show parameters. */
202         for (i = params; i; i = i->next) {
203                 if (!i->param)
204                         printf("%-16s%s%s%c", "parm:", i->name, i->type, sep);
205                 else if (i->type)
206                         printf("%-16s%s%s (%s)%c",
207                                "parm:", i->name, i->param, i->type, sep);
208                 else 
209                         printf("%-16s%s%s%c", "parm:", i->name, i->param, sep);
210         }
211 }
212
213 static struct option options[] =
214 {
215         {"author", 0, 0, 'a'},
216         {"description", 0, 0, 'd'},
217         {"license", 0, 0, 'l'},
218         {"parameters", 0, 0, 'p'},
219         {"filename", 0, 0, 'n'},
220         {"version", 0, 0, 'V'},
221         {"help", 0, 0, 'h'},
222         {"null", 0, 0, '0'},
223         {"field", 0, 0, 'F'},
224         {0, 0, 0, 0}
225 };
226
227 /* - and _ are equivalent, and expect suffix. */
228 static int name_matches(const char *line, const char *end, const char *modname)
229 {
230         unsigned int i;
231         char *p;
232
233         /* Ignore comment lines */
234         if (line[strspn(line, "\t ")] == '#')
235                 return 0;
236
237         /* Find last / before colon. */
238         p = memchr(line, ':', end - line);
239         if (!p)
240                 return 0;
241         while (p > line) {
242                 if (*p == '/') {
243                         p++;
244                         break;
245                 }
246                 p--;
247         }
248
249         for (i = 0; modname[i]; i++) {
250                 /* Module names can't have colons. */
251                 if (modname[i] == ':')
252                         continue;
253                 if (modname[i] == p[i])
254                         continue;
255                 if (modname[i] == '_' && p[i] == '-')
256                         continue;
257                 if (modname[i] == '-' && p[i] == '_')
258                         continue;
259                 return 0;
260         }
261         /* Must match all the way to the extension */
262         return (p[i] == '.');
263 }
264
265 static char *next_line(char *p, const char *end)
266 {
267         char *eol;
268
269         eol = memchr(p, '\n', end - p);
270         if (eol)
271                 return eol + 1;
272         return (char *)end + 1;
273 }
274
275 static void *grab_module(const char *name, unsigned long *size, char**filename)
276 {
277         char *data;
278         struct utsname buf;
279         char *depname, *p;
280
281         data = grab_file(name, size);
282         if (data) {
283                 *filename = strdup(name);
284                 return data;
285         }
286         if (errno != ENOENT) {
287                 fprintf(stderr, "modinfo: could not open %s: %s\n",
288                         name, strerror(errno));
289                 return NULL;
290         }
291
292         /* Search for it in modules.dep. */
293         uname(&buf);
294         asprintf(&depname, "%s/%s/modules.dep", MODULE_DIR, buf.release);
295         data = grab_file(depname, size);
296         if (!data) {
297                 fprintf(stderr, "modinfo: could not open %s\n", depname);
298                 free(depname);
299                 return NULL;
300         }
301         free(depname);
302
303         for (p = data; p < data + *size; p = next_line(p, data + *size)) {
304                 if (name_matches(p, data + *size, name)) {
305                         int namelen = strcspn(p, ":");
306                         *filename = malloc(namelen + 1);
307                         memcpy(*filename, p, namelen);
308                         (*filename)[namelen] = '\0';
309                         release_file(data, *size);
310                         data = grab_file(*filename, size);
311                         if (!data)
312                                 fprintf(stderr,
313                                         "modinfo: could not open %s: %s\n",
314                                         *filename, strerror(errno));
315                         return data;
316                 }
317         }
318         release_file(data, *size);
319         fprintf(stderr, "modinfo: could not find module %s\n", name);
320         return NULL;
321 }
322
323 static void usage(const char *name)
324 {
325         fprintf(stderr, "Usage: %s [-0][-F field] module...\n"
326                 " Prints out the information about one or more module(s).\n"
327                 " If a fieldname is given, just print out that field (or nothing if not found).\n"
328                 " Otherwise, print all information out in a readable form\n"
329                 " If -0 is given, separate with nul, not newline.\n",
330                 name);
331 }
332
333 int main(int argc, char *argv[])
334 {
335         union { short s; char c[2]; } endian_test;
336         const char *field = NULL;
337         char sep = '\n';
338         unsigned long infosize;
339         int opt, ret = 0;
340
341         if (!getenv("NEW_MODINFO"))
342                 try_old_version("modinfo", argv);
343
344         endian_test.s = 1;
345         if (endian_test.c[1] == 1) my_endian = ELFDATA2MSB;
346         else if (endian_test.c[0] == 1) my_endian = ELFDATA2LSB;
347         else
348                 abort();
349
350         while ((opt = getopt_long(argc,argv,"adlpVhn0F:",options,NULL)) >= 0){
351                 switch (opt) {
352                 case 'a': field = "author"; break;
353                 case 'd': field = "description"; break;
354                 case 'l': field = "license"; break;
355                 case 'p': field = "parm"; break;
356                 case 'n': field = "filename"; break;
357                 case 'V': printf(PACKAGE " version " VERSION "\n"); exit(0);
358                 case 'F': field = optarg; break;
359                 case '0': sep = '\0'; break;
360                 default:
361                         usage(argv[0]); exit(0);
362                 }
363         }
364         if (argc < optind + 1)
365                 usage(argv[0]);
366
367         for (opt = optind; opt < argc; opt++) {
368                 void *info, *mod;
369                 unsigned long modulesize;
370                 char *filename;
371
372                 mod = grab_module(argv[opt], &modulesize, &filename);
373                 if (!mod) {
374                         ret = 1;
375                         continue;
376                 }
377
378                 info = get_section(mod, modulesize, &infosize, ".modinfo");
379                 if (!info)
380                         continue;
381                 if (field)
382                         print_tag(field, info, infosize, filename, sep);
383                 else
384                         print_all(info, infosize, filename, sep);
385                 free(filename);
386         }
387         return ret;
388 }