freebsd-nq/sys/ddb/db_main.c
Roger Pau Monné c98a2727cc ddb: allow specifying the exact address of the symtab and strtab
When the FreeBSD kernel is loaded from Xen the symtab and strtab are
not loaded the same way as the native boot loader. This patch adds
three new global variables to ddb that can be used to specify the
exact position and size of those tables, so they can be directly used
as parameters to db_add_symbol_table. A new helper is introduced, so callers
that used to set ksym_start and ksym_end can use this helper to set the new
variables.

It also adds support for loading them from the Xen PVH port, that was
previously missing those tables.

Sponsored by: Citrix Systems R&D
Reviewed by:	kib

ddb/db_main.c:
 - Add three new global variables: ksymtab, kstrtab, ksymtab_size that
   can be used to specify the position and size of the symtab and
   strtab.
 - Use those new variables in db_init in order to call db_add_symbol_table.
 - Move the logic in db_init to db_fetch_symtab in order to set ksymtab,
   kstrtab, ksymtab_size from ksym_start and ksym_end.

ddb/ddb.h:
 - Add prototype for db_fetch_ksymtab.
 - Declate the extern variables ksymtab, kstrtab and ksymtab_size.

x86/xen/pv.c:
 - Add support for finding the symtab and strtab when booted as a Xen
   PVH guest. Since Xen loads the symtab and strtab as NetBSD expects
   to find them we have to adapt and use the same method.

amd64/amd64/machdep.c:
arm/arm/machdep.c:
i386/i386/machdep.c:
mips/mips/machdep.c:
pc98/pc98/machdep.c:
powerpc/aim/machdep.c:
powerpc/booke/machdep.c:
sparc64/sparc64/machdep.c:
 - Use the newly introduced db_fetch_ksymtab in order to set ksymtab,
   kstrtab and ksymtab_size.
2014-09-25 08:28:10 +00:00

283 lines
6.6 KiB
C

