|
@@ -55,6 +55,17 @@ void warn(const char *fmt, ...)
|
|
|
va_end(arglist);
|
|
|
}
|
|
|
|
|
|
+void merror(const char *fmt, ...)
|
|
|
+{
|
|
|
+ va_list arglist;
|
|
|
+
|
|
|
+ fprintf(stderr, "ERROR: ");
|
|
|
+
|
|
|
+ va_start(arglist, fmt);
|
|
|
+ vfprintf(stderr, fmt, arglist);
|
|
|
+ va_end(arglist);
|
|
|
+}
|
|
|
+
|
|
|
static int is_vmlinux(const char *modname)
|
|
|
{
|
|
|
const char *myname;
|
|
@@ -333,10 +344,10 @@ void release_file(void *file, unsigned long size)
|
|
|
munmap(file, size);
|
|
|
}
|
|
|
|
|
|
-static void parse_elf(struct elf_info *info, const char *filename)
|
|
|
+static int parse_elf(struct elf_info *info, const char *filename)
|
|
|
{
|
|
|
unsigned int i;
|
|
|
- Elf_Ehdr *hdr = info->hdr;
|
|
|
+ Elf_Ehdr *hdr;
|
|
|
Elf_Shdr *sechdrs;
|
|
|
Elf_Sym *sym;
|
|
|
|
|
@@ -346,9 +357,18 @@ static void parse_elf(struct elf_info *info, const char *filename)
|
|
|
exit(1);
|
|
|
}
|
|
|
info->hdr = hdr;
|
|
|
- if (info->size < sizeof(*hdr))
|
|
|
- goto truncated;
|
|
|
-
|
|
|
+ if (info->size < sizeof(*hdr)) {
|
|
|
+ /* file too small, assume this is an empty .o file */
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ /* Is this a valid ELF file? */
|
|
|
+ if ((hdr->e_ident[EI_MAG0] != ELFMAG0) ||
|
|
|
+ (hdr->e_ident[EI_MAG1] != ELFMAG1) ||
|
|
|
+ (hdr->e_ident[EI_MAG2] != ELFMAG2) ||
|
|
|
+ (hdr->e_ident[EI_MAG3] != ELFMAG3)) {
|
|
|
+ /* Not an ELF file - silently ignore it */
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
/* Fix endianness in ELF header */
|
|
|
hdr->e_shoff = TO_NATIVE(hdr->e_shoff);
|
|
|
hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx);
|
|
@@ -371,8 +391,10 @@ static void parse_elf(struct elf_info *info, const char *filename)
|
|
|
= (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
|
|
|
const char *secname;
|
|
|
|
|
|
- if (sechdrs[i].sh_offset > info->size)
|
|
|
- goto truncated;
|
|
|
+ if (sechdrs[i].sh_offset > info->size) {
|
|
|
+ fatal("%s is truncated. sechdrs[i].sh_offset=%u > sizeof(*hrd)=%ul\n", filename, (unsigned int)sechdrs[i].sh_offset, sizeof(*hdr));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
secname = secstrings + sechdrs[i].sh_name;
|
|
|
if (strcmp(secname, ".modinfo") == 0) {
|
|
|
info->modinfo = (void *)hdr + sechdrs[i].sh_offset;
|
|
@@ -407,10 +429,7 @@ static void parse_elf(struct elf_info *info, const char *filename)
|
|
|
sym->st_value = TO_NATIVE(sym->st_value);
|
|
|
sym->st_size = TO_NATIVE(sym->st_size);
|
|
|
}
|
|
|
- return;
|
|
|
-
|
|
|
- truncated:
|
|
|
- fatal("%s is truncated.\n", filename);
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
static void parse_elf_finish(struct elf_info *info)
|
|
@@ -581,9 +600,17 @@ static int strrcmp(const char *s, const char *sub)
|
|
|
* the pattern is identified by:
|
|
|
* tosec = .init.text | .exit.text | .init.data
|
|
|
* fromsec = .data
|
|
|
- * atsym = *driver, *_template, *_sht, *_ops, *_probe, *probe_one
|
|
|
+ * atsym = *driver, *_template, *_sht, *_ops, *_probe, *probe_one, *_console
|
|
|
*
|
|
|
* Pattern 3:
|
|
|
+ * Whitelist all references from .pci_fixup* section to .init.text
|
|
|
+ * This is part of the PCI init when built-in
|
|
|
+ *
|
|
|
+ * Pattern 4:
|
|
|
+ * Whitelist all refereces from .text.head to .init.data
|
|
|
+ * Whitelist all refereces from .text.head to .init.text
|
|
|
+ *
|
|
|
+ * Pattern 5:
|
|
|
* Some symbols belong to init section but still it is ok to reference
|
|
|
* these from non-init sections as these symbols don't have any memory
|
|
|
* allocated for them and symbol address and value are same. So even
|
|
@@ -591,6 +618,30 @@ static int strrcmp(const char *s, const char *sub)
|
|
|
* For ex. symbols marking the init section boundaries.
|
|
|
* This pattern is identified by
|
|
|
* refsymname = __init_begin, _sinittext, _einittext
|
|
|
+ *
|
|
|
+ * Pattern 6:
|
|
|
+ * During the early init phase we have references from .init.text to
|
|
|
+ * .text we have an intended section mismatch - do not warn about it.
|
|
|
+ * See kernel_init() in init/main.c
|
|
|
+ * tosec = .init.text
|
|
|
+ * fromsec = .text
|
|
|
+ * atsym = kernel_init
|
|
|
+ *
|
|
|
+ * Pattern 7:
|
|
|
+ * Logos used in drivers/video/logo reside in __initdata but the
|
|
|
+ * funtion that references them are EXPORT_SYMBOL() so cannot be
|
|
|
+ * marker __init. So we whitelist them here.
|
|
|
+ * The pattern is:
|
|
|
+ * tosec = .init.data
|
|
|
+ * fromsec = .text*
|
|
|
+ * refsymname = logo_
|
|
|
+ *
|
|
|
+ * Pattern 8:
|
|
|
+ * Symbols contained in .paravirtprobe may safely reference .init.text.
|
|
|
+ * The pattern is:
|
|
|
+ * tosec = .init.text
|
|
|
+ * fromsec = .paravirtprobe
|
|
|
+ *
|
|
|
**/
|
|
|
static int secref_whitelist(const char *modname, const char *tosec,
|
|
|
const char *fromsec, const char *atsym,
|
|
@@ -642,25 +693,39 @@ static int secref_whitelist(const char *modname, const char *tosec,
|
|
|
if (f1 && f2)
|
|
|
return 1;
|
|
|
|
|
|
- /* Whitelist all references from .pci_fixup section if vmlinux
|
|
|
- * Whitelist all refereces from .text.head to .init.data if vmlinux
|
|
|
- * Whitelist all refereces from .text.head to .init.text if vmlinux
|
|
|
- */
|
|
|
- if (is_vmlinux(modname)) {
|
|
|
- if ((strcmp(fromsec, ".pci_fixup") == 0) &&
|
|
|
- (strcmp(tosec, ".init.text") == 0))
|
|
|
+ /* Check for pattern 3 */
|
|
|
+ if ((strncmp(fromsec, ".pci_fixup", strlen(".pci_fixup")) == 0) &&
|
|
|
+ (strcmp(tosec, ".init.text") == 0))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ /* Check for pattern 4 */
|
|
|
+ if ((strcmp(fromsec, ".text.head") == 0) &&
|
|
|
+ ((strcmp(tosec, ".init.data") == 0) ||
|
|
|
+ (strcmp(tosec, ".init.text") == 0)))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ /* Check for pattern 5 */
|
|
|
+ for (s = pat3refsym; *s; s++)
|
|
|
+ if (strcmp(refsymname, *s) == 0)
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ /* Check for pattern 6 */
|
|
|
+ if ((strcmp(tosec, ".init.text") == 0) &&
|
|
|
+ (strcmp(fromsec, ".text") == 0) &&
|
|
|
+ (strcmp(refsymname, "kernel_init") == 0))
|
|
|
return 1;
|
|
|
|
|
|
- if ((strcmp(fromsec, ".text.head") == 0) &&
|
|
|
- ((strcmp(tosec, ".init.data") == 0) ||
|
|
|
- (strcmp(tosec, ".init.text") == 0)))
|
|
|
+ /* Check for pattern 7 */
|
|
|
+ if ((strcmp(tosec, ".init.data") == 0) &&
|
|
|
+ (strncmp(fromsec, ".text", strlen(".text")) == 0) &&
|
|
|
+ (strncmp(refsymname, "logo_", strlen("logo_")) == 0))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ /* Check for pattern 8 */
|
|
|
+ if ((strcmp(tosec, ".init.text") == 0) &&
|
|
|
+ (strcmp(fromsec, ".paravirtprobe") == 0))
|
|
|
return 1;
|
|
|
|
|
|
- /* Check for pattern 3 */
|
|
|
- for (s = pat3refsym; *s; s++)
|
|
|
- if (strcmp(refsymname, *s) == 0)
|
|
|
- return 1;
|
|
|
- }
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1090,7 +1155,8 @@ static void read_symbols(char *modname)
|
|
|
struct elf_info info = { };
|
|
|
Elf_Sym *sym;
|
|
|
|
|
|
- parse_elf(&info, modname);
|
|
|
+ if (!parse_elf(&info, modname))
|
|
|
+ return;
|
|
|
|
|
|
mod = new_module(modname);
|
|
|
|
|
@@ -1265,9 +1331,14 @@ static int add_versions(struct buffer *b, struct module *mod)
|
|
|
exp = find_symbol(s->name);
|
|
|
if (!exp || exp->module == mod) {
|
|
|
if (have_vmlinux && !s->weak) {
|
|
|
- warn("\"%s\" [%s.ko] undefined!\n",
|
|
|
- s->name, mod->name);
|
|
|
- err = warn_unresolved ? 0 : 1;
|
|
|
+ if (warn_unresolved) {
|
|
|
+ warn("\"%s\" [%s.ko] undefined!\n",
|
|
|
+ s->name, mod->name);
|
|
|
+ } else {
|
|
|
+ merror("\"%s\" [%s.ko] undefined!\n",
|
|
|
+ s->name, mod->name);
|
|
|
+ err = 1;
|
|
|
+ }
|
|
|
}
|
|
|
continue;
|
|
|
}
|
|
@@ -1318,6 +1389,7 @@ static void add_depends(struct buffer *b, struct module *mod,
|
|
|
buf_printf(b, "__attribute__((section(\".modinfo\"))) =\n");
|
|
|
buf_printf(b, "\"depends=");
|
|
|
for (s = mod->unres; s; s = s->next) {
|
|
|
+ const char *p;
|
|
|
if (!s->module)
|
|
|
continue;
|
|
|
|
|
@@ -1325,8 +1397,11 @@ static void add_depends(struct buffer *b, struct module *mod,
|
|
|
continue;
|
|
|
|
|
|
s->module->seen = 1;
|
|
|
- buf_printf(b, "%s%s", first ? "" : ",",
|
|
|
- strrchr(s->module->name, '/') + 1);
|
|
|
+ if ((p = strrchr(s->module->name, '/')) != NULL)
|
|
|
+ p++;
|
|
|
+ else
|
|
|
+ p = s->module->name;
|
|
|
+ buf_printf(b, "%s%s", first ? "" : ",", p);
|
|
|
first = 0;
|
|
|
}
|
|
|
buf_printf(b, "\";\n");
|