@@ -49,6 +49,7 @@
*.zst
Module.symvers
modules.order
+modules_thick.builtin
#
# Top-level generic files
@@ -183,6 +183,7 @@ modules.builtin
modules.builtin.modinfo
modules.nsdeps
modules.order
+modules_thick.builtin
modversions.h*
nconf
nconf-cfg
@@ -1195,7 +1195,7 @@ cmd_link-vmlinux = \
$(CONFIG_SHELL) $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)"; \
$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
-vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE
+vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) modules_thick.builtin FORCE
+$(call if_changed_dep,link-vmlinux)
targets := vmlinux
@@ -1496,6 +1496,23 @@ __modinst_pre:
endif # CONFIG_MODULES
+# modules_thick.builtin maps from kernel modules (or rather the object file
+# names they would have had had they not been built in) to their constituent
+# object files: we can use this to determine which modules any given object
+# file is part of. (We cannot eliminate the slight redundancy here without
+# double-expansion.)
+
+modthickbuiltin-files := $(addsuffix /modules_thick.builtin, $(build-dirs))
+
+modules_thick.builtin: $(modthickbuiltin-files)
+ $(Q)$(AWK) '!x[$$0]++' $(addsuffix /$@, $(build-dirs)) > $@
+
+# tristate.conf is not included from this Makefile. Add it as a prerequisite
+# here to make it self-healing in case somebody accidentally removes it.
+$(modthickbuiltin-files): include/config/tristate.conf
+ $(Q)$(MAKE) $(modbuiltin)=$(patsubst %/modules_thick.builtin,%,$@) builtin-file=modules_thick.builtin
+
+
###
# Cleaning is done on three levels.
# make clean Delete most generated files
@@ -1865,7 +1882,7 @@ clean: $(clean-dirs)
-o -name '*.lex.c' -o -name '*.tab.[ch]' \
-o -name '*.asn1.[ch]' \
-o -name '*.symtypes' -o -name 'modules.order' \
- -o -name '.tmp_*.o.*' \
+ -o -name '.tmp_*.o.*' -o -name modules_thick.builtin \
-o -name '*.c.[012]*.*' \
-o -name '*.ll' \
-o -name '*.gcno' \
@@ -579,7 +579,8 @@ struct module *find_module(const char *name);
/* Returns 0 and fills in value, defined and namebuf, or -ERANGE if
symnum out of range. */
int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
- char *name, char *module_name, int *exported);
+ char *name, char *module_name, unsigned long *size,
+ int *exported);
/* Look for this name: can be of form module:name. */
unsigned long module_kallsyms_lookup_name(const char *name);
@@ -756,8 +757,8 @@ static inline int lookup_module_symbol_attrs(unsigned long addr, unsigned long *
}
static inline int module_get_kallsym(unsigned int symnum, unsigned long *value,
- char *type, char *name,
- char *module_name, int *exported)
+ char *type, char *name, char *module_name,
+ unsigned long *size, int *exported)
{
return -ERANGE;
}
@@ -1495,6 +1495,14 @@ config POSIX_TIMERS
If unsure say y.
+config KALLMODSYMS
+ default y
+ bool "Enable support for /proc/kallmodsyms" if EXPERT
+ depends on KALLSYMS
+ help
+ This option enables the /proc/kallmodsyms file, which maps symbols
+ to addresses and their associated modules.
+
config PRINTK
default y
bool "Enable support for printk" if EXPERT
@@ -33,6 +33,7 @@
*/
extern const unsigned long kallsyms_addresses[] __weak;
extern const int kallsyms_offsets[] __weak;
+extern const unsigned long kallsyms_sizes[] __weak;
extern const u8 kallsyms_names[] __weak;
/*
@@ -45,8 +46,18 @@ __section(".rodata") __attribute__((weak));
extern const unsigned long kallsyms_relative_base
__section(".rodata") __attribute__((weak));
+extern const unsigned long kallsyms_num_modules
+__section(".rodata") __attribute__((weak));
+
+extern const unsigned long kallsyms_module_names_len
+__section(".rodata") __attribute__((weak));
+
extern const char kallsyms_token_table[] __weak;
extern const u16 kallsyms_token_index[] __weak;
+extern const unsigned long kallsyms_module_addresses[] __weak;
+extern const int kallsyms_module_offsets[] __weak;
+extern const u32 kallsyms_modules[] __weak;
+extern const char kallsyms_module_names[] __weak;
extern const unsigned int kallsyms_markers[] __weak;
@@ -182,6 +193,25 @@ static inline bool cleanup_symbol_name(char *s)
static inline bool cleanup_symbol_name(char *s) { return false; }
#endif
+#ifdef CONFIG_KALLMODSYMS
+static unsigned long kallsyms_builtin_module_address(int idx)
+{
+ if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
+ return kallsyms_module_addresses[idx];
+
+ /* values are unsigned offsets if --absolute-percpu is not in effect */
+ if (!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU))
+ return kallsyms_relative_base + (u32)kallsyms_module_offsets[idx];
+
+ /* ...otherwise, positive offsets are absolute values */
+ if (kallsyms_module_offsets[idx] >= 0)
+ return kallsyms_module_offsets[idx];
+
+ /* ...and negative offsets are relative to kallsyms_relative_base - 1 */
+ return kallsyms_relative_base - 1 - kallsyms_module_offsets[idx];
+}
+#endif
+
/* Lookup the address for this symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name)
{
@@ -225,12 +255,24 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
}
#endif /* CONFIG_LIVEPATCH */
+/*
+ * The caller passes in an address, and we return an index to the symbol --
+ * potentially also size and offset information.
+ * But an address might map to multiple symbols because:
+ * - some symbols might have zero size
+ * - some symbols might be aliases of one another
+ * - some symbols might span (encompass) others
+ * The symbols should already be ordered so that, for a particular address,
+ * we first have the zero-size ones, then the biggest, then the smallest.
+ * So we find the index by:
+ * - finding the last symbol with the target address
+ * - backing the index up so long as both the address and size are unchanged
+ */
static unsigned long get_symbol_pos(unsigned long addr,
unsigned long *symbolsize,
unsigned long *offset)
{
- unsigned long symbol_start = 0, symbol_end = 0;
- unsigned long i, low, high, mid;
+ unsigned long low, high, mid;
/* This kernel should never had been booted. */
if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
@@ -251,40 +293,69 @@ static unsigned long get_symbol_pos(unsigned long addr,
}
/*
- * Search for the first aliased symbol. Aliased
- * symbols are symbols with the same address.
+ * Search for the first aliased symbol.
*/
- while (low && kallsyms_sym_address(low-1) == kallsyms_sym_address(low))
+ while (low
+ && kallsyms_sym_address(low-1) == kallsyms_sym_address(low)
+ && kallsyms_sizes[low-1] == kallsyms_sizes[low])
--low;
- symbol_start = kallsyms_sym_address(low);
-
- /* Search for next non-aliased symbol. */
- for (i = low + 1; i < kallsyms_num_syms; i++) {
- if (kallsyms_sym_address(i) > symbol_start) {
- symbol_end = kallsyms_sym_address(i);
- break;
- }
- }
-
- /* If we found no next symbol, we use the end of the section. */
- if (!symbol_end) {
- if (is_kernel_inittext(addr))
- symbol_end = (unsigned long)_einittext;
- else if (IS_ENABLED(CONFIG_KALLSYMS_ALL))
- symbol_end = (unsigned long)_end;
- else
- symbol_end = (unsigned long)_etext;
- }
-
if (symbolsize)
- *symbolsize = symbol_end - symbol_start;
+ *symbolsize = kallsyms_sizes[low];
if (offset)
- *offset = addr - symbol_start;
+ *offset = addr - kallsyms_sym_address(low);
return low;
}
+/*
+ * The caller passes in an address, and we return an index to the corresponding
+ * builtin module index in .kallsyms_modules, or (unsigned long) -1 if none
+ * match.
+ *
+ * The hint_idx, if set, is a hint as to the possible return value, to handle
+ * the common case in which consecutive runs of addresses relate to the same
+ * index.
+ */
+#ifdef CONFIG_KALLMODSYMS
+static unsigned long get_builtin_module_idx(unsigned long addr, unsigned long hint_idx)
+{
+ unsigned long low, high, mid;
+
+ if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
+ BUG_ON(!kallsyms_module_addresses);
+ else
+ BUG_ON(!kallsyms_module_offsets);
+
+ /*
+ * Do a binary search on the sorted kallsyms_modules array. The last
+ * entry in this array indicates the end of the text section, not an
+ * object file.
+ */
+ low = 0;
+ high = kallsyms_num_modules - 1;
+
+ if (hint_idx > low && hint_idx < (high - 1) &&
+ addr >= kallsyms_builtin_module_address(hint_idx) &&
+ addr < kallsyms_builtin_module_address(hint_idx + 1))
+ return hint_idx;
+
+ if (addr >= kallsyms_builtin_module_address(low)
+ && addr < kallsyms_builtin_module_address(high)) {
+ while (high - low > 1) {
+ mid = low + (high - low) / 2;
+ if (kallsyms_builtin_module_address(mid) <= addr)
+ low = mid;
+ else
+ high = mid;
+ }
+ return low;
+ }
+
+ return (unsigned long) -1;
+}
+#endif
+
/*
* Lookup an address but don't bother to find any names.
*/
@@ -300,6 +371,7 @@ int kallsyms_lookup_size_offset(unsigned long addr, unsigned long *symbolsize,
return !!module_address_lookup(addr, symbolsize, offset, NULL, namebuf) ||
!!__bpf_address_lookup(addr, symbolsize, offset, namebuf);
}
+EXPORT_SYMBOL_GPL(kallsyms_lookup_size_offset);
/*
* Lookup an address
@@ -492,9 +564,12 @@ struct kallsym_iter {
loff_t pos_bpf_end;
unsigned long value;
unsigned int nameoff; /* If iterating in core kernel symbols. */
+ unsigned long size;
char type;
char name[KSYM_NAME_LEN];
char module_name[MODULE_NAME_LEN];
+ const char *builtin_module_names;
+ unsigned long hint_builtin_module_idx;
int exported;
int show_value;
};
@@ -524,7 +599,9 @@ static int get_ksymbol_mod(struct kallsym_iter *iter)
int ret = module_get_kallsym(iter->pos - iter->pos_arch_end,
&iter->value, &iter->type,
iter->name, iter->module_name,
- &iter->exported);
+ &iter->size, &iter->exported);
+ iter->builtin_module_names = NULL;
+
if (ret < 0) {
iter->pos_mod_end = iter->pos;
return 0;
@@ -544,6 +621,8 @@ static int get_ksymbol_ftrace_mod(struct kallsym_iter *iter)
&iter->value, &iter->type,
iter->name, iter->module_name,
&iter->exported);
+ iter->builtin_module_names = NULL;
+
if (ret < 0) {
iter->pos_ftrace_mod_end = iter->pos;
return 0;
@@ -558,6 +637,7 @@ static int get_ksymbol_bpf(struct kallsym_iter *iter)
strlcpy(iter->module_name, "bpf", MODULE_NAME_LEN);
iter->exported = 0;
+ iter->builtin_module_names = NULL;
ret = bpf_get_kallsym(iter->pos - iter->pos_ftrace_mod_end,
&iter->value, &iter->type,
iter->name);
@@ -578,23 +658,53 @@ static int get_ksymbol_kprobe(struct kallsym_iter *iter)
{
strlcpy(iter->module_name, "__builtin__kprobes", MODULE_NAME_LEN);
iter->exported = 0;
+ iter->builtin_module_names = NULL;
return kprobe_get_kallsym(iter->pos - iter->pos_bpf_end,
&iter->value, &iter->type,
iter->name) < 0 ? 0 : 1;
}
/* Returns space to next name. */
-static unsigned long get_ksymbol_core(struct kallsym_iter *iter)
+static unsigned long get_ksymbol_core(struct kallsym_iter *iter, int kallmodsyms)
{
unsigned off = iter->nameoff;
+ unsigned long mod_idx;
- iter->module_name[0] = '\0';
+ iter->exported = 0;
iter->value = kallsyms_sym_address(iter->pos);
+ iter->size = kallsyms_sizes[iter->pos];
iter->type = kallsyms_get_symbol_type(off);
+ iter->module_name[0] = '\0';
+ iter->builtin_module_names = NULL;
+
off = kallsyms_expand_symbol(off, iter->name, ARRAY_SIZE(iter->name));
+#ifdef CONFIG_KALLMODSYMS
+ if (kallmodsyms) {
+ if (kallsyms_module_offsets)
+ mod_idx =
+ get_builtin_module_idx(iter->value,
+ iter->hint_builtin_module_idx);
+ /*
+ * This is a built-in module iff the tables of built-in modules
+ * (address->module name mappings) and module names are known,
+ * and if the address was found there, and if the corresponding
+ * module index is nonzero. All other cases mean off the end of
+ * the binary or in a non-modular range in between one or more
+ * modules. (Also guard against a corrupt kallsyms_objfiles
+ * array pointing off the end of kallsyms_modules.)
+ */
+ if (kallsyms_modules != NULL && kallsyms_module_names != NULL &&
+ mod_idx != (unsigned long) -1 &&
+ kallsyms_modules[mod_idx] != 0 &&
+ kallsyms_modules[mod_idx] < kallsyms_module_names_len)
+ iter->builtin_module_names =
+ &kallsyms_module_names[kallsyms_modules[mod_idx]];
+ iter->hint_builtin_module_idx = mod_idx;
+ }
+#endif
return off - iter->nameoff;
}
@@ -640,7 +750,7 @@ static int update_iter_mod(struct kallsym_iter *iter, loff_t pos)
}
/* Returns false if pos at or past end of file. */
-static int update_iter(struct kallsym_iter *iter, loff_t pos)
+int update_iter(struct kallsym_iter *iter, loff_t pos, int kallmodsyms)
{
/* Module symbols can be accessed randomly. */
if (pos >= kallsyms_num_syms)
@@ -650,7 +760,7 @@ static int update_iter(struct kallsym_iter *iter, loff_t pos)
if (pos != iter->pos)
reset_iter(iter, pos);
- iter->nameoff += get_ksymbol_core(iter);
+ iter->nameoff += get_ksymbol_core(iter, kallmodsyms);
iter->pos++;
return 1;
@@ -660,14 +770,14 @@ static void *s_next(struct seq_file *m, void *p, loff_t *pos)
{
(*pos)++;
- if (!update_iter(m->private, *pos))
+ if (!update_iter(m->private, *pos, 0))
return NULL;
return p;
}
static void *s_start(struct seq_file *m, loff_t *pos)
{
- if (!update_iter(m->private, *pos))
+ if (!update_iter(m->private, *pos, 0))
return NULL;
return m->private;
}
@@ -676,34 +786,86 @@ static void s_stop(struct seq_file *m, void *p)
{
}
-static int s_show(struct seq_file *m, void *p)
+static int s_show_internal(struct seq_file *m, void *p, int kallmodsyms)
{
void *value;
struct kallsym_iter *iter = m->private;
+ unsigned long size;
/* Some debugging symbols have no name. Ignore them. */
if (!iter->name[0])
return 0;
value = iter->show_value ? (void *)iter->value : NULL;
+ size = iter->show_value ? iter->size : 0;
- if (iter->module_name[0]) {
+ /*
+ * Real module, or built-in module and /proc/kallsyms being shown.
+ */
+ if (iter->module_name[0] != '\0' ||
+ (iter->builtin_module_names != NULL && kallmodsyms != 0)) {
char type;
/*
- * Label it "global" if it is exported,
- * "local" if not exported.
+ * Label it "global" if it is exported, "local" if not exported.
*/
type = iter->exported ? toupper(iter->type) :
tolower(iter->type);
- seq_printf(m, "%px %c %s\t[%s]\n", value,
- type, iter->name, iter->module_name);
- } else
+#ifdef CONFIG_KALLMODSYMS
+ if (kallmodsyms) {
+ /*
+ * /proc/kallmodsyms, built as a module.
+ */
+ if (iter->builtin_module_names == NULL)
+ seq_printf(m, "%px %lx %c %s\t[%s]\n", value,
+ size, type, iter->name,
+ iter->module_name);
+ /*
+ * /proc/kallmodsyms, single-module symbol.
+ */
+ else if (*iter->builtin_module_names != '\0')
+ seq_printf(m, "%px %lx %c %s\t[%s]\n", value,
+ size, type, iter->name,
+ iter->builtin_module_names);
+ /*
+ * /proc/kallmodsyms, multimodule symbol. Formatted
+ * as \0MODULE_COUNTmodule-1\0module-2\0, where
+ * MODULE_COUNT is a single byte, 2 or higher.
+ */
+ else {
+ size_t i = *(char *)(iter->builtin_module_names + 1);
+ const char *walk = iter->builtin_module_names + 2;
+
+ seq_printf(m, "%px %lx %c %s\t[%s]", value,
+ size, type, iter->name, walk);
+
+ while (--i > 0) {
+ walk += strlen(walk) + 1;
+ seq_printf (m, " [%s]", walk);
+ }
+ seq_printf(m, "\n");
+ }
+ } else /* !kallmodsyms */
+#endif /* CONFIG_KALLMODSYMS */
+ seq_printf(m, "%px %c %s\t[%s]\n", value,
+ type, iter->name, iter->module_name);
+ /*
+ * Non-modular, /proc/kallmodsyms -> print size.
+ */
+ } else if (kallmodsyms)
+ seq_printf(m, "%px %lx %c %s\n", value, size,
+ iter->type, iter->name);
+ else
seq_printf(m, "%px %c %s\n", value,
iter->type, iter->name);
return 0;
}
+static int s_show(struct seq_file *m, void *p)
+{
+ return s_show_internal(m, p, 0);
+}
+
static const struct seq_operations kallsyms_op = {
.start = s_start,
.next = s_next,
@@ -711,6 +873,35 @@ static const struct seq_operations kallsyms_op = {
.show = s_show
};
+#ifdef CONFIG_KALLMODSYMS
+static int s_mod_show(struct seq_file *m, void *p)
+{
+ return s_show_internal(m, p, 1);
+}
+static void *s_mod_next(struct seq_file *m, void *p, loff_t *pos)
+{
+ (*pos)++;
+
+ if (!update_iter(m->private, *pos, 1))
+ return NULL;
+ return p;
+}
+
+static void *s_mod_start(struct seq_file *m, loff_t *pos)
+{
+ if (!update_iter(m->private, *pos, 1))
+ return NULL;
+ return m->private;
+}
+
+static const struct seq_operations kallmodsyms_op = {
+ .start = s_mod_start,
+ .next = s_mod_next,
+ .stop = s_stop,
+ .show = s_mod_show
+};
+#endif
+
static inline int kallsyms_for_perf(void)
{
#ifdef CONFIG_PERF_EVENTS
@@ -746,7 +937,8 @@ bool kallsyms_show_value(const struct cred *cred)
}
}
-static int kallsyms_open(struct inode *inode, struct file *file)
+static int kallsyms_open_internal(struct inode *inode, struct file *file,
+ const struct seq_operations *ops)
{
/*
* We keep iterator in m->private, since normal case is to
@@ -754,7 +946,7 @@ static int kallsyms_open(struct inode *inode, struct file *file)
* using get_symbol_offset for every symbol.
*/
struct kallsym_iter *iter;
- iter = __seq_open_private(file, &kallsyms_op, sizeof(*iter));
+ iter = __seq_open_private(file, ops, sizeof(*iter));
if (!iter)
return -ENOMEM;
reset_iter(iter, 0);
@@ -767,6 +959,18 @@ static int kallsyms_open(struct inode *inode, struct file *file)
return 0;
}
+static int kallsyms_open(struct inode *inode, struct file *file)
+{
+ return kallsyms_open_internal(inode, file, &kallsyms_op);
+}
+
+#ifdef CONFIG_KALLMODSYMS
+static int kallmodsyms_open(struct inode *inode, struct file *file)
+{
+ return kallsyms_open_internal(inode, file, &kallmodsyms_op);
+}
+#endif
+
#ifdef CONFIG_KGDB_KDB
const char *kdb_walk_kallsyms(loff_t *pos)
{
@@ -777,7 +981,7 @@ const char *kdb_walk_kallsyms(loff_t *pos)
reset_iter(&kdb_walk_kallsyms_iter, 0);
}
while (1) {
- if (!update_iter(&kdb_walk_kallsyms_iter, *pos))
+ if (!update_iter(&kdb_walk_kallsyms_iter, *pos, 0))
return NULL;
++*pos;
/* Some debugging symbols have no name. Ignore them. */
@@ -794,9 +998,21 @@ static const struct proc_ops kallsyms_proc_ops = {
.proc_release = seq_release_private,
};
+#ifdef CONFIG_KALLMODSYMS
+static const struct proc_ops kallmodsyms_proc_ops = {
+ .proc_open = kallmodsyms_open,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = seq_release_private,
+};
+#endif
+
static int __init kallsyms_init(void)
{
proc_create("kallsyms", 0444, NULL, &kallsyms_proc_ops);
+#ifdef CONFIG_KALLMODSYMS
+ proc_create("kallmodsyms", 0444, NULL, &kallmodsyms_proc_ops);
+#endif
return 0;
}
device_initcall(kallsyms_init);
@@ -4332,7 +4332,8 @@ int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size,
}
int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
- char *name, char *module_name, int *exported)
+ char *name, char *module_name, unsigned long *size,
+ int *exported)
{
struct module *mod;
@@ -4351,6 +4352,7 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
strlcpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN);
strlcpy(module_name, mod->name, MODULE_NAME_LEN);
*exported = is_exported(name, *value, mod);
+ *size = kallsyms->symtab[symnum].st_size;
preempt_enable();
return 0;
}
@@ -73,6 +73,12 @@ endef
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj
+###
+# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.modbuiltin obj=
+# Usage:
+# $(Q)$(MAKE) $(modbuiltin)=dir
+modbuiltin := -f $(srctree)/scripts/Makefile.modbuiltin obj
+
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.dtbinst obj=
# Usage:
@@ -32,6 +32,12 @@ HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLED
HOSTLDLIBS_sorttable = -lpthread
endif
+kallsyms-objs := kallsyms.o
+
+ifdef CONFIG_KALLMODSYMS
+kallsyms-objs += modules_thick.o
+endif
+
# The following programs are only built on demand
hostprogs += unifdef
new file mode 100644
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: GPL-2.0
+# ==========================================================================
+# Generating modules_thick.builtin
+# ==========================================================================
+
+src := $(obj)
+
+PHONY := __modbuiltin
+__modbuiltin:
+
+include include/config/auto.conf
+# tristate.conf sets tristate variables to uppercase 'Y' or 'M'
+# That way, we get the list of built-in modules in obj-Y
+include include/config/tristate.conf
+
+include scripts/Kbuild.include
+
+ifdef building_out_of_srctree
+# Create output directory if not already present
+_dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj))
+endif
+
+# The filename Kbuild has precedence over Makefile
+kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
+kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
+include $(kbuild-file)
+
+include scripts/Makefile.lib
+
+modthickbuiltin-subdirs := $(patsubst %,%/modules_thick.builtin, $(subdir-ym))
+modthickbuiltin-target := $(obj)/modules_thick.builtin
+
+__modbuiltin: $(obj)/$(builtin-file) $(subdir-ym)
+ @:
+
+$(modthickbuiltin-target): $(subdir-ym) FORCE
+ $(Q) rm -f $@
+ $(Q) $(foreach mod-o, $(filter %.o,$(obj-Y)),\
+ printf "%s:" $(addprefix $(obj)/,$(mod-o)) >> $@; \
+ printf " %s" $(sort $(strip $(addprefix $(obj)/,$($(mod-o:.o=-objs)) \
+ $($(mod-o:.o=-y)) $($(mod-o:.o=-Y))))) >> $@; \
+ printf "\n" >> $@; ) \
+ cat /dev/null $(modthickbuiltin-subdirs) >> $@;
+
+PHONY += FORCE
+
+FORCE:
+
+# Descending
+# ---------------------------------------------------------------------------
+
+PHONY += $(subdir-ym)
+$(subdir-ym):
+ $(Q)$(MAKE) $(modbuiltin)=$@ builtin-file=$(builtin-file)
+
+.PHONY: $(PHONY)
@@ -5,7 +5,10 @@
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
- * Usage: nm -n vmlinux | scripts/kallsyms [--all-symbols] > symbols.S
+ * Usage: nm -n -S vmlinux
+ * | scripts/kallsyms [--all-symbols] [--absolute-percpu]
+ * [--base-relative] [--builtin=modules_thick.builtin]
+ * > symbols.S
*
* Table compression uses all the unused char codes on the symbols and
* maps these to the most used substrings (tokens). For instance, it might
@@ -18,19 +21,31 @@
*
*/
+#define _GNU_SOURCE 1
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
+#include <assert.h>
+#include "modules_thick.h"
+#include "../include/generated/autoconf.h"
+
+#ifdef CONFIG_KALLMODSYMS
+#include <errno.h>
+#endif
+
+#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
+#endif
#define KSYM_NAME_LEN 128
struct sym_entry {
unsigned long long addr;
+ unsigned long long size;
unsigned int len;
unsigned int start_pos;
unsigned int percpu_absolute;
@@ -67,11 +82,304 @@ static int token_profit[0x10000];
static unsigned char best_table[256][2];
static unsigned char best_table_len[256];
+#ifdef CONFIG_KALLMODSYMS
+static unsigned int strhash(const char *s)
+{
+ /* fnv32 hash */
+ unsigned int hash = 2166136261U;
+
+ for (; *s; s++)
+ hash = (hash ^ *s) * 0x01000193;
+ return hash;
+}
+
+static unsigned int memhash(char *s, size_t len)
+{
+ /* fnv32 hash */
+ unsigned int hash = 2166136261U;
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ hash = (hash ^ *(s + i)) * 0x01000193;
+ return hash;
+}
+
+#define OBJ2MOD_BITS 10
+#define OBJ2MOD_N (1 << OBJ2MOD_BITS)
+#define OBJ2MOD_MASK (OBJ2MOD_N - 1)
+struct obj2mod_elem {
+ char *obj;
+ char *mods; /* sorted module name strtab */
+ size_t nmods; /* number of modules in "mods" */
+ size_t mods_size; /* size of all mods together */
+ int mod_offset; /* offset in .kallsyms_module_names */
+ /*
+ * If set at emission time, this points at another obj2mod entry that
+ * contains the module name we need (possibly at a slightly later
+ * offset, if the entry is for an objfile that appears in many modules).
+ */
+ struct obj2mod_elem *xref;
+ struct obj2mod_elem *obj2mod_next;
+ struct obj2mod_elem *mod2obj_next;
+};
+
+/*
+ * Map from object files to obj2mod entries (a unique mapping), and vice versa
+ * (not unique, but entries for objfiles in more than one module in this hash
+ * are ignored).
+ */
+
+static struct obj2mod_elem *obj2mod[OBJ2MOD_N];
+static struct obj2mod_elem *mod2obj[OBJ2MOD_N];
+static size_t num_objfiles;
+
+/*
+ * An ordered list of address ranges and the objfile that occupies that range.
+ */
+struct addrmap_entry {
+ unsigned long long addr;
+ struct obj2mod_elem *objfile;
+};
+static struct addrmap_entry *addrmap;
+static int addrmap_num, addrmap_alloced;
+
+static void obj2mod_init(void)
+{
+ memset(obj2mod, 0, sizeof(obj2mod));
+}
+
+static struct obj2mod_elem *obj2mod_get(const char *obj)
+{
+ int i = strhash(obj) & OBJ2MOD_MASK;
+ struct obj2mod_elem *elem;
+
+ for (elem = obj2mod[i]; elem; elem = elem->obj2mod_next) {
+ if (strcmp(elem->obj, obj) == 0)
+ return elem;
+ }
+ return NULL;
+}
+
+/*
+ * Note that a given object file is found in some module, interning it in the
+ * obj2mod hash. Should not be called more than once for any given (module,
+ * object) pair.
+ */
+static void obj2mod_add(char *obj, char *mod)
+{
+ int i = strhash(obj) & OBJ2MOD_MASK;
+ struct obj2mod_elem *elem;
+
+ elem = obj2mod_get(obj);
+ if (!elem) {
+ int j = strhash(mod) & OBJ2MOD_MASK;
+
+ elem = malloc(sizeof(struct obj2mod_elem));
+ if (!elem)
+ goto oom;
+ memset(elem, 0, sizeof(struct obj2mod_elem));
+ elem->obj = strdup(obj);
+ if (!elem->obj)
+ goto oom;
+ elem->mods = strdup(mod);
+ if (!elem->mods)
+ goto oom;
+
+ elem->obj2mod_next = obj2mod[i];
+ obj2mod[i] = elem;
+ elem->mod2obj_next = mod2obj[j];
+ mod2obj[j] = elem;
+ num_objfiles++;
+ } else {
+ /*
+ * TU appears in multiple modules. mod2obj for this entry will
+ * be ignored from now on, except insofar as it is needed to
+ * maintain the hash chain.
+ */
+ elem->mods = realloc(elem->mods, elem->mods_size +
+ strlen(mod) + 1);
+ if (!elem->mods)
+ goto oom;
+ strcpy(elem->mods + elem->mods_size, mod);
+ }
+
+ elem->mods_size += strlen(mod) + 1;
+ elem->nmods++;
+ if (elem->nmods > 255) {
+ fprintf(stderr, "kallsyms: %s: too many modules associated with this object file\n",
+ obj);
+ exit(EXIT_FAILURE);
+ }
+ return;
+oom:
+ fprintf(stderr, "kallsyms: out of memory\n");
+ exit(1);
+}
+
+/*
+ * Used inside optimize_obj2mod to identify duplicate module entries.
+ */
+struct obj2mod_modhash_elem {
+ struct obj2mod_elem *elem;
+ unsigned int modhash; /* hash value of this entry */
+};
+
+static int qstrcmp(const void *a, const void *b)
+{
+ return strcmp((const char *) a, (const char *) b);
+}
+
+static int qmodhash(const void *a, const void *b)
+{
+ const struct obj2mod_modhash_elem *el_a = a;
+ const struct obj2mod_modhash_elem *el_b = b;
+ if (el_a->modhash < el_b->modhash)
+ return -1;
+ else if (el_a->modhash > el_b->modhash)
+ return 1;
+ return 0;
+}
+
+/*
+ * Associate all TUs in obj2mod which refer to the same module with a single
+ * obj2mod entry for emission, preferring to point into the module list in a
+ * multi-module objfile.
+ */
+static void optimize_obj2mod(void)
+{
+ size_t i;
+ size_t n = 0;
+ struct obj2mod_elem *elem;
+ struct obj2mod_elem *dedup;
+ /* An array of all obj2mod_elems, later sorted by hashval. */
+ struct obj2mod_modhash_elem *uniq;
+ struct obj2mod_modhash_elem *last;
+
+ /*
+ * Canonicalize all module lists by sorting them, then compute their
+ * hash values.
+ */
+ uniq = malloc(sizeof(struct obj2mod_modhash_elem) * num_objfiles);
+ if (uniq == NULL)
+ goto oom;
+
+ for (i = 0; i < OBJ2MOD_N; i++) {
+ for (elem = obj2mod[i]; elem; elem = elem->obj2mod_next) {
+ if (elem->nmods >= 2) {
+ char **sorter;
+ char *walk;
+ char *tmp_mods;
+ size_t j;
+
+ tmp_mods = malloc(elem->mods_size);
+ sorter = malloc(sizeof(char *) * elem->nmods);
+ if (sorter == NULL || tmp_mods == NULL)
+ goto oom;
+ memcpy(tmp_mods, elem->mods, elem->mods_size);
+
+ for (j = 0, walk = tmp_mods; j < elem->nmods;
+ j++) {
+ sorter[j] = walk;
+ walk += strlen(walk) + 1;
+ }
+ qsort(sorter, elem->nmods, sizeof (char *),
+ qstrcmp);
+ for (j = 0, walk = elem->mods; j < elem->nmods;
+ j++) {
+ strcpy(walk, sorter[j]);
+ walk += strlen(walk) + 1;
+ }
+ free(tmp_mods);
+ free(sorter);
+ }
+
+ uniq[n].elem = elem;
+ uniq[n].modhash = memhash(elem->mods, elem->mods_size);
+ n++;
+ }
+ }
+
+ qsort (uniq, num_objfiles, sizeof (struct obj2mod_modhash_elem),
+ qmodhash);
+
+ /*
+ * Work over multimodule entries. These must be emitted into
+ * .kallsyms_module_names as a unit, but we can still optimize by
+ * reusing some other identical entry. Single-file modules are amenable
+ * to the same optimization, but we avoid doing it for now so that we
+ * can prefer to point them directly inside a multimodule entry.
+ */
+ for (i = 0, last = NULL; i < num_objfiles; i++) {
+ const char *onemod;
+ size_t j;
+
+ if (uniq[i].elem->nmods < 2)
+ continue;
+
+ /* Duplicate multimodule. Reuse the first we saw. */
+ if (last != NULL && last->modhash == uniq[i].modhash) {
+ uniq[i].elem->xref = last->elem;
+ continue;
+ }
+
+ /*
+ * Single-module entries relating to modules also emitted as
+ * part of this multimodule entry can refer to it: later, we
+ * will hunt down the right specific module name within this
+ * multimodule entry and point directly to it.
+ */
+ onemod = uniq[i].elem->mods;
+ for (j = uniq[i].elem->nmods; j > 0; j--) {
+ int h = strhash(onemod) & OBJ2MOD_MASK;
+
+ for (dedup = mod2obj[h]; dedup;
+ dedup = dedup->mod2obj_next) {
+ if (dedup->nmods > 1)
+ continue;
+
+ if (strcmp(dedup->mods, onemod) != 0)
+ continue;
+ dedup->xref = uniq[i].elem;
+ assert (uniq[i].elem->xref == NULL);
+ }
+ onemod += strlen(onemod) + 1;
+ }
+
+ last = &uniq[i];
+ }
+
+ /*
+ * Now traverse all single-module entries, xreffing every one that
+ * relates to a given module to the first one we saw that refers to that
+ * module.
+ */
+ for (i = 0, last = NULL; i < num_objfiles; i++) {
+ if (uniq[i].elem->nmods > 1)
+ continue;
+
+ if (uniq[i].elem->xref != NULL)
+ continue;
+
+ /* Duplicate module name. Reuse the first we saw. */
+ if (last != NULL && last->modhash == uniq[i].modhash) {
+ uniq[i].elem->xref = last->elem;
+ assert (last->elem->xref == NULL);
+ continue;
+ }
+ last = &uniq[i];
+ }
+ return;
+oom:
+ fprintf(stderr, "kallsyms: out of memory optimizing module list\n");
+ exit(EXIT_FAILURE);
+}
+#endif
static void usage(void)
{
- fprintf(stderr, "Usage: kallsyms [--all-symbols] "
- "[--base-relative] < in.map > out.S\n");
+ fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] "
+ "[--base-relative] [--builtin=modules_thick.builtin] "
+ "< nm_vmlinux.out > symbols.S\n");
exit(1);
}
@@ -94,11 +402,18 @@ static bool is_ignored_symbol(const char *name, char type)
"kallsyms_addresses",
"kallsyms_offsets",
"kallsyms_relative_base",
+ "kallsyms_sizes",
"kallsyms_num_syms",
+ "kallsyms_num_modules",
"kallsyms_names",
"kallsyms_markers",
"kallsyms_token_table",
"kallsyms_token_index",
+ "kallsyms_module_offsets",
+ "kallsyms_module_addresses",
+ "kallsyms_modules",
+ "kallsyms_module_names",
+ "kallsyms_module_names_len",
/* Exclude linker generated symbols which vary between passes */
"_SDA_BASE_", /* ppc */
"_SDA2_BASE_", /* ppc */
@@ -201,10 +516,11 @@ static struct sym_entry *read_symbol(FILE *in)
unsigned long long addr;
unsigned int len;
struct sym_entry *sym;
- int rc;
+ int rc = 0;
+ unsigned long long size;
- rc = fscanf(in, "%llx %c %499s\n", &addr, &type, name);
- if (rc != 3) {
+ rc = fscanf(in, "%llx %llx %c %499s\n", &addr, &size, &type, name);
+ if (rc != 4) {
if (rc != EOF && fgets(name, 500, in) == NULL)
fprintf(stderr, "Read error or end of file.\n");
return NULL;
@@ -242,12 +558,13 @@ static struct sym_entry *read_symbol(FILE *in)
sym->sym[0] = type;
strcpy(sym_name(sym), name);
sym->percpu_absolute = 0;
+ sym->size = size;
return sym;
}
-static int symbol_in_range(const struct sym_entry *s,
- const struct addr_range *ranges, int entries)
+static int addr_in_range(unsigned long long addr,
+ const struct addr_range *ranges, int entries)
{
size_t i;
const struct addr_range *ar;
@@ -255,7 +572,7 @@ static int symbol_in_range(const struct sym_entry *s,
for (i = 0; i < entries; ++i) {
ar = &ranges[i];
- if (s->addr >= ar->start && s->addr <= ar->end)
+ if (addr >= ar->start && addr <= ar->end)
return 1;
}
@@ -269,8 +586,8 @@ static int symbol_valid(const struct sym_entry *s)
/* if --all-symbols is not specified, then symbols outside the text
* and inittext sections are discarded */
if (!all_symbols) {
- if (symbol_in_range(s, text_ranges,
- ARRAY_SIZE(text_ranges)) == 0)
+ if (addr_in_range(s->addr, text_ranges,
+ ARRAY_SIZE(text_ranges)) == 0)
return 0;
/* Corner case. Discard any symbols with the same value as
* _etext _einittext; they can move between pass 1 and 2 when
@@ -352,6 +669,181 @@ static void output_address(unsigned long long addr)
printf("\tPTR\t_text - %#llx\n", _text - addr);
}
+#ifdef CONFIG_KALLMODSYMS
+/* Output the .kallmodsyms_modules symbol content. */
+static void output_kallmodsyms_modules(void)
+{
+ struct obj2mod_elem *elem;
+ size_t offset = 1;
+ size_t i;
+
+ /*
+ * Traverse and emit, chasing xref and updating mod_offset accordingly.
+ * Emit a single \0 at the start, to encode non-modular objfiles.
+ */
+ output_label("kallsyms_module_names");
+ printf("\t.byte\t0\n");
+ for (i = 0; i < OBJ2MOD_N; i++) {
+ for (elem = obj2mod[i]; elem;
+ elem = elem->obj2mod_next) {
+ const char *onemod;
+ size_t i;
+ struct obj2mod_elem *out_elem = elem;
+
+ if (elem->xref)
+ out_elem = elem->xref;
+ if (out_elem->mod_offset != 0)
+ continue; /* Already emitted. */
+
+ out_elem->mod_offset = offset;
+ onemod = out_elem->mods;
+
+ /*
+ * Technically this is a waste of space: we could just
+ * as well implement multimodule entries by pointing one
+ * byte further back, to the trailing \0 of the previous
+ * entry, but doing it this way makes it more obvious
+ * when an entry is a multimodule entry.
+ */
+ if (out_elem->nmods != 1) {
+ printf("\t.byte\t0\n");
+ printf("\t.byte\t%zi\n", out_elem->nmods);
+ offset += 2;
+ }
+
+ for (i = out_elem->nmods; i > 0; i--) {
+ printf("\t.asciz\t\"%s\"\n", onemod);
+ offset += strlen(onemod) + 1;
+ onemod += strlen(onemod) + 1;
+ }
+ }
+ }
+ printf("\n");
+ output_label("kallsyms_module_names_len");
+ printf("\t.long\t%zi\n", offset);
+}
+
+static void output_kallmodsyms_objfiles(void)
+{
+ size_t i = 0;
+ size_t emitted_offsets = 0;
+ size_t emitted_objfiles = 0;
+
+ if (base_relative)
+ output_label("kallsyms_module_offsets");
+ else
+ output_label("kallsyms_module_addresses");
+
+ for (i = 0; i < addrmap_num; i++) {
+ long long offset;
+ int overflow;
+
+ /*
+ * Fuse consecutive address ranges citing the same object file
+ * into one.
+ */
+ if (i > 0 && addrmap[i-1].objfile == addrmap[i].objfile)
+ continue;
+
+ if (base_relative) {
+ if (!absolute_percpu) {
+ offset = addrmap[i].addr - relative_base;
+ overflow = (offset < 0 || offset > UINT_MAX);
+ } else {
+ offset = relative_base - addrmap[i].addr - 1;
+ overflow = (offset < INT_MIN || offset >= 0);
+ }
+ if (overflow) {
+ fprintf(stderr, "kallsyms failure: "
+ "objfile %s at address %#llx out of range in relative mode\n",
+ addrmap[i].objfile ? addrmap[i].objfile->obj :
+ "in always-built-in object", table[i]->addr);
+ exit(EXIT_FAILURE);
+ }
+ printf("\t.long\t0x%x\n", (int)offset);
+ } else
+ printf("\tPTR\t%#llx\n", addrmap[i].addr);
+ emitted_offsets++;
+ }
+
+ output_label("kallsyms_modules");
+
+ for (i = 0; i < addrmap_num; i++) {
+ struct obj2mod_elem *elem = addrmap[i].objfile;
+ int orig_nmods;
+ const char *orig_modname;
+ int mod_offset;
+
+ if (i > 0 && addrmap[i-1].objfile == addrmap[i].objfile)
+ continue;
+
+ /*
+ * Address range cites no object file: point at 0, the built-in
+ * module.
+ */
+ if (addrmap[i].objfile == NULL) {
+ printf("\t.long\t0x0\n");
+ emitted_objfiles++;
+ continue;
+ }
+
+ orig_nmods = elem->nmods;
+ orig_modname = elem->mods;
+
+ /*
+ * Chase down xrefs, if need be. There can only be one layer of
+ * these: from single-module entry to other single-module entry,
+ * or from single- or multi-module entry to another multi-module
+ * entry. Single -> single and multi -> multi always points at
+ * the start of the xref target, so its offset can be used as is.
+ */
+ if (elem->xref)
+ elem = elem->xref;
+
+ if (elem->nmods == 1 || orig_nmods > 1)
+ mod_offset = elem->mod_offset;
+ else {
+ /*
+ * If this is a reference from a single-module entry to
+ * a multi-module entry, hunt down the offset to this
+ * specific module's name (which is guaranteed to be
+ * present: see optimize_obj2mod).
+ */
+
+ size_t j = elem->nmods;
+ const char *onemod = elem->mods;
+ mod_offset = elem->mod_offset;
+
+ for (; j > 0; j--) {
+ if (strcmp(orig_modname, onemod) == 0)
+ break;
+ onemod += strlen(onemod) + 1;
+ }
+ assert (j > 0);
+ /*
+ * +2 to skip the null byte and count at the start of
+ * the multimodule entry.
+ */
+ mod_offset += onemod - elem->mods + 2;
+ }
+
+ /*
+ * Zero offset is the initial \0, there to catch uninitialized
+ * obj2mod entries, and is forbidden.
+ */
+ assert (mod_offset != 0);
+
+ printf("\t.long\t0x%x\n", mod_offset);
+ emitted_objfiles++;
+ }
+
+ assert (emitted_offsets == emitted_objfiles);
+ output_label("kallsyms_num_modules");
+ printf("\t.long\t%zi\n", emitted_objfiles);
+ printf("\n");
+}
+#endif /* CONFIG_KALLMODSYMS */
+
/* uncompress a compressed symbol. When this function is called, the best table
* might still be compressed itself, so the function needs to be recursive */
static int expand_symbol(const unsigned char *data, int len, char *result)
@@ -451,6 +943,16 @@ static void write_src(void)
printf("\n");
}
+ output_label("kallsyms_sizes");
+ for (i = 0; i < table_cnt; i++)
+ printf("\tPTR\t%#llx\n", table[i]->size);
+ printf("\n");
+
+#ifdef CONFIG_KALLMODSYMS
+ output_kallmodsyms_modules();
+ output_kallmodsyms_objfiles();
+#endif
+
output_label("kallsyms_num_syms");
printf("\t.long\t%u\n", table_cnt);
printf("\n");
@@ -502,7 +1004,6 @@ static void write_src(void)
printf("\n");
}
-
/* table lookup compression functions */
/* count all the possible tokens in a symbol */
@@ -703,6 +1204,18 @@ static int compare_symbols(const void *a, const void *b)
if (sa->addr < sb->addr)
return -1;
+ /* zero-size markers before nonzero-size symbols */
+ if (sa->size > 0 && sb->size == 0)
+ return 1;
+ if (sa->size == 0 && sb->size > 0)
+ return -1;
+
+ /* sort by size (large size preceding symbols it encompasses) */
+ if (sa->size < sb->size)
+ return 1;
+ if (sa->size > sb->size)
+ return -1;
+
/* sort by "weakness" type */
wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W');
wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W');
@@ -735,7 +1248,7 @@ static void make_percpus_absolute(void)
unsigned int i;
for (i = 0; i < table_cnt; i++)
- if (symbol_in_range(table[i], &percpu_range, 1)) {
+ if (addr_in_range(table[i]->addr, &percpu_range, 1)) {
/*
* Keep the 'A' override for percpu symbols to
* ensure consistent behavior compared to older
@@ -762,17 +1275,125 @@ static void record_relative_base(void)
}
}
+#ifdef CONFIG_KALLMODSYMS
+/*
+ * Read the linker map.
+ */
+static void read_linker_map(void)
+{
+ unsigned long long addr, size;
+ char obj[PATH_MAX+1];
+ FILE *f = fopen(".tmp_vmlinux.ranges", "r");
+
+ if (!f) {
+ fprintf(stderr, "Cannot open '.tmp_vmlinux.ranges'.\n");
+ exit(1);
+ }
+
+ addrmap_num = 0;
+ addrmap_alloced = 4096;
+ addrmap = malloc(sizeof(*addrmap) * addrmap_alloced);
+ if (!addrmap)
+ goto oom;
+
+ /*
+ * For each address range, add to addrmap the address and the objfile
+ * entry to which the range maps. Only add entries relating to text
+ * ranges. (We assume that the text ranges are tightly packed, because
+ * in any reasonable object file format they will be, so we can ignore
+ * the size.)
+ *
+ * Ranges that do not correspond to a built-in module, but to an
+ * always-built-in object file, have no obj2mod_elem and point at NULL
+ * instead.
+ */
+
+ while (fscanf(f, "%llx %llx %s\n", &addr, &size, obj) == 3) {
+ struct obj2mod_elem *elem = obj2mod_get(obj);
+
+ if (addr == 0 || size == 0 ||
+ !addr_in_range(addr, text_ranges, ARRAY_SIZE(text_ranges)))
+ continue;
+
+ if (addrmap_num >= addrmap_alloced) {
+ addrmap_alloced *= 2;
+ addrmap = realloc(addrmap,
+ sizeof(*addrmap) * addrmap_alloced);
+ if (!addrmap)
+ goto oom;
+ }
+
+ addrmap[addrmap_num].addr = addr;
+ addrmap[addrmap_num].objfile = elem;
+ addrmap_num++;
+ }
+ fclose(f);
+ return;
+
+oom:
+ fprintf(stderr, "kallsyms: out of memory\n");
+ exit(1);
+}
+
+/*
+ * Read "modules_thick.builtin" (the list of built-in modules). Construct the
+ * obj2mod hash to track objfile -> module mappings. Read ".tmp_vmlinux.ranges"
+ * (the linker map) and build addrmap[], which maps address ranges to built-in
+ * module names (using obj2mod).
+ */
+static void read_modules(const char *modules_builtin)
+{
+ struct modules_thick_iter *i;
+ char *module_name = NULL;
+ char **module_paths;
+
+ obj2mod_init();
+ /*
+ * Iterate over all modules in modules_thick.builtin and add each.
+ */
+ i = modules_thick_iter_new(modules_builtin);
+ if (i == NULL) {
+ fprintf(stderr, "Cannot iterate over builtin modules.\n");
+ exit(1);
+ }
+
+ while ((module_paths = modules_thick_iter_next(i, &module_name))) {
+ char **walk = module_paths;
+ while (*walk) {
+ obj2mod_add(*walk, module_name);
+ walk++;
+ }
+ free(module_paths);
+ }
+
+ free(module_name);
+ modules_thick_iter_free(i);
+ optimize_obj2mod();
+
+ /*
+ * Read linker map.
+ */
+ read_linker_map();
+}
+#else
+static void read_modules(const char *unused) {}
+#endif /* CONFIG_KALLMODSYMS */
+
int main(int argc, char **argv)
{
+ const char *modules_builtin = "modules_thick.builtin";
+
if (argc >= 2) {
int i;
for (i = 1; i < argc; i++) {
- if(strcmp(argv[i], "--all-symbols") == 0)
+ if (strcmp(argv[i], "--all-symbols") == 0)
all_symbols = 1;
else if (strcmp(argv[i], "--absolute-percpu") == 0)
absolute_percpu = 1;
else if (strcmp(argv[i], "--base-relative") == 0)
base_relative = 1;
+ else if (strncmp(argv[i], "--builtin=", 10) == 0)
+ modules_builtin = &argv[i][10];
else
usage();
}
@@ -780,6 +1401,7 @@ int main(int argc, char **argv)
usage();
read_map(stdin);
+ read_modules(modules_builtin);
shrink_table();
if (absolute_percpu)
make_percpus_absolute();
@@ -725,6 +725,25 @@ static struct conf_printer header_printer_cb =
.print_comment = header_print_comment,
};
+/*
+ * Tristate printer
+ *
+ * This printer is used when generating the `include/config/tristate.conf' file.
+ */
+static void
+tristate_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
+{
+
+ if (sym->type == S_TRISTATE && *value != 'n')
+ fprintf(fp, "%s%s=%c\n", CONFIG_, sym->name, (char)toupper(*value));
+}
+
+static struct conf_printer tristate_printer_cb =
+{
+ .print_symbol = tristate_print_symbol,
+ .print_comment = kconfig_print_comment,
+};
+
static void conf_write_symbol(FILE *fp, struct symbol *sym,
struct conf_printer *printer, void *printer_arg)
{
@@ -1058,7 +1077,7 @@ int conf_write_autoconf(int overwrite)
struct symbol *sym;
const char *name;
const char *autoconf_name = conf_get_autoconfig_name();
- FILE *out, *out_h;
+ FILE *out, *tristate, *out_h;
int i;
if (!overwrite && is_present(autoconf_name))
@@ -1073,6 +1092,13 @@ int conf_write_autoconf(int overwrite)
if (!out)
return 1;
+ tristate = fopen(".tmpconfig_tristate", "w");
+ if (!tristate) {
+ fclose(out);
+ fclose(tristate);
+ return 1;
+ }
+
out_h = fopen(".tmpconfig.h", "w");
if (!out_h) {
fclose(out);
@@ -1080,6 +1106,7 @@ int conf_write_autoconf(int overwrite)
}
conf_write_heading(out, &kconfig_printer_cb, NULL);
+ conf_write_heading(tristate, &tristate_printer_cb, NULL);
conf_write_heading(out_h, &header_printer_cb, NULL);
for_all_symbols(i, sym) {
@@ -1087,11 +1114,13 @@ int conf_write_autoconf(int overwrite)
if (!(sym->flags & SYMBOL_WRITE) || !sym->name)
continue;
- /* write symbols to auto.conf and autoconf.h */
+ /* write symbols to auto.conf, tristate and autoconf.h */
conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1);
+ conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1);
conf_write_symbol(out_h, sym, &header_printer_cb, NULL);
}
fclose(out);
+ fclose(tristate);
fclose(out_h);
name = getenv("KCONFIG_AUTOHEADER");
@@ -1102,6 +1131,14 @@ int conf_write_autoconf(int overwrite)
if (rename(".tmpconfig.h", name))
return 1;
+ name = getenv("KCONFIG_TRISTATE");
+ if (!name)
+ name = "include/config/tristate.conf";
+ if (make_parent_dir(name))
+ return 1;
+ if (rename(".tmpconfig_tristate", name))
+ return 1;
+
if (make_parent_dir(autoconf_name))
return 1;
/*
@@ -178,6 +178,7 @@ vmlinux_link()
objects="--whole-archive \
vmlinux.o \
--no-whole-archive \
+ -Map=.tmp_vmlinux.map \
${@}"
else
objects="--whole-archive \
@@ -186,6 +187,7 @@ vmlinux_link()
--start-group \
${KBUILD_VMLINUX_LIBS} \
--end-group \
+ -Map=.tmp_vmlinux.map \
${@}"
fi
@@ -201,6 +203,7 @@ vmlinux_link()
-Wl,--start-group \
${KBUILD_VMLINUX_LIBS} \
-Wl,--end-group \
+ -Wl,-Map=.tmp_vmlinux.map \
${@}"
${CC} ${CFLAGS_vmlinux} \
@@ -259,6 +262,19 @@ kallsyms()
{
local kallsymopt;
+ # read the linker map to identify ranges of addresses:
+ # - for each *.o file, report address, size, pathname
+ # - most such lines will have four fields
+ # - but sometimes there is a line break after the first field
+ # - start reading at "Linker script and memory map"
+ # - stop reading at ".brk"
+ ${AWK} '
+ /\.o$/ && start==1 { print $(NF-2), $(NF-1), $NF }
+ /^Linker script and memory map/ { start = 1 }
+ /^\.brk/ { exit(0) }
+ ' .tmp_vmlinux.map | sort > .tmp_vmlinux.ranges
+
+ # get kallsyms options
if [ -n "${CONFIG_KALLSYMS_ALL}" ]; then
kallsymopt="${kallsymopt} --all-symbols"
fi
@@ -272,7 +288,12 @@ kallsyms()
fi
info KSYMS ${2}
- ${NM} -n ${1} | scripts/kallsyms ${kallsymopt} > ${2}
+ # "nm -S" does not print symbol size when size is 0
+ # Therefore use awk to regularize the data:
+ # - when there are only three fields, add an explicit "0"
+ # - when there are already four fields, pass through as is
+ ${NM} -n -S ${1} | ${AWK} 'NF==3 {print $1, 0, $2, $3}; NF==4' | \
+ scripts/kallsyms ${kallsymopt} > ${2}
}
# Perform one step in kallsyms generation, including temporary linking of
new file mode 100644
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * A simple modules_thick reader.
+ *
+ * (C) 2014, 2021 Oracle, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "modules_thick.h"
+
+/*
+ * Read a modules_thick.builtin file and translate it into a stream of
+ * name / module-name pairs.
+ */
+
+/*
+ * Construct a modules_thick.builtin iterator.
+ */
+struct modules_thick_iter *
+modules_thick_iter_new(const char *modules_thick_file)
+{
+ struct modules_thick_iter *i;
+
+ i = calloc(1, sizeof(struct modules_thick_iter));
+ if (i == NULL)
+ return NULL;
+
+ i->f = fopen(modules_thick_file, "r");
+
+ if (i->f == NULL) {
+ fprintf(stderr, "Cannot open builtin module file %s: %s\n",
+ modules_thick_file, strerror(errno));
+ return NULL;
+ }
+
+ return i;
+}
+
+/*
+ * Iterate, returning a new null-terminated array of object file names, and a
+ * new dynamically-allocated module name. (The module name passed in is freed.)
+ *
+ * The array of object file names should be freed by the caller: the strings it
+ * points to are owned by the iterator, and should not be freed.
+ */
+
+char ** __attribute__((__nonnull__))
+modules_thick_iter_next(struct modules_thick_iter *i, char **module_name)
+{
+ size_t npaths = 1;
+ char **module_paths;
+ char *last_slash;
+ char *last_dot;
+ char *trailing_linefeed;
+ char *object_name = i->line;
+ char *dash;
+ int composite = 0;
+
+ /*
+ * Read in all module entries, computing the suffixless, pathless name
+ * of the module and building the next arrayful of object file names for
+ * return.
+ *
+ * Modules can consist of multiple files: in this case, the portion
+ * before the colon is the path to the module (as before): the portion
+ * after the colon is a space-separated list of files that should be
+ * considered part of this module. In this case, the portion before the
+ * name is an "object file" that does not actually exist: it is merged
+ * into built-in.a without ever being written out.
+ *
+ * All module names have - translated to _, to match what is done to the
+ * names of the same things when built as modules.
+ */
+
+ /*
+ * Reinvocation of exhausted iterator. Return NULL, once.
+ */
+retry:
+ if (getline(&i->line, &i->line_size, i->f) < 0) {
+ if (ferror(i->f)) {
+ fprintf(stderr, "Error reading from modules_thick file:"
+ " %s\n", strerror(errno));
+ exit(1);
+ }
+ rewind(i->f);
+ return NULL;
+ }
+
+ if (i->line[0] == '\0')
+ goto retry;
+
+ /*
+ * Slice the line in two at the colon, if any. If there is anything
+ * past the ': ', this is a composite module. (We allow for no colon
+ * for robustness, even though one should always be present.)
+ */
+ if (strchr(i->line, ':') != NULL) {
+ char *name_start;
+
+ object_name = strchr(i->line, ':');
+ *object_name = '\0';
+ object_name++;
+ name_start = object_name + strspn(object_name, " \n");
+ if (*name_start != '\0') {
+ composite = 1;
+ object_name = name_start;
+ }
+ }
+
+ /*
+ * Figure out the module name.
+ */
+ last_slash = strrchr(i->line, '/');
+ last_slash = (!last_slash) ? i->line :
+ last_slash + 1;
+ free(*module_name);
+ *module_name = strdup(last_slash);
+ dash = *module_name;
+
+ while (dash != NULL) {
+ dash = strchr(dash, '-');
+ if (dash != NULL)
+ *dash = '_';
+ }
+
+ last_dot = strrchr(*module_name, '.');
+ if (last_dot != NULL)
+ *last_dot = '\0';
+
+ trailing_linefeed = strchr(object_name, '\n');
+ if (trailing_linefeed != NULL)
+ *trailing_linefeed = '\0';
+
+ /*
+ * Multifile separator? Object file names explicitly stated:
+ * slice them up and shuffle them in.
+ *
+ * The array size may be an overestimate if any object file
+ * names start or end with spaces (very unlikely) but cannot be
+ * an underestimate. (Check for it anyway.)
+ */
+ if (composite) {
+ char *one_object;
+
+ for (npaths = 0, one_object = object_name;
+ one_object != NULL;
+ npaths++, one_object = strchr(one_object + 1, ' '));
+ }
+
+ module_paths = malloc((npaths + 1) * sizeof(char *));
+ if (!module_paths) {
+ fprintf(stderr, "%s: out of memory on module %s\n", __func__,
+ *module_name);
+ exit(1);
+ }
+
+ if (composite) {
+ char *one_object;
+ size_t i = 0;
+
+ while ((one_object = strsep(&object_name, " ")) != NULL) {
+ if (i >= npaths) {
+ fprintf(stderr, "%s: num_objs overflow on module "
+ "%s: this is a bug.\n", __func__,
+ *module_name);
+ exit(1);
+ }
+
+ module_paths[i++] = one_object;
+ }
+ } else
+ module_paths[0] = i->line; /* untransformed module name */
+
+ module_paths[npaths] = NULL;
+
+ return module_paths;
+}
+
+/*
+ * Free an iterator. Can be called while iteration is underway, so even
+ * state that is freed at the end of iteration must be freed here too.
+ */
+void
+modules_thick_iter_free(struct modules_thick_iter *i)
+{
+ if (i == NULL)
+ return;
+ fclose(i->f);
+ free(i->line);
+ free(i);
+}
new file mode 100644
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * A simple modules_thick reader.
+ *
+ * (C) 2014, 2021 Oracle, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_MODULES_THICK_H
+#define _LINUX_MODULES_THICK_H
+
+#include <stdio.h>
+#include <stddef.h>
+
+/*
+ * modules_thick.builtin iteration state.
+ */
+struct modules_thick_iter {
+ FILE *f;
+ char *line;
+ size_t line_size;
+};
+
+/*
+ * Construct a modules_thick.builtin iterator.
+ */
+struct modules_thick_iter *
+modules_thick_iter_new(const char *modules_thick_file);
+
+/*
+ * Iterate, returning a new null-terminated array of object file names, and a
+ * new dynamically-allocated module name. (The module name passed in is freed.)
+ *
+ * The array of object file names should be freed by the caller: the strings it
+ * points to are owned by the iterator, and should not be freed.
+ */
+
+char ** __attribute__((__nonnull__))
+modules_thick_iter_next(struct modules_thick_iter *i, char **module_name);
+
+void
+modules_thick_iter_free(struct modules_thick_iter *i);
+
+#endif