/*-
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cons.h>
#include <sys/linker.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/pcpu.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/sysctl.h>
#include <machine/kdb.h>
#include <machine/pcb.h>
#include <machine/setjmp.h>
#include <ddb/ddb.h>
#include <ddb/db_command.h>
#include <ddb/db_sym.h>
SYSCTL_NODE(_debug, OID_AUTO, ddb, CTLFLAG_RW, 0, "DDB settings");
static dbbe_init_f db_init;
static dbbe_trap_f db_trap;
static dbbe_trace_f db_trace_self_wrapper;
static dbbe_trace_thread_f db_trace_thread_wrapper;
KDB_BACKEND(ddb, db_init, db_trace_self_wrapper, db_trace_thread_wrapper,
db_trap);
/*
* Symbols can be loaded by specifying the exact addresses of
* the symtab and strtab in memory. This is used when loaded from
* boot loaders different than the native one (like Xen).
*/
vm_offset_t ksymtab, kstrtab, ksymtab_size;
boolean_t
X_db_line_at_pc(db_symtab_t *symtab, c_db_sym_t sym, char **file, int *line,
db_expr_t off)
{
return (FALSE);
}
c_db_sym_t
X_db_lookup(db_symtab_t *symtab, const char *symbol)
{
c_linker_sym_t lsym;
Elf_Sym *sym;
if (symtab->private == NULL) {
return ((c_db_sym_t)((!linker_ddb_lookup(symbol, &lsym))
? lsym : NULL));
} else {
sym = (Elf_Sym *)symtab->start;
while ((char *)sym < symtab->end) {
if (sym->st_name != 0 &&
!strcmp(symtab->private + sym->st_name, symbol))
return ((c_db_sym_t)sym);
sym++;
}
}
return (NULL);
}
c_db_sym_t
X_db_search_symbol(db_symtab_t *symtab, db_addr_t off, db_strategy_t strat,
db_expr_t *diffp)
{
c_linker_sym_t lsym;
Elf_Sym *sym, *match;
unsigned long diff;
if (symtab->private == NULL) {
if (!linker_ddb_search_symbol((caddr_t)off, &lsym, &diff)) {
*diffp = (db_expr_t)diff;
return ((c_db_sym_t)lsym);
}
return (NULL);
}
diff = ~0UL;
match = NULL;
for (sym = (Elf_Sym*)symtab->start; (char*)sym < symtab->end; sym++) {
if (sym->st_name == 0)
continue;
if (off < sym->st_value)
continue;
if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT &&
ELF_ST_TYPE(sym->st_info) != STT_FUNC &&
ELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
continue;
if ((off - sym->st_value) > diff)
continue;
if ((off - sym->st_value) < diff) {
diff = off - sym->st_value;
match = sym;
} else {
if (match == NULL)
match = sym;
else if (ELF_ST_BIND(match->st_info) == STB_LOCAL &&
ELF_ST_BIND(sym->st_info) != STB_LOCAL)
match = sym;
}
if (diff == 0) {
if (strat == DB_STGY_PROC &&
ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
ELF_ST_BIND(sym->st_info) != STB_LOCAL)
break;
if (strat == DB_STGY_ANY &&
ELF_ST_BIND(sym->st_info) != STB_LOCAL)
break;
}
}
*diffp = (match == NULL) ? off : diff;
return ((c_db_sym_t)match);
}
boolean_t
X_db_sym_numargs(db_symtab_t *symtab, c_db_sym_t sym, int *nargp,
char **argp)
{
return (FALSE);
}
void
X_db_symbol_values(db_symtab_t *symtab, c_db_sym_t sym, const char **namep,
db_expr_t *valp)
{
linker_symval_t lval;
if (symtab->private == NULL) {
linker_ddb_symbol_values((c_linker_sym_t)sym, &lval);
if (namep != NULL)
*namep = (const char*)lval.name;
if (valp != NULL)
*valp = (db_expr_t)lval.value;
} else {
if (namep != NULL)
*namep = (const char *)symtab->private +
((const Elf_Sym *)sym)->st_name;
if (valp != NULL)
*valp = (db_expr_t)((const Elf_Sym *)sym)->st_value;
}
}
int
db_fetch_ksymtab(vm_offset_t ksym_start, vm_offset_t ksym_end)
{
Elf_Size strsz;
if (ksym_end > ksym_start && ksym_start != 0) {
ksymtab = ksym_start;
ksymtab_size = *(Elf_Size*)ksymtab;
ksymtab += sizeof(Elf_Size);
kstrtab = ksymtab + ksymtab_size;
strsz = *(Elf_Size*)kstrtab;
kstrtab += sizeof(Elf_Size);
if (kstrtab + strsz > ksym_end) {
/* Sizes doesn't match, unset everything. */
ksymtab = ksymtab_size = kstrtab = 0;
}
}
if (ksymtab == 0 || ksymtab_size == 0 || kstrtab == 0)
return (-1);
return (0);
}
static int
db_init(void)
{
db_command_init();
if (ksymtab != 0 && kstrtab != 0 && ksymtab_size != 0) {
db_add_symbol_table((char *)ksymtab,
(char *)(ksymtab + ksymtab_size), "elf", (char *)kstrtab);
}
db_add_symbol_table(NULL, NULL, "kld", NULL);
return (1); /* We're the default debugger. */
}
static int
db_trap(int type, int code)
{
jmp_buf jb;
void *prev_jb;
boolean_t bkpt, watchpt;
const char *why;
/*
* Don't handle the trap if the console is unavailable (i.e. it
* is in graphics mode).
*/
if (cnunavailable())
return (0);
bkpt = IS_BREAKPOINT_TRAP(type, code);
watchpt = IS_WATCHPOINT_TRAP(type, code);
if (db_stop_at_pc(&bkpt)) {
if (db_inst_count) {
db_printf("After %d instructions (%d loads, %d stores),\n",
db_inst_count, db_load_count, db_store_count);
}
prev_jb = kdb_jmpbuf(jb);
if (setjmp(jb) == 0) {
db_dot = PC_REGS();
db_print_thread();
if (bkpt)
db_printf("Breakpoint at\t");
else if (watchpt)
db_printf("Watchpoint at\t");
else
db_printf("Stopped at\t");
db_print_loc_and_inst(db_dot);
}
why = kdb_why;
db_script_kdbenter(why != KDB_WHY_UNSET ? why : "unknown");
db_command_loop();
(void)kdb_jmpbuf(prev_jb);
}
db_restart_at_pc(watchpt);
return (1);
}
static void
db_trace_self_wrapper(void)
{
jmp_buf jb;
void *prev_jb;
prev_jb = kdb_jmpbuf(jb);
if (setjmp(jb) == 0)
db_trace_self();
(void)kdb_jmpbuf(prev_jb);
}
static void
db_trace_thread_wrapper(struct thread *td)
{
jmp_buf jb;
void *prev_jb;
prev_jb = kdb_jmpbuf(jb);
if (setjmp(jb) == 0)
db_trace_thread(td, -1);
(void)kdb_jmpbuf(prev_jb);
}