*: introduce and use FAST_FUNC: regparm on i386, otherwise no-on
[platform/upstream/busybox.git] / modutils / depmod.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * depmod - generate modules.dep
4  * Copyright (c) 2008 Bernhard Fischer
5  *
6  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
7  */
8
9 #undef _GNU_SOURCE
10 #define _GNU_SOURCE
11 #include <libbb.h>
12 #include <sys/utsname.h> /* uname() */
13 /*
14  * Theory of operation:
15  * - iterate over all modules and record their full path
16  * - iterate over all modules looking for "depends=" entries
17  *   for each depends, look through our list of full paths and emit if found
18  */
19
20 typedef struct dep_lst_t {
21         char *name;
22         llist_t *dependencies;
23         llist_t *aliases;
24         struct dep_lst_t *next;
25 } dep_lst_t;
26
27 struct globals {
28         dep_lst_t *lst; /* modules without their corresponding extension */
29 };
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
34 static char* find_keyword(void *the_module, size_t len, const char * const word)
35 {
36         char *ptr = the_module;
37         do {
38                 /* search for the first char in word */
39                 ptr = memchr(ptr, *word, len - (ptr - (char*)the_module));
40                 if (ptr == NULL) /* no occurance left, done */
41                         return NULL;
42                 if (!strncmp(ptr, word, strlen(word))) {
43                         ptr += strlen(word);
44                         break;
45                 }
46                 ++ptr;
47         } while (1);
48         return ptr;
49 }
50 static int FAST_FUNC fileAction(const char *fname, struct stat *sb,
51                                         void ATTRIBUTE_UNUSED *data, int ATTRIBUTE_UNUSED 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
60         if (strrstr(fname, ".ko") == NULL) /* not a module */
61                 goto skip;
62
63 /*XXX: FIXME: does not handle compressed modules!
64  * There should be a function that looks at the extension and sets up
65  * open_transformer for us.
66  */
67         fd = xopen(fname, O_RDONLY);
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
77         this = xzalloc(sizeof(dep_lst_t));
78         this->name = xstrdup(fname);
79         this->next = G.lst;
80         G.lst = this;
81 //bb_info_msg("fname='%s'", fname);
82         ptr = find_keyword(the_module, len, "depends=");
83         if (!*ptr)
84                 goto d_none;
85         deps = depends = xstrdup(ptr);
86 //bb_info_msg(" depends='%s'", depends);
87         while (deps) {
88                 ptr = strsep(&deps, ",");
89 //bb_info_msg("[%s] -> '%s'", fname, (char*)ptr);
90                 llist_add_to_end(&this->dependencies, xstrdup(ptr));
91         }
92         free(depends);
93  d_none:
94         if (ENABLE_FEATURE_DEPMOD_ALIAS)
95         {
96                 size_t pos = 0;
97                 do {
98                         ptr = find_keyword(the_module + pos, len - pos, "alias=");
99                         if (ptr) {
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         }
107         munmap(the_module, sb->st_size);
108  skip:
109         return TRUE;
110 }
111
112 int depmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
113 int depmod_main(int ATTRIBUTE_UNUSED argc, char **argv)
114 {
115         int ret;
116         size_t moddir_base_len = 0; /* length of the "-b basedir" */
117         char *moddir_base = NULL, *moddir, *system_map, *chp;
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
129         getopt32(argv, "aAb:eF:n", &moddir_base, &system_map);
130         argv += optind;
131
132         /* If a version is provided, then that kernel version’s module directory
133          * is used, rather than the current kernel version (as returned by
134          * "uname -r").  */
135         if (*argv && (sscanf(*argv, "%d.%d.%d", &ret, &ret, &ret) == 3)) {
136                 moddir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, *argv++);
137         } else {
138                 struct utsname uts;
139                 if (uname(&uts) < 0)
140                         bb_simple_perror_msg_and_die("uname");
141                 moddir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, uts.release);
142         }
143         /* If no modules are given on the command-line, -a is on per default.  */
144         option_mask32 |= *argv == NULL;
145
146         if (option_mask32 & ARG_b) {
147                 moddir_base_len = strlen(moddir_base) + 1;
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(chp, "w");
154                 if (ENABLE_FEATURE_CLEAN_UP)
155                         free(chp);
156         }
157         ret = EXIT_SUCCESS;
158         do {
159                 chp = option_mask32 & ARG_a ? moddir : (*argv + moddir_base_len);
160
161                 if (!recursive_action(chp,
162                                 ACTION_RECURSE, /* flags */
163                                 fileAction, /* file action */
164                                 NULL, /* dir action */
165                                 NULL, /* user data */
166                                 0)) { /* depth */
167                         ret = EXIT_FAILURE;
168                 }
169         } while (!(option_mask32 & ARG_a) && *++argv);
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         }
203
204 #if ENABLE_FEATURE_DEPMOD_PRUNE_FANCY
205         /* modprobe allegedly wants dependencies without duplicates, i.e.
206          * mod1: mod2 mod3
207          * mod2: mod3
208          * mod3:
209          * implies that mod1 directly depends on mod2 and _not_ mod3 as mod3 is
210          * already implicitely pulled in via mod2. This leaves us with:
211          * mod1: mod2
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                 }
241                 mods = mods->next;
242         }
243 #endif
244
245         mods = G.lst;
246         /* Finally print them.  */
247         while (mods) {
248                 fprintf(filedes, "%s:", mods->name);
249                 /* If we did not resolve all modules, then it's likely that we just did
250                  * not see the names of all prerequisites (which will be NULL in this
251                  * case).  */
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                 }
273                 mods = mods->next;
274         }
275         }
276
277         if (ENABLE_FEATURE_CLEAN_UP) {
278                 fclose_if_not_stdin(filedes);
279                 free(moddir);
280                 while (G.lst) {
281                         dep_lst_t *old = G.lst;
282                         G.lst = G.lst->next;
283                         free(old->name);
284                         free(old);
285                 }
286         }
287         return ret;
288 }