runtime/sym.c - systemtap
Functions defined
Macros defined
Source code
#ifndef _STP_SYM_C_
#define _STP_SYM_C_
#include "sym.h"
#include "vma.c"
#include "stp_string.c"
#include <asm/unaligned.h>
#include <asm/uaccess.h>
#include <linux/list.h>
#include <linux/module.h>
#ifdef STAPCONF_PROBE_KERNEL
#include <linux/uaccess.h>
#endif
static unsigned long _stp_kmodule_relocate(const char *module,
const char *section,
unsigned long offset)
{
unsigned i, j;
dbug_sym(1, "%s, %s, %lx\n", module, section, offset);
if (!module || !strcmp(section, "")
||_stp_num_modules == 0) {
return offset;
}
for (i = 0; i < _stp_num_modules; i++) {
struct _stp_module *m = _stp_modules[i];
if (strcmp(module, m->name)) continue;
for (j = 0; j < m->num_sections; j++) {
struct _stp_section *s = &m->sections[j];
if (!strcmp(section, s->name)) {
if (s->static_addr != 0) {
unsigned long addr = offset + s->static_addr;
dbug_sym(1, "address=%lx\n", addr);
return addr;
} else {
dbug_sym(1, "section %s, not in memory yet?", s->name);
return 0;
}
}
}
}
return 0;
}
static unsigned long _stp_umodule_relocate(const char *path,
unsigned long offset,
struct task_struct *tsk)
{
unsigned i;
unsigned long vm_start = 0;
dbug_sym(1, "[%d] %s, %lx\n", tsk->pid, path, offset);
for (i = 0; i < _stp_num_modules; i++) {
struct _stp_module *m = _stp_modules[i];
if (strcmp(path, m->path)
|| m->num_sections != 1)
continue;
if (!strcmp(m->sections[0].name, ".absolute"))
return offset;
if (strcmp(m->sections[0].name, ".dynamic"))
continue;
if (stap_find_vma_map_info_user(tsk->group_leader, m,
&vm_start, NULL, NULL) == 0) {
offset += vm_start;
dbug_sym(1, "address=%lx\n", offset);
return offset;
}
}
return 0;
}
static struct _stp_module *_stp_kmod_sec_lookup(unsigned long addr,
struct _stp_section **sec)
{
unsigned midx = 0;
for (midx = 0; midx < _stp_num_modules; midx++)
{
unsigned secidx;
for (secidx = 0; secidx < _stp_modules[midx]->num_sections; secidx++)
{
unsigned long sec_addr;
unsigned long sec_size;
sec_addr = _stp_modules[midx]->sections[secidx].static_addr;
sec_size = _stp_modules[midx]->sections[secidx].size;
if (addr >= sec_addr && addr < sec_addr + sec_size)
{
if (sec)
*sec = & _stp_modules[midx]->sections[secidx];
return _stp_modules[midx];
}
}
}
return NULL;
}
static struct _stp_module *_stp_umod_lookup(unsigned long addr,
struct task_struct *task,
const char **name,
unsigned long *vm_start,
unsigned long *vm_end)
{
void *user = NULL;
#ifdef CONFIG_COMPAT
if (test_tsk_thread_flag(task, TIF_32BIT))
addr &= ((compat_ulong_t) ~0);
#endif
if (stap_find_vma_map_info(task->group_leader, addr,
vm_start, vm_end, name, &user) == 0)
if (user != NULL)
{
struct _stp_module *m = (struct _stp_module *)user;
dbug_sym(1, "found module %s at 0x%lx\n", m->path,
vm_start ? *vm_start : 0);
return m;
}
return NULL;
}
static const char *_stp_kallsyms_lookup(unsigned long addr,
unsigned long *symbolsize,
unsigned long *offset,
const char **modname,
struct task_struct *task)
{
struct _stp_module *m = NULL;
struct _stp_section *sec = NULL;
struct _stp_symbol *s = NULL;
unsigned end, begin = 0;
unsigned long rel_addr = 0;
if (addr == 0)
return NULL;
if (task)
{
unsigned long vm_start = 0;
unsigned long vm_end = 0;
#ifdef CONFIG_COMPAT
if (test_tsk_thread_flag(task, TIF_32BIT))
addr &= ((compat_ulong_t) ~0);
#endif
m = _stp_umod_lookup(addr, task, modname, &vm_start, &vm_end);
if (m)
{
sec = &m->sections[0];
XXX if (strcmp(".dynamic", m->sections[0].name) == 0)
rel_addr = addr - vm_start;
else
rel_addr = addr;
}
if (modname && *modname)
{
if (offset)
*offset = addr - vm_start;
if (symbolsize)
*symbolsize = vm_end - vm_start;
}
}
else
{
m = _stp_kmod_sec_lookup(addr, &sec);
if (m)
{
rel_addr = addr - sec->static_addr;
if (modname)
*modname = m->name;
}
}
if (unlikely (m == NULL || sec == NULL))
return NULL;
addr = rel_addr;
end = sec->num_symbols;
do {
unsigned mid = (begin + end) / 2;
if (addr < sec->symbols[mid].addr)
end = mid;
else
begin = mid;
} while (begin + 1 < end);
s = & sec->symbols[begin];
if (likely(addr >= s->addr)) {
if (offset)
*offset = addr - s->addr;
if (symbolsize) {
if ((begin + 1) < sec->num_symbols)
*symbolsize = sec->symbols[begin + 1].addr - s->addr;
else
*symbolsize = 0;
}
return s->symbol;
}
return NULL;
}
static int _stp_build_id_check (struct _stp_module *m,
unsigned long notes_addr,
struct task_struct *tsk)
{
int j;
for (j = 0; j < m->build_id_len; j++) {
mm_segment_t oldfs = get_fs();
int rc;
unsigned char theory, practice = 0;
#ifdef STAPCONF_PROBE_KERNEL
if (!tsk) {
theory = m->build_id_bits[j];
set_fs(KERNEL_DS);
rc = probe_kernel_read(&practice, (void*)(notes_addr + j), 1);
}
else
#endif
{
theory = m->build_id_bits[j];
set_fs (tsk ? USER_DS : KERNEL_DS);
#ifdef CONFIG_UTRACE
rc = get_user(practice, ((unsigned char*)(void*)(notes_addr + j)));
#else
if (!tsk || tsk == current) {
rc = get_user(practice, ((unsigned char*)(void*)(notes_addr + j)));
}
else {
rc = (__access_process_vm_noflush(tsk, (notes_addr + j), &practice,
1, 0) != 1);
}
#endif
}
set_fs(oldfs);
if (rc || (theory != practice)) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
_stp_error ("Build-id mismatch [man error::buildid]: \"%s\" byte %d (0x%02x vs 0x%02x) address %#lx rc %d\n",
m->path, j, theory, practice, notes_addr, rc);
return 1;
#else
_stp_warn (KERN_WARNING
"Build-id mismatch [man error::buildid]: \"%s\" byte %d (0x%02x vs 0x%02x) rc %d\n",
m->path, j, theory, practice, rc);
#endif
break;
} } return 0;
}
static int _stp_module_check(void)
{
struct _stp_module *m = NULL;
unsigned long notes_addr, base_addr;
unsigned i,j;
int rc = 0;
#ifdef STP_NO_BUILDID_CHECK
return 0;
#endif
for (i = 0; i < _stp_num_modules; i++)
{
m = _stp_modules[i];
if (m->build_id_len > 0 && m->notes_sect != 0) {
dbug_sym(1, "build-id validation [%s]\n", m->name);
if (m->name[0] != '/') continue;
if (!strcmp(m->name, "kernel")) {
notes_addr = _stp_kmodule_relocate("kernel",
"_stext", m->build_id_offset);
base_addr = _stp_kmodule_relocate("kernel",
"_stext", 0);
} else {
notes_addr = m->notes_sect + m->build_id_offset;
base_addr = m->notes_sect;
}
if (notes_addr <= base_addr) { _stp_warn ("build-id address %lx <= base %lx\n",
notes_addr, base_addr);
continue;
}
rc |= _stp_build_id_check (m, notes_addr, NULL);
} }
return rc;
}
static int _stp_kmodule_check (const char *name)
{
struct _stp_module *m = NULL;
unsigned long notes_addr, base_addr;
unsigned i,j;
#ifdef STP_NO_BUILDID_CHECK
return 0;
#endif
WARN_ON(!name || name[0]=='/');
for (i = 0; i < _stp_num_modules; i++)
{
m = _stp_modules[i];
if (strcmp (name, m->name)) continue;
if (m->build_id_len > 0 && m->notes_sect != 0) {
dbug_sym(1, "build-id validation [%s]\n", m->name);
notes_addr = m->notes_sect + m->build_id_offset;
base_addr = m->notes_sect;
if (notes_addr <= base_addr) { _stp_warn ("build-id address %lx < base %lx\n",
notes_addr, base_addr);
continue;
}
return _stp_build_id_check (m, notes_addr, NULL);
} }
return 0; }
static int _stp_usermodule_check(struct task_struct *tsk, const char *path_name, unsigned long addr)
{
struct _stp_module *m = NULL;
unsigned long notes_addr;
unsigned i, j;
unsigned char practice_id_bits[MAXSTRINGLEN];
unsigned long vm_end = 0;
#ifdef STP_NO_BUILDID_CHECK
return 0;
#endif
WARN_ON(!path_name || path_name[0]!='/');
for (i = 0; i < _stp_num_modules; i++)
{
m = _stp_modules[i];
if (strcmp(path_name, m->path) != 0) continue;
if (m->build_id_len > 0) {
int ret, build_id_len;
notes_addr = addr + m->build_id_offset ;
dbug_sym(1, "build-id validation [%d %s] address=%#lx build_id_offset=%#lx\n",
tsk->pid, m->path, addr, m->build_id_offset);
if (notes_addr <= addr) {
_stp_warn ("build-id address %lx < base %lx\n", notes_addr, addr);
continue;
}
return _stp_build_id_check (m, notes_addr, tsk);
}
}
return 0; }
static int _stp_snprint_addr(char *str, size_t len, unsigned long address,
int flags, struct task_struct *task)
{
const char *modname = NULL;
const char *name = NULL;
unsigned long offset = 0, size = 0;
char *exstr, *poststr, *prestr;
prestr = (flags & _STP_SYM_PRE_SPACE) ? " " : "";
exstr = (((flags & _STP_SYM_INEXACT) && (flags & _STP_SYM_SYMBOL))
? " (inexact)" : "");
if (flags & _STP_SYM_POST_SPACE)
poststr = " ";
else if (flags & _STP_SYM_NEWLINE)
poststr = "\n";
else
poststr = "";
if (flags & (_STP_SYM_SYMBOL | _STP_SYM_MODULE)) {
name = _stp_kallsyms_lookup(address, &size, &offset, &modname, task);
if (name && name[0] == '.')
name++;
}
if (modname && (flags & _STP_SYM_MODULE_BASENAME)) {
char *slash = strrchr (modname, '/');
if (slash)
modname = slash+1;
}
if (name && (flags & _STP_SYM_SYMBOL)) {
if ((flags & _STP_SYM_MODULE) && modname && *modname) {
if (flags & _STP_SYM_OFFSET) {
if (flags & _STP_SYM_SIZE) {
if (flags & _STP_SYM_HEX_SYMBOL)
return _stp_snprintf(str, len, "%s%p : %s+%#lx/%#lx [%s]%s%s",
prestr, (int64_t) address,
name, offset, size, modname,
exstr, poststr);
else
return _stp_snprintf(str, len, "%s%s+%#lx/%#lx [%s]%s%s",
prestr, name, offset, size,
modname, exstr, poststr);
} else {
if (flags & _STP_SYM_HEX_SYMBOL)
return _stp_snprintf(str, len, "%s%p : %s+%#lx [%s]%s%s",
prestr, (int64_t) address,
name, offset, modname,
exstr, poststr);
else
return _stp_snprintf(str, len, "%s%s+%#lx [%s]%s%s",
prestr, name, offset,
modname, exstr, poststr);
}
} else {
if (flags & _STP_SYM_HEX_SYMBOL)
return _stp_snprintf(str, len, "%s%p : %s [%s]%s%s", prestr,
(int64_t) address, name, modname,
exstr, poststr);
else
return _stp_snprintf(str, len, "%s%s [%s]%s%s", prestr, name,
modname, exstr, poststr);
}
} else if (flags & _STP_SYM_OFFSET) {
if (flags & _STP_SYM_SIZE) {
if (flags & _STP_SYM_HEX_SYMBOL)
return _stp_snprintf(str, len, "%s%p : %s+%#lx/%#lx%s%s", prestr,
(int64_t) address, name, offset,
size, exstr, poststr);
else
return _stp_snprintf(str, len, "%s%s+%#lx/%#lx%s%s", prestr, name,
offset, size, exstr, poststr);
} else {
if (flags & _STP_SYM_HEX_SYMBOL)
return _stp_snprintf(str, len, "%s%p : %s+%#lx%s%s", prestr,
(int64_t) address, name, offset,
exstr, poststr);
else
return _stp_snprintf(str, len, "%s%s+%#lx%s%s", prestr, name,
offset, exstr, poststr);
}
} else {
if (flags & _STP_SYM_HEX_SYMBOL)
return _stp_snprintf(str, len, "%s%p : %s%s%s", prestr,
(int64_t) address, name, exstr, poststr);
else
return _stp_snprintf(str, len, "%s%s%s%s", prestr, name,
exstr, poststr);
}
} else {
if (modname && *modname && (flags & _STP_SYM_MODULE)) {
if (flags & _STP_SYM_OFFSET) {
if (flags & _STP_SYM_SIZE) {
return _stp_snprintf(str, len, "%s%p [%s+%#lx/%#lx]%s%s", prestr,
(int64_t) address, modname, offset,
size, exstr, poststr);
} else {
return _stp_snprintf(str, len, "%s%p [%s+%#lx]%s%s", prestr,
(int64_t) address, modname, offset,
exstr, poststr);
}
} else {
return _stp_snprintf(str, len, "%s%p [%s]%s%s", prestr,
(int64_t) address, modname, exstr, poststr);
}
#ifdef STAPCONF_MODULE_TEXT_ADDRESS
} if ((flags & _STP_SYM_MODULE) && ! task) {
struct module *ko;
preempt_disable();
ko = __module_text_address (address);
if (ko && ko->name)
{
int ret = _stp_snprintf(str, len, "%s%p [%s]%s%s", prestr,
(int64_t) address, ko->name, exstr, poststr);
preempt_enable_no_resched();
return ret;
}
preempt_enable_no_resched();
return _stp_snprintf(str, len, "%s%p%s%s", prestr,
(int64_t) address, exstr, poststr);
#endif
} else {
return _stp_snprintf(str, len, "%s%p%s%s", prestr,
(int64_t) address, exstr, poststr);
}
}
}
static void _stp_print_addr(unsigned long address, int flags,
struct task_struct *task)
{
_stp_snprint_addr(NULL, 0, address, flags, task);
}
static void _stp_kmodule_update_address(const char* module,
const char* reloc, unsigned long address)
{
unsigned mi, si;
for (mi=0; mi<_stp_num_modules; mi++)
{
const char *note_sectname = ".note.gnu.build-id";
if (strcmp (_stp_modules[mi]->name, module))
continue;
if (reloc && !strcmp (note_sectname, reloc)) {
dbug_sym(1, "module %s special section %s address %#lx\n",
_stp_modules[mi]->name,
note_sectname,
address);
_stp_modules[mi]->notes_sect = address; }
for (si=0; si<_stp_modules[mi]->num_sections; si++)
{
if (reloc && strcmp (_stp_modules[mi]->sections[si].name, reloc))
continue;
else
{
dbug_sym(1, "module %s section %s address %#lx\n",
_stp_modules[mi]->name,
_stp_modules[mi]->sections[si].name,
address);
_stp_modules[mi]->sections[si].static_addr = address;
if (reloc) break;
else continue; }
} } }
#ifndef STAPCONF_KALLSYMS
unsigned long kallsyms_lookup_name (const char *name)
{
return 0;
}
#endif
#endif