1 /* Extract module info: useful for both the curious and for scripts. */
2 #define _GNU_SOURCE /* asprintf rocks */
13 #include <sys/utsname.h>
17 #include "zlibsupport.h"
21 #define MODULE_DIR "/lib/modules"
24 static int elf_endian
;
27 static inline void __endian(const void *src
, void *dest
, unsigned int size
)
30 for (i
= 0; i
< size
; i
++)
31 ((unsigned char*)dest
)[i
] = ((unsigned char*)src
)[size
- i
-1];
34 #define TO_NATIVE(x) \
37 if (elf_endian != my_endian) __endian(&(x), &(__x), sizeof(__x)); \
42 static void *get_section32(void *file
, unsigned long *size
, const char *name
)
44 Elf32_Ehdr
*hdr
= file
;
45 Elf32_Shdr
*sechdrs
= file
+ TO_NATIVE(hdr
->e_shoff
);
50 + TO_NATIVE(sechdrs
[TO_NATIVE(hdr
->e_shstrndx
)].sh_offset
);
51 for (i
= 1; i
< TO_NATIVE(hdr
->e_shnum
); i
++)
52 if (streq(secnames
+ TO_NATIVE(sechdrs
[i
].sh_name
), name
)) {
53 *size
= TO_NATIVE(sechdrs
[i
].sh_size
);
54 return file
+ TO_NATIVE(sechdrs
[i
].sh_offset
);
59 static void *get_section64(void *file
, unsigned long *size
, const char *name
)
61 Elf64_Ehdr
*hdr
= file
;
62 Elf64_Shdr
*sechdrs
= file
+ TO_NATIVE(hdr
->e_shoff
);
67 + TO_NATIVE(sechdrs
[TO_NATIVE(hdr
->e_shstrndx
)].sh_offset
);
68 for (i
= 1; i
< TO_NATIVE(hdr
->e_shnum
); i
++)
69 if (streq(secnames
+ TO_NATIVE(sechdrs
[i
].sh_name
), name
)) {
70 *size
= TO_NATIVE(sechdrs
[i
].sh_size
);
71 return file
+ TO_NATIVE(sechdrs
[i
].sh_offset
);
76 static int elf_ident(void *mod
, unsigned long size
)
78 /* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */
81 if (size
< EI_CLASS
|| memcmp(mod
, ELFMAG
, SELFMAG
) != 0)
83 elf_endian
= ident
[EI_DATA
];
84 return ident
[EI_CLASS
];
87 static void *get_section(void *file
, unsigned long filesize
,
88 unsigned long *size
, const char *name
)
90 switch (elf_ident(file
, filesize
)) {
92 return get_section32(file
, size
, name
);
94 return get_section64(file
, size
, name
);
103 const char *name
; /* Terminated by a colon */
108 static struct param
*add_param(const char *name
, struct param
**list
)
111 unsigned int namelen
= strcspn(name
, ":") + 1;
113 for (i
= *list
; i
; i
= i
->next
)
114 if (strncmp(i
->name
, name
, namelen
) == 0)
116 i
= malloc(sizeof(*i
) + namelen
+1);
117 strncpy((char *)(i
+ 1), name
, namelen
);
118 ((char *)(i
+ 1))[namelen
] = '\0';
119 i
->name
= (char *)(i
+ 1);
127 static void print_tag(const char *tag
, const char *info
, unsigned long size
,
128 const char *filename
, char sep
)
130 unsigned int taglen
= strlen(tag
);
132 if (streq(tag
, "filename")) {
133 printf("%s%c", filename
, sep
);
137 for (; info
; info
= next_string(info
, &size
))
138 if (strncmp(info
, tag
, taglen
) == 0 && info
[taglen
] == '=')
139 printf("%s%c", info
+ taglen
+ 1, sep
);
142 static void print_all(const char *info
, unsigned long size
,
143 const char *filename
, char sep
)
145 struct param
*i
, *params
= NULL
;
147 printf("%-16s%s%c", "filename:", filename
, sep
);
148 for (; info
; info
= next_string(info
, &size
)) {
151 /* We expect this in parm and parmtype. */
152 colon
= strchr(info
, ':');
154 /* We store these for handling at the end */
155 if (strstarts(info
, "parm=") && colon
) {
156 i
= add_param(info
+ strlen("parm="), ¶ms
);
157 i
->param
= colon
+ 1;
160 if (strstarts(info
, "parmtype=") && colon
) {
161 i
= add_param(info
+ strlen("parmtype="), ¶ms
);
167 printf("%s%c", info
, sep
);
171 eq
= strchr(info
, '=');
172 /* Warn if no '=' maybe? */
174 char tag
[eq
- info
+ 2];
175 strncpy(tag
, info
, eq
- info
);
177 tag
[eq
-info
+1] = '\0';
178 printf("%-16s%s%c", tag
, eq
+1, sep
);
182 /* Now show parameters. */
183 for (i
= params
; i
; i
= i
->next
) {
185 printf("%-16s%s%s%c", "parm:", i
->name
, i
->type
, sep
);
187 printf("%-16s%s%s (%s)%c",
188 "parm:", i
->name
, i
->param
, i
->type
, sep
);
190 printf("%-16s%s%s%c", "parm:", i
->name
, i
->param
, sep
);
194 static struct option options
[] =
196 {"author", 0, 0, 'a'},
197 {"description", 0, 0, 'd'},
198 {"license", 0, 0, 'l'},
199 {"parameters", 0, 0, 'p'},
200 {"filename", 0, 0, 'n'},
201 {"version", 0, 0, 'V'},
204 {"field", 1, 0, 'F'},
208 /* - and _ are equivalent, and expect suffix. */
209 static int name_matches(const char *line
, const char *end
, const char *modname
)
214 /* Ignore comment lines */
215 if (line
[strspn(line
, "\t ")] == '#')
218 /* Find last / before colon. */
219 p
= memchr(line
, ':', end
- line
);
230 for (i
= 0; modname
[i
]; i
++) {
231 /* Module names can't have colons. */
232 if (modname
[i
] == ':')
234 if (modname
[i
] == p
[i
])
236 if (modname
[i
] == '_' && p
[i
] == '-')
238 if (modname
[i
] == '-' && p
[i
] == '_')
242 /* Must match all the way to the extension */
243 return (p
[i
] == '.');
246 static char *next_line(char *p
, const char *end
)
250 eol
= memchr(p
, '\n', end
- p
);
253 return (char *)end
+ 1;
256 static void *grab_module(const char *name
, unsigned long *size
, char**filename
,
257 const char *kernel
, const char *basedir
)
261 char *depname
, *p
, *moddir
;
263 if (strchr(name
, '.') || strchr(name
, '/')) {
264 data
= grab_file(name
, size
);
266 *filename
= strdup(name
);
269 fprintf(stderr
, "modinfo: could not open %s: %s\n",
270 name
, strerror(errno
));
277 asprintf(&moddir
, "%s/%s/%s",
278 basedir
, MODULE_DIR
, kernel
);
280 asprintf(&moddir
, "%s/%s",
285 asprintf(&moddir
, "%s/%s/%s",
286 basedir
, MODULE_DIR
, buf
.release
);
288 asprintf(&moddir
, "%s/%s",
289 MODULE_DIR
, buf
.release
);
291 asprintf(&depname
, "%s/%s", moddir
, "modules.dep");
293 /* Search for it in modules.dep. */
294 data
= grab_file(depname
, size
);
296 fprintf(stderr
, "modinfo: could not open %s\n", depname
);
302 for (p
= data
; p
< data
+ *size
; p
= next_line(p
, data
+ *size
)) {
303 if (name_matches(p
, data
+ *size
, name
)) {
304 int namelen
= strcspn(p
, ":");
306 if ('/' == p
[0]) { /* old style deps - absolute path */
307 *filename
= malloc(namelen
+ strlen(basedir
)+2);
308 if (strlen(basedir
)) {
309 sprintf(*filename
, "%s/", basedir
);
310 memcpy(*filename
+strlen(basedir
)+1,p
,
313 +strlen(basedir
)+1] = '\0';
315 memcpy(*filename
,p
,namelen
);
316 (*filename
)[namelen
] = '\0';
319 *filename
= malloc(namelen
+ strlen(moddir
)+2);
320 sprintf(*filename
, "%s/", moddir
);
321 memcpy(*filename
+strlen(moddir
)+1, p
,namelen
);
322 (*filename
)[namelen
+strlen(moddir
)+1] ='\0';
324 release_file(data
, *size
);
325 data
= grab_file(*filename
, size
);
328 "modinfo: could not open %s: %s\n",
329 *filename
, strerror(errno
));
333 release_file(data
, *size
);
334 fprintf(stderr
, "modinfo: could not find module %s\n", name
);
338 static void usage(const char *name
)
340 fprintf(stderr
, "Usage: %s [-0][-F field][-k kernelversion][-b basedir] module...\n"
341 " Prints out the information about one or more module(s).\n"
342 " If a fieldname is given, just print out that field (or nothing if not found).\n"
343 " Otherwise, print all information out in a readable form\n"
344 " If -0 is given, separate with nul, not newline.\n"
345 " If -b is given, use an image of the module tree.\n",
349 int main(int argc
, char *argv
[])
351 union { short s
; char c
[2]; } endian_test
;
352 const char *field
= NULL
;
353 const char *kernel
= NULL
;
355 unsigned long infosize
= 0;
360 if (endian_test
.c
[1] == 1) my_endian
= ELFDATA2MSB
;
361 else if (endian_test
.c
[0] == 1) my_endian
= ELFDATA2LSB
;
365 while ((opt
= getopt_long(argc
,argv
,"adlpVhn0F:k:b:",options
,NULL
)) >= 0){
367 case 'a': field
= "author"; break;
368 case 'd': field
= "description"; break;
369 case 'l': field
= "license"; break;
370 case 'p': field
= "parm"; break;
371 case 'n': field
= "filename"; break;
372 case 'V': printf(PACKAGE
" version " VERSION
"\n"); exit(0);
373 case 'F': field
= optarg
; break;
374 case '0': sep
= '\0'; break;
375 case 'k': kernel
= optarg
; break;
376 case 'b': basedir
= optarg
; break;
377 case 'h': usage(argv
[0]); exit(0); break;
379 usage(argv
[0]); exit(1);
382 if (argc
< optind
+ 1) {
387 for (opt
= optind
; opt
< argc
; opt
++) {
389 unsigned long modulesize
;
392 mod
= grab_module(argv
[opt
], &modulesize
, &filename
,
399 info
= get_section(mod
, modulesize
, &infosize
, ".modinfo");
401 release_file(mod
, modulesize
);
406 print_tag(field
, info
, infosize
, filename
, sep
);
408 print_all(info
, infosize
, filename
, sep
);
409 release_file(mod
, modulesize
);