291 lines
6.1 KiB
C
Executable File
291 lines
6.1 KiB
C
Executable File
/*
|
|
* search symbol in kernel
|
|
*
|
|
* Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
|
|
*
|
|
* You should have received a copy of license along with this program;
|
|
* if not, ask for it from Baidu, Inc.
|
|
*
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/kallsyms.h>
|
|
#include <linux/module.h>
|
|
#include <asm/sections.h>
|
|
#include "kallsyms.h"
|
|
#include "util.h"
|
|
|
|
extern const unsigned long kallsyms_addresses[] __attribute__((weak));
|
|
extern const u8 kallsyms_names[] __attribute__((weak));
|
|
extern const unsigned long kallsyms_num_syms __attribute__((weak, section(".rodata")));
|
|
extern const u8 kallsyms_token_table[] __attribute__((weak));
|
|
extern const u16 kallsyms_token_index[] __attribute__((weak));
|
|
extern const unsigned long kallsyms_markers[] __attribute__((weak));
|
|
extern const int kallsyms_offsets[] __weak;
|
|
extern const unsigned long kallsyms_relative_base
|
|
__attribute__((weak, section(".rodata")));
|
|
|
|
static unsigned long kallsyms_sym_address(int idx)
|
|
{
|
|
if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
|
|
return kallsyms_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_offsets[idx];
|
|
|
|
/* ...otherwise, positive offsets are absolute values */
|
|
if (kallsyms_offsets[idx] >= 0)
|
|
return kallsyms_offsets[idx];
|
|
|
|
/* ...and negative offsets are relative to kallsyms_relative_base - 1 */
|
|
return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
|
|
}
|
|
|
|
/*
|
|
* This is taken from kallsyms_expand_symbol mostly
|
|
* return 0 means match
|
|
*/
|
|
static int kallsyms_compare_symbol(unsigned int *off, const char *name, int nlen)
|
|
{
|
|
int diff = -1;
|
|
int len, skipped_first = 0;
|
|
const u8 *tptr, *data;
|
|
|
|
/* Get the compressed symbol length from the first symbol byte. */
|
|
data = &kallsyms_names[*off];
|
|
len = *data;
|
|
data++;
|
|
|
|
/*
|
|
* Update the offset to return the offset for the next symbol on
|
|
* the compressed stream.
|
|
*/
|
|
*off += len + 1;
|
|
|
|
/*
|
|
* For every byte on the compressed symbol data, copy the table
|
|
* entry for that byte.
|
|
*/
|
|
while (len) {
|
|
tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
|
|
data++;
|
|
len--;
|
|
|
|
while (*tptr) {
|
|
if (skipped_first) {
|
|
diff = *tptr - *name;
|
|
if (diff)
|
|
return diff;
|
|
name++;
|
|
nlen--;
|
|
if (!nlen)
|
|
break;
|
|
} else
|
|
skipped_first = 1;
|
|
tptr++;
|
|
}
|
|
if (!nlen)
|
|
break;
|
|
}
|
|
|
|
return diff;
|
|
}
|
|
|
|
static unsigned int kallsyms_expand_symbol(unsigned int off,
|
|
char *result, size_t maxlen)
|
|
{
|
|
int len, skipped_first = 0;
|
|
const u8 *tptr, *data;
|
|
|
|
/* Get the compressed symbol length from the first symbol byte. */
|
|
data = &kallsyms_names[off];
|
|
len = *data;
|
|
data++;
|
|
|
|
/*
|
|
* Update the offset to return the offset for the next symbol on
|
|
* the compressed stream.
|
|
*/
|
|
off += len + 1;
|
|
|
|
/*
|
|
* For every byte on the compressed symbol data, copy the table
|
|
* entry for that byte.
|
|
*/
|
|
while (len) {
|
|
tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
|
|
data++;
|
|
len--;
|
|
|
|
while (*tptr) {
|
|
if (skipped_first) {
|
|
if (maxlen <= 1)
|
|
goto tail;
|
|
*result = *tptr;
|
|
result++;
|
|
maxlen--;
|
|
} else
|
|
skipped_first = 1;
|
|
tptr++;
|
|
}
|
|
}
|
|
|
|
tail:
|
|
if (maxlen)
|
|
*result = '\0';
|
|
|
|
/* Return to offset to the next symbol. */
|
|
return off;
|
|
}
|
|
|
|
static int module_lookup_name_callback(void *data, const char *name,
|
|
struct module *mod, unsigned long addr)
|
|
{
|
|
struct oases_find_symbol *s = data;
|
|
|
|
if (strcmp(s->name, name))
|
|
return 0;
|
|
|
|
#if IS_ENABLED(CONFIG_MODULES)
|
|
if ((s->type == LOOKUP_MODULE) && strcmp(s->mod, mod->name))
|
|
return 0;
|
|
#endif
|
|
|
|
if (s->callback && !(*s->callback)((void *)addr)) {
|
|
return 0;
|
|
}
|
|
|
|
s->addr = (void *)addr;
|
|
s->count++;
|
|
s->module = mod;
|
|
|
|
if (s->count > 1) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*
|
|
* Return 1 if found a match symbol, otherwise 0 to continue search
|
|
* Note we search until all symbols checked, trying to solve duplicate
|
|
* symbol problem.
|
|
*/
|
|
static int vmlinux_lookup_name_callback(void *data, const char *name,
|
|
struct module *mod, unsigned long addr)
|
|
{
|
|
struct oases_find_symbol *s = data;
|
|
|
|
if (strcmp(s->name, name))
|
|
return 0;
|
|
|
|
if (s->callback && !(*s->callback)((void *)addr)) {
|
|
return 0;
|
|
}
|
|
|
|
s->addr = (void *)addr;
|
|
s->count++;
|
|
s->module = mod;
|
|
|
|
if (s->count > 1) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* search a symbol address throughout vmlinux or modules or both,
|
|
* return early if found duplicate symbols.
|
|
*/
|
|
static int oases_vmlinux_kallsyms_on_each_symbol(
|
|
struct oases_find_symbol *data)
|
|
{
|
|
int ret = 0, i;
|
|
char namebuf[KSYM_NAME_LEN];
|
|
int nlen;
|
|
unsigned int off = 0, tmp;
|
|
|
|
if (!data->name)
|
|
return 0;
|
|
nlen = strlen(data->name);
|
|
if (!nlen)
|
|
return 0;
|
|
|
|
for (i = 0; i < kallsyms_num_syms; i++) {
|
|
tmp = off;
|
|
ret = kallsyms_compare_symbol(&off, data->name, nlen);
|
|
/* only expand when prefix is matched */
|
|
if (ret == 0) {
|
|
kallsyms_expand_symbol(tmp, namebuf, ARRAY_SIZE(namebuf));
|
|
ret = vmlinux_lookup_name_callback(data,
|
|
namebuf, NULL, kallsyms_sym_address(i));
|
|
if (ret != 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int oases_module_kallsyms_on_each_symbol(
|
|
struct oases_find_symbol *data)
|
|
{
|
|
return module_kallsyms_on_each_symbol(module_lookup_name_callback, data);
|
|
}
|
|
|
|
static int get_lookup_type(const char *mod)
|
|
{
|
|
if (!mod)
|
|
return LOOKUP_BOTH;
|
|
|
|
if (strcmp(mod, VMLINUX) == 0)
|
|
return LOOKUP_VMLINUX;
|
|
|
|
return LOOKUP_MODULE;
|
|
}
|
|
|
|
/*
|
|
* Return 0 if success otherwise error code.
|
|
*
|
|
* further check on symbol size, attributes, T/t/W/w/D/d/A...
|
|
* e.g. 000000000000a2a0 A mc_buffer
|
|
*/
|
|
int oases_lookup_name_internal(struct oases_find_symbol *args)
|
|
{
|
|
int ret = 0;
|
|
|
|
args->type = get_lookup_type(args->mod);
|
|
if (args->type & LOOKUP_VMLINUX) {
|
|
oases_vmlinux_kallsyms_on_each_symbol(args);
|
|
}
|
|
|
|
if (args->type & LOOKUP_MODULE) {
|
|
oases_module_lock();
|
|
oases_module_kallsyms_on_each_symbol(args);
|
|
}
|
|
|
|
if (!args->addr || args->count != 1) {
|
|
oases_error("invalid args->addr or args->count\n");
|
|
ret = -EINVAL;
|
|
goto bail;
|
|
}
|
|
|
|
/* oases_lookup_name api shouldn't increase module ref */
|
|
if (!args->api_use) {
|
|
if (args->module && !oases_ref_module_ptr(args->module)) {
|
|
oases_error("oases_ref_module_ptr failed\n");
|
|
ret = -EINVAL;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
bail:
|
|
if (args->type & LOOKUP_MODULE) {
|
|
oases_module_unlock();
|
|
}
|
|
return ret;
|
|
}
|