extern const u8 kallsyms_token_table[] __weak;
extern const u16 kallsyms_token_index[] __weak;
+extern const char kallsyms_modules[] __weak;
+extern const u32 kallsyms_symbol_modules[] __weak;
extern const unsigned long kallsyms_markers[] __weak;
static int get_ksymbol_mod(struct kallsym_iter *iter)
{
+ iter->builtin_module = 0;
if (module_get_kallsym(iter->pos - kallsyms_num_syms, &iter->value,
&iter->type, iter->name, iter->module_name,
&iter->exported) < 0)
static unsigned long get_ksymbol_core(struct kallsym_iter *iter)
{
unsigned off = iter->nameoff;
-
- iter->module_name[0] = '\0';
+ u32 mod_index = kallsyms_symbol_modules[iter->pos];
+
+ if (mod_index == 0) {
+ iter->module_name[0] = '\0';
+ iter->builtin_module = 0;
+ } else {
+ strcpy(iter->module_name, &kallsyms_modules[mod_index]);
+ iter->builtin_module = 1;
+ }
+ iter->exported = 0;
iter->value = kallsyms_addresses[iter->pos];
iter->type = kallsyms_get_symbol_type(off);
{
}
-static int s_show(struct seq_file *m, void *p)
+static int s_show_internal(struct seq_file *m, void *p, int builtin_modules)
{
struct kallsym_iter *iter = m->private;
if (!iter->name[0])
return 0;
- if (iter->module_name[0]) {
+ if ((iter->builtin_module == 0 && iter->module_name[0]) ||
+ (iter->builtin_module != 0 && iter->module_name[0] &&
+ builtin_modules != 0)) {
char type;
/*
tolower(iter->type);
seq_printf(m, "%pK %c %s\t[%s]\n", (void *)iter->value,
type, iter->name, iter->module_name);
- } else
+ } else {
seq_printf(m, "%pK %c %s\n", (void *)iter->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 int s_mod_show(struct seq_file *m, void *p)
+{
+ return s_show_internal(m, p, 1);
+}
+
static const struct seq_operations kallsyms_op = {
.start = s_start,
.next = s_next,
.show = s_show
};
-static int kallsyms_open(struct inode *inode, struct file *file)
+static const struct seq_operations kallmodsyms_op = {
+ .start = s_start,
+ .next = s_next,
+ .stop = s_stop,
+ .show = s_mod_show
+};
+
+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
* 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;
kallsyms_iter_reset(iter, 0);
return 0;
}
+static int kallsyms_open(struct inode *inode, struct file *file)
+{
+ return kallsyms_open_internal(inode, file, &kallsyms_op);
+}
+
+static int kallmodsyms_open(struct inode *inode, struct file *file)
+{
+ return kallsyms_open_internal(inode, file, &kallmodsyms_op);
+}
+
#ifdef CONFIG_KGDB_KDB
const char *kdb_walk_kallsyms(loff_t *pos)
{
.release = seq_release_private,
};
+static const struct file_operations kallmodsyms_operations = {
+ .open = kallmodsyms_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_private,
+};
+
static int __init kallsyms_init(void)
{
proc_create("kallsyms", 0444, NULL, &kallsyms_operations);
+ proc_create("kallmodsyms", 0444, NULL, &kallmodsyms_operations);
return 0;
}
device_initcall(kallsyms_init);
* 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 vmlinux | scripts/kallsyms [--all-symbols]
+ * [--symbol-prefix=<prefix char>]
+ * [--builtin=modules.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
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <libelf.h>
+#include <dwarf.h>
+#include <elfutils/libdwfl.h>
+#include <elfutils/libdw.h>
+#include <glib.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
unsigned int len;
unsigned int start_pos;
unsigned char *sym;
+ unsigned int module;
};
struct addr_range {
unsigned char best_table[256][2];
unsigned char best_table_len[256];
+/*
+ * The list of builtin module names.
+ */
+static char **builtin_modules;
+static unsigned int builtin_module_size, builtin_module_len;
+
+/*
+ * For each builtin module, its offset from the start of the builtin_module
+ * list, assuming consecutive placement.
+ */
+static unsigned int *builtin_module_offsets;
+
+/*
+ * A mapping from symbol name to the index of the module it is part of in the
+ * builtin_modules list. Symbols within the same module share pointers to the
+ * same index allocation (thus this is nearly impossible to free safely, but
+ * quite space-efficient).
+ */
+static GHashTable *symbol_to_module;
static void usage(void)
{
fprintf(stderr, "Usage: kallsyms [--all-symbols] "
"[--symbol-prefix=<prefix char>] "
"[--page-offset=<CONFIG_PAGE_OFFSET>] "
+ "[--builtin=modules.builtin] "
"< in.map > out.S\n");
exit(1);
}
{
char str[500];
char *sym, stype;
+ unsigned int *module;
int rc;
rc = fscanf(in, "%llx %c %499s\n", &s->addr, &stype, str);
else if (stype == 'N')
return -1;
+ /* look up the builtin module this is part of (if any). */
+ module = g_hash_table_lookup(symbol_to_module, sym);
+
+ if (module)
+ s->module = builtin_module_offsets[*module];
+ else
+ s->module = 0;
+
/* include the type field in the symbol name, so that it gets
* compressed together */
s->len = strlen(str) + 1;
"kallsyms_markers",
"kallsyms_token_table",
"kallsyms_token_index",
+ "kallsyms_symbol_modules",
+ "kallsyms_modules",
/* Exclude linker generated symbols which vary between passes */
"_SDA_BASE_", /* ppc */
for (i = 0; i < 256; i++)
printf("\t.short\t%d\n", best_idx[i]);
printf("\n");
-}
+ output_label("kallsyms_modules");
+ for (i = 0; i < builtin_module_len; i++)
+ printf("\t.asciz\t\"%s\"\n", builtin_modules[i]);
+ printf("\n");
+
+ output_label("kallsyms_symbol_modules");
+ for (i = 0; i < table_cnt; i++)
+ printf("\t.int\t%d\n", table[i].module);
+ printf("\n");
+}
/* table lookup compression functions */
table[i].sym[0] = 'A';
}
+/* Built-in module list computation. */
+
+/*
+ * Stub libdwfl callback, use only the ELF handle passed in.
+ */
+static int no_debuginfo(Dwfl_Module *mod,
+ void **userdata,
+ const char *modname,
+ Dwarf_Addr base,
+ const char *file_name,
+ const char *debuglink_file,
+ GElf_Word debuglink_crc,
+ char **debuginfo_file_name)
+{
+ return -1;
+}
+
+/*
+ * Wrap up dwfl_new() complexities.
+ */
+static Dwfl *private_dwfl_new(const char *file_name)
+{
+ const char *err;
+ static Dwfl_Callbacks cb = { .find_debuginfo = no_debuginfo,
+ .section_address = dwfl_offline_section_address };
+ Dwfl *dwfl = dwfl_begin(&cb);
+
+ if (dwfl == NULL) {
+ err = "initialize libdwfl";
+ goto fail;
+ }
+
+ if (dwfl_report_elf(dwfl, "", file_name, -1, 0) == NULL) {
+ err = "open object file with libdwfl";
+ goto fail;
+ }
+
+ if (dwfl_report_end(dwfl, NULL, NULL) != 0) {
+ err = "finish opening object file with libdwfl";
+ goto fail;
+ }
+
+ return dwfl;
+ fail:
+ fprintf(stderr, "Cannot %s for %s: %s\n", err, file_name,
+ dwfl_errmsg(dwfl_errno()));
+ exit(1);
+}
+
+/*
+ * The converse of private_dwfl_new().
+ */
+static void private_dwfl_free(Dwfl *dwfl)
+{
+ dwfl_report_end(dwfl, NULL, NULL);
+}
+
+/*
+ * Populate the symbol_to_module mapping for one built-in module.
+ */
+static void read_module_symbols(unsigned int module_name,
+ const char *module_path,
+ GHashTable *module_symbol_seen)
+{
+ Dwfl *dwfl = private_dwfl_new(module_path);
+ Dwarf_Die *tu = NULL;
+ Dwarf_Addr junk;
+ unsigned int *module_idx = NULL;
+
+ while ((tu = dwfl_nextcu(dwfl, tu, &junk)) != NULL) {
+ Dwarf_Die toplevel;
+ int sib_ret;
+
+ if (dwarf_tag(tu) != DW_TAG_compile_unit) {
+ fprintf(stderr, "Malformed DWARF: non-compile_unit at "
+ "top level in %s.\n", module_path);
+ exit(1);
+ }
+
+ switch (dwarf_child(tu, &toplevel)) {
+ case -1: fprintf(stderr, "Warning: looking for toplevel "
+ "child of %s in %s: %s\n", dwarf_diename(tu),
+ module_path, dwarf_errmsg(dwarf_errno()));
+ continue;
+ case 1: /* No DIEs at all in this TU */
+ continue;
+ default: /* Child DIE's exist. */
+ break;
+ }
+
+ do {
+ if (dwarf_tag(&toplevel) == DW_TAG_subprogram) {
+ if (module_idx == NULL) {
+ module_idx = malloc(sizeof(unsigned int));
+ if (module_idx == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+
+ *module_idx = module_name;
+ }
+ /*
+ * If we have never seen this symbol before, we
+ * note that we have seen it, and track it in
+ * the symbol_to_module mapping. Otherwise, we
+ * *remove* it from that mapping, if it is
+ * present there.
+ */
+
+ if (!g_hash_table_lookup_extended(module_symbol_seen,
+ dwarf_diename(&toplevel),
+ NULL, NULL)) {
+
+ g_hash_table_insert(module_symbol_seen,
+ strdup(dwarf_diename(&toplevel)),
+ NULL);
+
+ g_hash_table_insert(symbol_to_module,
+ strdup(dwarf_diename(&toplevel)),
+ module_idx);
+ } else {
+ g_hash_table_remove(symbol_to_module,
+ strdup(dwarf_diename(&toplevel)));
+ }
+ }
+ } while ((sib_ret = dwarf_siblingof(&toplevel, &toplevel)) == 0);
+
+ if (sib_ret == -1) {
+ fprintf(stderr, "Warning: Cannot advance to next sibling "
+ "of %s in %s: %s\n", dwarf_diename(&toplevel),
+ module_path, dwarf_errmsg(dwarf_errno()));
+ }
+ }
+ private_dwfl_free(dwfl);
+}
+
+/*
+ * Expand the builtin modules list.
+ */
+static void expand_builtin_modules(void)
+{
+ builtin_module_size += 50;
+
+ builtin_modules = realloc(builtin_modules,
+ sizeof(*builtin_modules) *
+ builtin_module_size);
+ builtin_module_offsets = realloc(builtin_module_offsets,
+ sizeof(*builtin_module_offsets) *
+ builtin_module_size);
+
+ if (!builtin_modules || !builtin_module_offsets) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+}
+
+/*
+ * Populate the symbol_to_module mapping for all built-in modules,
+ * and the list of built-in modules itself.
+ */
+static void read_modules(const char *modules_builtin)
+{
+ FILE *f;
+ char *line = NULL;
+ size_t line_size = 0;
+ size_t offset = 0;
+
+ /*
+ * A hash containing a mapping from a symbol name if that symbol has
+ * been seen in any modules so far. This is used to filter out symbols
+ * found in more than one built-in module, on the grounds that they are
+ * probably symbols from the out-of-line representation of inline
+ * functions in kernel header files shared between modules.
+ */
+ GHashTable *module_symbol_seen;
+
+ symbol_to_module = g_hash_table_new(g_str_hash, g_str_equal);
+ module_symbol_seen = g_hash_table_new_full(g_str_hash, g_str_equal,
+ free, NULL);
+
+ if (symbol_to_module == NULL || module_symbol_seen == NULL) {
+ fprintf(stderr, "Out of memory");
+ exit(1);
+ }
+
+ /*
+ * builtin_modules[0] is a null entry signifying a symbol that cannot be
+ * modular.
+ */
+ builtin_module_size = 50;
+ builtin_modules = malloc(sizeof(*builtin_modules) *
+ builtin_module_size);
+ builtin_module_offsets = malloc(sizeof(*builtin_module_offsets) *
+ builtin_module_size);
+ builtin_modules[0] = strdup("");
+ builtin_module_len = 1;
+ offset++;
+
+ f = fopen(modules_builtin, "r");
+ if (f == NULL) {
+ fprintf(stderr, "Cannot open builtin module file %s: "
+ "%s\n", modules_builtin, strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Read in, stripping off the leading path element, if any, transforming
+ * the suffix into .o from .ko, and also computing the suffixless,
+ * pathless name of the module. Any elements that don't have files
+ * corresponding to them elicit a warning, and do not have their names
+ * recorded.
+ */
+ while (getline(&line, &line_size, f) >= 0) {
+ if (line[0] != '\0') {
+ char *first_slash;
+ char *last_slash;
+ char *last_dot;
+
+ first_slash = strchr(line, '/');
+ last_slash = strrchr(line, '/');
+
+ first_slash = (!first_slash) ? line : first_slash + 1;
+ last_slash = (!last_slash) ? line : last_slash + 1;
+
+ last_dot = strrchr(line, '.');
+ if ((last_dot != NULL) &&
+ ((strcmp(last_dot, ".ko") == 0) ||
+ (strcmp(last_dot, ".ko\n") == 0))) {
+ strcpy(last_dot, ".o");
+ }
+
+ if (access(first_slash, R_OK) == 0) {
+ char *module_name = strdup(last_slash);
+
+ last_dot = strrchr(module_name, '.');
+ if (last_dot != NULL)
+ *last_dot = '\0';
+
+ read_module_symbols(builtin_module_len, first_slash,
+ module_symbol_seen);
+
+ if (builtin_module_size >= builtin_module_len)
+ expand_builtin_modules();
+ builtin_modules[builtin_module_len] = module_name;
+ builtin_module_offsets[builtin_module_len] = offset;
+ offset += strlen(module_name) + 1;
+ builtin_module_len++;
+ } else {
+ fprintf(stderr, "%s population: module %s is not "
+ "readable.\n", modules_builtin,
+ first_slash);
+ }
+ }
+ }
+ free(line);
+
+ if (ferror(f)) {
+ fprintf(stderr, "Error reading from %s: %s\n",
+ modules_builtin, strerror(errno));
+ exit(1);
+ }
+
+ fclose(f);
+ g_hash_table_destroy(module_symbol_seen);
+}
+
int main(int argc, char **argv)
{
- if (argc >= 2) {
+ const char *modules_builtin = "modules.builtin";
+
+ if (argc >= 1) {
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 (strncmp(argv[i], "--page-offset=", 14) == 0) {
const char *p = &argv[i][14];
kernel_start_addr = strtoull(p, NULL, 16);
- } else
+ } else if (strncmp(argv[i], "--builtin=", 10) == 0)
+ modules_builtin = &argv[i][10];
+ else
usage();
}
} else if (argc != 1)
usage();
+ read_modules(modules_builtin);
read_map(stdin);
if (absolute_percpu)
make_percpus_absolute();