diff options
Diffstat (limited to 'modutils/depmod.c')
-rw-r--r-- | modutils/depmod.c | 378 |
1 files changed, 152 insertions, 226 deletions
diff --git a/modutils/depmod.c b/modutils/depmod.c index b6a914eb0..35df4a8e2 100644 --- a/modutils/depmod.c +++ b/modutils/depmod.c | |||
@@ -2,6 +2,8 @@ | |||
2 | /* | 2 | /* |
3 | * depmod - generate modules.dep | 3 | * depmod - generate modules.dep |
4 | * Copyright (c) 2008 Bernhard Fischer | 4 | * Copyright (c) 2008 Bernhard Fischer |
5 | * Copyrihgt (c) 2008 Timo Teras <timo.teras@iki.fi> | ||
6 | * Copyright (c) 2008 Vladimir Dronnikov | ||
5 | * | 7 | * |
6 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
7 | */ | 9 | */ |
@@ -10,6 +12,8 @@ | |||
10 | #define _GNU_SOURCE | 12 | #define _GNU_SOURCE |
11 | #include <libbb.h> | 13 | #include <libbb.h> |
12 | #include <sys/utsname.h> /* uname() */ | 14 | #include <sys/utsname.h> /* uname() */ |
15 | #include "modutils.h" | ||
16 | |||
13 | /* | 17 | /* |
14 | * Theory of operation: | 18 | * Theory of operation: |
15 | * - iterate over all modules and record their full path | 19 | * - iterate over all modules and record their full path |
@@ -17,272 +21,194 @@ | |||
17 | * for each depends, look through our list of full paths and emit if found | 21 | * for each depends, look through our list of full paths and emit if found |
18 | */ | 22 | */ |
19 | 23 | ||
20 | typedef struct dep_lst_t { | 24 | typedef struct module_info { |
21 | char *name; | 25 | struct module_info *next; |
26 | char *name, *modname; | ||
22 | llist_t *dependencies; | 27 | llist_t *dependencies; |
23 | llist_t *aliases; | 28 | llist_t *aliases; |
24 | struct dep_lst_t *next; | 29 | llist_t *symbols; |
25 | } dep_lst_t; | 30 | struct module_info *dnext, *dprev; |
26 | 31 | } module_info; | |
27 | struct globals { | 32 | |
28 | dep_lst_t *lst; /* modules without their corresponding extension */ | 33 | enum { |
34 | ARG_a = (1<<0), /* All modules, ignore mods in argv */ | ||
35 | ARG_A = (1<<1), /* Only emit .ko that are newer than modules.dep file */ | ||
36 | ARG_b = (1<<2), /* not /lib/modules/$(uname -r)/ but this base-dir */ | ||
37 | ARG_e = (1<<3), /* with -F, print unresolved symbols */ | ||
38 | ARG_F = (1<<4), /* System.map that contains the symbols */ | ||
39 | ARG_n = (1<<5) /* dry-run, print to stdout only */ | ||
29 | }; | 40 | }; |
30 | #define G (*(struct globals*)&bb_common_bufsiz1) | ||
31 | /* We have to zero it out because of NOEXEC */ | ||
32 | #define INIT_G() memset(&G, 0, sizeof(G)) | ||
33 | 41 | ||
34 | static char* find_keyword(void *the_module, size_t len, const char * const word) | 42 | static int FAST_FUNC parse_module(const char *fname, struct stat *sb, |
43 | void *data, int UNUSED_PARAM depth) | ||
35 | { | 44 | { |
36 | char *ptr = the_module; | 45 | module_info **first = (module_info **) data; |
37 | do { | 46 | char *image, *ptr; |
38 | /* search for the first char in word */ | 47 | module_info *info; |
39 | ptr = memchr(ptr, *word, len - (ptr - (char*)the_module)); | 48 | size_t len = sb->st_size; |
40 | if (ptr == NULL) /* no occurance left, done */ | 49 | |
41 | return NULL; | 50 | if (strrstr(fname, ".ko") == NULL) |
42 | if (!strncmp(ptr, word, strlen(word))) { | 51 | return TRUE; |
43 | ptr += strlen(word); | 52 | |
44 | break; | 53 | image = (char *) xmalloc_open_zipped_read_close(fname, &len); |
54 | info = xzalloc(sizeof(module_info)); | ||
55 | |||
56 | info->next = *first; | ||
57 | *first = info; | ||
58 | |||
59 | info->dnext = info->dprev = info; | ||
60 | info->name = xstrdup(fname); | ||
61 | info->modname = filename2modname(fname, NULL); | ||
62 | for (ptr = image; ptr < image + len - 10; ptr++) { | ||
63 | if (strncmp(ptr, "depends=", 8) == 0) { | ||
64 | char *u; | ||
65 | |||
66 | ptr += 8; | ||
67 | for (u = ptr; *u; u++) | ||
68 | if (*u == '-') | ||
69 | *u = '_'; | ||
70 | ptr += string_to_llist(ptr, &info->dependencies, ","); | ||
71 | } else if (ENABLE_FEATURE_MODUTILS_ALIAS && | ||
72 | strncmp(ptr, "alias=", 6) == 0) { | ||
73 | llist_add_to(&info->aliases, xstrdup(ptr + 6)); | ||
74 | ptr += strlen(ptr); | ||
75 | } else if (ENABLE_FEATURE_MODUTILS_SYMBOLS && | ||
76 | strncmp(ptr, "__ksymtab_", 10) == 0) { | ||
77 | ptr += 10; | ||
78 | if (strncmp(ptr, "gpl", 3) == 0 || | ||
79 | strcmp(ptr, "strings") == 0) | ||
80 | continue; | ||
81 | llist_add_to(&info->symbols, xstrdup(ptr)); | ||
82 | ptr += strlen(ptr); | ||
45 | } | 83 | } |
46 | ++ptr; | 84 | } |
47 | } while (1); | 85 | free(image); |
48 | return ptr; | 86 | |
87 | return TRUE; | ||
49 | } | 88 | } |
50 | static int FAST_FUNC fileAction(const char *fname, struct stat *sb, | ||
51 | void UNUSED_PARAM *data, int UNUSED_PARAM depth) | ||
52 | { | ||
53 | size_t len = sb->st_size; | ||
54 | void *the_module; | ||
55 | char *ptr; | ||
56 | int fd; | ||
57 | char *depends, *deps; | ||
58 | dep_lst_t *this; | ||
59 | 89 | ||
60 | if (strrstr(fname, ".ko") == NULL) /* not a module */ | 90 | static module_info *find_module(module_info *modules, const char *modname) |
61 | goto skip; | 91 | { |
92 | module_info *m; | ||
62 | 93 | ||
63 | /*XXX: FIXME: does not handle compressed modules! | 94 | for (m = modules; m != NULL; m = m->next) |
64 | * There should be a function that looks at the extension and sets up | 95 | if (strcmp(m->modname, modname) == 0) |
65 | * open_transformer for us. | 96 | return m; |
66 | */ | 97 | return NULL; |
67 | fd = xopen(fname, O_RDONLY); | 98 | } |
68 | the_module = mmap(NULL, len, PROT_READ, MAP_SHARED | ||
69 | #if defined MAP_POPULATE | ||
70 | |MAP_POPULATE | ||
71 | #endif | ||
72 | , fd, 0); | ||
73 | close(fd); | ||
74 | if (the_module == MAP_FAILED) | ||
75 | bb_perror_msg_and_die("mmap"); | ||
76 | 99 | ||
77 | this = xzalloc(sizeof(dep_lst_t)); | 100 | static void order_dep_list(module_info *modules, module_info *start, |
78 | this->name = xstrdup(fname); | 101 | llist_t *add) |
79 | this->next = G.lst; | 102 | { |
80 | G.lst = this; | 103 | module_info *m; |
81 | //bb_info_msg("fname='%s'", fname); | 104 | llist_t *n; |
82 | ptr = find_keyword(the_module, len, "depends="); | 105 | |
83 | if (!*ptr) | 106 | for (n = add; n != NULL; n = n->link) { |
84 | goto d_none; | 107 | m = find_module(modules, n->data); |
85 | deps = depends = xstrdup(ptr); | 108 | if (m == NULL) |
86 | //bb_info_msg(" depends='%s'", depends); | 109 | continue; |
87 | while (deps) { | 110 | |
88 | ptr = strsep(&deps, ","); | 111 | /* unlink current entry */ |
89 | //bb_info_msg("[%s] -> '%s'", fname, (char*)ptr); | 112 | m->dnext->dprev = m->dprev; |
90 | llist_add_to_end(&this->dependencies, xstrdup(ptr)); | 113 | m->dprev->dnext = m->dnext; |
91 | } | 114 | |
92 | free(depends); | 115 | /* and add it to tail */ |
93 | d_none: | 116 | m->dnext = start; |
94 | if (ENABLE_FEATURE_DEPMOD_ALIAS) | 117 | m->dprev = start->dprev; |
95 | { | 118 | start->dprev->dnext = m; |
96 | size_t pos = 0; | 119 | start->dprev = m; |
97 | do { | 120 | |
98 | ptr = find_keyword(the_module + pos, len - pos, "alias="); | 121 | /* recurse */ |
99 | if (ptr) { | 122 | order_dep_list(modules, start, m->dependencies); |
100 | //bb_info_msg("[%s] alias '%s'", fname, (char*)ptr); | ||
101 | llist_add_to_end(&this->aliases, xstrdup(ptr)); | ||
102 | } else | ||
103 | break; | ||
104 | pos = (ptr - (char*)the_module); | ||
105 | } while (1); | ||
106 | } | 123 | } |
107 | munmap(the_module, sb->st_size); | ||
108 | skip: | ||
109 | return TRUE; | ||
110 | } | 124 | } |
111 | 125 | ||
112 | int depmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 126 | int depmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
113 | int depmod_main(int argc UNUSED_PARAM, char **argv) | 127 | int depmod_main(int argc UNUSED_PARAM, char **argv) |
114 | { | 128 | { |
115 | int ret; | 129 | module_info *modules = NULL, *m, *dep; |
116 | size_t moddir_base_len = 0; /* length of the "-b basedir" */ | 130 | char *moddir_base = (char *)CONFIG_DEFAULT_MODULES_DIR; |
117 | char *moddir_base = NULL, *moddir, *system_map, *chp; | 131 | int tmp; |
118 | FILE *filedes = stdout; | ||
119 | enum { | ||
120 | ARG_a = (1<<0), /* All modules, ignore mods in argv */ | ||
121 | ARG_A = (1<<1), /* Only emit .ko that are newer than modules.dep file */ | ||
122 | ARG_b = (1<<2), /* not /lib/modules/$(uname -r)/ but this base-dir */ | ||
123 | ARG_e = (1<<3), /* with -F, print unresolved symbols */ | ||
124 | ARG_F = (1<<4), /* System.map that contains the symbols */ | ||
125 | ARG_n = (1<<5) /* dry-run, print to stdout only */ | ||
126 | }; | ||
127 | INIT_G(); | ||
128 | 132 | ||
129 | getopt32(argv, "aAb:eF:n", &moddir_base, &system_map); | 133 | getopt32(argv, "aAb:eF:n", &moddir_base, NULL); |
130 | argv += optind; | 134 | argv += optind; |
131 | 135 | ||
132 | /* If a version is provided, then that kernel version’s module directory | 136 | /* goto modules location */ |
137 | |||
138 | /* If a version is provided, then that kernel version's module directory | ||
133 | * is used, rather than the current kernel version (as returned by | 139 | * is used, rather than the current kernel version (as returned by |
134 | * "uname -r"). */ | 140 | * "uname -r"). */ |
135 | if (*argv && (sscanf(*argv, "%d.%d.%d", &ret, &ret, &ret) == 3)) { | 141 | xchdir(moddir_base); |
136 | moddir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, *argv++); | 142 | if (*argv && (sscanf(*argv, "%d.%d.%d", &tmp, &tmp, &tmp) == 3)) { |
143 | xchdir(*argv++); | ||
137 | } else { | 144 | } else { |
138 | struct utsname uts; | 145 | struct utsname uts; |
139 | if (uname(&uts) < 0) | 146 | uname(&uts); |
140 | bb_simple_perror_msg_and_die("uname"); | 147 | xchdir(uts.release); |
141 | moddir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, uts.release); | ||
142 | } | 148 | } |
143 | /* If no modules are given on the command-line, -a is on per default. */ | 149 | /* If no modules are given on the command-line, -a is on per default. */ |
144 | option_mask32 |= *argv == NULL; | 150 | option_mask32 |= *argv == NULL; |
145 | 151 | ||
146 | if (option_mask32 & ARG_b) { | 152 | /* Scan modules */ |
147 | moddir_base_len = strlen(moddir_base) + 1; | 153 | moddir_base = xrealloc_getcwd_or_warn(NULL); |
148 | xchdir(moddir_base); | ||
149 | } | ||
150 | |||
151 | if (!(option_mask32 & ARG_n)) { /* --dry-run */ | ||
152 | chp = concat_path_file(moddir, CONFIG_DEFAULT_DEPMOD_FILE); | ||
153 | filedes = xfopen_for_write(chp); | ||
154 | if (ENABLE_FEATURE_CLEAN_UP) | ||
155 | free(chp); | ||
156 | } | ||
157 | ret = EXIT_SUCCESS; | ||
158 | do { | 154 | do { |
159 | chp = option_mask32 & ARG_a ? moddir : (*argv + moddir_base_len); | 155 | recursive_action((option_mask32 & ARG_a) ? moddir_base : *argv, |
160 | 156 | ACTION_RECURSE, parse_module, NULL, &modules, 0); | |
161 | if (!recursive_action(chp, | 157 | } while (!(option_mask32 & ARG_a) && *(++argv)); |
162 | ACTION_RECURSE, /* flags */ | 158 | if (ENABLE_FEATURE_CLEAN_UP) |
163 | fileAction, /* file action */ | 159 | free(moddir_base); |
164 | NULL, /* dir action */ | 160 | |
165 | NULL, /* user data */ | 161 | /* Generate dependency and alias files */ |
166 | 0)) { /* depth */ | 162 | if (!(option_mask32 & ARG_n)) |
167 | ret = EXIT_FAILURE; | 163 | freopen(CONFIG_DEFAULT_DEPMOD_FILE, "w", stdout); |
164 | for (m = modules; m != NULL; m = m->next) { | ||
165 | printf("%s:", m->name); | ||
166 | |||
167 | order_dep_list(modules, m, m->dependencies); | ||
168 | while (m->dnext != m) { | ||
169 | dep = m->dnext; | ||
170 | printf(" %s", dep->name); | ||
171 | |||
172 | /* unlink current entry */ | ||
173 | dep->dnext->dprev = dep->dprev; | ||
174 | dep->dprev->dnext = dep->dnext; | ||
175 | dep->dnext = dep->dprev = dep; | ||
168 | } | 176 | } |
169 | } while (!(option_mask32 & ARG_a) && *++argv); | 177 | puts(""); |
170 | |||
171 | { | ||
172 | dep_lst_t *mods = G.lst; | ||
173 | |||
174 | /* Fixup the module names in the depends list */ | ||
175 | while (mods) { | ||
176 | llist_t *deps = NULL, *old_deps = mods->dependencies; | ||
177 | |||
178 | while (old_deps) { | ||
179 | dep_lst_t *all = G.lst; | ||
180 | char *longname = NULL; | ||
181 | char *shortname = llist_pop(&old_deps); | ||
182 | |||
183 | while (all) { | ||
184 | char *nam = | ||
185 | xstrdup(bb_get_last_path_component_nostrip(all->name)); | ||
186 | char *tmp = strrstr(nam, ".ko"); | ||
187 | |||
188 | *tmp = '\0'; | ||
189 | if (!strcmp(nam, shortname)) { | ||
190 | if (ENABLE_FEATURE_CLEAN_UP) | ||
191 | free(nam); | ||
192 | longname = all->name; | ||
193 | break; | ||
194 | } | ||
195 | free(nam); | ||
196 | all = all->next; | ||
197 | } | ||
198 | llist_add_to_end(&deps, longname); | ||
199 | } | ||
200 | mods->dependencies = deps; | ||
201 | mods = mods->next; | ||
202 | } | 178 | } |
203 | 179 | ||
204 | #if ENABLE_FEATURE_DEPMOD_PRUNE_FANCY | 180 | #if ENABLE_FEATURE_MODUTILS_ALIAS |
205 | /* modprobe allegedly wants dependencies without duplicates, i.e. | 181 | if (!(option_mask32 & ARG_n)) |
206 | * mod1: mod2 mod3 | 182 | freopen("modules.alias", "w", stdout); |
207 | * mod2: mod3 | 183 | for (m = modules; m != NULL; m = m->next) { |
208 | * mod3: | 184 | while (m->aliases) { |
209 | * implies that mod1 directly depends on mod2 and _not_ mod3 as mod3 is | 185 | printf("alias %s %s\n", |
210 | * already implicitely pulled in via mod2. This leaves us with: | 186 | (char*)llist_pop(&m->aliases), |
211 | * mod1: mod2 | 187 | m->modname); |
212 | * mod2: mod3 | ||
213 | * mod3: | ||
214 | */ | ||
215 | mods = G.lst; | ||
216 | while (mods) { | ||
217 | llist_t *deps = mods->dependencies; | ||
218 | while (deps) { | ||
219 | dep_lst_t *all = G.lst; | ||
220 | while (all) { | ||
221 | if (!strcmp(all->name, deps->data)) { | ||
222 | llist_t *implied = all->dependencies; | ||
223 | while (implied) { | ||
224 | /* XXX:FIXME: erm, it would be nicer to just | ||
225 | * llist_unlink(&mods->dependencies, implied) */ | ||
226 | llist_t *prune = mods->dependencies; | ||
227 | while (prune) { | ||
228 | if (!strcmp(implied->data, prune->data)) | ||
229 | break; | ||
230 | prune = prune->link; | ||
231 | } | ||
232 | //if (prune) bb_info_msg("[%s] '%s' implies '%s', removing", mods->name, all->name, implied->data); | ||
233 | llist_unlink(&mods->dependencies, prune); | ||
234 | implied = implied->link; | ||
235 | } | ||
236 | } | ||
237 | all = all->next; | ||
238 | } | ||
239 | deps = deps->link; | ||
240 | } | 188 | } |
241 | mods = mods->next; | ||
242 | } | 189 | } |
243 | #endif | 190 | #endif |
244 | 191 | #if ENABLE_FEATURE_MODUTILS_SYMBOLS | |
245 | mods = G.lst; | 192 | if (!(option_mask32 & ARG_n)) |
246 | /* Finally print them. */ | 193 | freopen("modules.symbols", "w", stdout); |
247 | while (mods) { | 194 | for (m = modules; m != NULL; m = m->next) { |
248 | fprintf(filedes, "%s:", mods->name); | 195 | while (m->symbols) { |
249 | /* If we did not resolve all modules, then it's likely that we just did | 196 | printf("alias symbol:%s %s\n", |
250 | * not see the names of all prerequisites (which will be NULL in this | 197 | (char*)llist_pop(&m->symbols), |
251 | * case). */ | 198 | m->modname); |
252 | while (mods->dependencies) { | ||
253 | char *the_dep = llist_pop(&mods->dependencies); | ||
254 | if (the_dep) | ||
255 | fprintf(filedes, " %s", the_dep); | ||
256 | } | ||
257 | fprintf(filedes, "\n"); | ||
258 | if (ENABLE_FEATURE_DEPMOD_ALIAS) | ||
259 | { | ||
260 | char *shortname = | ||
261 | xstrdup(bb_get_last_path_component_nostrip(mods->name)); | ||
262 | char *tmp = strrstr(shortname, ".ko"); | ||
263 | |||
264 | *tmp = '\0'; | ||
265 | |||
266 | while (mods->aliases) { | ||
267 | fprintf(filedes, "alias %s %s\n", | ||
268 | (char*)llist_pop(&mods->aliases), | ||
269 | shortname); | ||
270 | } | ||
271 | free(shortname); | ||
272 | } | 199 | } |
273 | mods = mods->next; | ||
274 | } | ||
275 | } | 200 | } |
201 | #endif | ||
276 | 202 | ||
277 | if (ENABLE_FEATURE_CLEAN_UP) { | 203 | if (ENABLE_FEATURE_CLEAN_UP) { |
278 | fclose_if_not_stdin(filedes); | 204 | while (modules) { |
279 | free(moddir); | 205 | module_info *old = modules; |
280 | while (G.lst) { | 206 | modules = modules->next; |
281 | dep_lst_t *old = G.lst; | ||
282 | G.lst = G.lst->next; | ||
283 | free(old->name); | 207 | free(old->name); |
208 | free(old->modname); | ||
284 | free(old); | 209 | free(old); |
285 | } | 210 | } |
286 | } | 211 | } |
287 | return ret; | 212 | |
213 | return EXIT_SUCCESS; | ||
288 | } | 214 | } |