Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Postprocess module symbol versions |
2 | * | |
3 | * Copyright 2003 Kai Germaschewski | |
4 | * Copyright 2002-2004 Rusty Russell, IBM Corporation | |
df578e7d | 5 | * Copyright 2006-2008 Sam Ravnborg |
1da177e4 LT |
6 | * Based in part on module-init-tools/depmod.c,file2alias |
7 | * | |
8 | * This software may be used and distributed according to the terms | |
9 | * of the GNU General Public License, incorporated herein by reference. | |
10 | * | |
11 | * Usage: modpost vmlinux module1.o module2.o ... | |
12 | */ | |
13 | ||
b2e3e658 | 14 | #define _GNU_SOURCE |
5370d4ac | 15 | #include <elf.h> |
a89227d7 | 16 | #include <fnmatch.h> |
b2e3e658 | 17 | #include <stdio.h> |
1da177e4 | 18 | #include <ctype.h> |
5003bab8 | 19 | #include <string.h> |
712f9b46 | 20 | #include <limits.h> |
e54dd93a | 21 | #include <stdbool.h> |
eed380f3 | 22 | #include <errno.h> |
1da177e4 | 23 | #include "modpost.h" |
b817f6fe | 24 | #include "../../include/linux/license.h" |
987d2e0a | 25 | #include "../../include/linux/module_symbol.h" |
9e1b9b80 | 26 | |
481461f5 | 27 | static bool module_enabled; |
1da177e4 | 28 | /* Are we using CONFIG_MODVERSIONS? */ |
58e01fca | 29 | static bool modversions; |
1da177e4 | 30 | /* Is CONFIG_MODULE_SRCVERSION_ALL set? */ |
58e01fca | 31 | static bool all_versions; |
040fcc81 | 32 | /* If we are modposting external module set to 1 */ |
58e01fca | 33 | static bool external_module; |
c53ddacd | 34 | /* Only warn about unresolved symbols */ |
58e01fca | 35 | static bool warn_unresolved; |
2a66c312 | 36 | |
b5f1a52a | 37 | static int sec_mismatch_count; |
58e01fca | 38 | static bool sec_mismatch_warn_only = true; |
5e9e95cc MY |
39 | /* Trim EXPORT_SYMBOLs that are unused by in-tree modules */ |
40 | static bool trim_unused_exports; | |
41 | ||
eed380f3 | 42 | /* ignore missing files */ |
58e01fca | 43 | static bool ignore_missing_files; |
54b77847 | 44 | /* If set to 1, only warn (instead of error) about missing ns imports */ |
58e01fca | 45 | static bool allow_missing_ns_imports; |
588ccd73 | 46 | |
0fd3fbad MY |
47 | static bool error_occurred; |
48 | ||
20ff3685 MY |
49 | static bool extra_warn; |
50 | ||
4475dff5 MY |
51 | /* |
52 | * Cut off the warnings when there are too many. This typically occurs when | |
53 | * vmlinux is missing. ('make modules' without building vmlinux.) | |
54 | */ | |
55 | #define MAX_UNRESOLVED_REPORTS 10 | |
56 | static unsigned int nr_unresolved; | |
57 | ||
4fd3e4ef WG |
58 | /* In kernel, this size is defined in linux/module.h; |
59 | * here we use Elf_Addr instead of long for covering cross-compile | |
60 | */ | |
61 | ||
62 | #define MODULE_NAME_LEN (64 - sizeof(Elf_Addr)) | |
63 | ||
93c95e52 JY |
64 | void __attribute__((format(printf, 2, 3))) |
65 | modpost_log(enum loglevel loglevel, const char *fmt, ...) | |
1da177e4 LT |
66 | { |
67 | va_list arglist; | |
68 | ||
93c95e52 JY |
69 | switch (loglevel) { |
70 | case LOG_WARN: | |
71 | fprintf(stderr, "WARNING: "); | |
72 | break; | |
73 | case LOG_ERROR: | |
74 | fprintf(stderr, "ERROR: "); | |
75 | break; | |
76 | case LOG_FATAL: | |
77 | fprintf(stderr, "FATAL: "); | |
78 | break; | |
79 | default: /* invalid loglevel, ignore */ | |
80 | break; | |
81 | } | |
1da177e4 | 82 | |
93c95e52 | 83 | fprintf(stderr, "modpost: "); |
1da177e4 LT |
84 | |
85 | va_start(arglist, fmt); | |
86 | vfprintf(stderr, fmt, arglist); | |
87 | va_end(arglist); | |
2a116659 | 88 | |
93c95e52 JY |
89 | if (loglevel == LOG_FATAL) |
90 | exit(1); | |
0fd3fbad MY |
91 | if (loglevel == LOG_ERROR) |
92 | error_occurred = true; | |
2a116659 MW |
93 | } |
94 | ||
e54dd93a MY |
95 | static inline bool strends(const char *str, const char *postfix) |
96 | { | |
97 | if (strlen(str) < strlen(postfix)) | |
98 | return false; | |
99 | ||
100 | return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0; | |
101 | } | |
102 | ||
1da177e4 | 103 | void *do_nofail(void *ptr, const char *expr) |
040fcc81 | 104 | { |
df578e7d | 105 | if (!ptr) |
93c95e52 | 106 | fatal("Memory allocation failure: %s.\n", expr); |
040fcc81 | 107 | |
1da177e4 LT |
108 | return ptr; |
109 | } | |
110 | ||
ac5100f5 MY |
111 | char *read_text_file(const char *filename) |
112 | { | |
113 | struct stat st; | |
114 | size_t nbytes; | |
115 | int fd; | |
116 | char *buf; | |
117 | ||
118 | fd = open(filename, O_RDONLY); | |
119 | if (fd < 0) { | |
120 | perror(filename); | |
121 | exit(1); | |
122 | } | |
040fcc81 | 123 | |
ac5100f5 MY |
124 | if (fstat(fd, &st) < 0) { |
125 | perror(filename); | |
126 | exit(1); | |
127 | } | |
128 | ||
129 | buf = NOFAIL(malloc(st.st_size + 1)); | |
130 | ||
131 | nbytes = st.st_size; | |
132 | ||
133 | while (nbytes) { | |
134 | ssize_t bytes_read; | |
135 | ||
136 | bytes_read = read(fd, buf, nbytes); | |
137 | if (bytes_read < 0) { | |
138 | perror(filename); | |
139 | exit(1); | |
140 | } | |
141 | ||
142 | nbytes -= bytes_read; | |
143 | } | |
144 | buf[st.st_size] = '\0'; | |
145 | ||
146 | close(fd); | |
147 | ||
148 | return buf; | |
040fcc81 SR |
149 | } |
150 | ||
ac5100f5 | 151 | char *get_line(char **stringp) |
1da177e4 | 152 | { |
736bb118 NS |
153 | char *orig = *stringp, *next; |
154 | ||
ac5100f5 | 155 | /* do not return the unwanted extra line at EOF */ |
736bb118 | 156 | if (!orig || *orig == '\0') |
ac5100f5 | 157 | return NULL; |
df578e7d | 158 | |
6020db50 | 159 | /* don't use strsep here, it is not available everywhere */ |
736bb118 NS |
160 | next = strchr(orig, '\n'); |
161 | if (next) | |
162 | *next++ = '\0'; | |
163 | ||
164 | *stringp = next; | |
165 | ||
166 | return orig; | |
1da177e4 LT |
167 | } |
168 | ||
169 | /* A list of all modules we processed */ | |
325eba05 | 170 | LIST_HEAD(modules); |
1da177e4 | 171 | |
8b185743 | 172 | static struct module *find_module(const char *modname) |
1da177e4 LT |
173 | { |
174 | struct module *mod; | |
175 | ||
325eba05 | 176 | list_for_each_entry(mod, &modules, list) { |
1da177e4 | 177 | if (strcmp(mod->name, modname) == 0) |
325eba05 MY |
178 | return mod; |
179 | } | |
180 | return NULL; | |
1da177e4 LT |
181 | } |
182 | ||
8c9ce89c | 183 | static struct module *new_module(const char *name, size_t namelen) |
1da177e4 LT |
184 | { |
185 | struct module *mod; | |
62070fa4 | 186 | |
8c9ce89c | 187 | mod = NOFAIL(malloc(sizeof(*mod) + namelen + 1)); |
1da177e4 | 188 | memset(mod, 0, sizeof(*mod)); |
1da177e4 | 189 | |
f841536e | 190 | INIT_LIST_HEAD(&mod->exported_symbols); |
8a69152b | 191 | INIT_LIST_HEAD(&mod->unresolved_symbols); |
ab489d60 MY |
192 | INIT_LIST_HEAD(&mod->missing_namespaces); |
193 | INIT_LIST_HEAD(&mod->imported_namespaces); | |
8a69152b | 194 | |
8c9ce89c MY |
195 | memcpy(mod->name, name, namelen); |
196 | mod->name[namelen] = '\0'; | |
197 | mod->is_vmlinux = (strcmp(mod->name, "vmlinux") == 0); | |
5066743e MY |
198 | |
199 | /* | |
200 | * Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE() | |
201 | * is missing, do not check the use for EXPORT_SYMBOL_GPL() becasue | |
202 | * modpost will exit wiht error anyway. | |
203 | */ | |
204 | mod->is_gpl_compatible = true; | |
205 | ||
325eba05 | 206 | list_add_tail(&mod->list, &modules); |
1da177e4 LT |
207 | |
208 | return mod; | |
209 | } | |
210 | ||
211 | /* A hash of all exported symbols, | |
212 | * struct symbol is also used for lists of unresolved symbols */ | |
213 | ||
214 | #define SYMBOL_HASH_SIZE 1024 | |
215 | ||
216 | struct symbol { | |
217 | struct symbol *next; | |
f841536e | 218 | struct list_head list; /* link to module::exported_symbols or module::unresolved_symbols */ |
1da177e4 | 219 | struct module *module; |
389eb3f5 | 220 | char *namespace; |
58e01fca MY |
221 | unsigned int crc; |
222 | bool crc_valid; | |
223 | bool weak; | |
ddb5cdba | 224 | bool is_func; |
2a66c312 | 225 | bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */ |
5e9e95cc | 226 | bool used; /* there exists a user of this symbol */ |
859c8175 | 227 | char name[]; |
1da177e4 LT |
228 | }; |
229 | ||
230 | static struct symbol *symbolhash[SYMBOL_HASH_SIZE]; | |
231 | ||
f3945833 | 232 | /* This is based on the hash algorithm from gdbm, via tdb */ |
1da177e4 LT |
233 | static inline unsigned int tdb_hash(const char *name) |
234 | { | |
235 | unsigned value; /* Used to compute the hash value. */ | |
236 | unsigned i; /* Used to cycle through random values. */ | |
237 | ||
238 | /* Set the initial value from the key size. */ | |
df578e7d | 239 | for (value = 0x238F13AF * strlen(name), i = 0; name[i]; i++) |
1da177e4 LT |
240 | value = (value + (((unsigned char *)name)[i] << (i*5 % 24))); |
241 | ||
242 | return (1103515243 * value + 12345); | |
243 | } | |
244 | ||
5c3ead8c SR |
245 | /** |
246 | * Allocate a new symbols for use in the hash of exported symbols or | |
247 | * the list of unresolved symbols per module | |
248 | **/ | |
f18379a3 | 249 | static struct symbol *alloc_symbol(const char *name) |
1da177e4 LT |
250 | { |
251 | struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1)); | |
252 | ||
253 | memset(s, 0, sizeof(*s)); | |
254 | strcpy(s->name, name); | |
31cb50b5 | 255 | |
1da177e4 LT |
256 | return s; |
257 | } | |
258 | ||
259 | /* For the hash of exported symbols */ | |
f18379a3 | 260 | static void hash_add_symbol(struct symbol *sym) |
1da177e4 LT |
261 | { |
262 | unsigned int hash; | |
1da177e4 | 263 | |
f18379a3 MY |
264 | hash = tdb_hash(sym->name) % SYMBOL_HASH_SIZE; |
265 | sym->next = symbolhash[hash]; | |
266 | symbolhash[hash] = sym; | |
1da177e4 LT |
267 | } |
268 | ||
e882e89b MY |
269 | static void sym_add_unresolved(const char *name, struct module *mod, bool weak) |
270 | { | |
8a69152b MY |
271 | struct symbol *sym; |
272 | ||
f18379a3 | 273 | sym = alloc_symbol(name); |
8a69152b MY |
274 | sym->weak = weak; |
275 | ||
276 | list_add_tail(&sym->list, &mod->unresolved_symbols); | |
e882e89b MY |
277 | } |
278 | ||
69c4cc99 | 279 | static struct symbol *sym_find_with_module(const char *name, struct module *mod) |
1da177e4 LT |
280 | { |
281 | struct symbol *s; | |
282 | ||
283 | /* For our purposes, .foo matches foo. PPC64 needs this. */ | |
284 | if (name[0] == '.') | |
285 | name++; | |
286 | ||
df578e7d | 287 | for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next) { |
69c4cc99 | 288 | if (strcmp(s->name, name) == 0 && (!mod || s->module == mod)) |
1da177e4 LT |
289 | return s; |
290 | } | |
291 | return NULL; | |
292 | } | |
293 | ||
69c4cc99 MY |
294 | static struct symbol *find_symbol(const char *name) |
295 | { | |
296 | return sym_find_with_module(name, NULL); | |
297 | } | |
298 | ||
70ddb48d | 299 | struct namespace_list { |
ab489d60 | 300 | struct list_head list; |
70ddb48d MY |
301 | char namespace[]; |
302 | }; | |
303 | ||
ab489d60 | 304 | static bool contains_namespace(struct list_head *head, const char *namespace) |
cb9b55d2 | 305 | { |
ab489d60 MY |
306 | struct namespace_list *list; |
307 | ||
700c48b4 MY |
308 | /* |
309 | * The default namespace is null string "", which is always implicitly | |
310 | * contained. | |
311 | */ | |
312 | if (!namespace[0]) | |
313 | return true; | |
314 | ||
ab489d60 | 315 | list_for_each_entry(list, head, list) { |
76b54cf0 | 316 | if (!strcmp(list->namespace, namespace)) |
cb9b55d2 | 317 | return true; |
ab489d60 | 318 | } |
cb9b55d2 MM |
319 | |
320 | return false; | |
321 | } | |
322 | ||
ab489d60 | 323 | static void add_namespace(struct list_head *head, const char *namespace) |
cb9b55d2 MM |
324 | { |
325 | struct namespace_list *ns_entry; | |
326 | ||
ab489d60 MY |
327 | if (!contains_namespace(head, namespace)) { |
328 | ns_entry = NOFAIL(malloc(sizeof(*ns_entry) + | |
cb9b55d2 MM |
329 | strlen(namespace) + 1)); |
330 | strcpy(ns_entry->namespace, namespace); | |
ab489d60 | 331 | list_add_tail(&ns_entry->list, head); |
cb9b55d2 MM |
332 | } |
333 | } | |
334 | ||
d2e4d05c MY |
335 | static void *sym_get_data_by_offset(const struct elf_info *info, |
336 | unsigned int secindex, unsigned long offset) | |
6124c04c | 337 | { |
4b8a5cfb | 338 | Elf_Shdr *sechdr = &info->sechdrs[secindex]; |
6124c04c | 339 | |
afa0459d | 340 | return (void *)info->hdr + sechdr->sh_offset + offset; |
6124c04c | 341 | } |
62a26356 | 342 | |
abe864b8 | 343 | void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym) |
afa0459d | 344 | { |
d2e4d05c MY |
345 | return sym_get_data_by_offset(info, get_secindex(info, sym), |
346 | sym->st_value); | |
347 | } | |
afa0459d | 348 | |
565587d8 MY |
349 | static const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr) |
350 | { | |
351 | return sym_get_data_by_offset(info, info->secindex_strings, | |
352 | sechdr->sh_name); | |
353 | } | |
afa0459d | 354 | |
125ed24a | 355 | static const char *sec_name(const struct elf_info *info, unsigned int secindex) |
565587d8 | 356 | { |
125ed24a MY |
357 | /* |
358 | * If sym->st_shndx is a special section index, there is no | |
359 | * corresponding section header. | |
360 | * Return "" if the index is out of range of info->sechdrs[] array. | |
361 | */ | |
362 | if (secindex >= info->num_sections) | |
363 | return ""; | |
364 | ||
565587d8 | 365 | return sech_name(info, &info->sechdrs[secindex]); |
afa0459d MY |
366 | } |
367 | ||
62a26356 AIB |
368 | #define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0) |
369 | ||
9ae5bd18 | 370 | static struct symbol *sym_add_exported(const char *name, struct module *mod, |
6e7611c4 | 371 | bool gpl_only, const char *namespace) |
1da177e4 LT |
372 | { |
373 | struct symbol *s = find_symbol(name); | |
374 | ||
e76cc48d | 375 | if (s && (!external_module || s->module->is_vmlinux || s->module == mod)) { |
b8422711 MY |
376 | error("%s: '%s' exported twice. Previous export was in %s%s\n", |
377 | mod->name, name, s->module->name, | |
378 | s->module->is_vmlinux ? "" : ".ko"); | |
1da177e4 | 379 | } |
7ef9ab3b | 380 | |
f18379a3 | 381 | s = alloc_symbol(name); |
7ef9ab3b | 382 | s->module = mod; |
2a66c312 | 383 | s->is_gpl_only = gpl_only; |
700c48b4 | 384 | s->namespace = NOFAIL(strdup(namespace)); |
e76cc48d | 385 | list_add_tail(&s->list, &mod->exported_symbols); |
f18379a3 | 386 | hash_add_symbol(s); |
e76cc48d | 387 | |
040fcc81 SR |
388 | return s; |
389 | } | |
390 | ||
f292d875 | 391 | static void sym_set_crc(struct symbol *sym, unsigned int crc) |
040fcc81 | 392 | { |
f292d875 MY |
393 | sym->crc = crc; |
394 | sym->crc_valid = true; | |
1da177e4 LT |
395 | } |
396 | ||
3b09efc4 | 397 | static void *grab_file(const char *filename, size_t *size) |
1da177e4 LT |
398 | { |
399 | struct stat st; | |
eb3d5cc6 | 400 | void *map = MAP_FAILED; |
1da177e4 LT |
401 | int fd; |
402 | ||
403 | fd = open(filename, O_RDONLY); | |
eb3d5cc6 | 404 | if (fd < 0) |
1da177e4 | 405 | return NULL; |
eb3d5cc6 JJ |
406 | if (fstat(fd, &st)) |
407 | goto failed; | |
1da177e4 LT |
408 | |
409 | *size = st.st_size; | |
410 | map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); | |
1da177e4 | 411 | |
eb3d5cc6 JJ |
412 | failed: |
413 | close(fd); | |
1da177e4 LT |
414 | if (map == MAP_FAILED) |
415 | return NULL; | |
416 | return map; | |
417 | } | |
418 | ||
3b09efc4 | 419 | static void release_file(void *file, size_t size) |
1da177e4 LT |
420 | { |
421 | munmap(file, size); | |
422 | } | |
423 | ||
85bd2fdd | 424 | static int parse_elf(struct elf_info *info, const char *filename) |
1da177e4 LT |
425 | { |
426 | unsigned int i; | |
85bd2fdd | 427 | Elf_Ehdr *hdr; |
1da177e4 LT |
428 | Elf_Shdr *sechdrs; |
429 | Elf_Sym *sym; | |
1ce53adf DV |
430 | const char *secstrings; |
431 | unsigned int symtab_idx = ~0U, symtab_shndx_idx = ~0U; | |
1da177e4 LT |
432 | |
433 | hdr = grab_file(filename, &info->size); | |
434 | if (!hdr) { | |
eed380f3 GR |
435 | if (ignore_missing_files) { |
436 | fprintf(stderr, "%s: %s (ignored)\n", filename, | |
437 | strerror(errno)); | |
438 | return 0; | |
439 | } | |
1da177e4 | 440 | perror(filename); |
6803dc0e | 441 | exit(1); |
1da177e4 LT |
442 | } |
443 | info->hdr = hdr; | |
85bd2fdd SR |
444 | if (info->size < sizeof(*hdr)) { |
445 | /* file too small, assume this is an empty .o file */ | |
446 | return 0; | |
447 | } | |
448 | /* Is this a valid ELF file? */ | |
449 | if ((hdr->e_ident[EI_MAG0] != ELFMAG0) || | |
450 | (hdr->e_ident[EI_MAG1] != ELFMAG1) || | |
451 | (hdr->e_ident[EI_MAG2] != ELFMAG2) || | |
452 | (hdr->e_ident[EI_MAG3] != ELFMAG3)) { | |
453 | /* Not an ELF file - silently ignore it */ | |
454 | return 0; | |
455 | } | |
1da177e4 | 456 | /* Fix endianness in ELF header */ |
7d875a02 AK |
457 | hdr->e_type = TO_NATIVE(hdr->e_type); |
458 | hdr->e_machine = TO_NATIVE(hdr->e_machine); | |
459 | hdr->e_version = TO_NATIVE(hdr->e_version); | |
460 | hdr->e_entry = TO_NATIVE(hdr->e_entry); | |
461 | hdr->e_phoff = TO_NATIVE(hdr->e_phoff); | |
462 | hdr->e_shoff = TO_NATIVE(hdr->e_shoff); | |
463 | hdr->e_flags = TO_NATIVE(hdr->e_flags); | |
464 | hdr->e_ehsize = TO_NATIVE(hdr->e_ehsize); | |
465 | hdr->e_phentsize = TO_NATIVE(hdr->e_phentsize); | |
466 | hdr->e_phnum = TO_NATIVE(hdr->e_phnum); | |
467 | hdr->e_shentsize = TO_NATIVE(hdr->e_shentsize); | |
468 | hdr->e_shnum = TO_NATIVE(hdr->e_shnum); | |
469 | hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx); | |
1da177e4 LT |
470 | sechdrs = (void *)hdr + hdr->e_shoff; |
471 | info->sechdrs = sechdrs; | |
472 | ||
5764f662 MY |
473 | /* modpost only works for relocatable objects */ |
474 | if (hdr->e_type != ET_REL) | |
475 | fatal("%s: not relocatable object.", filename); | |
476 | ||
a83710e5 PS |
477 | /* Check if file offset is correct */ |
478 | if (hdr->e_shoff > info->size) { | |
3b09efc4 MY |
479 | fatal("section header offset=%lu in file '%s' is bigger than filesize=%zu\n", |
480 | (unsigned long)hdr->e_shoff, filename, info->size); | |
a83710e5 PS |
481 | return 0; |
482 | } | |
483 | ||
6845756b | 484 | if (hdr->e_shnum == SHN_UNDEF) { |
1ce53adf DV |
485 | /* |
486 | * There are more than 64k sections, | |
487 | * read count from .sh_size. | |
1ce53adf DV |
488 | */ |
489 | info->num_sections = TO_NATIVE(sechdrs[0].sh_size); | |
490 | } | |
491 | else { | |
492 | info->num_sections = hdr->e_shnum; | |
493 | } | |
494 | if (hdr->e_shstrndx == SHN_XINDEX) { | |
6845756b | 495 | info->secindex_strings = TO_NATIVE(sechdrs[0].sh_link); |
1ce53adf DV |
496 | } |
497 | else { | |
498 | info->secindex_strings = hdr->e_shstrndx; | |
499 | } | |
500 | ||
1da177e4 | 501 | /* Fix endianness in section headers */ |
1ce53adf | 502 | for (i = 0; i < info->num_sections; i++) { |
7d875a02 AK |
503 | sechdrs[i].sh_name = TO_NATIVE(sechdrs[i].sh_name); |
504 | sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type); | |
505 | sechdrs[i].sh_flags = TO_NATIVE(sechdrs[i].sh_flags); | |
506 | sechdrs[i].sh_addr = TO_NATIVE(sechdrs[i].sh_addr); | |
507 | sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset); | |
508 | sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size); | |
509 | sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link); | |
510 | sechdrs[i].sh_info = TO_NATIVE(sechdrs[i].sh_info); | |
511 | sechdrs[i].sh_addralign = TO_NATIVE(sechdrs[i].sh_addralign); | |
512 | sechdrs[i].sh_entsize = TO_NATIVE(sechdrs[i].sh_entsize); | |
1da177e4 LT |
513 | } |
514 | /* Find symbol table. */ | |
1ce53adf DV |
515 | secstrings = (void *)hdr + sechdrs[info->secindex_strings].sh_offset; |
516 | for (i = 1; i < info->num_sections; i++) { | |
bd5cbced | 517 | const char *secname; |
56fc82c5 | 518 | int nobits = sechdrs[i].sh_type == SHT_NOBITS; |
1da177e4 | 519 | |
56fc82c5 | 520 | if (!nobits && sechdrs[i].sh_offset > info->size) { |
0d2573a2 GU |
521 | fatal("%s is truncated. sechdrs[i].sh_offset=%lu > sizeof(*hrd)=%zu\n", |
522 | filename, (unsigned long)sechdrs[i].sh_offset, | |
df578e7d | 523 | sizeof(*hdr)); |
85bd2fdd SR |
524 | return 0; |
525 | } | |
bd5cbced RP |
526 | secname = secstrings + sechdrs[i].sh_name; |
527 | if (strcmp(secname, ".modinfo") == 0) { | |
56fc82c5 TH |
528 | if (nobits) |
529 | fatal("%s has NOBITS .modinfo\n", filename); | |
1da177e4 LT |
530 | info->modinfo = (void *)hdr + sechdrs[i].sh_offset; |
531 | info->modinfo_len = sechdrs[i].sh_size; | |
ddb5cdba MY |
532 | } else if (!strcmp(secname, ".export_symbol")) { |
533 | info->export_symbol_secndx = i; | |
7ce3e410 | 534 | } |
bd5cbced | 535 | |
1ce53adf DV |
536 | if (sechdrs[i].sh_type == SHT_SYMTAB) { |
537 | unsigned int sh_link_idx; | |
538 | symtab_idx = i; | |
539 | info->symtab_start = (void *)hdr + | |
540 | sechdrs[i].sh_offset; | |
541 | info->symtab_stop = (void *)hdr + | |
542 | sechdrs[i].sh_offset + sechdrs[i].sh_size; | |
6845756b | 543 | sh_link_idx = sechdrs[i].sh_link; |
1ce53adf DV |
544 | info->strtab = (void *)hdr + |
545 | sechdrs[sh_link_idx].sh_offset; | |
546 | } | |
1da177e4 | 547 | |
1ce53adf DV |
548 | /* 32bit section no. table? ("more than 64k sections") */ |
549 | if (sechdrs[i].sh_type == SHT_SYMTAB_SHNDX) { | |
550 | symtab_shndx_idx = i; | |
551 | info->symtab_shndx_start = (void *)hdr + | |
552 | sechdrs[i].sh_offset; | |
553 | info->symtab_shndx_stop = (void *)hdr + | |
554 | sechdrs[i].sh_offset + sechdrs[i].sh_size; | |
555 | } | |
1da177e4 | 556 | } |
df578e7d | 557 | if (!info->symtab_start) |
cb80514d | 558 | fatal("%s has no symtab?\n", filename); |
df578e7d | 559 | |
1da177e4 LT |
560 | /* Fix endianness in symbols */ |
561 | for (sym = info->symtab_start; sym < info->symtab_stop; sym++) { | |
562 | sym->st_shndx = TO_NATIVE(sym->st_shndx); | |
563 | sym->st_name = TO_NATIVE(sym->st_name); | |
564 | sym->st_value = TO_NATIVE(sym->st_value); | |
565 | sym->st_size = TO_NATIVE(sym->st_size); | |
566 | } | |
1ce53adf DV |
567 | |
568 | if (symtab_shndx_idx != ~0U) { | |
569 | Elf32_Word *p; | |
6845756b | 570 | if (symtab_idx != sechdrs[symtab_shndx_idx].sh_link) |
1ce53adf | 571 | fatal("%s: SYMTAB_SHNDX has bad sh_link: %u!=%u\n", |
6845756b | 572 | filename, sechdrs[symtab_shndx_idx].sh_link, |
1ce53adf DV |
573 | symtab_idx); |
574 | /* Fix endianness */ | |
575 | for (p = info->symtab_shndx_start; p < info->symtab_shndx_stop; | |
576 | p++) | |
577 | *p = TO_NATIVE(*p); | |
578 | } | |
579 | ||
85bd2fdd | 580 | return 1; |
1da177e4 LT |
581 | } |
582 | ||
5c3ead8c | 583 | static void parse_elf_finish(struct elf_info *info) |
1da177e4 LT |
584 | { |
585 | release_file(info->hdr, info->size); | |
586 | } | |
587 | ||
4d7365d6 SR |
588 | static int ignore_undef_symbol(struct elf_info *info, const char *symname) |
589 | { | |
590 | /* ignore __this_module, it will be resolved shortly */ | |
b2c5cdcf | 591 | if (strcmp(symname, "__this_module") == 0) |
4d7365d6 SR |
592 | return 1; |
593 | /* ignore global offset table */ | |
594 | if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0) | |
595 | return 1; | |
596 | if (info->hdr->e_machine == EM_PPC) | |
597 | /* Special register function linked on all modules during final link of .ko */ | |
d62c4765 MY |
598 | if (strstarts(symname, "_restgpr_") || |
599 | strstarts(symname, "_savegpr_") || | |
600 | strstarts(symname, "_rest32gpr_") || | |
601 | strstarts(symname, "_save32gpr_") || | |
602 | strstarts(symname, "_restvr_") || | |
603 | strstarts(symname, "_savevr_")) | |
4d7365d6 | 604 | return 1; |
7fca5dc8 SR |
605 | if (info->hdr->e_machine == EM_PPC64) |
606 | /* Special register function linked on all modules during final link of .ko */ | |
d62c4765 MY |
607 | if (strstarts(symname, "_restgpr0_") || |
608 | strstarts(symname, "_savegpr0_") || | |
609 | strstarts(symname, "_restvr_") || | |
610 | strstarts(symname, "_savevr_") || | |
c153693d | 611 | strcmp(symname, ".TOC.") == 0) |
7fca5dc8 | 612 | return 1; |
1d2ad084 VG |
613 | |
614 | if (info->hdr->e_machine == EM_S390) | |
615 | /* Expoline thunks are linked on all kernel modules during final link of .ko */ | |
616 | if (strstarts(symname, "__s390_indirect_jump_r")) | |
617 | return 1; | |
4d7365d6 SR |
618 | /* Do not ignore this symbol */ |
619 | return 0; | |
620 | } | |
621 | ||
9bd2a099 MY |
622 | static void handle_symbol(struct module *mod, struct elf_info *info, |
623 | const Elf_Sym *sym, const char *symname) | |
1da177e4 | 624 | { |
1da177e4 LT |
625 | switch (sym->st_shndx) { |
626 | case SHN_COMMON: | |
d62c4765 | 627 | if (strstarts(symname, "__gnu_lto_")) { |
ef178f92 AK |
628 | /* Should warn here, but modpost runs before the linker */ |
629 | } else | |
630 | warn("\"%s\" [%s] is COMMON symbol\n", symname, mod->name); | |
1da177e4 | 631 | break; |
1da177e4 LT |
632 | case SHN_UNDEF: |
633 | /* undefined symbol */ | |
634 | if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL && | |
635 | ELF_ST_BIND(sym->st_info) != STB_WEAK) | |
636 | break; | |
4d7365d6 | 637 | if (ignore_undef_symbol(info, symname)) |
1da177e4 | 638 | break; |
1da177e4 LT |
639 | if (info->hdr->e_machine == EM_SPARC || |
640 | info->hdr->e_machine == EM_SPARCV9) { | |
641 | /* Ignore register directives. */ | |
8d529014 | 642 | if (ELF_ST_TYPE(sym->st_info) == STT_SPARC_REGISTER) |
1da177e4 | 643 | break; |
62070fa4 | 644 | if (symname[0] == '.') { |
1f3aa900 | 645 | char *munged = NOFAIL(strdup(symname)); |
62070fa4 SR |
646 | munged[0] = '_'; |
647 | munged[1] = toupper(munged[1]); | |
648 | symname = munged; | |
649 | } | |
1da177e4 | 650 | } |
62070fa4 | 651 | |
e882e89b MY |
652 | sym_add_unresolved(symname, mod, |
653 | ELF_ST_BIND(sym->st_info) == STB_WEAK); | |
1da177e4 LT |
654 | break; |
655 | default: | |
b2c5cdcf | 656 | if (strcmp(symname, "init_module") == 0) |
58e01fca | 657 | mod->has_init = true; |
b2c5cdcf | 658 | if (strcmp(symname, "cleanup_module") == 0) |
58e01fca | 659 | mod->has_cleanup = true; |
1da177e4 LT |
660 | break; |
661 | } | |
662 | } | |
663 | ||
5c3ead8c SR |
664 | /** |
665 | * Parse tag=value strings from .modinfo section | |
666 | **/ | |
1da177e4 LT |
667 | static char *next_string(char *string, unsigned long *secsize) |
668 | { | |
669 | /* Skip non-zero chars */ | |
670 | while (string[0]) { | |
671 | string++; | |
672 | if ((*secsize)-- <= 1) | |
673 | return NULL; | |
674 | } | |
675 | ||
676 | /* Skip any zero padding. */ | |
677 | while (!string[0]) { | |
678 | string++; | |
679 | if ((*secsize)-- <= 1) | |
680 | return NULL; | |
681 | } | |
682 | return string; | |
683 | } | |
684 | ||
bca2ccee MY |
685 | static char *get_next_modinfo(struct elf_info *info, const char *tag, |
686 | char *prev) | |
1da177e4 LT |
687 | { |
688 | char *p; | |
689 | unsigned int taglen = strlen(tag); | |
bca2ccee MY |
690 | char *modinfo = info->modinfo; |
691 | unsigned long size = info->modinfo_len; | |
1da177e4 | 692 | |
bca2ccee MY |
693 | if (prev) { |
694 | size -= prev - modinfo; | |
695 | modinfo = next_string(prev, &size); | |
b817f6fe SR |
696 | } |
697 | ||
1da177e4 LT |
698 | for (p = modinfo; p; p = next_string(p, &size)) { |
699 | if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=') | |
700 | return p + taglen + 1; | |
701 | } | |
702 | return NULL; | |
703 | } | |
704 | ||
bca2ccee | 705 | static char *get_modinfo(struct elf_info *info, const char *tag) |
b817f6fe SR |
706 | |
707 | { | |
bca2ccee | 708 | return get_next_modinfo(info, tag, NULL); |
b817f6fe SR |
709 | } |
710 | ||
ff13f926 SR |
711 | static const char *sym_name(struct elf_info *elf, Elf_Sym *sym) |
712 | { | |
58fb0d4f SR |
713 | if (sym) |
714 | return elf->strtab + sym->st_name; | |
715 | else | |
f666751a | 716 | return "(unknown)"; |
ff13f926 SR |
717 | } |
718 | ||
a89227d7 MY |
719 | /* |
720 | * Check whether the 'string' argument matches one of the 'patterns', | |
721 | * an array of shell wildcard patterns (glob). | |
722 | * | |
723 | * Return true is there is a match. | |
10668220 | 724 | */ |
a89227d7 | 725 | static bool match(const char *string, const char *const patterns[]) |
10668220 | 726 | { |
a89227d7 | 727 | const char *pattern; |
10668220 | 728 | |
a89227d7 MY |
729 | while ((pattern = *patterns++)) { |
730 | if (!fnmatch(pattern, string, 0)) | |
731 | return true; | |
10668220 | 732 | } |
a89227d7 MY |
733 | |
734 | return false; | |
10668220 SR |
735 | } |
736 | ||
7452dd26 MY |
737 | /* useful to pass patterns to match() directly */ |
738 | #define PATTERNS(...) \ | |
739 | ({ \ | |
740 | static const char *const patterns[] = {__VA_ARGS__, NULL}; \ | |
741 | patterns; \ | |
742 | }) | |
743 | ||
10668220 | 744 | /* sections that we do not want to do full section mismatch check on */ |
7a3ee753 | 745 | static const char *const section_white_list[] = |
4391ed6a SR |
746 | { |
747 | ".comment*", | |
748 | ".debug*", | |
1121584f | 749 | ".zdebug*", /* Compressed debug sections. */ |
739d875d | 750 | ".GCC.command.line", /* record-gcc-switches */ |
4391ed6a SR |
751 | ".mdebug*", /* alpha, score, mips etc. */ |
752 | ".pdr", /* alpha, score, mips etc. */ | |
753 | ".stab*", | |
754 | ".note*", | |
755 | ".got*", | |
756 | ".toc*", | |
af42e970 MF |
757 | ".xt.prop", /* xtensa */ |
758 | ".xt.lit", /* xtensa */ | |
f2e207f3 VG |
759 | ".arcextmap*", /* arc */ |
760 | ".gnu.linkonce.arcext*", /* arc : modules */ | |
d1189c63 NC |
761 | ".cmem*", /* EZchip */ |
762 | ".fmt_slot*", /* EZchip */ | |
ef178f92 | 763 | ".gnu.lto*", |
e390f9a9 | 764 | ".discard.*", |
1ef061a4 | 765 | ".llvm.call-graph-profile", /* call graph */ |
4391ed6a SR |
766 | NULL |
767 | }; | |
10668220 | 768 | |
e241a630 | 769 | /* |
b614a697 | 770 | * This is used to find sections missing the SHF_ALLOC flag. |
e241a630 | 771 | * The cause of this is often a section specified in assembler |
b614a697 | 772 | * without "ax" / "aw". |
e241a630 | 773 | */ |
b614a697 | 774 | static void check_section(const char *modname, struct elf_info *elf, |
bb66fc67 | 775 | Elf_Shdr *sechdr) |
e241a630 | 776 | { |
b614a697 AK |
777 | const char *sec = sech_name(elf, sechdr); |
778 | ||
779 | if (sechdr->sh_type == SHT_PROGBITS && | |
780 | !(sechdr->sh_flags & SHF_ALLOC) && | |
781 | !match(sec, section_white_list)) { | |
782 | warn("%s (%s): unexpected non-allocatable section.\n" | |
783 | "Did you forget to use \"ax\"/\"aw\" in a .S file?\n" | |
784 | "Note that for example <linux/init.h> contains\n" | |
785 | "section definitions for use in .S files.\n\n", | |
786 | modname, sec); | |
e241a630 | 787 | } |
e241a630 SR |
788 | } |
789 | ||
790 | ||
791 | ||
eb8f6890 | 792 | #define ALL_INIT_DATA_SECTIONS \ |
a0d8f803 RV |
793 | ".init.setup", ".init.rodata", ".meminit.rodata", \ |
794 | ".init.data", ".meminit.data" | |
eb8f6890 | 795 | #define ALL_EXIT_DATA_SECTIONS \ |
a0d8f803 | 796 | ".exit.data", ".memexit.data" |
10668220 | 797 | |
eb8f6890 | 798 | #define ALL_INIT_TEXT_SECTIONS \ |
a0d8f803 | 799 | ".init.text", ".meminit.text" |
eb8f6890 | 800 | #define ALL_EXIT_TEXT_SECTIONS \ |
a0d8f803 | 801 | ".exit.text", ".memexit.text" |
10668220 | 802 | |
bb15d8db | 803 | #define ALL_PCI_INIT_SECTIONS \ |
a0d8f803 RV |
804 | ".pci_fixup_early", ".pci_fixup_header", ".pci_fixup_final", \ |
805 | ".pci_fixup_enable", ".pci_fixup_resume", \ | |
806 | ".pci_fixup_resume_early", ".pci_fixup_suspend" | |
bb15d8db | 807 | |
e24f6628 PG |
808 | #define ALL_XXXINIT_SECTIONS MEM_INIT_SECTIONS |
809 | #define ALL_XXXEXIT_SECTIONS MEM_EXIT_SECTIONS | |
4a31a229 UKK |
810 | |
811 | #define ALL_INIT_SECTIONS INIT_SECTIONS, ALL_XXXINIT_SECTIONS | |
812 | #define ALL_EXIT_SECTIONS EXIT_SECTIONS, ALL_XXXEXIT_SECTIONS | |
10668220 | 813 | |
a0d8f803 | 814 | #define DATA_SECTIONS ".data", ".data.rel" |
19331e84 | 815 | #define TEXT_SECTIONS ".text", ".text.*", ".sched.text", \ |
65538966 | 816 | ".kprobes.text", ".cpuidle.text", ".noinstr.text" |
52dc0595 | 817 | #define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \ |
19331e84 | 818 | ".fixup", ".entry.text", ".exception.text", \ |
1e688dd2 | 819 | ".coldtext", ".softirqentry.text" |
10668220 | 820 | |
fd6c3a8d | 821 | #define INIT_SECTIONS ".init.*" |
fd6c3a8d | 822 | #define MEM_INIT_SECTIONS ".meminit.*" |
eb8f6890 | 823 | |
fd6c3a8d | 824 | #define EXIT_SECTIONS ".exit.*" |
fd6c3a8d | 825 | #define MEM_EXIT_SECTIONS ".memexit.*" |
eb8f6890 | 826 | |
52dc0595 QC |
827 | #define ALL_TEXT_SECTIONS ALL_INIT_TEXT_SECTIONS, ALL_EXIT_TEXT_SECTIONS, \ |
828 | TEXT_SECTIONS, OTHER_TEXT_SECTIONS | |
829 | ||
588ccd73 | 830 | enum mismatch { |
bbd3f4fb UKK |
831 | TEXT_TO_ANY_INIT, |
832 | DATA_TO_ANY_INIT, | |
abc23979 | 833 | TEXTDATA_TO_ANY_EXIT, |
bbd3f4fb UKK |
834 | XXXINIT_TO_SOME_INIT, |
835 | XXXEXIT_TO_SOME_EXIT, | |
836 | ANY_INIT_TO_ANY_EXIT, | |
837 | ANY_EXIT_TO_ANY_INIT, | |
52dc0595 | 838 | EXTABLE_TO_NON_TEXT, |
588ccd73 SR |
839 | }; |
840 | ||
e5d8f59a | 841 | /** |
f3945833 | 842 | * Describe how to match sections on different criteria: |
e5d8f59a QC |
843 | * |
844 | * @fromsec: Array of sections to be matched. | |
845 | * | |
846 | * @bad_tosec: Relocations applied to a section in @fromsec to a section in | |
847 | * this array is forbidden (black-list). Can be empty. | |
848 | * | |
849 | * @good_tosec: Relocations applied to a section in @fromsec must be | |
f3945833 | 850 | * targeting sections in this array (white-list). Can be empty. |
e5d8f59a QC |
851 | * |
852 | * @mismatch: Type of mismatch. | |
e5d8f59a | 853 | */ |
10668220 SR |
854 | struct sectioncheck { |
855 | const char *fromsec[20]; | |
050e57fd QC |
856 | const char *bad_tosec[20]; |
857 | const char *good_tosec[20]; | |
588ccd73 | 858 | enum mismatch mismatch; |
10668220 SR |
859 | }; |
860 | ||
7a3ee753 | 861 | static const struct sectioncheck sectioncheck[] = { |
10668220 SR |
862 | /* Do not reference init/exit code/data from |
863 | * normal code and data | |
864 | */ | |
865 | { | |
588ccd73 | 866 | .fromsec = { TEXT_SECTIONS, NULL }, |
050e57fd | 867 | .bad_tosec = { ALL_INIT_SECTIONS, NULL }, |
bbd3f4fb | 868 | .mismatch = TEXT_TO_ANY_INIT, |
588ccd73 SR |
869 | }, |
870 | { | |
871 | .fromsec = { DATA_SECTIONS, NULL }, | |
d4323e83 | 872 | .bad_tosec = { ALL_XXXINIT_SECTIONS, INIT_SECTIONS, NULL }, |
0db25245 | 873 | .mismatch = DATA_TO_ANY_INIT, |
0db25245 | 874 | }, |
588ccd73 | 875 | { |
abc23979 | 876 | .fromsec = { TEXT_SECTIONS, DATA_SECTIONS, NULL }, |
050e57fd | 877 | .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, |
abc23979 | 878 | .mismatch = TEXTDATA_TO_ANY_EXIT, |
eb8f6890 | 879 | }, |
e24f6628 | 880 | /* Do not reference init code/data from meminit code/data */ |
eb8f6890 | 881 | { |
4a31a229 | 882 | .fromsec = { ALL_XXXINIT_SECTIONS, NULL }, |
050e57fd | 883 | .bad_tosec = { INIT_SECTIONS, NULL }, |
bbd3f4fb | 884 | .mismatch = XXXINIT_TO_SOME_INIT, |
eb8f6890 | 885 | }, |
e24f6628 | 886 | /* Do not reference exit code/data from memexit code/data */ |
eb8f6890 | 887 | { |
4a31a229 | 888 | .fromsec = { ALL_XXXEXIT_SECTIONS, NULL }, |
050e57fd | 889 | .bad_tosec = { EXIT_SECTIONS, NULL }, |
bbd3f4fb | 890 | .mismatch = XXXEXIT_TO_SOME_EXIT, |
10668220 SR |
891 | }, |
892 | /* Do not use exit code/data from init code */ | |
893 | { | |
eb8f6890 | 894 | .fromsec = { ALL_INIT_SECTIONS, NULL }, |
050e57fd | 895 | .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, |
bbd3f4fb | 896 | .mismatch = ANY_INIT_TO_ANY_EXIT, |
10668220 SR |
897 | }, |
898 | /* Do not use init code/data from exit code */ | |
899 | { | |
eb8f6890 | 900 | .fromsec = { ALL_EXIT_SECTIONS, NULL }, |
050e57fd | 901 | .bad_tosec = { ALL_INIT_SECTIONS, NULL }, |
bbd3f4fb | 902 | .mismatch = ANY_EXIT_TO_ANY_INIT, |
10668220 | 903 | }, |
bb15d8db SAS |
904 | { |
905 | .fromsec = { ALL_PCI_INIT_SECTIONS, NULL }, | |
050e57fd | 906 | .bad_tosec = { INIT_SECTIONS, NULL }, |
bb15d8db | 907 | .mismatch = ANY_INIT_TO_ANY_EXIT, |
bb15d8db | 908 | }, |
52dc0595 QC |
909 | { |
910 | .fromsec = { "__ex_table", NULL }, | |
911 | /* If you're adding any new black-listed sections in here, consider | |
912 | * adding a special 'printer' for them in scripts/check_extable. | |
913 | */ | |
914 | .bad_tosec = { ".altinstr_replacement", NULL }, | |
915 | .good_tosec = {ALL_TEXT_SECTIONS , NULL}, | |
916 | .mismatch = EXTABLE_TO_NON_TEXT, | |
10668220 SR |
917 | } |
918 | }; | |
919 | ||
0d2a636e UKK |
920 | static const struct sectioncheck *section_mismatch( |
921 | const char *fromsec, const char *tosec) | |
10668220 SR |
922 | { |
923 | int i; | |
10668220 | 924 | |
c5c3439a QC |
925 | /* |
926 | * The target section could be the SHT_NUL section when we're | |
927 | * handling relocations to un-resolved symbols, trying to match it | |
739d875d DH |
928 | * doesn't make much sense and causes build failures on parisc |
929 | * architectures. | |
c5c3439a QC |
930 | */ |
931 | if (*tosec == '\0') | |
932 | return NULL; | |
933 | ||
c5c468dc MY |
934 | for (i = 0; i < ARRAY_SIZE(sectioncheck); i++) { |
935 | const struct sectioncheck *check = §ioncheck[i]; | |
936 | ||
050e57fd QC |
937 | if (match(fromsec, check->fromsec)) { |
938 | if (check->bad_tosec[0] && match(tosec, check->bad_tosec)) | |
939 | return check; | |
940 | if (check->good_tosec[0] && !match(tosec, check->good_tosec)) | |
941 | return check; | |
942 | } | |
10668220 | 943 | } |
0d2a636e | 944 | return NULL; |
10668220 SR |
945 | } |
946 | ||
4c8fbca5 SR |
947 | /** |
948 | * Whitelist to allow certain references to pass with no warning. | |
0e0d314e | 949 | * |
4c8fbca5 SR |
950 | * Pattern 1: |
951 | * If a module parameter is declared __initdata and permissions=0 | |
952 | * then this is legal despite the warning generated. | |
953 | * We cannot see value of permissions here, so just ignore | |
954 | * this pattern. | |
955 | * The pattern is identified by: | |
956 | * tosec = .init.data | |
9209aed0 | 957 | * fromsec = .data* |
4c8fbca5 | 958 | * atsym =__param* |
62070fa4 | 959 | * |
6a841528 RR |
960 | * Pattern 1a: |
961 | * module_param_call() ops can refer to __init set function if permissions=0 | |
962 | * The pattern is identified by: | |
963 | * tosec = .init.text | |
964 | * fromsec = .data* | |
965 | * atsym = __param_ops_* | |
966 | * | |
ee6a8545 | 967 | * Pattern 3: |
c993971f | 968 | * Whitelist all references from .head.text to any init section |
9bf8cb9b | 969 | * |
1d8af559 | 970 | * Pattern 4: |
ee6a8545 VG |
971 | * Some symbols belong to init section but still it is ok to reference |
972 | * these from non-init sections as these symbols don't have any memory | |
973 | * allocated for them and symbol address and value are same. So even | |
974 | * if init section is freed, its ok to reference those symbols. | |
975 | * For ex. symbols marking the init section boundaries. | |
976 | * This pattern is identified by | |
977 | * refsymname = __init_begin, _sinittext, _einittext | |
9bf8cb9b | 978 | * |
4a3893d0 PG |
979 | * Pattern 5: |
980 | * GCC may optimize static inlines when fed constant arg(s) resulting | |
981 | * in functions like cpumask_empty() -- generating an associated symbol | |
982 | * cpumask_empty.constprop.3 that appears in the audit. If the const that | |
983 | * is passed in comes from __init, like say nmi_ipi_mask, we get a | |
984 | * meaningless section warning. May need to add isra symbols too... | |
985 | * This pattern is identified by | |
986 | * tosec = init section | |
987 | * fromsec = text section | |
988 | * refsymname = *.constprop.* | |
989 | * | |
4c8fbca5 | 990 | **/ |
05bb0704 | 991 | static int secref_whitelist(const char *fromsec, const char *fromsym, |
58fb0d4f | 992 | const char *tosec, const char *tosym) |
4c8fbca5 | 993 | { |
4c8fbca5 | 994 | /* Check for pattern 1 */ |
1df380ff MY |
995 | if (match(tosec, PATTERNS(ALL_INIT_DATA_SECTIONS)) && |
996 | match(fromsec, PATTERNS(DATA_SECTIONS)) && | |
d62c4765 | 997 | strstarts(fromsym, "__param")) |
58fb0d4f | 998 | return 0; |
4c8fbca5 | 999 | |
6a841528 RR |
1000 | /* Check for pattern 1a */ |
1001 | if (strcmp(tosec, ".init.text") == 0 && | |
1df380ff | 1002 | match(fromsec, PATTERNS(DATA_SECTIONS)) && |
d62c4765 | 1003 | strstarts(fromsym, "__param_ops_")) |
6a841528 RR |
1004 | return 0; |
1005 | ||
672fb674 MY |
1006 | /* symbols in data sections that may refer to any init/exit sections */ |
1007 | if (match(fromsec, PATTERNS(DATA_SECTIONS)) && | |
1008 | match(tosec, PATTERNS(ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS)) && | |
1009 | match(fromsym, PATTERNS("*_template", // scsi uses *_template a lot | |
1010 | "*_timer", // arm uses ops structures named _timer a lot | |
1011 | "*_sht", // scsi also used *_sht to some extent | |
1012 | "*_ops", | |
1013 | "*_probe", | |
1014 | "*_probe_one", | |
1015 | "*_console"))) | |
1016 | return 0; | |
1017 | ||
f177cd0c | 1018 | /* symbols in data sections that may refer to meminit sections */ |
672fb674 | 1019 | if (match(fromsec, PATTERNS(DATA_SECTIONS)) && |
f177cd0c UKK |
1020 | match(tosec, PATTERNS(ALL_XXXINIT_SECTIONS, ALL_XXXEXIT_SECTIONS)) && |
1021 | match(fromsym, PATTERNS("*driver"))) | |
1022 | return 0; | |
1023 | ||
1024 | /* | |
1025 | * symbols in data sections must not refer to .exit.*, but there are | |
1026 | * quite a few offenders, so hide these unless for W=1 builds until | |
1027 | * these are fixed. | |
1028 | */ | |
1029 | if (!extra_warn && | |
1030 | match(fromsec, PATTERNS(DATA_SECTIONS)) && | |
1031 | match(tosec, PATTERNS(EXIT_SECTIONS)) && | |
672fb674 | 1032 | match(fromsym, PATTERNS("*driver"))) |
58fb0d4f | 1033 | return 0; |
4c8fbca5 | 1034 | |
9bf8cb9b | 1035 | /* Check for pattern 3 */ |
1df380ff MY |
1036 | if (strstarts(fromsec, ".head.text") && |
1037 | match(tosec, PATTERNS(ALL_INIT_SECTIONS))) | |
58fb0d4f | 1038 | return 0; |
9bf8cb9b | 1039 | |
1d8af559 | 1040 | /* Check for pattern 4 */ |
1df380ff | 1041 | if (match(tosym, PATTERNS("__init_begin", "_sinittext", "_einittext"))) |
58fb0d4f | 1042 | return 0; |
9bf8cb9b | 1043 | |
4a3893d0 | 1044 | /* Check for pattern 5 */ |
1df380ff MY |
1045 | if (match(fromsec, PATTERNS(ALL_TEXT_SECTIONS)) && |
1046 | match(tosec, PATTERNS(ALL_INIT_SECTIONS)) && | |
1047 | match(fromsym, PATTERNS("*.constprop.*"))) | |
a4d26f1a PW |
1048 | return 0; |
1049 | ||
58fb0d4f | 1050 | return 1; |
4c8fbca5 SR |
1051 | } |
1052 | ||
5818c683 ST |
1053 | /* |
1054 | * If there's no name there, ignore it; likewise, ignore it if it's | |
0a3bf860 | 1055 | * one of the magic symbols emitted used by current tools. |
5818c683 ST |
1056 | * |
1057 | * Otherwise if find_symbols_between() returns those symbols, they'll | |
1058 | * fail the whitelist tests and cause lots of false alarms ... fixable | |
1059 | * only by merging __exit and __init sections into __text, bloating | |
1060 | * the kernel (which is especially evil on embedded platforms). | |
1061 | */ | |
1062 | static inline int is_valid_name(struct elf_info *elf, Elf_Sym *sym) | |
1063 | { | |
1064 | const char *name = elf->strtab + sym->st_name; | |
1065 | ||
1066 | if (!name || !strlen(name)) | |
1067 | return 0; | |
0a3bf860 | 1068 | return !is_mapping_symbol(name); |
5818c683 ST |
1069 | } |
1070 | ||
b1a9651d MY |
1071 | /* Look up the nearest symbol based on the section and the address */ |
1072 | static Elf_Sym *find_nearest_sym(struct elf_info *elf, Elf_Addr addr, | |
1073 | unsigned int secndx, bool allow_negative, | |
1074 | Elf_Addr min_distance) | |
93684d3b SR |
1075 | { |
1076 | Elf_Sym *sym; | |
9ad21c3f | 1077 | Elf_Sym *near = NULL; |
cd1824fb MY |
1078 | Elf_Addr sym_addr, distance; |
1079 | bool is_arm = (elf->hdr->e_machine == EM_ARM); | |
1ce53adf | 1080 | |
93684d3b | 1081 | for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) { |
b1a9651d | 1082 | if (get_secindex(elf, sym) != secndx) |
ae4ac123 | 1083 | continue; |
5818c683 ST |
1084 | if (!is_valid_name(elf, sym)) |
1085 | continue; | |
93684d3b | 1086 | |
cd1824fb | 1087 | sym_addr = sym->st_value; |
62070fa4 | 1088 | |
cd1824fb MY |
1089 | /* |
1090 | * For ARM Thumb instruction, the bit 0 of st_value is set | |
1091 | * if the symbol is STT_FUNC type. Mask it to get the address. | |
1092 | */ | |
1093 | if (is_arm && ELF_ST_TYPE(sym->st_info) == STT_FUNC) | |
1094 | sym_addr &= ~1; | |
b39927cf | 1095 | |
cd1824fb MY |
1096 | if (addr >= sym_addr) |
1097 | distance = addr - sym_addr; | |
b1a9651d | 1098 | else if (allow_negative) |
cd1824fb | 1099 | distance = sym_addr - addr; |
b1a9651d | 1100 | else |
da68d61f | 1101 | continue; |
b1a9651d MY |
1102 | |
1103 | if (distance <= min_distance) { | |
1104 | min_distance = distance; | |
68fef670 | 1105 | near = sym; |
b39927cf | 1106 | } |
b1a9651d MY |
1107 | |
1108 | if (min_distance == 0) | |
1109 | break; | |
b39927cf | 1110 | } |
157c23c8 | 1111 | return near; |
b39927cf SR |
1112 | } |
1113 | ||
ac263349 MY |
1114 | static Elf_Sym *find_fromsym(struct elf_info *elf, Elf_Addr addr, |
1115 | unsigned int secndx) | |
588ccd73 | 1116 | { |
b1a9651d | 1117 | return find_nearest_sym(elf, addr, secndx, false, ~0); |
588ccd73 SR |
1118 | } |
1119 | ||
b1a9651d | 1120 | static Elf_Sym *find_tosym(struct elf_info *elf, Elf_Addr addr, Elf_Sym *sym) |
356ad538 | 1121 | { |
b1a9651d MY |
1122 | /* If the supplied symbol has a valid name, return it */ |
1123 | if (is_valid_name(elf, sym)) | |
1124 | return sym; | |
1125 | ||
1126 | /* | |
1127 | * Strive to find a better symbol name, but the resulting name may not | |
1128 | * match the symbol referenced in the original code. | |
1129 | */ | |
1130 | return find_nearest_sym(elf, addr, get_secindex(elf, sym), true, 20); | |
356ad538 QC |
1131 | } |
1132 | ||
f4c35484 | 1133 | static bool is_executable_section(struct elf_info *elf, unsigned int secndx) |
588ccd73 | 1134 | { |
3a3f1e57 | 1135 | if (secndx >= elf->num_sections) |
f4c35484 | 1136 | return false; |
e5f95c8b | 1137 | |
f4c35484 | 1138 | return (elf->sechdrs[secndx].sh_flags & SHF_EXECINSTR) != 0; |
58fb0d4f SR |
1139 | } |
1140 | ||
644e8f14 QC |
1141 | static void default_mismatch_handler(const char *modname, struct elf_info *elf, |
1142 | const struct sectioncheck* const mismatch, | |
04ed3b47 MY |
1143 | Elf_Sym *tsym, |
1144 | unsigned int fsecndx, const char *fromsec, Elf_Addr faddr, | |
1145 | const char *tosec, Elf_Addr taddr) | |
58fb0d4f | 1146 | { |
644e8f14 QC |
1147 | Elf_Sym *from; |
1148 | const char *tosym; | |
1149 | const char *fromsym; | |
58fb0d4f | 1150 | |
04ed3b47 | 1151 | from = find_fromsym(elf, faddr, fsecndx); |
644e8f14 | 1152 | fromsym = sym_name(elf, from); |
644e8f14 | 1153 | |
04ed3b47 | 1154 | tsym = find_tosym(elf, taddr, tsym); |
a23e7584 | 1155 | tosym = sym_name(elf, tsym); |
c7a65e06 | 1156 | |
644e8f14 | 1157 | /* check whitelist - we may ignore it */ |
05bb0704 | 1158 | if (!secref_whitelist(fromsec, fromsym, tosec, tosym)) |
fc5fa862 MY |
1159 | return; |
1160 | ||
e5f95c8b | 1161 | sec_mismatch_count++; |
e5f95c8b | 1162 | |
f2346278 MY |
1163 | warn("%s: section mismatch in reference: %s+0x%x (section: %s) -> %s (section: %s)\n", |
1164 | modname, fromsym, (unsigned int)(faddr - from->st_value), fromsec, tosym, tosec); | |
856567d5 | 1165 | |
78dac1a2 | 1166 | if (mismatch->mismatch == EXTABLE_TO_NON_TEXT) { |
856567d5 MY |
1167 | if (match(tosec, mismatch->bad_tosec)) |
1168 | fatal("The relocation at %s+0x%lx references\n" | |
1169 | "section \"%s\" which is black-listed.\n" | |
1170 | "Something is seriously wrong and should be fixed.\n" | |
1171 | "You might get more information about where this is\n" | |
1172 | "coming from by using scripts/check_extable.sh %s\n", | |
04ed3b47 | 1173 | fromsec, (long)faddr, tosec, modname); |
a23e7584 | 1174 | else if (is_executable_section(elf, get_secindex(elf, tsym))) |
856567d5 MY |
1175 | warn("The relocation at %s+0x%lx references\n" |
1176 | "section \"%s\" which is not in the list of\n" | |
1177 | "authorized sections. If you're adding a new section\n" | |
1178 | "and/or if this reference is valid, add \"%s\" to the\n" | |
1179 | "list of authorized sections to jump to on fault.\n" | |
1180 | "This can be achieved by adding \"%s\" to\n" | |
1181 | "OTHER_TEXT_SECTIONS in scripts/mod/modpost.c.\n", | |
04ed3b47 | 1182 | fromsec, (long)faddr, tosec, tosec, tosec); |
856567d5 MY |
1183 | else |
1184 | error("%s+0x%lx references non-executable section '%s'\n", | |
04ed3b47 | 1185 | fromsec, (long)faddr, tosec); |
644e8f14 QC |
1186 | } |
1187 | } | |
1188 | ||
ddb5cdba MY |
1189 | static void check_export_symbol(struct module *mod, struct elf_info *elf, |
1190 | Elf_Addr faddr, const char *secname, | |
1191 | Elf_Sym *sym) | |
52dc0595 | 1192 | { |
ddb5cdba MY |
1193 | static const char *prefix = "__export_symbol_"; |
1194 | const char *label_name, *name, *data; | |
1195 | Elf_Sym *label; | |
1196 | struct symbol *s; | |
1197 | bool is_gpl; | |
52dc0595 | 1198 | |
ddb5cdba MY |
1199 | label = find_fromsym(elf, faddr, elf->export_symbol_secndx); |
1200 | label_name = sym_name(elf, label); | |
52dc0595 | 1201 | |
ddb5cdba MY |
1202 | if (!strstarts(label_name, prefix)) { |
1203 | error("%s: .export_symbol section contains strange symbol '%s'\n", | |
1204 | mod->name, label_name); | |
1205 | return; | |
1206 | } | |
e84048aa | 1207 | |
6d62b1c4 MY |
1208 | if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL && |
1209 | ELF_ST_BIND(sym->st_info) != STB_WEAK) { | |
1210 | error("%s: local symbol '%s' was exported\n", mod->name, | |
1211 | label_name + strlen(prefix)); | |
1212 | return; | |
1213 | } | |
52dc0595 | 1214 | |
ddb5cdba MY |
1215 | name = sym_name(elf, sym); |
1216 | if (strcmp(label_name + strlen(prefix), name)) { | |
1217 | error("%s: .export_symbol section references '%s', but it does not seem to be an export symbol\n", | |
1218 | mod->name, name); | |
1219 | return; | |
1220 | } | |
52dc0595 | 1221 | |
ddb5cdba MY |
1222 | data = sym_get_data(elf, label); /* license */ |
1223 | if (!strcmp(data, "GPL")) { | |
1224 | is_gpl = true; | |
1225 | } else if (!strcmp(data, "")) { | |
1226 | is_gpl = false; | |
1227 | } else { | |
1228 | error("%s: unknown license '%s' was specified for '%s'\n", | |
1229 | mod->name, data, name); | |
1230 | return; | |
52dc0595 | 1231 | } |
ddb5cdba MY |
1232 | |
1233 | data += strlen(data) + 1; /* namespace */ | |
6e7611c4 | 1234 | s = sym_add_exported(name, mod, is_gpl, data); |
ddb5cdba MY |
1235 | |
1236 | /* | |
1237 | * We need to be aware whether we are exporting a function or | |
1238 | * a data on some architectures. | |
1239 | */ | |
1240 | s->is_func = (ELF_ST_TYPE(sym->st_info) == STT_FUNC); | |
1241 | ||
08700ec7 MY |
1242 | /* |
1243 | * For parisc64, symbols prefixed $$ from the library have the symbol type | |
1244 | * STT_LOPROC. They should be handled as functions too. | |
1245 | */ | |
1246 | if (elf->hdr->e_ident[EI_CLASS] == ELFCLASS64 && | |
1247 | elf->hdr->e_machine == EM_PARISC && | |
1248 | ELF_ST_TYPE(sym->st_info) == STT_LOPROC) | |
1249 | s->is_func = true; | |
1250 | ||
ddb5cdba MY |
1251 | if (match(secname, PATTERNS(INIT_SECTIONS))) |
1252 | warn("%s: %s: EXPORT_SYMBOL used for init symbol. Remove __init or EXPORT_SYMBOL.\n", | |
1253 | mod->name, name); | |
1254 | else if (match(secname, PATTERNS(EXIT_SECTIONS))) | |
1255 | warn("%s: %s: EXPORT_SYMBOL used for exit symbol. Remove __exit or EXPORT_SYMBOL.\n", | |
1256 | mod->name, name); | |
52dc0595 QC |
1257 | } |
1258 | ||
94d6cb68 | 1259 | static void check_section_mismatch(struct module *mod, struct elf_info *elf, |
04ed3b47 MY |
1260 | Elf_Sym *sym, |
1261 | unsigned int fsecndx, const char *fromsec, | |
1262 | Elf_Addr faddr, Elf_Addr taddr) | |
644e8f14 | 1263 | { |
0cad61d7 | 1264 | const char *tosec = sec_name(elf, get_secindex(elf, sym)); |
ddb5cdba | 1265 | const struct sectioncheck *mismatch; |
644e8f14 | 1266 | |
481461f5 | 1267 | if (module_enabled && elf->export_symbol_secndx == fsecndx) { |
ddb5cdba MY |
1268 | check_export_symbol(mod, elf, faddr, tosec, sym); |
1269 | return; | |
b39927cf | 1270 | } |
b39927cf | 1271 | |
ddb5cdba | 1272 | mismatch = section_mismatch(fromsec, tosec); |
856567d5 MY |
1273 | if (!mismatch) |
1274 | return; | |
1275 | ||
94d6cb68 | 1276 | default_mismatch_handler(mod->name, elf, mismatch, sym, |
04ed3b47 MY |
1277 | fsecndx, fromsec, faddr, |
1278 | tosec, taddr); | |
ae4ac123 AN |
1279 | } |
1280 | ||
71d965cf | 1281 | static Elf_Addr addend_386_rel(uint32_t *location, unsigned int r_type) |
ae4ac123 | 1282 | { |
71d965cf | 1283 | switch (r_type) { |
ae4ac123 | 1284 | case R_386_32: |
a68914a5 | 1285 | return TO_NATIVE(*location); |
ae4ac123 | 1286 | case R_386_PC32: |
a68914a5 | 1287 | return TO_NATIVE(*location) + 4; |
ae4ac123 | 1288 | } |
a68914a5 MY |
1289 | |
1290 | return (Elf_Addr)(-1); | |
ae4ac123 AN |
1291 | } |
1292 | ||
6e2e340b TL |
1293 | #ifndef R_ARM_CALL |
1294 | #define R_ARM_CALL 28 | |
1295 | #endif | |
1296 | #ifndef R_ARM_JUMP24 | |
1297 | #define R_ARM_JUMP24 29 | |
1298 | #endif | |
1299 | ||
c9698e5c DL |
1300 | #ifndef R_ARM_THM_CALL |
1301 | #define R_ARM_THM_CALL 10 | |
1302 | #endif | |
1303 | #ifndef R_ARM_THM_JUMP24 | |
1304 | #define R_ARM_THM_JUMP24 30 | |
1305 | #endif | |
f5983dab MY |
1306 | |
1307 | #ifndef R_ARM_MOVW_ABS_NC | |
1308 | #define R_ARM_MOVW_ABS_NC 43 | |
1309 | #endif | |
1310 | ||
1311 | #ifndef R_ARM_MOVT_ABS | |
1312 | #define R_ARM_MOVT_ABS 44 | |
1313 | #endif | |
1314 | ||
1315 | #ifndef R_ARM_THM_MOVW_ABS_NC | |
1316 | #define R_ARM_THM_MOVW_ABS_NC 47 | |
1317 | #endif | |
1318 | ||
1319 | #ifndef R_ARM_THM_MOVT_ABS | |
1320 | #define R_ARM_THM_MOVT_ABS 48 | |
1321 | #endif | |
1322 | ||
c9698e5c DL |
1323 | #ifndef R_ARM_THM_JUMP19 |
1324 | #define R_ARM_THM_JUMP19 51 | |
1325 | #endif | |
1326 | ||
56a24b8c MY |
1327 | static int32_t sign_extend32(int32_t value, int index) |
1328 | { | |
1329 | uint8_t shift = 31 - index; | |
1330 | ||
1331 | return (int32_t)(value << shift) >> shift; | |
1332 | } | |
1333 | ||
71d965cf | 1334 | static Elf_Addr addend_arm_rel(void *loc, Elf_Sym *sym, unsigned int r_type) |
56a974fa | 1335 | { |
3310bae8 | 1336 | uint32_t inst, upper, lower, sign, j1, j2; |
56a24b8c | 1337 | int32_t offset; |
56a974fa | 1338 | |
71d965cf | 1339 | switch (r_type) { |
56a974fa | 1340 | case R_ARM_ABS32: |
2cb74946 | 1341 | case R_ARM_REL32: |
b7c63520 | 1342 | inst = TO_NATIVE(*(uint32_t *)loc); |
a68914a5 | 1343 | return inst + sym->st_value; |
12ca2c67 MY |
1344 | case R_ARM_MOVW_ABS_NC: |
1345 | case R_ARM_MOVT_ABS: | |
1346 | inst = TO_NATIVE(*(uint32_t *)loc); | |
1347 | offset = sign_extend32(((inst & 0xf0000) >> 4) | (inst & 0xfff), | |
1348 | 15); | |
a68914a5 | 1349 | return offset + sym->st_value; |
56a974fa | 1350 | case R_ARM_PC24: |
6e2e340b TL |
1351 | case R_ARM_CALL: |
1352 | case R_ARM_JUMP24: | |
56a24b8c MY |
1353 | inst = TO_NATIVE(*(uint32_t *)loc); |
1354 | offset = sign_extend32((inst & 0x00ffffff) << 2, 25); | |
a68914a5 | 1355 | return offset + sym->st_value + 8; |
cd1824fb MY |
1356 | case R_ARM_THM_MOVW_ABS_NC: |
1357 | case R_ARM_THM_MOVT_ABS: | |
1358 | upper = TO_NATIVE(*(uint16_t *)loc); | |
1359 | lower = TO_NATIVE(*((uint16_t *)loc + 1)); | |
1360 | offset = sign_extend32(((upper & 0x000f) << 12) | | |
1361 | ((upper & 0x0400) << 1) | | |
1362 | ((lower & 0x7000) >> 4) | | |
1363 | (lower & 0x00ff), | |
1364 | 15); | |
a68914a5 | 1365 | return offset + sym->st_value; |
3310bae8 MY |
1366 | case R_ARM_THM_JUMP19: |
1367 | /* | |
1368 | * Encoding T3: | |
1369 | * S = upper[10] | |
1370 | * imm6 = upper[5:0] | |
1371 | * J1 = lower[13] | |
1372 | * J2 = lower[11] | |
1373 | * imm11 = lower[10:0] | |
1374 | * imm32 = SignExtend(S:J2:J1:imm6:imm11:'0') | |
1375 | */ | |
1376 | upper = TO_NATIVE(*(uint16_t *)loc); | |
1377 | lower = TO_NATIVE(*((uint16_t *)loc + 1)); | |
1378 | ||
1379 | sign = (upper >> 10) & 1; | |
1380 | j1 = (lower >> 13) & 1; | |
1381 | j2 = (lower >> 11) & 1; | |
1382 | offset = sign_extend32((sign << 20) | (j2 << 19) | (j1 << 18) | | |
1383 | ((upper & 0x03f) << 12) | | |
1384 | ((lower & 0x07ff) << 1), | |
1385 | 20); | |
a68914a5 | 1386 | return offset + sym->st_value + 4; |
c9698e5c DL |
1387 | case R_ARM_THM_CALL: |
1388 | case R_ARM_THM_JUMP24: | |
3310bae8 MY |
1389 | /* |
1390 | * Encoding T4: | |
1391 | * S = upper[10] | |
1392 | * imm10 = upper[9:0] | |
1393 | * J1 = lower[13] | |
1394 | * J2 = lower[11] | |
1395 | * imm11 = lower[10:0] | |
1396 | * I1 = NOT(J1 XOR S) | |
1397 | * I2 = NOT(J2 XOR S) | |
1398 | * imm32 = SignExtend(S:I1:I2:imm10:imm11:'0') | |
1399 | */ | |
1400 | upper = TO_NATIVE(*(uint16_t *)loc); | |
1401 | lower = TO_NATIVE(*((uint16_t *)loc + 1)); | |
1402 | ||
1403 | sign = (upper >> 10) & 1; | |
1404 | j1 = (lower >> 13) & 1; | |
1405 | j2 = (lower >> 11) & 1; | |
1406 | offset = sign_extend32((sign << 24) | | |
1407 | ((~(j1 ^ sign) & 1) << 23) | | |
1408 | ((~(j2 ^ sign) & 1) << 22) | | |
1409 | ((upper & 0x03ff) << 12) | | |
1410 | ((lower & 0x07ff) << 1), | |
1411 | 24); | |
a68914a5 | 1412 | return offset + sym->st_value + 4; |
56a974fa | 1413 | } |
a68914a5 MY |
1414 | |
1415 | return (Elf_Addr)(-1); | |
56a974fa SR |
1416 | } |
1417 | ||
71d965cf | 1418 | static Elf_Addr addend_mips_rel(uint32_t *location, unsigned int r_type) |
ae4ac123 | 1419 | { |
b31db651 | 1420 | uint32_t inst; |
ae4ac123 | 1421 | |
ae4ac123 | 1422 | inst = TO_NATIVE(*location); |
71d965cf | 1423 | switch (r_type) { |
ae4ac123 | 1424 | case R_MIPS_LO16: |
a68914a5 | 1425 | return inst & 0xffff; |
ae4ac123 | 1426 | case R_MIPS_26: |
a68914a5 | 1427 | return (inst & 0x03ffffff) << 2; |
ae4ac123 | 1428 | case R_MIPS_32: |
a68914a5 | 1429 | return inst; |
ae4ac123 | 1430 | } |
a68914a5 | 1431 | return (Elf_Addr)(-1); |
ae4ac123 AN |
1432 | } |
1433 | ||
bb1f85d6 JZ |
1434 | #ifndef EM_RISCV |
1435 | #define EM_RISCV 243 | |
1436 | #endif | |
1437 | ||
1438 | #ifndef R_RISCV_SUB32 | |
1439 | #define R_RISCV_SUB32 39 | |
1440 | #endif | |
1441 | ||
3d36f429 YT |
1442 | #ifndef EM_LOONGARCH |
1443 | #define EM_LOONGARCH 258 | |
1444 | #endif | |
1445 | ||
1446 | #ifndef R_LARCH_SUB32 | |
1447 | #define R_LARCH_SUB32 55 | |
1448 | #endif | |
1449 | ||
4732acb7 MY |
1450 | static void get_rel_type_and_sym(struct elf_info *elf, uint64_t r_info, |
1451 | unsigned int *r_type, unsigned int *r_sym) | |
1452 | { | |
1453 | typedef struct { | |
1454 | Elf64_Word r_sym; /* Symbol index */ | |
1455 | unsigned char r_ssym; /* Special symbol for 2nd relocation */ | |
1456 | unsigned char r_type3; /* 3rd relocation type */ | |
1457 | unsigned char r_type2; /* 2nd relocation type */ | |
1458 | unsigned char r_type; /* 1st relocation type */ | |
1459 | } Elf64_Mips_R_Info; | |
1460 | ||
1461 | bool is_64bit = (elf->hdr->e_ident[EI_CLASS] == ELFCLASS64); | |
1462 | ||
1463 | if (elf->hdr->e_machine == EM_MIPS && is_64bit) { | |
1464 | Elf64_Mips_R_Info *mips64_r_info = (void *)&r_info; | |
1465 | ||
1466 | *r_type = mips64_r_info->r_type; | |
1467 | *r_sym = TO_NATIVE(mips64_r_info->r_sym); | |
1468 | return; | |
1469 | } | |
1470 | ||
1471 | if (is_64bit) { | |
1472 | Elf64_Xword r_info64 = r_info; | |
1473 | ||
1474 | r_info = TO_NATIVE(r_info64); | |
1475 | } else { | |
1476 | Elf32_Word r_info32 = r_info; | |
1477 | ||
1478 | r_info = TO_NATIVE(r_info32); | |
1479 | } | |
1480 | ||
1481 | *r_type = ELF_R_TYPE(r_info); | |
1482 | *r_sym = ELF_R_SYM(r_info); | |
1483 | } | |
1484 | ||
94d6cb68 | 1485 | static void section_rela(struct module *mod, struct elf_info *elf, |
bb66fc67 | 1486 | Elf_Shdr *sechdr) |
5b24c071 | 1487 | { |
5b24c071 | 1488 | Elf_Rela *rela; |
9990ca35 MY |
1489 | unsigned int fsecndx = sechdr->sh_info; |
1490 | const char *fromsec = sec_name(elf, fsecndx); | |
ff13f926 | 1491 | Elf_Rela *start = (void *)elf->hdr + sechdr->sh_offset; |
5b24c071 SR |
1492 | Elf_Rela *stop = (void *)start + sechdr->sh_size; |
1493 | ||
5b24c071 | 1494 | /* if from section (name) is know good then skip it */ |
b614a697 | 1495 | if (match(fromsec, section_white_list)) |
5b24c071 | 1496 | return; |
e241a630 | 1497 | |
5b24c071 | 1498 | for (rela = start; rela < stop; rela++) { |
77f39e93 MY |
1499 | Elf_Addr taddr, r_offset; |
1500 | unsigned int r_type, r_sym; | |
4732acb7 | 1501 | |
77f39e93 | 1502 | r_offset = TO_NATIVE(rela->r_offset); |
4732acb7 MY |
1503 | get_rel_type_and_sym(elf, rela->r_info, &r_type, &r_sym); |
1504 | ||
77f39e93 MY |
1505 | taddr = TO_NATIVE(rela->r_addend); |
1506 | ||
bb1f85d6 JZ |
1507 | switch (elf->hdr->e_machine) { |
1508 | case EM_RISCV: | |
1509 | if (!strcmp("__ex_table", fromsec) && | |
4732acb7 | 1510 | r_type == R_RISCV_SUB32) |
bb1f85d6 JZ |
1511 | continue; |
1512 | break; | |
3d36f429 YT |
1513 | case EM_LOONGARCH: |
1514 | if (!strcmp("__ex_table", fromsec) && | |
4732acb7 | 1515 | r_type == R_LARCH_SUB32) |
3d36f429 YT |
1516 | continue; |
1517 | break; | |
bb1f85d6 | 1518 | } |
a9bb3e5d | 1519 | |
94d6cb68 | 1520 | check_section_mismatch(mod, elf, elf->symtab_start + r_sym, |
77f39e93 | 1521 | fsecndx, fromsec, r_offset, taddr); |
5b24c071 SR |
1522 | } |
1523 | } | |
1524 | ||
94d6cb68 | 1525 | static void section_rel(struct module *mod, struct elf_info *elf, |
bb66fc67 | 1526 | Elf_Shdr *sechdr) |
5b24c071 | 1527 | { |
5b24c071 | 1528 | Elf_Rel *rel; |
9990ca35 MY |
1529 | unsigned int fsecndx = sechdr->sh_info; |
1530 | const char *fromsec = sec_name(elf, fsecndx); | |
ff13f926 | 1531 | Elf_Rel *start = (void *)elf->hdr + sechdr->sh_offset; |
5b24c071 SR |
1532 | Elf_Rel *stop = (void *)start + sechdr->sh_size; |
1533 | ||
5b24c071 | 1534 | /* if from section (name) is know good then skip it */ |
b614a697 | 1535 | if (match(fromsec, section_white_list)) |
5b24c071 SR |
1536 | return; |
1537 | ||
1538 | for (rel = start; rel < stop; rel++) { | |
8aa00e2c | 1539 | Elf_Sym *tsym; |
77f39e93 MY |
1540 | Elf_Addr taddr = 0, r_offset; |
1541 | unsigned int r_type, r_sym; | |
b31db651 | 1542 | void *loc; |
4732acb7 | 1543 | |
77f39e93 | 1544 | r_offset = TO_NATIVE(rel->r_offset); |
4732acb7 | 1545 | get_rel_type_and_sym(elf, rel->r_info, &r_type, &r_sym); |
b31db651 | 1546 | |
77f39e93 | 1547 | loc = sym_get_data_by_offset(elf, fsecndx, r_offset); |
8e86ebef | 1548 | tsym = elf->symtab_start + r_sym; |
b31db651 | 1549 | |
ff13f926 | 1550 | switch (elf->hdr->e_machine) { |
5b24c071 | 1551 | case EM_386: |
71d965cf | 1552 | taddr = addend_386_rel(loc, r_type); |
5b24c071 SR |
1553 | break; |
1554 | case EM_ARM: | |
71d965cf | 1555 | taddr = addend_arm_rel(loc, tsym, r_type); |
5b24c071 SR |
1556 | break; |
1557 | case EM_MIPS: | |
71d965cf | 1558 | taddr = addend_mips_rel(loc, r_type); |
5b24c071 | 1559 | break; |
64f14041 MY |
1560 | default: |
1561 | fatal("Please add code to calculate addend for this architecture\n"); | |
5b24c071 | 1562 | } |
a9bb3e5d | 1563 | |
8aa00e2c | 1564 | check_section_mismatch(mod, elf, tsym, |
77f39e93 | 1565 | fsecndx, fromsec, r_offset, taddr); |
5b24c071 SR |
1566 | } |
1567 | } | |
1568 | ||
b39927cf SR |
1569 | /** |
1570 | * A module includes a number of sections that are discarded | |
1571 | * either when loaded or when used as built-in. | |
1572 | * For loaded modules all functions marked __init and all data | |
b595076a | 1573 | * marked __initdata will be discarded when the module has been initialized. |
b39927cf SR |
1574 | * Likewise for modules used built-in the sections marked __exit |
1575 | * are discarded because __exit marked function are supposed to be called | |
32be1d22 | 1576 | * only when a module is unloaded which never happens for built-in modules. |
b39927cf SR |
1577 | * The check_sec_ref() function traverses all relocation records |
1578 | * to find all references to a section that reference a section that will | |
1579 | * be discarded and warns about it. | |
1580 | **/ | |
94d6cb68 | 1581 | static void check_sec_ref(struct module *mod, struct elf_info *elf) |
b39927cf SR |
1582 | { |
1583 | int i; | |
b39927cf | 1584 | Elf_Shdr *sechdrs = elf->sechdrs; |
62070fa4 | 1585 | |
b39927cf | 1586 | /* Walk through all sections */ |
1ce53adf | 1587 | for (i = 0; i < elf->num_sections; i++) { |
94d6cb68 | 1588 | check_section(mod->name, elf, &elf->sechdrs[i]); |
b39927cf | 1589 | /* We want to process only relocation sections and not .init */ |
5b24c071 | 1590 | if (sechdrs[i].sh_type == SHT_RELA) |
94d6cb68 | 1591 | section_rela(mod, elf, &elf->sechdrs[i]); |
5b24c071 | 1592 | else if (sechdrs[i].sh_type == SHT_REL) |
94d6cb68 | 1593 | section_rel(mod, elf, &elf->sechdrs[i]); |
b39927cf SR |
1594 | } |
1595 | } | |
1596 | ||
7d02b490 AK |
1597 | static char *remove_dot(char *s) |
1598 | { | |
fcd38ed0 | 1599 | size_t n = strcspn(s, "."); |
7d02b490 | 1600 | |
fcd38ed0 MN |
1601 | if (n && s[n]) { |
1602 | size_t m = strspn(s + n + 1, "0123456789"); | |
b5beffa2 | 1603 | if (m && (s[n + m + 1] == '.' || s[n + m + 1] == 0)) |
7d02b490 AK |
1604 | s[n] = 0; |
1605 | } | |
1606 | return s; | |
1607 | } | |
1608 | ||
f292d875 MY |
1609 | /* |
1610 | * The CRCs are recorded in .*.cmd files in the form of: | |
1611 | * #SYMVER <name> <crc> | |
1612 | */ | |
1613 | static void extract_crcs_for_object(const char *object, struct module *mod) | |
1614 | { | |
1615 | char cmd_file[PATH_MAX]; | |
1616 | char *buf, *p; | |
1617 | const char *base; | |
1618 | int dirlen, ret; | |
1619 | ||
1620 | base = strrchr(object, '/'); | |
1621 | if (base) { | |
1622 | base++; | |
1623 | dirlen = base - object; | |
1624 | } else { | |
1625 | dirlen = 0; | |
1626 | base = object; | |
1627 | } | |
1628 | ||
1629 | ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%s.cmd", | |
1630 | dirlen, object, base); | |
1631 | if (ret >= sizeof(cmd_file)) { | |
1632 | error("%s: too long path was truncated\n", cmd_file); | |
1633 | return; | |
1634 | } | |
1635 | ||
1636 | buf = read_text_file(cmd_file); | |
1637 | p = buf; | |
1638 | ||
1639 | while ((p = strstr(p, "\n#SYMVER "))) { | |
1640 | char *name; | |
1641 | size_t namelen; | |
1642 | unsigned int crc; | |
1643 | struct symbol *sym; | |
1644 | ||
1645 | name = p + strlen("\n#SYMVER "); | |
1646 | ||
1647 | p = strchr(name, ' '); | |
1648 | if (!p) | |
1649 | break; | |
1650 | ||
1651 | namelen = p - name; | |
1652 | p++; | |
1653 | ||
1654 | if (!isdigit(*p)) | |
1655 | continue; /* skip this line */ | |
1656 | ||
fb27e70f | 1657 | crc = strtoul(p, &p, 0); |
f292d875 MY |
1658 | if (*p != '\n') |
1659 | continue; /* skip this line */ | |
1660 | ||
1661 | name[namelen] = '\0'; | |
1662 | ||
1663 | /* | |
1664 | * sym_find_with_module() may return NULL here. | |
1665 | * It typically occurs when CONFIG_TRIM_UNUSED_KSYMS=y. | |
1666 | * Since commit e1327a127703, genksyms calculates CRCs of all | |
1667 | * symbols, including trimmed ones. Ignore orphan CRCs. | |
1668 | */ | |
1669 | sym = sym_find_with_module(name, mod); | |
1670 | if (sym) | |
1671 | sym_set_crc(sym, crc); | |
1672 | } | |
1673 | ||
1674 | free(buf); | |
1675 | } | |
1676 | ||
1677 | /* | |
1678 | * The symbol versions (CRC) are recorded in the .*.cmd files. | |
1679 | * Parse them to retrieve CRCs for the current module. | |
1680 | */ | |
1681 | static void mod_set_crcs(struct module *mod) | |
1682 | { | |
1683 | char objlist[PATH_MAX]; | |
1684 | char *buf, *p, *obj; | |
1685 | int ret; | |
1686 | ||
1687 | if (mod->is_vmlinux) { | |
1688 | strcpy(objlist, ".vmlinux.objs"); | |
1689 | } else { | |
1690 | /* objects for a module are listed in the *.mod file. */ | |
1691 | ret = snprintf(objlist, sizeof(objlist), "%s.mod", mod->name); | |
1692 | if (ret >= sizeof(objlist)) { | |
1693 | error("%s: too long path was truncated\n", objlist); | |
1694 | return; | |
1695 | } | |
1696 | } | |
1697 | ||
1698 | buf = read_text_file(objlist); | |
1699 | p = buf; | |
1700 | ||
1701 | while ((obj = strsep(&p, "\n")) && obj[0]) | |
1702 | extract_crcs_for_object(obj, mod); | |
1703 | ||
1704 | free(buf); | |
1705 | } | |
1706 | ||
8b185743 | 1707 | static void read_symbols(const char *modname) |
1da177e4 LT |
1708 | { |
1709 | const char *symname; | |
1710 | char *version; | |
b817f6fe | 1711 | char *license; |
cb9b55d2 | 1712 | char *namespace; |
1da177e4 LT |
1713 | struct module *mod; |
1714 | struct elf_info info = { }; | |
1715 | Elf_Sym *sym; | |
1716 | ||
85bd2fdd SR |
1717 | if (!parse_elf(&info, modname)) |
1718 | return; | |
1da177e4 | 1719 | |
8c9ce89c MY |
1720 | if (!strends(modname, ".o")) { |
1721 | error("%s: filename must be suffixed with .o\n", modname); | |
1722 | return; | |
a82f794c | 1723 | } |
1da177e4 | 1724 | |
8c9ce89c MY |
1725 | /* strip trailing .o */ |
1726 | mod = new_module(modname, strlen(modname) - strlen(".o")); | |
1727 | ||
5a438af9 | 1728 | if (!mod->is_vmlinux) { |
4ddea2f8 MY |
1729 | license = get_modinfo(&info, "license"); |
1730 | if (!license) | |
1d6cd392 | 1731 | error("missing MODULE_LICENSE() in %s\n", modname); |
4ddea2f8 | 1732 | while (license) { |
5066743e MY |
1733 | if (!license_is_gpl_compatible(license)) { |
1734 | mod->is_gpl_compatible = false; | |
4ddea2f8 MY |
1735 | break; |
1736 | } | |
1737 | license = get_next_modinfo(&info, "license", license); | |
b817f6fe | 1738 | } |
b817f6fe | 1739 | |
4ddea2f8 MY |
1740 | namespace = get_modinfo(&info, "import_ns"); |
1741 | while (namespace) { | |
1742 | add_namespace(&mod->imported_namespaces, namespace); | |
1743 | namespace = get_next_modinfo(&info, "import_ns", | |
1744 | namespace); | |
1745 | } | |
cb9b55d2 MM |
1746 | } |
1747 | ||
1fffe7a3 VP |
1748 | if (extra_warn && !get_modinfo(&info, "description")) |
1749 | warn("missing MODULE_DESCRIPTION() in %s\n", modname); | |
1da177e4 | 1750 | for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { |
7d02b490 | 1751 | symname = remove_dot(info.strtab + sym->st_name); |
1da177e4 | 1752 | |
9bd2a099 | 1753 | handle_symbol(mod, &info, sym, symname); |
1da177e4 LT |
1754 | handle_moddevtable(mod, &info, sym, symname); |
1755 | } | |
15bfc234 | 1756 | |
94d6cb68 | 1757 | check_sec_ref(mod, &info); |
1da177e4 | 1758 | |
5a438af9 | 1759 | if (!mod->is_vmlinux) { |
4ddea2f8 MY |
1760 | version = get_modinfo(&info, "version"); |
1761 | if (version || all_versions) | |
e54dd93a | 1762 | get_src_version(mod->name, mod->srcversion, |
4ddea2f8 MY |
1763 | sizeof(mod->srcversion) - 1); |
1764 | } | |
1da177e4 LT |
1765 | |
1766 | parse_elf_finish(&info); | |
1767 | ||
f292d875 MY |
1768 | if (modversions) { |
1769 | /* | |
1770 | * Our trick to get versioning for module struct etc. - it's | |
1771 | * never passed as an argument to an exported function, so | |
1772 | * the automatic versioning doesn't pick it up, but it's really | |
1773 | * important anyhow. | |
1774 | */ | |
e882e89b | 1775 | sym_add_unresolved("module_layout", mod, false); |
f292d875 MY |
1776 | |
1777 | mod_set_crcs(mod); | |
1778 | } | |
1da177e4 LT |
1779 | } |
1780 | ||
712f9b46 RR |
1781 | static void read_symbols_from_files(const char *filename) |
1782 | { | |
1783 | FILE *in = stdin; | |
1784 | char fname[PATH_MAX]; | |
1785 | ||
f65a4868 MY |
1786 | in = fopen(filename, "r"); |
1787 | if (!in) | |
1788 | fatal("Can't open filenames file %s: %m", filename); | |
712f9b46 RR |
1789 | |
1790 | while (fgets(fname, PATH_MAX, in) != NULL) { | |
1791 | if (strends(fname, "\n")) | |
1792 | fname[strlen(fname)-1] = '\0'; | |
1793 | read_symbols(fname); | |
1794 | } | |
1795 | ||
f65a4868 | 1796 | fclose(in); |
712f9b46 RR |
1797 | } |
1798 | ||
1da177e4 LT |
1799 | #define SZ 500 |
1800 | ||
1801 | /* We first write the generated file into memory using the | |
1802 | * following helper, then compare to the file on disk and | |
1803 | * only update the later if anything changed */ | |
1804 | ||
5c3ead8c SR |
1805 | void __attribute__((format(printf, 2, 3))) buf_printf(struct buffer *buf, |
1806 | const char *fmt, ...) | |
1da177e4 LT |
1807 | { |
1808 | char tmp[SZ]; | |
1809 | int len; | |
1810 | va_list ap; | |
62070fa4 | 1811 | |
1da177e4 LT |
1812 | va_start(ap, fmt); |
1813 | len = vsnprintf(tmp, SZ, fmt, ap); | |
7670f023 | 1814 | buf_write(buf, tmp, len); |
1da177e4 LT |
1815 | va_end(ap); |
1816 | } | |
1817 | ||
5c3ead8c | 1818 | void buf_write(struct buffer *buf, const char *s, int len) |
1da177e4 LT |
1819 | { |
1820 | if (buf->size - buf->pos < len) { | |
7670f023 | 1821 | buf->size += len + SZ; |
1f3aa900 | 1822 | buf->p = NOFAIL(realloc(buf->p, buf->size)); |
1da177e4 LT |
1823 | } |
1824 | strncpy(buf->p + buf->pos, s, len); | |
1825 | buf->pos += len; | |
1826 | } | |
1827 | ||
0fd3fbad | 1828 | static void check_exports(struct module *mod) |
b817f6fe SR |
1829 | { |
1830 | struct symbol *s, *exp; | |
1831 | ||
8a69152b | 1832 | list_for_each_entry(s, &mod->unresolved_symbols, list) { |
6449bd62 | 1833 | const char *basename; |
b817f6fe | 1834 | exp = find_symbol(s->name); |
23beb44a | 1835 | if (!exp) { |
4475dff5 | 1836 | if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS) |
93c95e52 JY |
1837 | modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR, |
1838 | "\"%s\" [%s.ko] undefined!\n", | |
1839 | s->name, mod->name); | |
b817f6fe | 1840 | continue; |
3b415288 | 1841 | } |
23beb44a MY |
1842 | if (exp->module == mod) { |
1843 | error("\"%s\" [%s.ko] was exported without definition\n", | |
1844 | s->name, mod->name); | |
1845 | continue; | |
1846 | } | |
4cae77ac | 1847 | |
5e9e95cc | 1848 | exp->used = true; |
4cae77ac MY |
1849 | s->module = exp->module; |
1850 | s->crc_valid = exp->crc_valid; | |
1851 | s->crc = exp->crc; | |
1852 | ||
6449bd62 | 1853 | basename = strrchr(mod->name, '/'); |
b817f6fe SR |
1854 | if (basename) |
1855 | basename++; | |
c96fca21 SR |
1856 | else |
1857 | basename = mod->name; | |
cb9b55d2 | 1858 | |
700c48b4 | 1859 | if (!contains_namespace(&mod->imported_namespaces, exp->namespace)) { |
54b77847 JY |
1860 | modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR, |
1861 | "module %s uses symbol %s from namespace %s, but does not import it.\n", | |
1862 | basename, exp->name, exp->namespace); | |
bbc55bde | 1863 | add_namespace(&mod->missing_namespaces, exp->namespace); |
cb9b55d2 MM |
1864 | } |
1865 | ||
2a66c312 MY |
1866 | if (!mod->is_gpl_compatible && exp->is_gpl_only) |
1867 | error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", | |
1868 | basename, exp->name); | |
df578e7d | 1869 | } |
b817f6fe SR |
1870 | } |
1871 | ||
5e9e95cc MY |
1872 | static void handle_white_list_exports(const char *white_list) |
1873 | { | |
1874 | char *buf, *p, *name; | |
1875 | ||
1876 | buf = read_text_file(white_list); | |
1877 | p = buf; | |
1878 | ||
1879 | while ((name = strsep(&p, "\n"))) { | |
1880 | struct symbol *sym = find_symbol(name); | |
1881 | ||
1882 | if (sym) | |
1883 | sym->used = true; | |
1884 | } | |
1885 | ||
1886 | free(buf); | |
1887 | } | |
1888 | ||
0fd3fbad | 1889 | static void check_modname_len(struct module *mod) |
4fd3e4ef WG |
1890 | { |
1891 | const char *mod_name; | |
1892 | ||
1893 | mod_name = strrchr(mod->name, '/'); | |
1894 | if (mod_name == NULL) | |
1895 | mod_name = mod->name; | |
1896 | else | |
1897 | mod_name++; | |
0fd3fbad | 1898 | if (strlen(mod_name) >= MODULE_NAME_LEN) |
bc72d723 | 1899 | error("module name is too long [%s.ko]\n", mod->name); |
4fd3e4ef WG |
1900 | } |
1901 | ||
5c3ead8c SR |
1902 | /** |
1903 | * Header for the generated file | |
1904 | **/ | |
1905 | static void add_header(struct buffer *b, struct module *mod) | |
1da177e4 LT |
1906 | { |
1907 | buf_printf(b, "#include <linux/module.h>\n"); | |
f58dd03b VF |
1908 | /* |
1909 | * Include build-salt.h after module.h in order to | |
1910 | * inherit the definitions. | |
1911 | */ | |
51161bfc | 1912 | buf_printf(b, "#define INCLUDE_VERMAGIC\n"); |
f58dd03b | 1913 | buf_printf(b, "#include <linux/build-salt.h>\n"); |
1fdd7433 | 1914 | buf_printf(b, "#include <linux/elfnote-lto.h>\n"); |
7b453719 | 1915 | buf_printf(b, "#include <linux/export-internal.h>\n"); |
1da177e4 LT |
1916 | buf_printf(b, "#include <linux/vermagic.h>\n"); |
1917 | buf_printf(b, "#include <linux/compiler.h>\n"); | |
1918 | buf_printf(b, "\n"); | |
b9f174c8 OS |
1919 | buf_printf(b, "#ifdef CONFIG_UNWINDER_ORC\n"); |
1920 | buf_printf(b, "#include <asm/orc_header.h>\n"); | |
1921 | buf_printf(b, "ORC_HEADER;\n"); | |
1922 | buf_printf(b, "#endif\n"); | |
1923 | buf_printf(b, "\n"); | |
9afb719e | 1924 | buf_printf(b, "BUILD_SALT;\n"); |
1fdd7433 | 1925 | buf_printf(b, "BUILD_LTO_INFO;\n"); |
9afb719e | 1926 | buf_printf(b, "\n"); |
1da177e4 | 1927 | buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n"); |
3e2e857f | 1928 | buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n"); |
1da177e4 | 1929 | buf_printf(b, "\n"); |
e0f244c6 | 1930 | buf_printf(b, "__visible struct module __this_module\n"); |
33def849 | 1931 | buf_printf(b, "__section(\".gnu.linkonce.this_module\") = {\n"); |
3c7ec94d | 1932 | buf_printf(b, "\t.name = KBUILD_MODNAME,\n"); |
1da177e4 | 1933 | if (mod->has_init) |
3c7ec94d | 1934 | buf_printf(b, "\t.init = init_module,\n"); |
1da177e4 LT |
1935 | if (mod->has_cleanup) |
1936 | buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n" | |
3c7ec94d | 1937 | "\t.exit = cleanup_module,\n" |
1da177e4 | 1938 | "#endif\n"); |
3c7ec94d | 1939 | buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n"); |
1da177e4 | 1940 | buf_printf(b, "};\n"); |
1da177e4 | 1941 | |
7fedac96 | 1942 | if (!external_module) |
2449b8ba | 1943 | buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); |
2449b8ba | 1944 | |
7fedac96 MY |
1945 | buf_printf(b, |
1946 | "\n" | |
1947 | "#ifdef CONFIG_RETPOLINE\n" | |
1948 | "MODULE_INFO(retpoline, \"Y\");\n" | |
1949 | "#endif\n"); | |
caf7501a | 1950 | |
7fedac96 | 1951 | if (strstarts(mod->name, "drivers/staging")) |
a9860bf0 | 1952 | buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); |
74829ddf DG |
1953 | |
1954 | if (strstarts(mod->name, "tools/testing")) | |
1955 | buf_printf(b, "\nMODULE_INFO(test, \"Y\");\n"); | |
a9860bf0 GKH |
1956 | } |
1957 | ||
7b453719 | 1958 | static void add_exported_symbols(struct buffer *buf, struct module *mod) |
f292d875 MY |
1959 | { |
1960 | struct symbol *sym; | |
1961 | ||
ddb5cdba MY |
1962 | /* generate struct for exported symbols */ |
1963 | buf_printf(buf, "\n"); | |
5e9e95cc MY |
1964 | list_for_each_entry(sym, &mod->exported_symbols, list) { |
1965 | if (trim_unused_exports && !sym->used) | |
1966 | continue; | |
1967 | ||
ddb5cdba MY |
1968 | buf_printf(buf, "KSYMTAB_%s(%s, \"%s\", \"%s\");\n", |
1969 | sym->is_func ? "FUNC" : "DATA", sym->name, | |
700c48b4 | 1970 | sym->is_gpl_only ? "_gpl" : "", sym->namespace); |
5e9e95cc | 1971 | } |
ddb5cdba | 1972 | |
f292d875 MY |
1973 | if (!modversions) |
1974 | return; | |
1975 | ||
7b453719 MY |
1976 | /* record CRCs for exported symbols */ |
1977 | buf_printf(buf, "\n"); | |
f292d875 | 1978 | list_for_each_entry(sym, &mod->exported_symbols, list) { |
5e9e95cc MY |
1979 | if (trim_unused_exports && !sym->used) |
1980 | continue; | |
1981 | ||
5b8a9a8f | 1982 | if (!sym->crc_valid) |
f292d875 MY |
1983 | warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" |
1984 | "Is \"%s\" prototyped in <asm/asm-prototypes.h>?\n", | |
1985 | sym->name, mod->name, mod->is_vmlinux ? "" : ".ko", | |
1986 | sym->name); | |
7b453719 MY |
1987 | |
1988 | buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x, \"%s\");\n", | |
1989 | sym->name, sym->crc, sym->is_gpl_only ? "_gpl" : ""); | |
f292d875 MY |
1990 | } |
1991 | } | |
1992 | ||
5c3ead8c SR |
1993 | /** |
1994 | * Record CRCs for unresolved symbols | |
1995 | **/ | |
0fd3fbad | 1996 | static void add_versions(struct buffer *b, struct module *mod) |
1da177e4 | 1997 | { |
4cae77ac | 1998 | struct symbol *s; |
1da177e4 LT |
1999 | |
2000 | if (!modversions) | |
0fd3fbad | 2001 | return; |
1da177e4 LT |
2002 | |
2003 | buf_printf(b, "\n"); | |
2004 | buf_printf(b, "static const struct modversion_info ____versions[]\n"); | |
33def849 | 2005 | buf_printf(b, "__used __section(\"__versions\") = {\n"); |
1da177e4 | 2006 | |
8a69152b | 2007 | list_for_each_entry(s, &mod->unresolved_symbols, list) { |
df578e7d | 2008 | if (!s->module) |
1da177e4 | 2009 | continue; |
1da177e4 | 2010 | if (!s->crc_valid) { |
cb80514d | 2011 | warn("\"%s\" [%s.ko] has no CRC!\n", |
1da177e4 LT |
2012 | s->name, mod->name); |
2013 | continue; | |
2014 | } | |
5cfb203a | 2015 | if (strlen(s->name) >= MODULE_NAME_LEN) { |
bc72d723 MY |
2016 | error("too long symbol \"%s\" [%s.ko]\n", |
2017 | s->name, mod->name); | |
5cfb203a TI |
2018 | break; |
2019 | } | |
b2c5cdcf | 2020 | buf_printf(b, "\t{ %#8x, \"%s\" },\n", |
a4b6a77b | 2021 | s->crc, s->name); |
1da177e4 LT |
2022 | } |
2023 | ||
2024 | buf_printf(b, "};\n"); | |
2025 | } | |
2026 | ||
d2665ca8 | 2027 | static void add_depends(struct buffer *b, struct module *mod) |
1da177e4 LT |
2028 | { |
2029 | struct symbol *s; | |
1da177e4 LT |
2030 | int first = 1; |
2031 | ||
d2665ca8 | 2032 | /* Clear ->seen flag of modules that own symbols needed by this. */ |
8a69152b | 2033 | list_for_each_entry(s, &mod->unresolved_symbols, list) { |
d2665ca8 | 2034 | if (s->module) |
5a438af9 | 2035 | s->module->seen = s->module->is_vmlinux; |
8a69152b | 2036 | } |
1da177e4 LT |
2037 | |
2038 | buf_printf(b, "\n"); | |
6df7e1ec | 2039 | buf_printf(b, "MODULE_INFO(depends, \""); |
8a69152b | 2040 | list_for_each_entry(s, &mod->unresolved_symbols, list) { |
a61b2dfd | 2041 | const char *p; |
1da177e4 LT |
2042 | if (!s->module) |
2043 | continue; | |
2044 | ||
2045 | if (s->module->seen) | |
2046 | continue; | |
2047 | ||
58e01fca | 2048 | s->module->seen = true; |
df578e7d SR |
2049 | p = strrchr(s->module->name, '/'); |
2050 | if (p) | |
a61b2dfd SR |
2051 | p++; |
2052 | else | |
2053 | p = s->module->name; | |
2054 | buf_printf(b, "%s%s", first ? "" : ",", p); | |
1da177e4 LT |
2055 | first = 0; |
2056 | } | |
6df7e1ec | 2057 | buf_printf(b, "\");\n"); |
1da177e4 LT |
2058 | } |
2059 | ||
5c3ead8c | 2060 | static void add_srcversion(struct buffer *b, struct module *mod) |
1da177e4 LT |
2061 | { |
2062 | if (mod->srcversion[0]) { | |
2063 | buf_printf(b, "\n"); | |
2064 | buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n", | |
2065 | mod->srcversion); | |
2066 | } | |
2067 | } | |
2068 | ||
436b2ac6 MY |
2069 | static void write_buf(struct buffer *b, const char *fname) |
2070 | { | |
2071 | FILE *file; | |
2072 | ||
c155a47d MY |
2073 | if (error_occurred) |
2074 | return; | |
2075 | ||
436b2ac6 MY |
2076 | file = fopen(fname, "w"); |
2077 | if (!file) { | |
2078 | perror(fname); | |
2079 | exit(1); | |
2080 | } | |
2081 | if (fwrite(b->p, 1, b->pos, file) != b->pos) { | |
2082 | perror(fname); | |
2083 | exit(1); | |
2084 | } | |
2085 | if (fclose(file) != 0) { | |
2086 | perror(fname); | |
2087 | exit(1); | |
2088 | } | |
2089 | } | |
2090 | ||
5c3ead8c | 2091 | static void write_if_changed(struct buffer *b, const char *fname) |
1da177e4 LT |
2092 | { |
2093 | char *tmp; | |
2094 | FILE *file; | |
2095 | struct stat st; | |
2096 | ||
2097 | file = fopen(fname, "r"); | |
2098 | if (!file) | |
2099 | goto write; | |
2100 | ||
2101 | if (fstat(fileno(file), &st) < 0) | |
2102 | goto close_write; | |
2103 | ||
2104 | if (st.st_size != b->pos) | |
2105 | goto close_write; | |
2106 | ||
2107 | tmp = NOFAIL(malloc(b->pos)); | |
2108 | if (fread(tmp, 1, b->pos, file) != b->pos) | |
2109 | goto free_write; | |
2110 | ||
2111 | if (memcmp(tmp, b->p, b->pos) != 0) | |
2112 | goto free_write; | |
2113 | ||
2114 | free(tmp); | |
2115 | fclose(file); | |
2116 | return; | |
2117 | ||
2118 | free_write: | |
2119 | free(tmp); | |
2120 | close_write: | |
2121 | fclose(file); | |
2122 | write: | |
436b2ac6 | 2123 | write_buf(b, fname); |
1da177e4 LT |
2124 | } |
2125 | ||
7b453719 MY |
2126 | static void write_vmlinux_export_c_file(struct module *mod) |
2127 | { | |
2128 | struct buffer buf = { }; | |
2129 | ||
2130 | buf_printf(&buf, | |
2131 | "#include <linux/export-internal.h>\n"); | |
2132 | ||
2133 | add_exported_symbols(&buf, mod); | |
2134 | write_if_changed(&buf, ".vmlinux.export.c"); | |
2135 | free(buf.p); | |
2136 | } | |
2137 | ||
a44abaca MY |
2138 | /* do sanity checks, and generate *.mod.c file */ |
2139 | static void write_mod_c_file(struct module *mod) | |
2140 | { | |
2141 | struct buffer buf = { }; | |
2142 | char fname[PATH_MAX]; | |
2143 | int ret; | |
2144 | ||
a44abaca | 2145 | add_header(&buf, mod); |
7b453719 | 2146 | add_exported_symbols(&buf, mod); |
a44abaca MY |
2147 | add_versions(&buf, mod); |
2148 | add_depends(&buf, mod); | |
2149 | add_moddevtable(&buf, mod); | |
2150 | add_srcversion(&buf, mod); | |
2151 | ||
2152 | ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); | |
2153 | if (ret >= sizeof(fname)) { | |
2154 | error("%s: too long path was truncated\n", fname); | |
2155 | goto free; | |
2156 | } | |
2157 | ||
2158 | write_if_changed(&buf, fname); | |
2159 | ||
2160 | free: | |
2161 | free(buf.p); | |
2162 | } | |
2163 | ||
bd5cbced | 2164 | /* parse Module.symvers file. line format: |
5190044c | 2165 | * 0x12345678<tab>symbol<tab>module<tab>export<tab>namespace |
bd5cbced | 2166 | **/ |
52c3416d | 2167 | static void read_dump(const char *fname) |
1da177e4 | 2168 | { |
70f30cfe | 2169 | char *buf, *pos, *line; |
1da177e4 | 2170 | |
70f30cfe MY |
2171 | buf = read_text_file(fname); |
2172 | if (!buf) | |
1da177e4 LT |
2173 | /* No symbol versions, silently ignore */ |
2174 | return; | |
2175 | ||
70f30cfe MY |
2176 | pos = buf; |
2177 | ||
2178 | while ((line = get_line(&pos))) { | |
5190044c | 2179 | char *symname, *namespace, *modname, *d, *export; |
1da177e4 LT |
2180 | unsigned int crc; |
2181 | struct module *mod; | |
040fcc81 | 2182 | struct symbol *s; |
2a66c312 | 2183 | bool gpl_only; |
1da177e4 LT |
2184 | |
2185 | if (!(symname = strchr(line, '\t'))) | |
2186 | goto fail; | |
2187 | *symname++ = '\0'; | |
5190044c | 2188 | if (!(modname = strchr(symname, '\t'))) |
1da177e4 LT |
2189 | goto fail; |
2190 | *modname++ = '\0'; | |
5190044c JY |
2191 | if (!(export = strchr(modname, '\t'))) |
2192 | goto fail; | |
2193 | *export++ = '\0'; | |
2194 | if (!(namespace = strchr(export, '\t'))) | |
2195 | goto fail; | |
2196 | *namespace++ = '\0'; | |
2197 | ||
1da177e4 LT |
2198 | crc = strtoul(line, &d, 16); |
2199 | if (*symname == '\0' || *modname == '\0' || *d != '\0') | |
2200 | goto fail; | |
2a66c312 MY |
2201 | |
2202 | if (!strcmp(export, "EXPORT_SYMBOL_GPL")) { | |
2203 | gpl_only = true; | |
2204 | } else if (!strcmp(export, "EXPORT_SYMBOL")) { | |
2205 | gpl_only = false; | |
2206 | } else { | |
2207 | error("%s: unknown license %s. skip", symname, export); | |
2208 | continue; | |
2209 | } | |
2210 | ||
df578e7d SR |
2211 | mod = find_module(modname); |
2212 | if (!mod) { | |
8c9ce89c | 2213 | mod = new_module(modname, strlen(modname)); |
58e01fca | 2214 | mod->from_dump = true; |
1da177e4 | 2215 | } |
6e7611c4 | 2216 | s = sym_add_exported(symname, mod, gpl_only, namespace); |
f292d875 | 2217 | sym_set_crc(s, crc); |
1da177e4 | 2218 | } |
70f30cfe | 2219 | free(buf); |
1da177e4 LT |
2220 | return; |
2221 | fail: | |
70f30cfe | 2222 | free(buf); |
1da177e4 LT |
2223 | fatal("parse error in symbol dump file\n"); |
2224 | } | |
2225 | ||
5c3ead8c | 2226 | static void write_dump(const char *fname) |
1da177e4 LT |
2227 | { |
2228 | struct buffer buf = { }; | |
f841536e MY |
2229 | struct module *mod; |
2230 | struct symbol *sym; | |
2231 | ||
2232 | list_for_each_entry(mod, &modules, list) { | |
2233 | if (mod->from_dump) | |
2234 | continue; | |
2235 | list_for_each_entry(sym, &mod->exported_symbols, list) { | |
5e9e95cc MY |
2236 | if (trim_unused_exports && !sym->used) |
2237 | continue; | |
2238 | ||
2a66c312 | 2239 | buf_printf(&buf, "0x%08x\t%s\t%s\tEXPORT_SYMBOL%s\t%s\n", |
f841536e | 2240 | sym->crc, sym->name, mod->name, |
2a66c312 | 2241 | sym->is_gpl_only ? "_GPL" : "", |
700c48b4 | 2242 | sym->namespace); |
1da177e4 LT |
2243 | } |
2244 | } | |
436b2ac6 | 2245 | write_buf(&buf, fname); |
c7d47f26 | 2246 | free(buf.p); |
1da177e4 LT |
2247 | } |
2248 | ||
bbc55bde | 2249 | static void write_namespace_deps_files(const char *fname) |
1d082773 MM |
2250 | { |
2251 | struct module *mod; | |
2252 | struct namespace_list *ns; | |
2253 | struct buffer ns_deps_buf = {}; | |
2254 | ||
325eba05 | 2255 | list_for_each_entry(mod, &modules, list) { |
1d082773 | 2256 | |
ab489d60 | 2257 | if (mod->from_dump || list_empty(&mod->missing_namespaces)) |
1d082773 MM |
2258 | continue; |
2259 | ||
bbc55bde | 2260 | buf_printf(&ns_deps_buf, "%s.ko:", mod->name); |
1d082773 | 2261 | |
ab489d60 | 2262 | list_for_each_entry(ns, &mod->missing_namespaces, list) |
bbc55bde | 2263 | buf_printf(&ns_deps_buf, " %s", ns->namespace); |
1d082773 | 2264 | |
bbc55bde | 2265 | buf_printf(&ns_deps_buf, "\n"); |
1d082773 | 2266 | } |
0241ea8c | 2267 | |
bbc55bde | 2268 | write_if_changed(&ns_deps_buf, fname); |
0241ea8c | 2269 | free(ns_deps_buf.p); |
1d082773 MM |
2270 | } |
2271 | ||
7924799e | 2272 | struct dump_list { |
44840548 | 2273 | struct list_head list; |
2d04b5ae RH |
2274 | const char *file; |
2275 | }; | |
2276 | ||
5c3ead8c | 2277 | int main(int argc, char **argv) |
1da177e4 LT |
2278 | { |
2279 | struct module *mod; | |
bbc55bde | 2280 | char *missing_namespace_deps = NULL; |
5e9e95cc | 2281 | char *unused_exports_white_list = NULL; |
712f9b46 | 2282 | char *dump_write = NULL, *files_source = NULL; |
1da177e4 | 2283 | int opt; |
44840548 MY |
2284 | LIST_HEAD(dump_lists); |
2285 | struct dump_list *dl, *dl2; | |
1da177e4 | 2286 | |
481461f5 | 2287 | while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:")) != -1) { |
df578e7d | 2288 | switch (opt) { |
2d04b5ae | 2289 | case 'e': |
58e01fca | 2290 | external_module = true; |
e3fb4df7 MY |
2291 | break; |
2292 | case 'i': | |
44840548 MY |
2293 | dl = NOFAIL(malloc(sizeof(*dl))); |
2294 | dl->file = optarg; | |
2295 | list_add_tail(&dl->list, &dump_lists); | |
2d04b5ae | 2296 | break; |
481461f5 MY |
2297 | case 'M': |
2298 | module_enabled = true; | |
2299 | break; | |
df578e7d | 2300 | case 'm': |
58e01fca | 2301 | modversions = true; |
df578e7d | 2302 | break; |
eed380f3 | 2303 | case 'n': |
58e01fca | 2304 | ignore_missing_files = true; |
eed380f3 | 2305 | break; |
df578e7d SR |
2306 | case 'o': |
2307 | dump_write = optarg; | |
2308 | break; | |
2309 | case 'a': | |
58e01fca | 2310 | all_versions = true; |
df578e7d | 2311 | break; |
712f9b46 RR |
2312 | case 'T': |
2313 | files_source = optarg; | |
2314 | break; | |
5e9e95cc MY |
2315 | case 't': |
2316 | trim_unused_exports = true; | |
2317 | break; | |
2318 | case 'u': | |
2319 | unused_exports_white_list = optarg; | |
2320 | break; | |
20ff3685 MY |
2321 | case 'W': |
2322 | extra_warn = true; | |
2323 | break; | |
df578e7d | 2324 | case 'w': |
58e01fca | 2325 | warn_unresolved = true; |
df578e7d | 2326 | break; |
47490ec1 | 2327 | case 'E': |
c7299d98 | 2328 | sec_mismatch_warn_only = false; |
47490ec1 | 2329 | break; |
54b77847 | 2330 | case 'N': |
58e01fca | 2331 | allow_missing_ns_imports = true; |
54b77847 | 2332 | break; |
1d082773 | 2333 | case 'd': |
bbc55bde | 2334 | missing_namespace_deps = optarg; |
1d082773 | 2335 | break; |
df578e7d SR |
2336 | default: |
2337 | exit(1); | |
1da177e4 LT |
2338 | } |
2339 | } | |
2340 | ||
44840548 MY |
2341 | list_for_each_entry_safe(dl, dl2, &dump_lists, list) { |
2342 | read_dump(dl->file); | |
2343 | list_del(&dl->list); | |
2344 | free(dl); | |
2d04b5ae | 2345 | } |
1da177e4 | 2346 | |
df578e7d | 2347 | while (optind < argc) |
1da177e4 | 2348 | read_symbols(argv[optind++]); |
1da177e4 | 2349 | |
712f9b46 RR |
2350 | if (files_source) |
2351 | read_symbols_from_files(files_source); | |
2352 | ||
5e9e95cc MY |
2353 | list_for_each_entry(mod, &modules, list) { |
2354 | if (mod->from_dump || mod->is_vmlinux) | |
2355 | continue; | |
2356 | ||
2357 | check_modname_len(mod); | |
2358 | check_exports(mod); | |
2359 | } | |
2360 | ||
2361 | if (unused_exports_white_list) | |
2362 | handle_white_list_exports(unused_exports_white_list); | |
2363 | ||
325eba05 | 2364 | list_for_each_entry(mod, &modules, list) { |
a44abaca | 2365 | if (mod->from_dump) |
15a28c7c | 2366 | continue; |
15a28c7c | 2367 | |
7b453719 MY |
2368 | if (mod->is_vmlinux) |
2369 | write_vmlinux_export_c_file(mod); | |
2370 | else | |
a44abaca | 2371 | write_mod_c_file(mod); |
1da177e4 | 2372 | } |
1d082773 | 2373 | |
bbc55bde MY |
2374 | if (missing_namespace_deps) |
2375 | write_namespace_deps_files(missing_namespace_deps); | |
1d082773 | 2376 | |
1da177e4 LT |
2377 | if (dump_write) |
2378 | write_dump(dump_write); | |
c7299d98 MY |
2379 | if (sec_mismatch_count && !sec_mismatch_warn_only) |
2380 | error("Section mismatches detected.\n" | |
46c7dd56 | 2381 | "Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n"); |
15bfc234 | 2382 | |
4475dff5 MY |
2383 | if (nr_unresolved > MAX_UNRESOLVED_REPORTS) |
2384 | warn("suppressed %u unresolved symbol warnings because there were too many)\n", | |
2385 | nr_unresolved - MAX_UNRESOLVED_REPORTS); | |
2386 | ||
0fd3fbad | 2387 | return error_occurred ? 1 : 0; |
1da177e4 | 2388 | } |