Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
215 lines
5.2 KiB
C
215 lines
5.2 KiB
C
#include <common.h>
|
|
#include <init.h>
|
|
#include <kallsyms.h>
|
|
#include <asm/sections.h>
|
|
|
|
/* These will be re-linked against their real values during the second link stage */
|
|
extern const unsigned long kallsyms_addresses[] __attribute__((weak));
|
|
extern const unsigned long kallsyms_num_syms __attribute__((weak));
|
|
extern const u8 kallsyms_names[] __attribute__((weak));
|
|
|
|
extern const u8 kallsyms_token_table[] __attribute__((weak));
|
|
extern const u16 kallsyms_token_index[] __attribute__((weak));
|
|
|
|
extern const unsigned long kallsyms_markers[] __attribute__((weak));
|
|
|
|
static inline int is_kernel_text(unsigned long addr)
|
|
{
|
|
if ((addr >= (unsigned long)_stext && addr <= (unsigned long)_etext))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* expand a compressed symbol data into the resulting uncompressed string,
|
|
given the offset to where the symbol is in the compressed stream */
|
|
static unsigned int kallsyms_expand_symbol(unsigned int off, char *result)
|
|
{
|
|
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) {
|
|
*result = *tptr;
|
|
result++;
|
|
} else
|
|
skipped_first = 1;
|
|
tptr++;
|
|
}
|
|
}
|
|
|
|
*result = '\0';
|
|
|
|
/* return to offset to the next symbol */
|
|
return off;
|
|
}
|
|
|
|
/*
|
|
* Find the offset on the compressed stream given and index in the
|
|
* kallsyms array.
|
|
*/
|
|
static unsigned int get_symbol_offset(unsigned long pos)
|
|
{
|
|
const u8 *name;
|
|
int i;
|
|
|
|
/*
|
|
* Use the closest marker we have. We have markers every 256 positions,
|
|
* so that should be close enough.
|
|
*/
|
|
name = &kallsyms_names[kallsyms_markers[pos >> 8]];
|
|
|
|
/*
|
|
* Sequentially scan all the symbols up to the point we're searching
|
|
* for. Every symbol is stored in a [<len>][<len> bytes of data] format,
|
|
* so we just need to add the len to the current pointer for every
|
|
* symbol we wish to skip.
|
|
*/
|
|
for (i = 0; i < (pos & 0xFF); i++)
|
|
name = name + (*name) + 1;
|
|
|
|
return name - kallsyms_names;
|
|
}
|
|
|
|
/* Lookup the address for this symbol. Returns 0 if not found. */
|
|
unsigned long kallsyms_lookup_name(const char *name)
|
|
{
|
|
char namebuf[KSYM_NAME_LEN];
|
|
unsigned long i;
|
|
unsigned int off;
|
|
|
|
for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
|
|
off = kallsyms_expand_symbol(off, namebuf);
|
|
|
|
if (strcmp(namebuf, name) == 0)
|
|
return kallsyms_addresses[i];
|
|
}
|
|
|
|
/* module kallsyms not yet supported */
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
|
|
/* This kernel should never had been booted. */
|
|
BUG_ON(!kallsyms_addresses);
|
|
|
|
/* Do a binary search on the sorted kallsyms_addresses array. */
|
|
low = 0;
|
|
high = kallsyms_num_syms;
|
|
|
|
while (high - low > 1) {
|
|
mid = low + (high - low) / 2;
|
|
if (kallsyms_addresses[mid] <= addr)
|
|
low = mid;
|
|
else
|
|
high = mid;
|
|
}
|
|
|
|
/*
|
|
* Search for the first aliased symbol. Aliased
|
|
* symbols are symbols with the same address.
|
|
*/
|
|
while (low && kallsyms_addresses[low-1] == kallsyms_addresses[low])
|
|
--low;
|
|
|
|
symbol_start = kallsyms_addresses[low];
|
|
|
|
/* Search for next non-aliased symbol. */
|
|
for (i = low + 1; i < kallsyms_num_syms; i++) {
|
|
if (kallsyms_addresses[i] > symbol_start) {
|
|
symbol_end = kallsyms_addresses[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If we found no next symbol, we use the end of the section. */
|
|
if (!symbol_end) {
|
|
symbol_end = (unsigned long)_etext;
|
|
}
|
|
|
|
if (symbolsize)
|
|
*symbolsize = symbol_end - symbol_start;
|
|
if (offset)
|
|
*offset = addr - symbol_start;
|
|
|
|
return low;
|
|
}
|
|
|
|
/*
|
|
* Lookup an address
|
|
* - modname is set to NULL if it's in the kernel.
|
|
* - We guarantee that the returned name is valid until we reschedule even if.
|
|
* It resides in a module.
|
|
* - We also guarantee that modname will be valid until rescheduled.
|
|
*/
|
|
static const char *kallsyms_lookup(unsigned long addr,
|
|
unsigned long *symbolsize,
|
|
unsigned long *offset,
|
|
char **modname, char *namebuf)
|
|
{
|
|
namebuf[KSYM_NAME_LEN - 1] = 0;
|
|
namebuf[0] = 0;
|
|
|
|
if (is_kernel_text(addr)) {
|
|
unsigned long pos;
|
|
|
|
pos = get_symbol_pos(addr, symbolsize, offset);
|
|
/* Grab name */
|
|
kallsyms_expand_symbol(get_symbol_offset(pos), namebuf);
|
|
if (modname)
|
|
*modname = NULL;
|
|
return namebuf;
|
|
}
|
|
|
|
/* moduled not yet supported in kallsyms */
|
|
return NULL;
|
|
}
|
|
|
|
/* Look up a kernel symbol and return it in a text buffer. */
|
|
int sprint_symbol(char *buffer, unsigned long address)
|
|
{
|
|
char *modname;
|
|
const char *name;
|
|
unsigned long offset, size;
|
|
int len;
|
|
|
|
name = kallsyms_lookup(address, &size, &offset, &modname, buffer);
|
|
if (!name)
|
|
return sprintf(buffer, "0x%lx", address);
|
|
|
|
if (name != buffer)
|
|
strcpy(buffer, name);
|
|
len = strlen(buffer);
|
|
buffer += len;
|
|
|
|
if (modname)
|
|
len += sprintf(buffer, "+%#lx/%#lx [%s]",
|
|
offset, size, modname);
|
|
else
|
|
len += sprintf(buffer, "+%#lx/%#lx", offset, size);
|
|
|
|
return len;
|
|
}
|
|
EXPORT_SYMBOL_GPL(sprint_symbol);
|