aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/mod
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/mod')
-rw-r--r--scripts/mod/.gitignore9
-rw-r--r--scripts/mod/Makefile5
-rw-r--r--scripts/mod/devicetable-offsets.c31
-rw-r--r--scripts/mod/file2alias.c138
-rw-r--r--scripts/mod/list.h213
-rw-r--r--scripts/mod/modpost.c1583
-rw-r--r--scripts/mod/modpost.h110
-rw-r--r--scripts/mod/sumversion.c127
8 files changed, 1120 insertions, 1096 deletions
diff --git a/scripts/mod/.gitignore b/scripts/mod/.gitignore
index 3bd11b603173..0465ec33c9bf 100644
--- a/scripts/mod/.gitignore
+++ b/scripts/mod/.gitignore
@@ -1,4 +1,5 @@
-elfconfig.h
-mk_elfconfig
-modpost
-devicetable-offsets.h
+# SPDX-License-Identifier: GPL-2.0-only
+/devicetable-offsets.h
+/elfconfig.h
+/mk_elfconfig
+/modpost
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index 296b6a3878b2..c9e38ad937fd 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -1,8 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
OBJECT_FILES_NON_STANDARD := y
+CFLAGS_REMOVE_empty.o += $(CC_FLAGS_LTO)
-hostprogs := modpost mk_elfconfig
-always-y := $(hostprogs) empty.o
+hostprogs-always-y += modpost mk_elfconfig
+always-y += empty.o
modpost-objs := modpost.o file2alias.o sumversion.o
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
index 054405b90ba4..c0d3bcb99138 100644
--- a/scripts/mod/devicetable-offsets.c
+++ b/scripts/mod/devicetable-offsets.c
@@ -42,6 +42,7 @@ int main(void)
DEVID_FIELD(pci_device_id, subdevice);
DEVID_FIELD(pci_device_id, class);
DEVID_FIELD(pci_device_id, class_mask);
+ DEVID_FIELD(pci_device_id, override_only);
DEVID(ccw_device_id);
DEVID_FIELD(ccw_device_id, match_flags);
@@ -145,6 +146,13 @@ int main(void)
DEVID(i2c_device_id);
DEVID_FIELD(i2c_device_id, name);
+ DEVID(i3c_device_id);
+ DEVID_FIELD(i3c_device_id, match_flags);
+ DEVID_FIELD(i3c_device_id, dcr);
+ DEVID_FIELD(i3c_device_id, manuf_id);
+ DEVID_FIELD(i3c_device_id, part_id);
+ DEVID_FIELD(i3c_device_id, extra_info);
+
DEVID(spi_device_id);
DEVID_FIELD(spi_device_id, name);
@@ -209,6 +217,8 @@ int main(void)
DEVID(sdw_device_id);
DEVID_FIELD(sdw_device_id, mfg_id);
DEVID_FIELD(sdw_device_id, part_id);
+ DEVID_FIELD(sdw_device_id, sdw_version);
+ DEVID_FIELD(sdw_device_id, class_id);
DEVID(fsl_mc_device_id);
DEVID_FIELD(fsl_mc_device_id, vendor);
@@ -231,5 +241,26 @@ int main(void)
DEVID(wmi_device_id);
DEVID_FIELD(wmi_device_id, guid_string);
+ DEVID(mhi_device_id);
+ DEVID_FIELD(mhi_device_id, chan);
+
+ DEVID(auxiliary_device_id);
+ DEVID_FIELD(auxiliary_device_id, name);
+
+ DEVID(ssam_device_id);
+ DEVID_FIELD(ssam_device_id, match_flags);
+ DEVID_FIELD(ssam_device_id, domain);
+ DEVID_FIELD(ssam_device_id, category);
+ DEVID_FIELD(ssam_device_id, target);
+ DEVID_FIELD(ssam_device_id, instance);
+ DEVID_FIELD(ssam_device_id, function);
+
+ DEVID(dfl_device_id);
+ DEVID_FIELD(dfl_device_id, type);
+ DEVID_FIELD(dfl_device_id, feature_id);
+
+ DEVID(ishtp_device_id);
+ DEVID_FIELD(ishtp_device_id, guid);
+
return 0;
}
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index c91eba751804..80d973144fde 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -115,6 +115,17 @@ static inline void add_uuid(char *str, uuid_le uuid)
uuid.b[12], uuid.b[13], uuid.b[14], uuid.b[15]);
}
+static inline void add_guid(char *str, guid_t guid)
+{
+ int len = strlen(str);
+
+ sprintf(str + len, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ guid.b[3], guid.b[2], guid.b[1], guid.b[0],
+ guid.b[5], guid.b[4], guid.b[7], guid.b[6],
+ guid.b[8], guid.b[9], guid.b[10], guid.b[11],
+ guid.b[12], guid.b[13], guid.b[14], guid.b[15]);
+}
+
/**
* Check that sizeof(device_id type) are consistent with size of section
* in .o file. If in-consistent then userspace and kernel does not agree
@@ -426,7 +437,7 @@ static int do_ieee1394_entry(const char *filename,
return 1;
}
-/* Looks like: pci:vNdNsvNsdNbcNscNiN. */
+/* Looks like: pci:vNdNsvNsdNbcNscNiN or <prefix>_pci:vNdNsvNsdNbcNscNiN. */
static int do_pci_entry(const char *filename,
void *symval, char *alias)
{
@@ -440,8 +451,21 @@ static int do_pci_entry(const char *filename,
DEF_FIELD(symval, pci_device_id, subdevice);
DEF_FIELD(symval, pci_device_id, class);
DEF_FIELD(symval, pci_device_id, class_mask);
+ DEF_FIELD(symval, pci_device_id, override_only);
+
+ switch (override_only) {
+ case 0:
+ strcpy(alias, "pci:");
+ break;
+ case PCI_ID_F_VFIO_DRIVER_OVERRIDE:
+ strcpy(alias, "vfio_pci:");
+ break;
+ default:
+ warn("Unknown PCI driver_override alias %08X\n",
+ override_only);
+ return 0;
+ }
- strcpy(alias, "pci:");
ADD(alias, "v", vendor != PCI_ANY_ID, vendor);
ADD(alias, "d", device != PCI_ANY_ID, device);
ADD(alias, "sv", subvendor != PCI_ANY_ID, subvendor);
@@ -710,8 +734,6 @@ static int do_vio_entry(const char *filename, void *symval,
return 1;
}
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
static void do_input(char *alias,
kernel_ulong_t *arr, unsigned int min, unsigned int max)
{
@@ -919,6 +941,24 @@ static int do_i2c_entry(const char *filename, void *symval,
return 1;
}
+static int do_i3c_entry(const char *filename, void *symval,
+ char *alias)
+{
+ DEF_FIELD(symval, i3c_device_id, match_flags);
+ DEF_FIELD(symval, i3c_device_id, dcr);
+ DEF_FIELD(symval, i3c_device_id, manuf_id);
+ DEF_FIELD(symval, i3c_device_id, part_id);
+ DEF_FIELD(symval, i3c_device_id, extra_info);
+
+ strcpy(alias, "i3c:");
+ ADD(alias, "dcr", match_flags & I3C_MATCH_DCR, dcr);
+ ADD(alias, "manuf", match_flags & I3C_MATCH_MANUF, manuf_id);
+ ADD(alias, "part", match_flags & I3C_MATCH_PART, part_id);
+ ADD(alias, "ext", match_flags & I3C_MATCH_EXTRA_INFO, extra_info);
+
+ return 1;
+}
+
/* Looks like: spi:S */
static int do_spi_entry(const char *filename, void *symval,
char *alias)
@@ -936,6 +976,8 @@ static const struct dmifield {
{ "bvn", DMI_BIOS_VENDOR },
{ "bvr", DMI_BIOS_VERSION },
{ "bd", DMI_BIOS_DATE },
+ { "br", DMI_BIOS_RELEASE },
+ { "efr", DMI_EC_FIRMWARE_RELEASE },
{ "svn", DMI_SYS_VENDOR },
{ "pn", DMI_PRODUCT_NAME },
{ "pvr", DMI_PRODUCT_VERSION },
@@ -1238,15 +1280,19 @@ static int do_hda_entry(const char *filename, void *symval, char *alias)
return 1;
}
-/* Looks like: sdw:mNpN */
+/* Looks like: sdw:mNpNvNcN */
static int do_sdw_entry(const char *filename, void *symval, char *alias)
{
DEF_FIELD(symval, sdw_device_id, mfg_id);
DEF_FIELD(symval, sdw_device_id, part_id);
+ DEF_FIELD(symval, sdw_device_id, sdw_version);
+ DEF_FIELD(symval, sdw_device_id, class_id);
strcpy(alias, "sdw:");
ADD(alias, "m", mfg_id != 0, mfg_id);
ADD(alias, "p", part_id != 0, part_id);
+ ADD(alias, "v", sdw_version != 0, sdw_version);
+ ADD(alias, "c", class_id != 0, class_id);
add_wildcard(alias);
return 1;
@@ -1335,6 +1381,77 @@ static int do_wmi_entry(const char *filename, void *symval, char *alias)
return 1;
}
+/* Looks like: mhi:S */
+static int do_mhi_entry(const char *filename, void *symval, char *alias)
+{
+ DEF_FIELD_ADDR(symval, mhi_device_id, chan);
+ sprintf(alias, MHI_DEVICE_MODALIAS_FMT, *chan);
+ return 1;
+}
+
+/* Looks like: mhi_ep:S */
+static int do_mhi_ep_entry(const char *filename, void *symval, char *alias)
+{
+ DEF_FIELD_ADDR(symval, mhi_device_id, chan);
+ sprintf(alias, MHI_EP_DEVICE_MODALIAS_FMT, *chan);
+
+ return 1;
+}
+
+/* Looks like: ishtp:{guid} */
+static int do_ishtp_entry(const char *filename, void *symval, char *alias)
+{
+ DEF_FIELD(symval, ishtp_device_id, guid);
+
+ strcpy(alias, ISHTP_MODULE_PREFIX "{");
+ add_guid(alias, guid);
+ strcat(alias, "}");
+
+ return 1;
+}
+
+static int do_auxiliary_entry(const char *filename, void *symval, char *alias)
+{
+ DEF_FIELD_ADDR(symval, auxiliary_device_id, name);
+ sprintf(alias, AUXILIARY_MODULE_PREFIX "%s", *name);
+
+ return 1;
+}
+
+/*
+ * Looks like: ssam:dNcNtNiNfN
+ *
+ * N is exactly 2 digits, where each is an upper-case hex digit.
+ */
+static int do_ssam_entry(const char *filename, void *symval, char *alias)
+{
+ DEF_FIELD(symval, ssam_device_id, match_flags);
+ DEF_FIELD(symval, ssam_device_id, domain);
+ DEF_FIELD(symval, ssam_device_id, category);
+ DEF_FIELD(symval, ssam_device_id, target);
+ DEF_FIELD(symval, ssam_device_id, instance);
+ DEF_FIELD(symval, ssam_device_id, function);
+
+ sprintf(alias, "ssam:d%02Xc%02X", domain, category);
+ ADD(alias, "t", match_flags & SSAM_MATCH_TARGET, target);
+ ADD(alias, "i", match_flags & SSAM_MATCH_INSTANCE, instance);
+ ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function);
+
+ return 1;
+}
+
+/* Looks like: dfl:tNfN */
+static int do_dfl_entry(const char *filename, void *symval, char *alias)
+{
+ DEF_FIELD(symval, dfl_device_id, type);
+ DEF_FIELD(symval, dfl_device_id, feature_id);
+
+ sprintf(alias, "dfl:t%04Xf%04X", type, feature_id);
+
+ add_wildcard(alias);
+ return 1;
+}
+
/* Does namelen bytes of name exactly match the symbol? */
static bool sym_is(const char *name, unsigned namelen, const char *symbol)
{
@@ -1386,6 +1503,7 @@ static const struct devtable devtable[] = {
{"vmbus", SIZE_hv_vmbus_device_id, do_vmbus_entry},
{"rpmsg", SIZE_rpmsg_device_id, do_rpmsg_entry},
{"i2c", SIZE_i2c_device_id, do_i2c_entry},
+ {"i3c", SIZE_i3c_device_id, do_i3c_entry},
{"spi", SIZE_spi_device_id, do_spi_entry},
{"dmi", SIZE_dmi_system_id, do_dmi_entry},
{"platform", SIZE_platform_device_id, do_platform_entry},
@@ -1407,6 +1525,12 @@ static const struct devtable devtable[] = {
{"typec", SIZE_typec_device_id, do_typec_entry},
{"tee", SIZE_tee_client_device_id, do_tee_entry},
{"wmi", SIZE_wmi_device_id, do_wmi_entry},
+ {"mhi", SIZE_mhi_device_id, do_mhi_entry},
+ {"mhi_ep", SIZE_mhi_device_id, do_mhi_ep_entry},
+ {"auxiliary", SIZE_auxiliary_device_id, do_auxiliary_entry},
+ {"ssam", SIZE_ssam_device_id, do_ssam_entry},
+ {"dfl", SIZE_dfl_device_id, do_dfl_entry},
+ {"ishtp", SIZE_ishtp_device_id, do_ishtp_entry},
};
/* Create MODULE_ALIAS() statements.
@@ -1447,9 +1571,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
zeros = calloc(1, sym->st_size);
symval = zeros;
} else {
- symval = (void *)info->hdr
- + info->sechdrs[get_secindex(info, sym)].sh_offset
- + sym->st_value;
+ symval = sym_get_data(info, sym);
}
/* First handle the "special" cases */
diff --git a/scripts/mod/list.h b/scripts/mod/list.h
new file mode 100644
index 000000000000..a924a6c4aa4d
--- /dev/null
+++ b/scripts/mod/list.h
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef LIST_H
+#define LIST_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/* Are two types/vars the same type (ignoring qualifiers)? */
+#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ void *__mptr = (void *)(ptr); \
+ _Static_assert(__same_type(*(ptr), ((type *)0)->member) || \
+ __same_type(*(ptr), void), \
+ "pointer type mismatch in container_of()"); \
+ ((type *)(__mptr - offsetof(type, member))); })
+
+#define LIST_POISON1 ((void *) 0x100)
+#define LIST_POISON2 ((void *) 0x122)
+
+/*
+ * Circular doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+/**
+ * INIT_LIST_HEAD - Initialize a list_head structure
+ * @list: list_head structure to be initialized.
+ *
+ * Initializes the list_head to point to itself. If it is a list header,
+ * the result is an empty list.
+ */
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void __list_del_entry(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del_entry(entry);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_is_head - tests whether @list is the list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_head(const struct list_head *list, const struct list_head *head)
+{
+ return list == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/**
+ * list_next_entry - get the next element in list
+ * @pos: the type * to cursor
+ * @member: the name of the list_head within the struct.
+ */
+#define list_next_entry(pos, member) \
+ list_entry((pos)->member.next, typeof(*(pos)), member)
+
+/**
+ * list_entry_is_head - test if the entry points to the head of the list
+ * @pos: the type * to cursor
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_entry_is_head(pos, head, member) \
+ (&pos->member == (head))
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_first_entry(head, typeof(*pos), member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type. Safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_first_entry(head, typeof(*pos), member), \
+ n = list_next_entry(pos, member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = n, n = list_next_entry(n, member))
+
+#endif /* LIST_H */
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 55a0a2eccbd2..2c80da0220c3 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -13,6 +13,7 @@
#define _GNU_SOURCE
#include <elf.h>
+#include <fnmatch.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
@@ -23,27 +24,29 @@
#include "../../include/linux/license.h"
/* Are we using CONFIG_MODVERSIONS? */
-static int modversions = 0;
-/* Warn about undefined symbols? (do so if we have vmlinux) */
-static int have_vmlinux = 0;
+static bool modversions;
/* Is CONFIG_MODULE_SRCVERSION_ALL set? */
-static int all_versions = 0;
+static bool all_versions;
/* If we are modposting external module set to 1 */
-static int external_module = 0;
-/* Warn about section mismatch in vmlinux if set to 1 */
-static int vmlinux_section_warnings = 1;
+static bool external_module;
/* Only warn about unresolved symbols */
-static int warn_unresolved = 0;
-/* How a symbol is exported */
-static int sec_mismatch_count = 0;
-static int sec_mismatch_fatal = 0;
+static bool warn_unresolved;
+
+static int sec_mismatch_count;
+static bool sec_mismatch_warn_only = true;
/* ignore missing files */
-static int ignore_missing_files;
+static bool ignore_missing_files;
+/* If set to 1, only warn (instead of error) about missing ns imports */
+static bool allow_missing_ns_imports;
-enum export {
- export_plain, export_unused, export_gpl,
- export_unused_gpl, export_gpl_future, export_unknown
-};
+static bool error_occurred;
+
+/*
+ * Cut off the warnings when there are too many. This typically occurs when
+ * vmlinux is missing. ('make modules' without building vmlinux.)
+ */
+#define MAX_UNRESOLVED_REPORTS 10
+static unsigned int nr_unresolved;
/* In kernel, this size is defined in linux/module.h;
* here we use Elf_Addr instead of long for covering cross-compile
@@ -51,106 +54,149 @@ enum export {
#define MODULE_NAME_LEN (64 - sizeof(Elf_Addr))
-#define PRINTF __attribute__ ((format (printf, 1, 2)))
-
-PRINTF void fatal(const char *fmt, ...)
+void __attribute__((format(printf, 2, 3)))
+modpost_log(enum loglevel loglevel, const char *fmt, ...)
{
va_list arglist;
- fprintf(stderr, "FATAL: ");
+ switch (loglevel) {
+ case LOG_WARN:
+ fprintf(stderr, "WARNING: ");
+ break;
+ case LOG_ERROR:
+ fprintf(stderr, "ERROR: ");
+ break;
+ case LOG_FATAL:
+ fprintf(stderr, "FATAL: ");
+ break;
+ default: /* invalid loglevel, ignore */
+ break;
+ }
+
+ fprintf(stderr, "modpost: ");
va_start(arglist, fmt);
vfprintf(stderr, fmt, arglist);
va_end(arglist);
- exit(1);
+ if (loglevel == LOG_FATAL)
+ exit(1);
+ if (loglevel == LOG_ERROR)
+ error_occurred = true;
}
-PRINTF void warn(const char *fmt, ...)
+static inline bool strends(const char *str, const char *postfix)
{
- va_list arglist;
-
- fprintf(stderr, "WARNING: ");
+ if (strlen(str) < strlen(postfix))
+ return false;
- va_start(arglist, fmt);
- vfprintf(stderr, fmt, arglist);
- va_end(arglist);
+ return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
}
-PRINTF void merror(const char *fmt, ...)
+void *do_nofail(void *ptr, const char *expr)
{
- va_list arglist;
-
- fprintf(stderr, "ERROR: ");
+ if (!ptr)
+ fatal("Memory allocation failure: %s.\n", expr);
- va_start(arglist, fmt);
- vfprintf(stderr, fmt, arglist);
- va_end(arglist);
+ return ptr;
}
-static inline bool strends(const char *str, const char *postfix)
+char *read_text_file(const char *filename)
{
- if (strlen(str) < strlen(postfix))
- return false;
+ struct stat st;
+ size_t nbytes;
+ int fd;
+ char *buf;
- return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
-}
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror(filename);
+ exit(1);
+ }
-static int is_vmlinux(const char *modname)
-{
- const char *myname;
+ if (fstat(fd, &st) < 0) {
+ perror(filename);
+ exit(1);
+ }
- myname = strrchr(modname, '/');
- if (myname)
- myname++;
- else
- myname = modname;
+ buf = NOFAIL(malloc(st.st_size + 1));
+
+ nbytes = st.st_size;
+
+ while (nbytes) {
+ ssize_t bytes_read;
+
+ bytes_read = read(fd, buf, nbytes);
+ if (bytes_read < 0) {
+ perror(filename);
+ exit(1);
+ }
+
+ nbytes -= bytes_read;
+ }
+ buf[st.st_size] = '\0';
- return (strcmp(myname, "vmlinux") == 0) ||
- (strcmp(myname, "vmlinux.o") == 0);
+ close(fd);
+
+ return buf;
}
-void *do_nofail(void *ptr, const char *expr)
+char *get_line(char **stringp)
{
- if (!ptr)
- fatal("modpost: Memory allocation failure: %s.\n", expr);
+ char *orig = *stringp, *next;
- return ptr;
+ /* do not return the unwanted extra line at EOF */
+ if (!orig || *orig == '\0')
+ return NULL;
+
+ /* don't use strsep here, it is not available everywhere */
+ next = strchr(orig, '\n');
+ if (next)
+ *next++ = '\0';
+
+ *stringp = next;
+
+ return orig;
}
/* A list of all modules we processed */
-static struct module *modules;
+LIST_HEAD(modules);
static struct module *find_module(const char *modname)
{
struct module *mod;
- for (mod = modules; mod; mod = mod->next)
+ list_for_each_entry(mod, &modules, list) {
if (strcmp(mod->name, modname) == 0)
- break;
- return mod;
+ return mod;
+ }
+ return NULL;
}
-static struct module *new_module(const char *modname)
+static struct module *new_module(const char *name, size_t namelen)
{
struct module *mod;
- char *p;
- mod = NOFAIL(malloc(sizeof(*mod)));
+ mod = NOFAIL(malloc(sizeof(*mod) + namelen + 1));
memset(mod, 0, sizeof(*mod));
- p = NOFAIL(strdup(modname));
- /* strip trailing .o */
- if (strends(p, ".o")) {
- p[strlen(p) - 2] = '\0';
- mod->is_dot_o = 1;
- }
+ INIT_LIST_HEAD(&mod->exported_symbols);
+ INIT_LIST_HEAD(&mod->unresolved_symbols);
+ INIT_LIST_HEAD(&mod->missing_namespaces);
+ INIT_LIST_HEAD(&mod->imported_namespaces);
+
+ memcpy(mod->name, name, namelen);
+ mod->name[namelen] = '\0';
+ mod->is_vmlinux = (strcmp(mod->name, "vmlinux") == 0);
- /* add to list */
- mod->name = p;
- mod->gpl_compatible = -1;
- mod->next = modules;
- modules = mod;
+ /*
+ * Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE()
+ * is missing, do not check the use for EXPORT_SYMBOL_GPL() becasue
+ * modpost will exit wiht error anyway.
+ */
+ mod->is_gpl_compatible = true;
+
+ list_add_tail(&mod->list, &modules);
return mod;
}
@@ -162,22 +208,19 @@ static struct module *new_module(const char *modname)
struct symbol {
struct symbol *next;
+ struct list_head list; /* link to module::exported_symbols or module::unresolved_symbols */
struct module *module;
- unsigned int crc;
- int crc_valid;
char *namespace;
- unsigned int weak:1;
- unsigned int vmlinux:1; /* 1 if symbol is defined in vmlinux */
- unsigned int kernel:1; /* 1 if symbol is from kernel
- * (only for external modules) **/
- unsigned int is_static:1; /* 1 if symbol is not global */
- enum export export; /* Type of export */
- char name[0];
+ unsigned int crc;
+ bool crc_valid;
+ bool weak;
+ bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */
+ char name[];
};
static struct symbol *symbolhash[SYMBOL_HASH_SIZE];
-/* This is based on the hash agorithm from gdbm, via tdb */
+/* This is based on the hash algorithm from gdbm, via tdb */
static inline unsigned int tdb_hash(const char *name)
{
unsigned value; /* Used to compute the hash value. */
@@ -194,32 +237,37 @@ static inline unsigned int tdb_hash(const char *name)
* Allocate a new symbols for use in the hash of exported symbols or
* the list of unresolved symbols per module
**/
-static struct symbol *alloc_symbol(const char *name, unsigned int weak,
- struct symbol *next)
+static struct symbol *alloc_symbol(const char *name)
{
struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));
memset(s, 0, sizeof(*s));
strcpy(s->name, name);
- s->weak = weak;
- s->next = next;
- s->is_static = 1;
+
return s;
}
/* For the hash of exported symbols */
-static struct symbol *new_symbol(const char *name, struct module *module,
- enum export export)
+static void hash_add_symbol(struct symbol *sym)
{
unsigned int hash;
- hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
- symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]);
+ hash = tdb_hash(sym->name) % SYMBOL_HASH_SIZE;
+ sym->next = symbolhash[hash];
+ symbolhash[hash] = sym;
+}
+
+static void sym_add_unresolved(const char *name, struct module *mod, bool weak)
+{
+ struct symbol *sym;
+
+ sym = alloc_symbol(name);
+ sym->weak = weak;
- return symbolhash[hash];
+ list_add_tail(&sym->list, &mod->unresolved_symbols);
}
-static struct symbol *find_symbol(const char *name)
+static struct symbol *sym_find_with_module(const char *name, struct module *mod)
{
struct symbol *s;
@@ -228,140 +276,81 @@ static struct symbol *find_symbol(const char *name)
name++;
for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next) {
- if (strcmp(s->name, name) == 0)
+ if (strcmp(s->name, name) == 0 && (!mod || s->module == mod))
return s;
}
return NULL;
}
-static bool contains_namespace(struct namespace_list *list,
- const char *namespace)
+static struct symbol *find_symbol(const char *name)
{
- for (; list; list = list->next)
+ return sym_find_with_module(name, NULL);
+}
+
+struct namespace_list {
+ struct list_head list;
+ char namespace[];
+};
+
+static bool contains_namespace(struct list_head *head, const char *namespace)
+{
+ struct namespace_list *list;
+
+ list_for_each_entry(list, head, list) {
if (!strcmp(list->namespace, namespace))
return true;
+ }
return false;
}
-static void add_namespace(struct namespace_list **list, const char *namespace)
+static void add_namespace(struct list_head *head, const char *namespace)
{
struct namespace_list *ns_entry;
- if (!contains_namespace(*list, namespace)) {
- ns_entry = NOFAIL(malloc(sizeof(struct namespace_list) +
+ if (!contains_namespace(head, namespace)) {
+ ns_entry = NOFAIL(malloc(sizeof(*ns_entry) +
strlen(namespace) + 1));
strcpy(ns_entry->namespace, namespace);
- ns_entry->next = *list;
- *list = ns_entry;
+ list_add_tail(&ns_entry->list, head);
}
}
-static bool module_imports_namespace(struct module *module,
- const char *namespace)
-{
- return contains_namespace(module->imported_namespaces, namespace);
-}
-
-static const struct {
- const char *str;
- enum export export;
-} export_list[] = {
- { .str = "EXPORT_SYMBOL", .export = export_plain },
- { .str = "EXPORT_UNUSED_SYMBOL", .export = export_unused },
- { .str = "EXPORT_SYMBOL_GPL", .export = export_gpl },
- { .str = "EXPORT_UNUSED_SYMBOL_GPL", .export = export_unused_gpl },
- { .str = "EXPORT_SYMBOL_GPL_FUTURE", .export = export_gpl_future },
- { .str = "(unknown)", .export = export_unknown },
-};
-
-
-static const char *export_str(enum export ex)
-{
- return export_list[ex].str;
-}
-
-static enum export export_no(const char *s)
+static void *sym_get_data_by_offset(const struct elf_info *info,
+ unsigned int secindex, unsigned long offset)
{
- int i;
+ Elf_Shdr *sechdr = &info->sechdrs[secindex];
- if (!s)
- return export_unknown;
- for (i = 0; export_list[i].export != export_unknown; i++) {
- if (strcmp(export_list[i].str, s) == 0)
- return export_list[i].export;
- }
- return export_unknown;
+ return (void *)info->hdr + sechdr->sh_offset + offset;
}
-static const char *sech_name(struct elf_info *elf, Elf_Shdr *sechdr)
+void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym)
{
- return (void *)elf->hdr +
- elf->sechdrs[elf->secindex_strings].sh_offset +
- sechdr->sh_name;
+ return sym_get_data_by_offset(info, get_secindex(info, sym),
+ sym->st_value);
}
-static const char *sec_name(struct elf_info *elf, int secindex)
+static const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr)
{
- return sech_name(elf, &elf->sechdrs[secindex]);
+ return sym_get_data_by_offset(info, info->secindex_strings,
+ sechdr->sh_name);
}
-static void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym)
+static const char *sec_name(const struct elf_info *info, unsigned int secindex)
{
- unsigned int secindex = get_secindex(info, sym);
- Elf_Shdr *sechdr = &info->sechdrs[secindex];
- unsigned long offset;
-
- offset = sym->st_value;
- if (info->hdr->e_type != ET_REL)
- offset -= sechdr->sh_addr;
+ /*
+ * If sym->st_shndx is a special section index, there is no
+ * corresponding section header.
+ * Return "" if the index is out of range of info->sechdrs[] array.
+ */
+ if (secindex >= info->num_sections)
+ return "";
- return (void *)info->hdr + sechdr->sh_offset + offset;
+ return sech_name(info, &info->sechdrs[secindex]);
}
#define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0)
-static enum export export_from_secname(struct elf_info *elf, unsigned int sec)
-{
- const char *secname = sec_name(elf, sec);
-
- if (strstarts(secname, "___ksymtab+"))
- return export_plain;
- else if (strstarts(secname, "___ksymtab_unused+"))
- return export_unused;
- else if (strstarts(secname, "___ksymtab_gpl+"))
- return export_gpl;
- else if (strstarts(secname, "___ksymtab_unused_gpl+"))
- return export_unused_gpl;
- else if (strstarts(secname, "___ksymtab_gpl_future+"))
- return export_gpl_future;
- else
- return export_unknown;
-}
-
-static enum export export_from_sec(struct elf_info *elf, unsigned int sec)
-{
- if (sec == elf->export_sec)
- return export_plain;
- else if (sec == elf->export_unused_sec)
- return export_unused;
- else if (sec == elf->export_gpl_sec)
- return export_gpl;
- else if (sec == elf->export_unused_gpl_sec)
- return export_unused_gpl;
- else if (sec == elf->export_gpl_future_sec)
- return export_gpl_future;
- else
- return export_unknown;
-}
-
-static const char *namespace_from_kstrtabns(const struct elf_info *info,
- const Elf_Sym *sym)
-{
- const char *value = sym_get_data(info, sym);
- return value[0] ? value : NULL;
-}
-
static void sym_update_namespace(const char *symname, const char *namespace)
{
struct symbol *s = find_symbol(symname);
@@ -371,58 +360,42 @@ static void sym_update_namespace(const char *symname, const char *namespace)
* actually an assertion.
*/
if (!s) {
- merror("Could not update namespace(%s) for symbol %s\n",
- namespace, symname);
+ error("Could not update namespace(%s) for symbol %s\n",
+ namespace, symname);
return;
}
free(s->namespace);
- s->namespace =
- namespace && namespace[0] ? NOFAIL(strdup(namespace)) : NULL;
+ s->namespace = namespace[0] ? NOFAIL(strdup(namespace)) : NULL;
}
-/**
- * Add an exported symbol - it may have already been added without a
- * CRC, in this case just update the CRC
- **/
static struct symbol *sym_add_exported(const char *name, struct module *mod,
- enum export export)
+ bool gpl_only)
{
struct symbol *s = find_symbol(name);
- if (!s) {
- s = new_symbol(name, mod, export);
- } else if (!external_module || is_vmlinux(s->module->name) ||
- s->module == mod) {
- warn("%s: '%s' exported twice. Previous export was in %s%s\n",
- mod->name, name, s->module->name,
- is_vmlinux(s->module->name) ? "" : ".ko");
- return s;
+ if (s && (!external_module || s->module->is_vmlinux || s->module == mod)) {
+ error("%s: '%s' exported twice. Previous export was in %s%s\n",
+ mod->name, name, s->module->name,
+ s->module->is_vmlinux ? "" : ".ko");
}
+ s = alloc_symbol(name);
s->module = mod;
- s->vmlinux = is_vmlinux(mod->name);
- s->kernel = 0;
- s->export = export;
+ s->is_gpl_only = gpl_only;
+ list_add_tail(&s->list, &mod->exported_symbols);
+ hash_add_symbol(s);
+
return s;
}
-static void sym_set_crc(const char *name, unsigned int crc)
+static void sym_set_crc(struct symbol *sym, unsigned int crc)
{
- struct symbol *s = find_symbol(name);
-
- /*
- * Ignore stand-alone __crc_*, which might be auto-generated symbols
- * such as __*_veneer in ARM ELF.
- */
- if (!s)
- return;
-
- s->crc = crc;
- s->crc_valid = 1;
+ sym->crc = crc;
+ sym->crc_valid = true;
}
-void *grab_file(const char *filename, unsigned long *size)
+static void *grab_file(const char *filename, size_t *size)
{
struct stat st;
void *map = MAP_FAILED;
@@ -444,41 +417,7 @@ failed:
return map;
}
-/**
- * Return a copy of the next line in a mmap'ed file.
- * spaces in the beginning of the line is trimmed away.
- * Return a pointer to a static buffer.
- **/
-char *get_next_line(unsigned long *pos, void *file, unsigned long size)
-{
- static char line[4096];
- int skip = 1;
- size_t len = 0;
- signed char *p = (signed char *)file + *pos;
- char *s = line;
-
- for (; *pos < size ; (*pos)++) {
- if (skip && isspace(*p)) {
- p++;
- continue;
- }
- skip = 0;
- if (*p != '\n' && (*pos < size)) {
- len++;
- *s++ = *p++;
- if (len > 4095)
- break; /* Too long, stop */
- } else {
- /* End of string */
- *s = '\0';
- return line;
- }
- }
- /* End of buffer */
- return NULL;
-}
-
-void release_file(void *file, unsigned long size)
+static void release_file(void *file, size_t size)
{
munmap(file, size);
}
@@ -532,11 +471,14 @@ static int parse_elf(struct elf_info *info, const char *filename)
sechdrs = (void *)hdr + hdr->e_shoff;
info->sechdrs = sechdrs;
+ /* modpost only works for relocatable objects */
+ if (hdr->e_type != ET_REL)
+ fatal("%s: not relocatable object.", filename);
+
/* Check if file offset is correct */
if (hdr->e_shoff > info->size) {
- fatal("section header offset=%lu in file '%s' is bigger than "
- "filesize=%lu\n", (unsigned long)hdr->e_shoff,
- filename, info->size);
+ fatal("section header offset=%lu in file '%s' is bigger than filesize=%zu\n",
+ (unsigned long)hdr->e_shoff, filename, info->size);
return 0;
}
@@ -589,16 +531,7 @@ static int parse_elf(struct elf_info *info, const char *filename)
fatal("%s has NOBITS .modinfo\n", filename);
info->modinfo = (void *)hdr + sechdrs[i].sh_offset;
info->modinfo_len = sechdrs[i].sh_size;
- } else if (strcmp(secname, "__ksymtab") == 0)
- info->export_sec = i;
- else if (strcmp(secname, "__ksymtab_unused") == 0)
- info->export_unused_sec = i;
- else if (strcmp(secname, "__ksymtab_gpl") == 0)
- info->export_gpl_sec = i;
- else if (strcmp(secname, "__ksymtab_unused_gpl") == 0)
- info->export_unused_gpl_sec = i;
- else if (strcmp(secname, "__ksymtab_gpl_future") == 0)
- info->export_gpl_future_sec = i;
+ }
if (sechdrs[i].sh_type == SHT_SYMTAB) {
unsigned int sh_link_idx;
@@ -677,46 +610,18 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname)
strstarts(symname, "_savevr_") ||
strcmp(symname, ".TOC.") == 0)
return 1;
+
+ if (info->hdr->e_machine == EM_S390)
+ /* Expoline thunks are linked on all kernel modules during final link of .ko */
+ if (strstarts(symname, "__s390_indirect_jump_r"))
+ return 1;
/* Do not ignore this symbol */
return 0;
}
-static void handle_modversion(const struct module *mod,
- const struct elf_info *info,
- const Elf_Sym *sym, const char *symname)
-{
- unsigned int crc;
-
- if (sym->st_shndx == SHN_UNDEF) {
- warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n",
- symname, mod->name, is_vmlinux(mod->name) ? "":".ko");
- return;
- }
-
- if (sym->st_shndx == SHN_ABS) {
- crc = sym->st_value;
- } else {
- unsigned int *crcp;
-
- /* symbol points to the CRC in the ELF object */
- crcp = sym_get_data(info, sym);
- crc = TO_NATIVE(*crcp);
- }
- sym_set_crc(symname, crc);
-}
-
static void handle_symbol(struct module *mod, struct elf_info *info,
const Elf_Sym *sym, const char *symname)
{
- enum export export;
- const char *name;
-
- if ((!is_vmlinux(mod->name) || mod->is_dot_o) &&
- strstarts(symname, "__ksymtab"))
- export = export_from_secname(info, get_secindex(info, sym));
- else
- export = export_from_sec(info, get_secindex(info, sym));
-
switch (sym->st_shndx) {
case SHN_COMMON:
if (strstarts(symname, "__gnu_lto_")) {
@@ -744,20 +649,26 @@ static void handle_symbol(struct module *mod, struct elf_info *info,
}
}
- mod->unres = alloc_symbol(symname,
- ELF_ST_BIND(sym->st_info) == STB_WEAK,
- mod->unres);
+ sym_add_unresolved(symname, mod,
+ ELF_ST_BIND(sym->st_info) == STB_WEAK);
break;
default:
/* All exported symbols */
if (strstarts(symname, "__ksymtab_")) {
+ const char *name, *secname;
+
name = symname + strlen("__ksymtab_");
- sym_add_exported(name, mod, export);
+ secname = sec_name(info, get_secindex(info, sym));
+
+ if (strstarts(secname, "___ksymtab_gpl+"))
+ sym_add_exported(name, mod, true);
+ else if (strstarts(secname, "___ksymtab+"))
+ sym_add_exported(name, mod, false);
}
if (strcmp(symname, "init_module") == 0)
- mod->has_init = 1;
+ mod->has_init = true;
if (strcmp(symname, "cleanup_module") == 0)
- mod->has_cleanup = 1;
+ mod->has_cleanup = true;
break;
}
}
@@ -809,29 +720,6 @@ static char *get_modinfo(struct elf_info *info, const char *tag)
return get_next_modinfo(info, tag, NULL);
}
-/**
- * Test if string s ends in string sub
- * return 0 if match
- **/
-static int strrcmp(const char *s, const char *sub)
-{
- int slen, sublen;
-
- if (!s || !sub)
- return 1;
-
- slen = strlen(s);
- sublen = strlen(sub);
-
- if ((slen == 0) || (sublen == 0))
- return 1;
-
- if (sublen > slen)
- return 1;
-
- return memcmp(s + slen - sublen, sub, sublen);
-}
-
static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
{
if (sym)
@@ -840,54 +728,36 @@ static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
return "(unknown)";
}
-/* The pattern is an array of simple patterns.
- * "foo" will match an exact string equal to "foo"
- * "*foo" will match a string that ends with "foo"
- * "foo*" will match a string that begins with "foo"
- * "*foo*" will match a string that contains "foo"
+/*
+ * Check whether the 'string' argument matches one of the 'patterns',
+ * an array of shell wildcard patterns (glob).
+ *
+ * Return true is there is a match.
*/
-static int match(const char *sym, const char * const pat[])
+static bool match(const char *string, const char *const patterns[])
{
- const char *p;
- while (*pat) {
- p = *pat++;
- const char *endp = p + strlen(p) - 1;
+ const char *pattern;
- /* "*foo*" */
- if (*p == '*' && *endp == '*') {
- char *bare = NOFAIL(strndup(p + 1, strlen(p) - 2));
- char *here = strstr(sym, bare);
-
- free(bare);
- if (here != NULL)
- return 1;
- }
- /* "*foo" */
- else if (*p == '*') {
- if (strrcmp(sym, p + 1) == 0)
- return 1;
- }
- /* "foo*" */
- else if (*endp == '*') {
- if (strncmp(sym, p, strlen(p) - 1) == 0)
- return 1;
- }
- /* no wildcards */
- else {
- if (strcmp(p, sym) == 0)
- return 1;
- }
+ while ((pattern = *patterns++)) {
+ if (!fnmatch(pattern, string, 0))
+ return true;
}
- /* no match */
- return 0;
+
+ return false;
}
+/* useful to pass patterns to match() directly */
+#define PATTERNS(...) \
+ ({ \
+ static const char *const patterns[] = {__VA_ARGS__, NULL}; \
+ patterns; \
+ })
+
/* sections that we do not want to do full section mismatch check on */
static const char *const section_white_list[] =
{
".comment*",
".debug*",
- ".cranges", /* sh64 */
".zdebug*", /* Compressed debug sections. */
".GCC.command.line", /* record-gcc-switches */
".mdebug*", /* alpha, score, mips etc. */
@@ -954,10 +824,10 @@ static void check_section(const char *modname, struct elf_info *elf,
#define DATA_SECTIONS ".data", ".data.rel"
#define TEXT_SECTIONS ".text", ".text.unlikely", ".sched.text", \
- ".kprobes.text", ".cpuidle.text"
+ ".kprobes.text", ".cpuidle.text", ".noinstr.text"
#define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \
".fixup", ".entry.text", ".exception.text", ".text.*", \
- ".coldtext"
+ ".coldtext", ".softirqentry.text"
#define INIT_SECTIONS ".init.*"
#define MEM_INIT_SECTIONS ".meminit.*"
@@ -975,28 +845,12 @@ static const char *const init_data_sections[] =
/* all init sections */
static const char *const init_sections[] = { ALL_INIT_SECTIONS, NULL };
-/* All init and exit sections (code + data) */
-static const char *const init_exit_sections[] =
- {ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL };
-
/* all text sections */
static const char *const text_sections[] = { ALL_TEXT_SECTIONS, NULL };
/* data section */
static const char *const data_sections[] = { DATA_SECTIONS, NULL };
-
-/* symbols in .data that may refer to init/exit sections */
-#define DEFAULT_SYMBOL_WHITE_LIST \
- "*driver", \
- "*_template", /* scsi uses *_template a lot */ \
- "*_timer", /* arm uses ops structures named _timer a lot */ \
- "*_sht", /* scsi also used *_sht to some extent */ \
- "*_ops", \
- "*_probe", \
- "*_probe_one", \
- "*_console"
-
static const char *const head_sections[] = { ".head.text*", NULL };
static const char *const linker_symbols[] =
{ "__init_begin", "_sinittext", "_einittext", NULL };
@@ -1016,7 +870,7 @@ enum mismatch {
};
/**
- * Describe how to match sections on different criterias:
+ * Describe how to match sections on different criteria:
*
* @fromsec: Array of sections to be matched.
*
@@ -1024,13 +878,10 @@ enum mismatch {
* this array is forbidden (black-list). Can be empty.
*
* @good_tosec: Relocations applied to a section in @fromsec must be
- * targetting sections in this array (white-list). Can be empty.
+ * targeting sections in this array (white-list). Can be empty.
*
* @mismatch: Type of mismatch.
*
- * @symbol_white_list: Do not match a relocation to a symbol in this list
- * even if it is targetting a section in @bad_to_sec.
- *
* @handler: Specific handler to call when a match is found. If NULL,
* default_mismatch_handler() will be called.
*
@@ -1040,7 +891,6 @@ struct sectioncheck {
const char *bad_tosec[20];
const char *good_tosec[20];
enum mismatch mismatch;
- const char *symbol_white_list[20];
void (*handler)(const char *modname, struct elf_info *elf,
const struct sectioncheck* const mismatch,
Elf_Rela *r, Elf_Sym *sym, const char *fromsec);
@@ -1060,75 +910,61 @@ static const struct sectioncheck sectioncheck[] = {
.fromsec = { TEXT_SECTIONS, NULL },
.bad_tosec = { ALL_INIT_SECTIONS, NULL },
.mismatch = TEXT_TO_ANY_INIT,
- .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
.fromsec = { DATA_SECTIONS, NULL },
.bad_tosec = { ALL_XXXINIT_SECTIONS, NULL },
.mismatch = DATA_TO_ANY_INIT,
- .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
.fromsec = { DATA_SECTIONS, NULL },
.bad_tosec = { INIT_SECTIONS, NULL },
.mismatch = DATA_TO_ANY_INIT,
- .symbol_white_list = {
- "*_template", "*_timer", "*_sht", "*_ops",
- "*_probe", "*_probe_one", "*_console", NULL
- },
},
{
.fromsec = { TEXT_SECTIONS, NULL },
.bad_tosec = { ALL_EXIT_SECTIONS, NULL },
.mismatch = TEXT_TO_ANY_EXIT,
- .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
.fromsec = { DATA_SECTIONS, NULL },
.bad_tosec = { ALL_EXIT_SECTIONS, NULL },
.mismatch = DATA_TO_ANY_EXIT,
- .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
/* Do not reference init code/data from meminit code/data */
{
.fromsec = { ALL_XXXINIT_SECTIONS, NULL },
.bad_tosec = { INIT_SECTIONS, NULL },
.mismatch = XXXINIT_TO_SOME_INIT,
- .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
/* Do not reference exit code/data from memexit code/data */
{
.fromsec = { ALL_XXXEXIT_SECTIONS, NULL },
.bad_tosec = { EXIT_SECTIONS, NULL },
.mismatch = XXXEXIT_TO_SOME_EXIT,
- .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
/* Do not use exit code/data from init code */
{
.fromsec = { ALL_INIT_SECTIONS, NULL },
.bad_tosec = { ALL_EXIT_SECTIONS, NULL },
.mismatch = ANY_INIT_TO_ANY_EXIT,
- .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
/* Do not use init code/data from exit code */
{
.fromsec = { ALL_EXIT_SECTIONS, NULL },
.bad_tosec = { ALL_INIT_SECTIONS, NULL },
.mismatch = ANY_EXIT_TO_ANY_INIT,
- .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
.fromsec = { ALL_PCI_INIT_SECTIONS, NULL },
.bad_tosec = { INIT_SECTIONS, NULL },
.mismatch = ANY_INIT_TO_ANY_EXIT,
- .symbol_white_list = { NULL },
},
/* Do not export init/exit functions or data */
{
- .fromsec = { "__ksymtab*", NULL },
+ .fromsec = { "___ksymtab*", NULL },
.bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL },
.mismatch = EXPORT_TO_INIT_EXIT,
- .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
.fromsec = { "__ex_table", NULL },
@@ -1146,8 +982,6 @@ static const struct sectioncheck *section_mismatch(
const char *fromsec, const char *tosec)
{
int i;
- int elems = sizeof(sectioncheck) / sizeof(struct sectioncheck);
- const struct sectioncheck *check = &sectioncheck[0];
/*
* The target section could be the SHT_NUL section when we're
@@ -1158,14 +992,15 @@ static const struct sectioncheck *section_mismatch(
if (*tosec == '\0')
return NULL;
- for (i = 0; i < elems; i++) {
+ for (i = 0; i < ARRAY_SIZE(sectioncheck); i++) {
+ const struct sectioncheck *check = &sectioncheck[i];
+
if (match(fromsec, check->fromsec)) {
if (check->bad_tosec[0] && match(tosec, check->bad_tosec))
return check;
if (check->good_tosec[0] && !match(tosec, check->good_tosec))
return check;
}
- check++;
}
return NULL;
}
@@ -1190,15 +1025,6 @@ static const struct sectioncheck *section_mismatch(
* fromsec = .data*
* atsym = __param_ops_*
*
- * Pattern 2:
- * Many drivers utilise a *driver container with references to
- * add, remove, probe functions etc.
- * the pattern is identified by:
- * tosec = init or exit section
- * fromsec = data section
- * atsym = *driver, *_template, *_sht, *_ops, *_probe,
- * *probe_one, *_console, *_timer
- *
* Pattern 3:
* Whitelist all references from .head.text to any init section
*
@@ -1247,10 +1073,22 @@ static int secref_whitelist(const struct sectioncheck *mismatch,
strstarts(fromsym, "__param_ops_"))
return 0;
- /* Check for pattern 2 */
- if (match(tosec, init_exit_sections) &&
- match(fromsec, data_sections) &&
- match(fromsym, mismatch->symbol_white_list))
+ /* symbols in data sections that may refer to any init/exit sections */
+ if (match(fromsec, PATTERNS(DATA_SECTIONS)) &&
+ match(tosec, PATTERNS(ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS)) &&
+ match(fromsym, PATTERNS("*_template", // scsi uses *_template a lot
+ "*_timer", // arm uses ops structures named _timer a lot
+ "*_sht", // scsi also used *_sht to some extent
+ "*_ops",
+ "*_probe",
+ "*_probe_one",
+ "*_console")))
+ return 0;
+
+ /* symbols in data sections that may refer to meminit/exit sections */
+ if (match(fromsec, PATTERNS(DATA_SECTIONS)) &&
+ match(tosec, PATTERNS(ALL_XXXINIT_SECTIONS, ALL_EXIT_SECTIONS)) &&
+ match(fromsym, PATTERNS("*driver")))
return 0;
/* Check for pattern 3 */
@@ -1277,7 +1115,8 @@ static int secref_whitelist(const struct sectioncheck *mismatch,
static inline int is_arm_mapping_symbol(const char *str)
{
- return str[0] == '$' && strchr("axtd", str[1])
+ return str[0] == '$' &&
+ (str[1] == 'a' || str[1] == 'd' || str[1] == 't' || str[1] == 'x')
&& (str[2] == '\0' || str[2] == '.');
}
@@ -1367,54 +1206,14 @@ static Elf_Sym *find_elf_symbol2(struct elf_info *elf, Elf_Addr addr,
continue;
if (!is_valid_name(elf, sym))
continue;
- if (sym->st_value <= addr) {
- if ((addr - sym->st_value) < distance) {
- distance = addr - sym->st_value;
- near = sym;
- } else if ((addr - sym->st_value) == distance) {
- near = sym;
- }
+ if (sym->st_value <= addr && addr - sym->st_value <= distance) {
+ distance = addr - sym->st_value;
+ near = sym;
}
}
return near;
}
-/*
- * Convert a section name to the function/data attribute
- * .init.text => __init
- * .memexitconst => __memconst
- * etc.
- *
- * The memory of returned value has been allocated on a heap. The user of this
- * method should free it after usage.
-*/
-static char *sec2annotation(const char *s)
-{
- if (match(s, init_exit_sections)) {
- char *p = NOFAIL(malloc(20));
- char *r = p;
-
- *p++ = '_';
- *p++ = '_';
- if (*s == '.')
- s++;
- while (*s && *s != '.')
- *p++ = *s++;
- *p = '\0';
- if (*s == '.')
- s++;
- if (strstr(s, "rodata") != NULL)
- strcat(p, "const ");
- else if (strstr(s, "data") != NULL)
- strcat(p, "data ");
- else
- strcat(p, " ");
- return r;
- } else {
- return NOFAIL(strdup(""));
- }
-}
-
static int is_function(Elf_Sym *sym)
{
if (sym)
@@ -1423,19 +1222,6 @@ static int is_function(Elf_Sym *sym)
return -1;
}
-static void print_section_list(const char * const list[20])
-{
- const char *const *s = list;
-
- while (*s) {
- fprintf(stderr, "%s", *s);
- s++;
- if (*s)
- fprintf(stderr, ", ");
- }
- fprintf(stderr, "\n");
-}
-
static inline void get_pretty_name(int is_func, const char** name, const char** name_p)
{
switch (is_func) {
@@ -1453,141 +1239,31 @@ static inline void get_pretty_name(int is_func, const char** name, const char**
static void report_sec_mismatch(const char *modname,
const struct sectioncheck *mismatch,
const char *fromsec,
- unsigned long long fromaddr,
const char *fromsym,
- int from_is_func,
- const char *tosec, const char *tosym,
- int to_is_func)
+ const char *tosec, const char *tosym)
{
- const char *from, *from_p;
- const char *to, *to_p;
- char *prl_from;
- char *prl_to;
-
sec_mismatch_count++;
- get_pretty_name(from_is_func, &from, &from_p);
- get_pretty_name(to_is_func, &to, &to_p);
-
- warn("%s(%s+0x%llx): Section mismatch in reference from the %s %s%s "
- "to the %s %s:%s%s\n",
- modname, fromsec, fromaddr, from, fromsym, from_p, to, tosec,
- tosym, to_p);
-
switch (mismatch->mismatch) {
case TEXT_TO_ANY_INIT:
- prl_from = sec2annotation(fromsec);
- prl_to = sec2annotation(tosec);
- fprintf(stderr,
- "The function %s%s() references\n"
- "the %s %s%s%s.\n"
- "This is often because %s lacks a %s\n"
- "annotation or the annotation of %s is wrong.\n",
- prl_from, fromsym,
- to, prl_to, tosym, to_p,
- fromsym, prl_to, tosym);
- free(prl_from);
- free(prl_to);
- break;
- case DATA_TO_ANY_INIT: {
- prl_to = sec2annotation(tosec);
- fprintf(stderr,
- "The variable %s references\n"
- "the %s %s%s%s\n"
- "If the reference is valid then annotate the\n"
- "variable with __init* or __refdata (see linux/init.h) "
- "or name the variable:\n",
- fromsym, to, prl_to, tosym, to_p);
- print_section_list(mismatch->symbol_white_list);
- free(prl_to);
- break;
- }
+ case DATA_TO_ANY_INIT:
case TEXT_TO_ANY_EXIT:
- prl_to = sec2annotation(tosec);
- fprintf(stderr,
- "The function %s() references a %s in an exit section.\n"
- "Often the %s %s%s has valid usage outside the exit section\n"
- "and the fix is to remove the %sannotation of %s.\n",
- fromsym, to, to, tosym, to_p, prl_to, tosym);
- free(prl_to);
- break;
- case DATA_TO_ANY_EXIT: {
- prl_to = sec2annotation(tosec);
- fprintf(stderr,
- "The variable %s references\n"
- "the %s %s%s%s\n"
- "If the reference is valid then annotate the\n"
- "variable with __exit* (see linux/init.h) or "
- "name the variable:\n",
- fromsym, to, prl_to, tosym, to_p);
- print_section_list(mismatch->symbol_white_list);
- free(prl_to);
- break;
- }
+ case DATA_TO_ANY_EXIT:
case XXXINIT_TO_SOME_INIT:
case XXXEXIT_TO_SOME_EXIT:
- prl_from = sec2annotation(fromsec);
- prl_to = sec2annotation(tosec);
- fprintf(stderr,
- "The %s %s%s%s references\n"
- "a %s %s%s%s.\n"
- "If %s is only used by %s then\n"
- "annotate %s with a matching annotation.\n",
- from, prl_from, fromsym, from_p,
- to, prl_to, tosym, to_p,
- tosym, fromsym, tosym);
- free(prl_from);
- free(prl_to);
- break;
case ANY_INIT_TO_ANY_EXIT:
- prl_from = sec2annotation(fromsec);
- prl_to = sec2annotation(tosec);
- fprintf(stderr,
- "The %s %s%s%s references\n"
- "a %s %s%s%s.\n"
- "This is often seen when error handling "
- "in the init function\n"
- "uses functionality in the exit path.\n"
- "The fix is often to remove the %sannotation of\n"
- "%s%s so it may be used outside an exit section.\n",
- from, prl_from, fromsym, from_p,
- to, prl_to, tosym, to_p,
- prl_to, tosym, to_p);
- free(prl_from);
- free(prl_to);
- break;
case ANY_EXIT_TO_ANY_INIT:
- prl_from = sec2annotation(fromsec);
- prl_to = sec2annotation(tosec);
- fprintf(stderr,
- "The %s %s%s%s references\n"
- "a %s %s%s%s.\n"
- "This is often seen when error handling "
- "in the exit function\n"
- "uses functionality in the init path.\n"
- "The fix is often to remove the %sannotation of\n"
- "%s%s so it may be used outside an init section.\n",
- from, prl_from, fromsym, from_p,
- to, prl_to, tosym, to_p,
- prl_to, tosym, to_p);
- free(prl_from);
- free(prl_to);
+ warn("%s: section mismatch in reference: %s (section: %s) -> %s (section: %s)\n",
+ modname, fromsym, fromsec, tosym, tosec);
break;
case EXPORT_TO_INIT_EXIT:
- prl_to = sec2annotation(tosec);
- fprintf(stderr,
- "The symbol %s is exported and annotated %s\n"
- "Fix this by removing the %sannotation of %s "
- "or drop the export.\n",
- tosym, prl_to, prl_to, tosym);
- free(prl_to);
+ warn("%s: EXPORT_SYMBOL used for init/exit symbol: %s (section: %s)\n",
+ modname, tosym, tosec);
break;
case EXTABLE_TO_NON_TEXT:
- fatal("There's a special handler for this mismatch type, "
- "we should never get here.");
+ fatal("There's a special handler for this mismatch type, we should never get here.\n");
break;
}
- fprintf(stderr, "\n");
}
static void default_mismatch_handler(const char *modname, struct elf_info *elf,
@@ -1603,9 +1279,6 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
from = find_elf_symbol2(elf, r->r_offset, fromsec);
fromsym = sym_name(elf, from);
- if (strstarts(fromsym, "reference___initcall"))
- return;
-
tosec = sec_name(elf, get_secindex(elf, sym));
to = find_elf_symbol(elf, r->r_addend, sym);
tosym = sym_name(elf, to);
@@ -1614,9 +1287,7 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
if (secref_whitelist(mismatch,
fromsec, fromsym, tosec, tosym)) {
report_sec_mismatch(modname, mismatch,
- fromsec, r->r_offset, fromsym,
- is_function(from), tosec, tosym,
- is_function(to));
+ fromsec, fromsym, tosec, tosym);
}
}
@@ -1758,11 +1429,7 @@ static void check_section_mismatch(const char *modname, struct elf_info *elf,
static unsigned int *reloc_location(struct elf_info *elf,
Elf_Shdr *sechdr, Elf_Rela *r)
{
- Elf_Shdr *sechdrs = elf->sechdrs;
- int section = sechdr->sh_info;
-
- return (void *)elf->hdr + sechdrs[section].sh_offset +
- r->r_offset;
+ return sym_get_data_by_offset(elf, sechdr->sh_info, r->r_offset);
}
static int addend_386_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
@@ -1776,9 +1443,6 @@ static int addend_386_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
break;
case R_386_PC32:
r->r_addend = TO_NATIVE(*location) + 4;
- /* For CONFIG_RELOCATABLE=y */
- if (elf->hdr->e_type == ET_EXEC)
- r->r_addend += r->r_offset;
break;
}
return 0;
@@ -1851,6 +1515,14 @@ static int addend_mips_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
return 0;
}
+#ifndef EM_RISCV
+#define EM_RISCV 243
+#endif
+
+#ifndef R_RISCV_SUB32
+#define R_RISCV_SUB32 39
+#endif
+
static void section_rela(const char *modname, struct elf_info *elf,
Elf_Shdr *sechdr)
{
@@ -1863,8 +1535,7 @@ static void section_rela(const char *modname, struct elf_info *elf,
Elf_Rela *start = (void *)elf->hdr + sechdr->sh_offset;
Elf_Rela *stop = (void *)start + sechdr->sh_size;
- fromsec = sech_name(elf, sechdr);
- fromsec += strlen(".rela");
+ fromsec = sec_name(elf, sechdr->sh_info);
/* if from section (name) is know good then skip it */
if (match(fromsec, section_white_list))
return;
@@ -1887,6 +1558,13 @@ static void section_rela(const char *modname, struct elf_info *elf,
r_sym = ELF_R_SYM(r.r_info);
#endif
r.r_addend = TO_NATIVE(rela->r_addend);
+ switch (elf->hdr->e_machine) {
+ case EM_RISCV:
+ if (!strcmp("__ex_table", fromsec) &&
+ ELF_R_TYPE(r.r_info) == R_RISCV_SUB32)
+ continue;
+ break;
+ }
sym = elf->symtab_start + r_sym;
/* Skip special sections */
if (is_shndx_special(sym->st_shndx))
@@ -1909,8 +1587,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
Elf_Rel *start = (void *)elf->hdr + sechdr->sh_offset;
Elf_Rel *stop = (void *)start + sechdr->sh_size;
- fromsec = sech_name(elf, sechdr);
- fromsec += strlen(".rel");
+ fromsec = sec_name(elf, sechdr->sh_info);
/* if from section (name) is know good then skip it */
if (match(fromsec, section_white_list))
return;
@@ -1969,8 +1646,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
* to find all references to a section that reference a section that will
* be discarded and warns about it.
**/
-static void check_sec_ref(struct module *mod, const char *modname,
- struct elf_info *elf)
+static void check_sec_ref(const char *modname, struct elf_info *elf)
{
int i;
Elf_Shdr *sechdrs = elf->sechdrs;
@@ -1992,12 +1668,110 @@ static char *remove_dot(char *s)
if (n && s[n]) {
size_t m = strspn(s + n + 1, "0123456789");
- if (m && (s[n + m] == '.' || s[n + m] == 0))
+ if (m && (s[n + m + 1] == '.' || s[n + m + 1] == 0))
s[n] = 0;
}
return s;
}
+/*
+ * The CRCs are recorded in .*.cmd files in the form of:
+ * #SYMVER <name> <crc>
+ */
+static void extract_crcs_for_object(const char *object, struct module *mod)
+{
+ char cmd_file[PATH_MAX];
+ char *buf, *p;
+ const char *base;
+ int dirlen, ret;
+
+ base = strrchr(object, '/');
+ if (base) {
+ base++;
+ dirlen = base - object;
+ } else {
+ dirlen = 0;
+ base = object;
+ }
+
+ ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%s.cmd",
+ dirlen, object, base);
+ if (ret >= sizeof(cmd_file)) {
+ error("%s: too long path was truncated\n", cmd_file);
+ return;
+ }
+
+ buf = read_text_file(cmd_file);
+ p = buf;
+
+ while ((p = strstr(p, "\n#SYMVER "))) {
+ char *name;
+ size_t namelen;
+ unsigned int crc;
+ struct symbol *sym;
+
+ name = p + strlen("\n#SYMVER ");
+
+ p = strchr(name, ' ');
+ if (!p)
+ break;
+
+ namelen = p - name;
+ p++;
+
+ if (!isdigit(*p))
+ continue; /* skip this line */
+
+ crc = strtol(p, &p, 0);
+ if (*p != '\n')
+ continue; /* skip this line */
+
+ name[namelen] = '\0';
+
+ /*
+ * sym_find_with_module() may return NULL here.
+ * It typically occurs when CONFIG_TRIM_UNUSED_KSYMS=y.
+ * Since commit e1327a127703, genksyms calculates CRCs of all
+ * symbols, including trimmed ones. Ignore orphan CRCs.
+ */
+ sym = sym_find_with_module(name, mod);
+ if (sym)
+ sym_set_crc(sym, crc);
+ }
+
+ free(buf);
+}
+
+/*
+ * The symbol versions (CRC) are recorded in the .*.cmd files.
+ * Parse them to retrieve CRCs for the current module.
+ */
+static void mod_set_crcs(struct module *mod)
+{
+ char objlist[PATH_MAX];
+ char *buf, *p, *obj;
+ int ret;
+
+ if (mod->is_vmlinux) {
+ strcpy(objlist, ".vmlinux.objs");
+ } else {
+ /* objects for a module are listed in the *.mod file. */
+ ret = snprintf(objlist, sizeof(objlist), "%s.mod", mod->name);
+ if (ret >= sizeof(objlist)) {
+ error("%s: too long path was truncated\n", objlist);
+ return;
+ }
+ }
+
+ buf = read_text_file(objlist);
+ p = buf;
+
+ while ((obj = strsep(&p, "\n")) && obj[0])
+ extract_crcs_for_object(obj, mod);
+
+ free(buf);
+}
+
static void read_symbols(const char *modname)
{
const char *symname;
@@ -2011,34 +1785,32 @@ static void read_symbols(const char *modname)
if (!parse_elf(&info, modname))
return;
- mod = new_module(modname);
-
- /* When there's no vmlinux, don't print warnings about
- * unresolved symbols (since there'll be too many ;) */
- if (is_vmlinux(modname)) {
- have_vmlinux = 1;
- mod->skip = 1;
- }
-
- license = get_modinfo(&info, "license");
- if (!license && !is_vmlinux(modname))
- warn("modpost: missing MODULE_LICENSE() in %s\n"
- "see include/linux/module.h for "
- "more information\n", modname);
- while (license) {
- if (license_is_gpl_compatible(license))
- mod->gpl_compatible = 1;
- else {
- mod->gpl_compatible = 0;
- break;
- }
- license = get_next_modinfo(&info, "license", license);
+ if (!strends(modname, ".o")) {
+ error("%s: filename must be suffixed with .o\n", modname);
+ return;
}
- namespace = get_modinfo(&info, "import_ns");
- while (namespace) {
- add_namespace(&mod->imported_namespaces, namespace);
- namespace = get_next_modinfo(&info, "import_ns", namespace);
+ /* strip trailing .o */
+ mod = new_module(modname, strlen(modname) - strlen(".o"));
+
+ if (!mod->is_vmlinux) {
+ license = get_modinfo(&info, "license");
+ if (!license)
+ error("missing MODULE_LICENSE() in %s\n", modname);
+ while (license) {
+ if (!license_is_gpl_compatible(license)) {
+ mod->is_gpl_compatible = false;
+ break;
+ }
+ license = get_next_modinfo(&info, "license", license);
+ }
+
+ namespace = get_modinfo(&info, "import_ns");
+ while (namespace) {
+ add_namespace(&mod->imported_namespaces, namespace);
+ namespace = get_next_modinfo(&info, "import_ns",
+ namespace);
+ }
}
for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
@@ -2054,47 +1826,31 @@ static void read_symbols(const char *modname)
/* Apply symbol namespaces from __kstrtabns_<symbol> entries. */
if (strstarts(symname, "__kstrtabns_"))
sym_update_namespace(symname + strlen("__kstrtabns_"),
- namespace_from_kstrtabns(&info,
- sym));
-
- if (strstarts(symname, "__crc_"))
- handle_modversion(mod, &info, sym,
- symname + strlen("__crc_"));
+ sym_get_data(&info, sym));
}
- // check for static EXPORT_SYMBOL_* functions && global vars
- for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
- unsigned char bind = ELF_ST_BIND(sym->st_info);
-
- if (bind == STB_GLOBAL || bind == STB_WEAK) {
- struct symbol *s =
- find_symbol(remove_dot(info.strtab +
- sym->st_name));
+ check_sec_ref(modname, &info);
- if (s)
- s->is_static = 0;
- }
+ if (!mod->is_vmlinux) {
+ version = get_modinfo(&info, "version");
+ if (version || all_versions)
+ get_src_version(mod->name, mod->srcversion,
+ sizeof(mod->srcversion) - 1);
}
- if (!is_vmlinux(modname) || vmlinux_section_warnings)
- check_sec_ref(mod, modname, &info);
-
- version = get_modinfo(&info, "version");
- if (version)
- maybe_frob_rcs_version(modname, version, info.modinfo,
- version - (char *)info.hdr);
- if (version || (all_versions && !is_vmlinux(modname)))
- get_src_version(modname, mod->srcversion,
- sizeof(mod->srcversion)-1);
-
parse_elf_finish(&info);
- /* Our trick to get versioning for module struct etc. - it's
- * never passed as an argument to an exported function, so
- * the automatic versioning doesn't pick it up, but it's really
- * important anyhow */
- if (modversions)
- mod->unres = alloc_symbol("module_layout", 0, mod->unres);
+ if (modversions) {
+ /*
+ * Our trick to get versioning for module struct etc. - it's
+ * never passed as an argument to an exported function, so
+ * the automatic versioning doesn't pick it up, but it's really
+ * important anyhow.
+ */
+ sym_add_unresolved("module_layout", mod, false);
+
+ mod_set_crcs(mod);
+ }
}
static void read_symbols_from_files(const char *filename)
@@ -2147,68 +1903,30 @@ void buf_write(struct buffer *buf, const char *s, int len)
buf->pos += len;
}
-static void check_for_gpl_usage(enum export exp, const char *m, const char *s)
-{
- const char *e = is_vmlinux(m) ?"":".ko";
-
- switch (exp) {
- case export_gpl:
- fatal("modpost: GPL-incompatible module %s%s "
- "uses GPL-only symbol '%s'\n", m, e, s);
- break;
- case export_unused_gpl:
- fatal("modpost: GPL-incompatible module %s%s "
- "uses GPL-only symbol marked UNUSED '%s'\n", m, e, s);
- break;
- case export_gpl_future:
- warn("modpost: GPL-incompatible module %s%s "
- "uses future GPL-only symbol '%s'\n", m, e, s);
- break;
- case export_plain:
- case export_unused:
- case export_unknown:
- /* ignore */
- break;
- }
-}
-
-static void check_for_unused(enum export exp, const char *m, const char *s)
-{
- const char *e = is_vmlinux(m) ?"":".ko";
-
- switch (exp) {
- case export_unused:
- case export_unused_gpl:
- warn("modpost: module %s%s "
- "uses symbol '%s' marked UNUSED\n", m, e, s);
- break;
- default:
- /* ignore */
- break;
- }
-}
-
-static int check_exports(struct module *mod)
+static void check_exports(struct module *mod)
{
struct symbol *s, *exp;
- int err = 0;
- for (s = mod->unres; s; s = s->next) {
+ list_for_each_entry(s, &mod->unresolved_symbols, list) {
const char *basename;
exp = find_symbol(s->name);
- if (!exp || exp->module == mod) {
- if (have_vmlinux && !s->weak) {
- 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;
- }
- }
+ if (!exp) {
+ if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS)
+ modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR,
+ "\"%s\" [%s.ko] undefined!\n",
+ s->name, mod->name);
continue;
}
+ if (exp->module == mod) {
+ error("\"%s\" [%s.ko] was exported without definition\n",
+ s->name, mod->name);
+ continue;
+ }
+
+ s->module = exp->module;
+ s->crc_valid = exp->crc_valid;
+ s->crc = exp->crc;
+
basename = strrchr(mod->name, '/');
if (basename)
basename++;
@@ -2216,21 +1934,20 @@ static int check_exports(struct module *mod)
basename = mod->name;
if (exp->namespace &&
- !module_imports_namespace(mod, exp->namespace)) {
- warn("module %s uses symbol %s from namespace %s, but does not import it.\n",
- basename, exp->name, exp->namespace);
+ !contains_namespace(&mod->imported_namespaces, exp->namespace)) {
+ modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR,
+ "module %s uses symbol %s from namespace %s, but does not import it.\n",
+ basename, exp->name, exp->namespace);
add_namespace(&mod->missing_namespaces, exp->namespace);
}
- if (!mod->gpl_compatible)
- check_for_gpl_usage(exp->export, basename, exp->name);
- check_for_unused(exp->export, basename, exp->name);
+ if (!mod->is_gpl_compatible && exp->is_gpl_only)
+ error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n",
+ basename, exp->name);
}
-
- return err;
}
-static int check_modname_len(struct module *mod)
+static void check_modname_len(struct module *mod)
{
const char *mod_name;
@@ -2239,12 +1956,8 @@ static int check_modname_len(struct module *mod)
mod_name = mod->name;
else
mod_name++;
- if (strlen(mod_name) >= MODULE_NAME_LEN) {
- merror("module name is too long [%s.ko]\n", mod->name);
- return 1;
- }
-
- return 0;
+ if (strlen(mod_name) >= MODULE_NAME_LEN)
+ error("module name is too long [%s.ko]\n", mod->name);
}
/**
@@ -2252,18 +1965,26 @@ static int check_modname_len(struct module *mod)
**/
static void add_header(struct buffer *b, struct module *mod)
{
- buf_printf(b, "#include <linux/build-salt.h>\n");
buf_printf(b, "#include <linux/module.h>\n");
+ /*
+ * Include build-salt.h after module.h in order to
+ * inherit the definitions.
+ */
+ buf_printf(b, "#define INCLUDE_VERMAGIC\n");
+ buf_printf(b, "#include <linux/build-salt.h>\n");
+ buf_printf(b, "#include <linux/elfnote-lto.h>\n");
+ buf_printf(b, "#include <linux/export-internal.h>\n");
buf_printf(b, "#include <linux/vermagic.h>\n");
buf_printf(b, "#include <linux/compiler.h>\n");
buf_printf(b, "\n");
buf_printf(b, "BUILD_SALT;\n");
+ buf_printf(b, "BUILD_LTO_INFO;\n");
buf_printf(b, "\n");
buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n");
buf_printf(b, "\n");
buf_printf(b, "__visible struct module __this_module\n");
- buf_printf(b, "__section(.gnu.linkonce.this_module) = {\n");
+ buf_printf(b, "__section(\".gnu.linkonce.this_module\") = {\n");
buf_printf(b, "\t.name = KBUILD_MODNAME,\n");
if (mod->has_init)
buf_printf(b, "\t.init = init_module,\n");
@@ -2273,53 +1994,59 @@ static void add_header(struct buffer *b, struct module *mod)
"#endif\n");
buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n");
buf_printf(b, "};\n");
-}
-static void add_intree_flag(struct buffer *b, int is_intree)
-{
- if (is_intree)
+ if (!external_module)
buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
-}
-/* Cannot check for assembler */
-static void add_retpoline(struct buffer *b)
-{
- buf_printf(b, "\n#ifdef CONFIG_RETPOLINE\n");
- buf_printf(b, "MODULE_INFO(retpoline, \"Y\");\n");
- buf_printf(b, "#endif\n");
+ buf_printf(b,
+ "\n"
+ "#ifdef CONFIG_RETPOLINE\n"
+ "MODULE_INFO(retpoline, \"Y\");\n"
+ "#endif\n");
+
+ if (strstarts(mod->name, "drivers/staging"))
+ buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
+
+ if (strstarts(mod->name, "tools/testing"))
+ buf_printf(b, "\nMODULE_INFO(test, \"Y\");\n");
}
-static void add_staging_flag(struct buffer *b, const char *name)
+static void add_exported_symbols(struct buffer *buf, struct module *mod)
{
- if (strstarts(name, "drivers/staging"))
- buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
+ struct symbol *sym;
+
+ if (!modversions)
+ return;
+
+ /* record CRCs for exported symbols */
+ buf_printf(buf, "\n");
+ list_for_each_entry(sym, &mod->exported_symbols, list) {
+ if (!sym->crc_valid)
+ warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n"
+ "Is \"%s\" prototyped in <asm/asm-prototypes.h>?\n",
+ sym->name, mod->name, mod->is_vmlinux ? "" : ".ko",
+ sym->name);
+
+ buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x, \"%s\");\n",
+ sym->name, sym->crc, sym->is_gpl_only ? "_gpl" : "");
+ }
}
/**
* Record CRCs for unresolved symbols
**/
-static int add_versions(struct buffer *b, struct module *mod)
+static void add_versions(struct buffer *b, struct module *mod)
{
- struct symbol *s, *exp;
- int err = 0;
-
- for (s = mod->unres; s; s = s->next) {
- exp = find_symbol(s->name);
- if (!exp || exp->module == mod)
- continue;
- s->module = exp->module;
- s->crc_valid = exp->crc_valid;
- s->crc = exp->crc;
- }
+ struct symbol *s;
if (!modversions)
- return err;
+ return;
buf_printf(b, "\n");
buf_printf(b, "static const struct modversion_info ____versions[]\n");
- buf_printf(b, "__used __section(__versions) = {\n");
+ buf_printf(b, "__used __section(\"__versions\") = {\n");
- for (s = mod->unres; s; s = s->next) {
+ list_for_each_entry(s, &mod->unresolved_symbols, list) {
if (!s->module)
continue;
if (!s->crc_valid) {
@@ -2328,9 +2055,8 @@ static int add_versions(struct buffer *b, struct module *mod)
continue;
}
if (strlen(s->name) >= MODULE_NAME_LEN) {
- merror("too long symbol \"%s\" [%s.ko]\n",
- s->name, mod->name);
- err = 1;
+ error("too long symbol \"%s\" [%s.ko]\n",
+ s->name, mod->name);
break;
}
buf_printf(b, "\t{ %#8x, \"%s\" },\n",
@@ -2338,8 +2064,6 @@ static int add_versions(struct buffer *b, struct module *mod)
}
buf_printf(b, "};\n");
-
- return err;
}
static void add_depends(struct buffer *b, struct module *mod)
@@ -2348,13 +2072,14 @@ static void add_depends(struct buffer *b, struct module *mod)
int first = 1;
/* Clear ->seen flag of modules that own symbols needed by this. */
- for (s = mod->unres; s; s = s->next)
+ list_for_each_entry(s, &mod->unresolved_symbols, list) {
if (s->module)
- s->module->seen = is_vmlinux(s->module->name);
+ s->module->seen = s->module->is_vmlinux;
+ }
buf_printf(b, "\n");
buf_printf(b, "MODULE_INFO(depends, \"");
- for (s = mod->unres; s; s = s->next) {
+ list_for_each_entry(s, &mod->unresolved_symbols, list) {
const char *p;
if (!s->module)
continue;
@@ -2362,7 +2087,7 @@ static void add_depends(struct buffer *b, struct module *mod)
if (s->module->seen)
continue;
- s->module->seen = 1;
+ s->module->seen = true;
p = strrchr(s->module->name, '/');
if (p)
p++;
@@ -2383,6 +2108,28 @@ static void add_srcversion(struct buffer *b, struct module *mod)
}
}
+static void write_buf(struct buffer *b, const char *fname)
+{
+ FILE *file;
+
+ if (error_occurred)
+ return;
+
+ file = fopen(fname, "w");
+ if (!file) {
+ perror(fname);
+ exit(1);
+ }
+ if (fwrite(b->p, 1, b->pos, file) != b->pos) {
+ perror(fname);
+ exit(1);
+ }
+ if (fclose(file) != 0) {
+ perror(fname);
+ exit(1);
+ }
+}
+
static void write_if_changed(struct buffer *b, const char *fname)
{
char *tmp;
@@ -2415,36 +2162,70 @@ static void write_if_changed(struct buffer *b, const char *fname)
close_write:
fclose(file);
write:
- file = fopen(fname, "w");
- if (!file) {
- perror(fname);
- exit(1);
- }
- if (fwrite(b->p, 1, b->pos, file) != b->pos) {
- perror(fname);
- exit(1);
+ write_buf(b, fname);
+}
+
+static void write_vmlinux_export_c_file(struct module *mod)
+{
+ struct buffer buf = { };
+
+ buf_printf(&buf,
+ "#include <linux/export-internal.h>\n");
+
+ add_exported_symbols(&buf, mod);
+ write_if_changed(&buf, ".vmlinux.export.c");
+ free(buf.p);
+}
+
+/* do sanity checks, and generate *.mod.c file */
+static void write_mod_c_file(struct module *mod)
+{
+ struct buffer buf = { };
+ char fname[PATH_MAX];
+ int ret;
+
+ check_modname_len(mod);
+ check_exports(mod);
+
+ add_header(&buf, mod);
+ add_exported_symbols(&buf, mod);
+ add_versions(&buf, mod);
+ add_depends(&buf, mod);
+ add_moddevtable(&buf, mod);
+ add_srcversion(&buf, mod);
+
+ ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name);
+ if (ret >= sizeof(fname)) {
+ error("%s: too long path was truncated\n", fname);
+ goto free;
}
- fclose(file);
+
+ write_if_changed(&buf, fname);
+
+free:
+ free(buf.p);
}
/* parse Module.symvers file. line format:
* 0x12345678<tab>symbol<tab>module<tab>export<tab>namespace
**/
-static void read_dump(const char *fname, unsigned int kernel)
+static void read_dump(const char *fname)
{
- unsigned long size, pos = 0;
- void *file = grab_file(fname, &size);
- char *line;
+ char *buf, *pos, *line;
- if (!file)
+ buf = read_text_file(fname);
+ if (!buf)
/* No symbol versions, silently ignore */
return;
- while ((line = get_next_line(&pos, file, size))) {
+ pos = buf;
+
+ while ((line = get_line(&pos))) {
char *symname, *namespace, *modname, *d, *export;
unsigned int crc;
struct module *mod;
struct symbol *s;
+ bool gpl_only;
if (!(symname = strchr(line, '\t')))
goto fail;
@@ -2462,61 +2243,49 @@ static void read_dump(const char *fname, unsigned int kernel)
crc = strtoul(line, &d, 16);
if (*symname == '\0' || *modname == '\0' || *d != '\0')
goto fail;
+
+ if (!strcmp(export, "EXPORT_SYMBOL_GPL")) {
+ gpl_only = true;
+ } else if (!strcmp(export, "EXPORT_SYMBOL")) {
+ gpl_only = false;
+ } else {
+ error("%s: unknown license %s. skip", symname, export);
+ continue;
+ }
+
mod = find_module(modname);
if (!mod) {
- if (is_vmlinux(modname))
- have_vmlinux = 1;
- mod = new_module(modname);
- mod->skip = 1;
+ mod = new_module(modname, strlen(modname));
+ mod->from_dump = true;
}
- s = sym_add_exported(symname, mod, export_no(export));
- s->kernel = kernel;
- s->is_static = 0;
- sym_set_crc(symname, crc);
+ s = sym_add_exported(symname, mod, gpl_only);
+ sym_set_crc(s, crc);
sym_update_namespace(symname, namespace);
}
- release_file(file, size);
+ free(buf);
return;
fail:
- release_file(file, size);
+ free(buf);
fatal("parse error in symbol dump file\n");
}
-/* For normal builds always dump all symbols.
- * For external modules only dump symbols
- * that are not read from kernel Module.symvers.
- **/
-static int dump_sym(struct symbol *sym)
-{
- if (!external_module)
- return 1;
- if (sym->vmlinux || sym->kernel)
- return 0;
- return 1;
-}
-
static void write_dump(const char *fname)
{
struct buffer buf = { };
- struct symbol *symbol;
- const char *namespace;
- int n;
-
- for (n = 0; n < SYMBOL_HASH_SIZE ; n++) {
- symbol = symbolhash[n];
- while (symbol) {
- if (dump_sym(symbol)) {
- namespace = symbol->namespace;
- buf_printf(&buf, "0x%08x\t%s\t%s\t%s\t%s\n",
- symbol->crc, symbol->name,
- symbol->module->name,
- export_str(symbol->export),
- namespace ? namespace : "");
- }
- symbol = symbol->next;
+ struct module *mod;
+ struct symbol *sym;
+
+ list_for_each_entry(mod, &modules, list) {
+ if (mod->from_dump)
+ continue;
+ list_for_each_entry(sym, &mod->exported_symbols, list) {
+ buf_printf(&buf, "0x%08x\t%s\t%s\tEXPORT_SYMBOL%s\t%s\n",
+ sym->crc, sym->name, mod->name,
+ sym->is_gpl_only ? "_GPL" : "",
+ sym->namespace ?: "");
}
}
- write_if_changed(&buf, fname);
+ write_buf(&buf, fname);
free(buf.p);
}
@@ -2526,14 +2295,14 @@ static void write_namespace_deps_files(const char *fname)
struct namespace_list *ns;
struct buffer ns_deps_buf = {};
- for (mod = modules; mod; mod = mod->next) {
+ list_for_each_entry(mod, &modules, list) {
- if (mod->skip || !mod->missing_namespaces)
+ if (mod->from_dump || list_empty(&mod->missing_namespaces))
continue;
buf_printf(&ns_deps_buf, "%s.ko:", mod->name);
- for (ns = mod->missing_namespaces; ns; ns = ns->next)
+ list_for_each_entry(ns, &mod->missing_namespaces, list)
buf_printf(&ns_deps_buf, " %s", ns->namespace);
buf_printf(&ns_deps_buf, "\n");
@@ -2543,61 +2312,53 @@ static void write_namespace_deps_files(const char *fname)
free(ns_deps_buf.p);
}
-struct ext_sym_list {
- struct ext_sym_list *next;
+struct dump_list {
+ struct list_head list;
const char *file;
};
int main(int argc, char **argv)
{
struct module *mod;
- struct buffer buf = { };
- char *kernel_read = NULL;
char *missing_namespace_deps = NULL;
char *dump_write = NULL, *files_source = NULL;
int opt;
- int err;
- int n;
- struct ext_sym_list *extsym_iter;
- struct ext_sym_list *extsym_start = NULL;
+ LIST_HEAD(dump_lists);
+ struct dump_list *dl, *dl2;
- while ((opt = getopt(argc, argv, "i:e:mnsT:o:awEd:")) != -1) {
+ while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:")) != -1) {
switch (opt) {
- case 'i':
- kernel_read = optarg;
- external_module = 1;
- break;
case 'e':
- external_module = 1;
- extsym_iter =
- NOFAIL(malloc(sizeof(*extsym_iter)));
- extsym_iter->next = extsym_start;
- extsym_iter->file = optarg;
- extsym_start = extsym_iter;
+ external_module = true;
+ break;
+ case 'i':
+ dl = NOFAIL(malloc(sizeof(*dl)));
+ dl->file = optarg;
+ list_add_tail(&dl->list, &dump_lists);
break;
case 'm':
- modversions = 1;
+ modversions = true;
break;
case 'n':
- ignore_missing_files = 1;
+ ignore_missing_files = true;
break;
case 'o':
dump_write = optarg;
break;
case 'a':
- all_versions = 1;
- break;
- case 's':
- vmlinux_section_warnings = 0;
+ all_versions = true;
break;
case 'T':
files_source = optarg;
break;
case 'w':
- warn_unresolved = 1;
+ warn_unresolved = true;
break;
case 'E':
- sec_mismatch_fatal = 1;
+ sec_mismatch_warn_only = false;
+ break;
+ case 'N':
+ allow_missing_ns_imports = true;
break;
case 'd':
missing_namespace_deps = optarg;
@@ -2607,13 +2368,10 @@ int main(int argc, char **argv)
}
}
- if (kernel_read)
- read_dump(kernel_read, 1);
- while (extsym_start) {
- read_dump(extsym_start->file, 0);
- extsym_iter = extsym_start->next;
- free(extsym_start);
- extsym_start = extsym_iter;
+ list_for_each_entry_safe(dl, dl2, &dump_lists, list) {
+ read_dump(dl->file);
+ list_del(&dl->list);
+ free(dl);
}
while (optind < argc)
@@ -2622,30 +2380,14 @@ int main(int argc, char **argv)
if (files_source)
read_symbols_from_files(files_source);
- err = 0;
-
- for (mod = modules; mod; mod = mod->next) {
- char fname[PATH_MAX];
-
- if (mod->skip)
+ list_for_each_entry(mod, &modules, list) {
+ if (mod->from_dump)
continue;
- buf.pos = 0;
-
- err |= check_modname_len(mod);
- err |= check_exports(mod);
-
- add_header(&buf, mod);
- add_intree_flag(&buf, !external_module);
- add_retpoline(&buf);
- add_staging_flag(&buf, mod->name);
- err |= add_versions(&buf, mod);
- add_depends(&buf, mod);
- add_moddevtable(&buf, mod);
- add_srcversion(&buf, mod);
-
- sprintf(fname, "%s.mod.c", mod->name);
- write_if_changed(&buf, fname);
+ if (mod->is_vmlinux)
+ write_vmlinux_export_c_file(mod);
+ else
+ write_mod_c_file(mod);
}
if (missing_namespace_deps)
@@ -2653,28 +2395,13 @@ int main(int argc, char **argv)
if (dump_write)
write_dump(dump_write);
- if (sec_mismatch_count && sec_mismatch_fatal)
- fatal("modpost: Section mismatches detected.\n"
+ if (sec_mismatch_count && !sec_mismatch_warn_only)
+ error("Section mismatches detected.\n"
"Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n");
- for (n = 0; n < SYMBOL_HASH_SIZE; n++) {
- struct symbol *s;
- for (s = symbolhash[n]; s; s = s->next) {
- /*
- * Do not check "vmlinux". This avoids the same warnings
- * shown twice, and false-positives for ARCH=um.
- */
- if (is_vmlinux(s->module->name) && !s->module->is_dot_o)
- continue;
-
- if (s->is_static)
- warn("\"%s\" [%s] is a static %s\n",
- s->name, s->module->name,
- export_str(s->export));
- }
- }
-
- free(buf.p);
+ if (nr_unresolved > MAX_UNRESOLVED_REPORTS)
+ warn("suppressed %u unresolved symbol warnings because there were too many)\n",
+ nr_unresolved - MAX_UNRESOLVED_REPORTS);
- return err;
+ return error_occurred ? 1 : 0;
}
diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
index 64a82d2d85f6..1178f40a73f3 100644
--- a/scripts/mod/modpost.h
+++ b/scripts/mod/modpost.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -10,6 +11,7 @@
#include <unistd.h>
#include <elf.h>
+#include "list.h"
#include "elfconfig.h"
/* On BSD-alike OSes elf.h defines these according to host's word size */
@@ -24,7 +26,6 @@
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Addr Elf32_Addr
-#define Elf_Sword Elf64_Sword
#define Elf_Section Elf32_Half
#define ELF_ST_BIND ELF32_ST_BIND
#define ELF_ST_TYPE ELF32_ST_TYPE
@@ -39,7 +40,6 @@
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Addr Elf64_Addr
-#define Elf_Sword Elf64_Sxword
#define Elf_Section Elf64_Half
#define ELF_ST_BIND ELF64_ST_BIND
#define ELF_ST_TYPE ELF64_ST_TYPE
@@ -95,6 +95,9 @@ static inline void __endian(const void *src, void *dest, unsigned int size)
#endif
#define NOFAIL(ptr) do_nofail((ptr), #ptr)
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
void *do_nofail(void *ptr, const char *expr);
struct buffer {
@@ -109,40 +112,31 @@ buf_printf(struct buffer *buf, const char *fmt, ...);
void
buf_write(struct buffer *buf, const char *s, int len);
-struct namespace_list {
- struct namespace_list *next;
- char namespace[0];
-};
-
struct module {
- struct module *next;
- const char *name;
- int gpl_compatible;
- struct symbol *unres;
- int seen;
- int skip;
- int has_init;
- int has_cleanup;
+ struct list_head list;
+ struct list_head exported_symbols;
+ struct list_head unresolved_symbols;
+ bool is_gpl_compatible;
+ bool from_dump; /* true if module was loaded from *.symvers */
+ bool is_vmlinux;
+ bool seen;
+ bool has_init;
+ bool has_cleanup;
struct buffer dev_table_buf;
char srcversion[25];
- int is_dot_o;
// Missing namespace dependencies
- struct namespace_list *missing_namespaces;
+ struct list_head missing_namespaces;
// Actual imported namespaces
- struct namespace_list *imported_namespaces;
+ struct list_head imported_namespaces;
+ char name[];
};
struct elf_info {
- unsigned long size;
+ size_t size;
Elf_Ehdr *hdr;
Elf_Shdr *sechdrs;
Elf_Sym *symtab_start;
Elf_Sym *symtab_stop;
- Elf_Section export_sec;
- Elf_Section export_unused_sec;
- Elf_Section export_gpl_sec;
- Elf_Section export_unused_gpl_sec;
- Elf_Section export_gpl_future_sec;
char *strtab;
char *modinfo;
unsigned int modinfo_len;
@@ -162,42 +156,64 @@ static inline int is_shndx_special(unsigned int i)
return i != SHN_XINDEX && i >= SHN_LORESERVE && i <= SHN_HIRESERVE;
}
-/*
- * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
- * the way to -256..-1, to avoid conflicting with real section
- * indices.
- */
-#define SPECIAL(i) ((i) - (SHN_HIRESERVE + 1))
-
/* Accessor for sym->st_shndx, hides ugliness of "64k sections" */
static inline unsigned int get_secindex(const struct elf_info *info,
const Elf_Sym *sym)
{
- if (is_shndx_special(sym->st_shndx))
- return SPECIAL(sym->st_shndx);
- if (sym->st_shndx != SHN_XINDEX)
- return sym->st_shndx;
- return info->symtab_shndx_start[sym - info->symtab_start];
+ unsigned int index = sym->st_shndx;
+
+ /*
+ * Elf{32,64}_Sym::st_shndx is 2 byte. Big section numbers are available
+ * in the .symtab_shndx section.
+ */
+ if (index == SHN_XINDEX)
+ return info->symtab_shndx_start[sym - info->symtab_start];
+
+ /*
+ * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
+ * the way to UINT_MAX-255..UINT_MAX, to avoid conflicting with real
+ * section indices.
+ */
+ if (index >= SHN_LORESERVE && index <= SHN_HIRESERVE)
+ return index - SHN_HIRESERVE - 1;
+
+ return index;
}
/* file2alias.c */
-extern unsigned int cross_build;
void handle_moddevtable(struct module *mod, struct elf_info *info,
Elf_Sym *sym, const char *symname);
void add_moddevtable(struct buffer *buf, struct module *mod);
/* sumversion.c */
-void maybe_frob_rcs_version(const char *modfilename,
- char *version,
- void *modinfo,
- unsigned long modinfo_offset);
void get_src_version(const char *modname, char sum[], unsigned sumlen);
/* from modpost.c */
-void *grab_file(const char *filename, unsigned long *size);
-char* get_next_line(unsigned long *pos, void *file, unsigned long size);
-void release_file(void *file, unsigned long size);
+char *read_text_file(const char *filename);
+char *get_line(char **stringp);
+void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym);
+
+enum loglevel {
+ LOG_WARN,
+ LOG_ERROR,
+ LOG_FATAL
+};
+
+void modpost_log(enum loglevel loglevel, const char *fmt, ...);
-void fatal(const char *fmt, ...);
-void warn(const char *fmt, ...);
-void merror(const char *fmt, ...);
+/*
+ * warn - show the given message, then let modpost continue running, still
+ * allowing modpost to exit successfully. This should be used when
+ * we still allow to generate vmlinux and modules.
+ *
+ * error - show the given message, then let modpost continue running, but fail
+ * in the end. This should be used when we should stop building vmlinux
+ * or modules, but we can continue running modpost to catch as many
+ * issues as possible.
+ *
+ * fatal - show the given message, and bail out immediately. This should be
+ * used when there is no point to continue running modpost.
+ */
+#define warn(fmt, args...) modpost_log(LOG_WARN, fmt, ##args)
+#define error(fmt, args...) modpost_log(LOG_ERROR, fmt, ##args)
+#define fatal(fmt, args...) modpost_log(LOG_FATAL, fmt, ##args)
diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
index 63062024ce0e..6bf9caca0968 100644
--- a/scripts/mod/sumversion.c
+++ b/scripts/mod/sumversion.c
@@ -258,9 +258,8 @@ static int parse_file(const char *fname, struct md4_ctx *md)
char *file;
unsigned long i, len;
- file = grab_file(fname, &len);
- if (!file)
- return 0;
+ file = read_text_file(fname);
+ len = strlen(file);
for (i = 0; i < len; i++) {
/* Collapse and ignore \ and CR. */
@@ -287,26 +286,23 @@ static int parse_file(const char *fname, struct md4_ctx *md)
add_char(file[i], md);
}
- release_file(file, len);
+ free(file);
return 1;
}
/* Check whether the file is a static library or not */
-static int is_static_library(const char *objfile)
+static bool is_static_library(const char *objfile)
{
int len = strlen(objfile);
- if (objfile[len - 2] == '.' && objfile[len - 1] == 'a')
- return 1;
- else
- return 0;
+
+ return objfile[len - 2] == '.' && objfile[len - 1] == 'a';
}
/* We have dir/file.o. Open dir/.file.o.cmd, look for source_ and deps_ line
* to figure out source files. */
static int parse_source_files(const char *objfile, struct md4_ctx *md)
{
- char *cmd, *file, *line, *dir;
+ char *cmd, *file, *line, *dir, *pos;
const char *base;
- unsigned long flen, pos = 0;
int dirlen, ret = 0, check_files = 0;
cmd = NOFAIL(malloc(strlen(objfile) + sizeof("..cmd")));
@@ -324,14 +320,12 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
strncpy(dir, objfile, dirlen);
dir[dirlen] = '\0';
- file = grab_file(cmd, &flen);
- if (!file) {
- warn("could not find %s for %s\n", cmd, objfile);
- goto out;
- }
+ file = read_text_file(cmd);
+
+ pos = file;
/* Sum all files in the same dir or subdirs. */
- while ((line = get_next_line(&pos, file, flen)) != NULL) {
+ while ((line = get_line(&pos))) {
char* p = line;
if (strncmp(line, "source_", sizeof("source_")-1) == 0) {
@@ -382,8 +376,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
/* Everyone parsed OK */
ret = 1;
out_file:
- release_file(file, flen);
-out:
+ free(file);
free(dir);
free(cmd);
return ret;
@@ -392,106 +385,26 @@ out:
/* Calc and record src checksum. */
void get_src_version(const char *modname, char sum[], unsigned sumlen)
{
- void *file;
- unsigned long len;
+ char *buf;
struct md4_ctx md;
- char *sources, *end, *fname;
+ char *fname;
char filelist[PATH_MAX + 1];
/* objects for a module are listed in the first line of *.mod file. */
- snprintf(filelist, sizeof(filelist), "%.*smod",
- (int)strlen(modname) - 1, modname);
-
- file = grab_file(filelist, &len);
- if (!file)
- /* not a module or .mod file missing - ignore */
- return;
-
- sources = file;
+ snprintf(filelist, sizeof(filelist), "%s.mod", modname);
- end = strchr(sources, '\n');
- if (!end) {
- warn("bad ending versions file for %s\n", modname);
- goto release;
- }
- *end = '\0';
+ buf = read_text_file(filelist);
md4_init(&md);
- while ((fname = strsep(&sources, " ")) != NULL) {
+ while ((fname = strsep(&buf, "\n"))) {
if (!*fname)
continue;
if (!(is_static_library(fname)) &&
!parse_source_files(fname, &md))
- goto release;
+ goto free;
}
md4_final_ascii(&md, sum, sumlen);
-release:
- release_file(file, len);
-}
-
-static void write_version(const char *filename, const char *sum,
- unsigned long offset)
-{
- int fd;
-
- fd = open(filename, O_RDWR);
- if (fd < 0) {
- warn("changing sum in %s failed: %s\n",
- filename, strerror(errno));
- return;
- }
-
- if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
- warn("changing sum in %s:%lu failed: %s\n",
- filename, offset, strerror(errno));
- goto out;
- }
-
- if (write(fd, sum, strlen(sum)+1) != strlen(sum)+1) {
- warn("writing sum in %s failed: %s\n",
- filename, strerror(errno));
- goto out;
- }
-out:
- close(fd);
-}
-
-static int strip_rcs_crap(char *version)
-{
- unsigned int len, full_len;
-
- if (strncmp(version, "$Revision", strlen("$Revision")) != 0)
- return 0;
-
- /* Space for version string follows. */
- full_len = strlen(version) + strlen(version + strlen(version) + 1) + 2;
-
- /* Move string to start with version number: prefix will be
- * $Revision$ or $Revision: */
- len = strlen("$Revision");
- if (version[len] == ':' || version[len] == '$')
- len++;
- while (isspace(version[len]))
- len++;
- memmove(version, version+len, full_len-len);
- full_len -= len;
-
- /* Preserve up to next whitespace. */
- len = 0;
- while (version[len] && !isspace(version[len]))
- len++;
- memmove(version + len, version + strlen(version),
- full_len - strlen(version));
- return 1;
-}
-
-/* Clean up RCS-style version numbers. */
-void maybe_frob_rcs_version(const char *modfilename,
- char *version,
- void *modinfo,
- unsigned long version_offset)
-{
- if (strip_rcs_crap(version))
- write_version(modfilename, version, version_offset);
+free:
+ free(buf);
}