1 /* Extract module info: useful for both the curious and for scripts. */
2 #define _GNU_SOURCE /* asprintf rocks */
13 #include <sys/utsname.h>
15 #include "zlibsupport.h"
16 #include "backwards_compat.c"
18 #define streq(a,b) (strcmp((a),(b)) == 0)
19 #define strstarts(a,start) (strncmp((a),(start), strlen(start)) == 0)
22 #define MODULE_DIR "/lib/modules"
25 static int elf_endian;
28 static inline void __endian(const void *src, void *dest, unsigned int size)
31 for (i = 0; i < size; i++)
32 ((unsigned char*)dest)[i] = ((unsigned char*)src)[size - i-1];
35 #define TO_NATIVE(x) \
38 if (elf_endian != my_endian) __endian(&(x), &(__x), sizeof(__x)); \
43 static void *get_section32(void *file, unsigned long *size, const char *name)
45 Elf32_Ehdr *hdr = file;
46 Elf32_Shdr *sechdrs = file + TO_NATIVE(hdr->e_shoff);
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);
60 static void *get_section64(void *file, unsigned long *size, const char *name)
62 Elf64_Ehdr *hdr = file;
63 Elf64_Shdr *sechdrs = file + TO_NATIVE(hdr->e_shoff);
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);
77 static int elf_ident(void *mod, unsigned long size)
79 /* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */
82 if (size < EI_CLASS || memcmp(mod, ELFMAG, SELFMAG) != 0)
84 elf_endian = ident[EI_DATA];
85 return ident[EI_CLASS];
88 static void *get_section(void *file, unsigned long filesize,
89 unsigned long *size, const char *name)
91 switch (elf_ident(file, filesize)) {
93 return get_section32(file, size, name);
95 return get_section64(file, size, name);
101 static const char *next_string(const char *string, unsigned long *secsize)
103 /* Skip non-zero chars */
106 if ((*secsize)-- <= 1)
110 /* Skip any zero padding. */
113 if ((*secsize)-- <= 1)
122 const char *name; /* Terminated by a colon */
127 static struct param *add_param(const char *name, struct param **list)
130 unsigned int namelen = strcspn(name, ":") + 1;
132 for (i = *list; i; i = i->next)
133 if (strncmp(i->name, name, namelen) == 0)
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);
146 static void print_tag(const char *tag, const char *info, unsigned long size,
147 const char *filename, char sep)
149 unsigned int taglen = strlen(tag);
151 if (streq(tag, "filename")) {
152 printf("%s%c", filename, sep);
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);
161 static void print_all(const char *info, unsigned long size,
162 const char *filename, char sep)
164 struct param *i, *params = NULL;
166 printf("%-16s%s%c", "filename:", filename, sep);
167 for (; info; info = next_string(info, &size)) {
170 /* We expect this in parm and parmtype. */
171 colon = strchr(info, ':');
173 /* We store these for handling at the end */
174 if (strstarts(info, "parm=") && colon) {
175 i = add_param(info + strlen("parm="), ¶ms);
176 i->param = colon + 1;
179 if (strstarts(info, "parmtype=") && colon) {
180 i = add_param(info + strlen("parmtype="), ¶ms);
186 printf("%s%c", info, sep);
190 eq = strchr(info, '=');
191 /* Warn if no '=' maybe? */
193 char tag[eq - info + 2];
194 strncpy(tag, info, eq - info);
196 tag[eq-info+1] = '\0';
197 printf("%-16s%s%c", tag, eq+1, sep);
201 /* Now show parameters. */
202 for (i = params; i; i = i->next) {
204 printf("%-16s%s%s%c", "parm:", i->name, i->type, sep);
206 printf("%-16s%s%s (%s)%c",
207 "parm:", i->name, i->param, i->type, sep);
209 printf("%-16s%s%s%c", "parm:", i->name, i->param, sep);
213 static struct option options[] =
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'},
223 {"field", 0, 0, 'F'},
227 /* - and _ are equivalent, and expect suffix. */
228 static int name_matches(const char *line, const char *end, const char *modname)
233 /* Ignore comment lines */
234 if (line[strspn(line, "\t ")] == '#')
237 /* Find last / before colon. */
238 p = memchr(line, ':', end - line);
249 for (i = 0; modname[i]; i++) {
250 /* Module names can't have colons. */
251 if (modname[i] == ':')
253 if (modname[i] == p[i])
255 if (modname[i] == '_' && p[i] == '-')
257 if (modname[i] == '-' && p[i] == '_')
261 /* Must match all the way to the extension */
262 return (p[i] == '.');
265 static char *next_line(char *p, const char *end)
269 eol = memchr(p, '\n', end - p);
272 return (char *)end + 1;
275 static void *grab_module(const char *name, unsigned long *size, char**filename)
281 data = grab_file(name, size);
283 *filename = strdup(name);
286 if (errno != ENOENT) {
287 fprintf(stderr, "modinfo: could not open %s: %s\n",
288 name, strerror(errno));
292 /* Search for it in modules.dep. */
294 asprintf(&depname, "%s/%s/modules.dep", MODULE_DIR, buf.release);
295 data = grab_file(depname, size);
297 fprintf(stderr, "modinfo: could not open %s\n", depname);
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);
313 "modinfo: could not open %s: %s\n",
314 *filename, strerror(errno));
318 release_file(data, *size);
319 fprintf(stderr, "modinfo: could not find module %s\n", name);
323 static void usage(const char *name)
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",
333 int main(int argc, char *argv[])
335 union { short s; char c[2]; } endian_test;
336 const char *field = NULL;
338 unsigned long infosize;
341 if (!getenv("NEW_MODINFO"))
342 try_old_version("modinfo", argv);
345 if (endian_test.c[1] == 1) my_endian = ELFDATA2MSB;
346 else if (endian_test.c[0] == 1) my_endian = ELFDATA2LSB;
350 while ((opt = getopt_long(argc,argv,"adlpVhn0F:",options,NULL)) >= 0){
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;
361 usage(argv[0]); exit(0);
364 if (argc < optind + 1)
367 for (opt = optind; opt < argc; opt++) {
369 unsigned long modulesize;
372 mod = grab_module(argv[opt], &modulesize, &filename);
378 info = get_section(mod, modulesize, &infosize, ".modinfo");
382 print_tag(field, info, infosize, filename, sep);
384 print_all(info, infosize, filename, sep);