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", 1, 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
,
276 const char *kernel
, const char *basedir
)
280 char *depname
, *p
, *moddir
;
282 if (strchr(name
, '.') || strchr(name
, '/')) {
283 data
= grab_file(name
, size
);
285 *filename
= strdup(name
);
288 fprintf(stderr
, "modinfo: could not open %s: %s\n",
289 name
, strerror(errno
));
296 asprintf(&moddir
, "%s/%s/%s",
297 basedir
, MODULE_DIR
, kernel
);
299 asprintf(&moddir
, "%s/%s",
304 asprintf(&moddir
, "%s/%s/%s",
305 basedir
, MODULE_DIR
, buf
.release
);
307 asprintf(&moddir
, "%s/%s",
308 MODULE_DIR
, buf
.release
);
310 asprintf(&depname
, "%s/%s", moddir
, "modules.dep");
312 /* Search for it in modules.dep. */
313 data
= grab_file(depname
, size
);
315 fprintf(stderr
, "modinfo: could not open %s\n", depname
);
321 for (p
= data
; p
< data
+ *size
; p
= next_line(p
, data
+ *size
)) {
322 if (name_matches(p
, data
+ *size
, name
)) {
323 int namelen
= strcspn(p
, ":");
325 if ('/' == p
[0]) { /* old style deps - absolute path */
326 *filename
= malloc(namelen
+ strlen(basedir
)+2);
327 if (strlen(basedir
)) {
328 sprintf(*filename
, "%s/", basedir
);
329 memcpy(*filename
+strlen(basedir
)+1,p
,
332 +strlen(basedir
)+1] = '\0';
334 memcpy(*filename
,p
,namelen
);
335 (*filename
)[namelen
] = '\0';
338 *filename
= malloc(namelen
+ strlen(moddir
)+2);
339 sprintf(*filename
, "%s/", moddir
);
340 memcpy(*filename
+strlen(moddir
)+1, p
,namelen
);
341 (*filename
)[namelen
+strlen(moddir
)+1] ='\0';
343 release_file(data
, *size
);
344 data
= grab_file(*filename
, size
);
347 "modinfo: could not open %s: %s\n",
348 *filename
, strerror(errno
));
352 release_file(data
, *size
);
353 fprintf(stderr
, "modinfo: could not find module %s\n", name
);
357 static void usage(const char *name
)
359 fprintf(stderr
, "Usage: %s [-0][-F field][-k kernelversion][-b basedir] module...\n"
360 " Prints out the information about one or more module(s).\n"
361 " If a fieldname is given, just print out that field (or nothing if not found).\n"
362 " Otherwise, print all information out in a readable form\n"
363 " If -0 is given, separate with nul, not newline.\n"
364 " If -b is given, use an image of the module tree.\n",
368 int main(int argc
, char *argv
[])
370 union { short s
; char c
[2]; } endian_test
;
371 const char *field
= NULL
;
372 const char *kernel
= NULL
;
374 unsigned long infosize
= 0;
378 if (!getenv("NEW_MODINFO"))
379 try_old_version("modinfo", argv
);
382 if (endian_test
.c
[1] == 1) my_endian
= ELFDATA2MSB
;
383 else if (endian_test
.c
[0] == 1) my_endian
= ELFDATA2LSB
;
387 while ((opt
= getopt_long(argc
,argv
,"adlpVhn0F:k:b:",options
,NULL
)) >= 0){
389 case 'a': field
= "author"; break;
390 case 'd': field
= "description"; break;
391 case 'l': field
= "license"; break;
392 case 'p': field
= "parm"; break;
393 case 'n': field
= "filename"; break;
394 case 'V': printf(PACKAGE
" version " VERSION
"\n"); exit(0);
395 case 'F': field
= optarg
; break;
396 case '0': sep
= '\0'; break;
397 case 'k': kernel
= optarg
; break;
398 case 'b': basedir
= optarg
; break;
399 case 'h': usage(argv
[0]); exit(0); break;
401 usage(argv
[0]); exit(1);
404 if (argc
< optind
+ 1) {
409 for (opt
= optind
; opt
< argc
; opt
++) {
411 unsigned long modulesize
;
414 mod
= grab_module(argv
[opt
], &modulesize
, &filename
,
421 info
= get_section(mod
, modulesize
, &infosize
, ".modinfo");
423 release_file(mod
, modulesize
);
428 print_tag(field
, info
, infosize
, filename
, sep
);
430 print_all(info
, infosize
, filename
, sep
);
431 release_file(mod
, modulesize
);