blob: 0a145e2c6346bbd5369f51754f20c935a35d799b [file] [log] [blame] [edit]
#ifdef __SHARC__
#pragma nosharc
#endif
#include <stab.h>
#include <string.h>
#include <assert.h>
#include <kdebug.h>
#include <pmap.h>
#include <process.h>
#include <kmalloc.h>
#include <ros/memlayout.h>
// Beginning of stabs table
extern const stab_t (RO BND(__this,__STAB_END__) __STAB_BEGIN__)[];
// End of stabs table
extern const stab_t (RO SNT __STAB_END__)[];
// Beginning of string table
extern const char (RO NT BND(__this,__STABSTR_END__) __STABSTR_BEGIN__)[];
// End of string table
extern const char (RO SNT __STABSTR_END__)[];
typedef struct UserStabData {
const stab_t *BND(__this,stab_end) stabs;
const stab_t *SNT stab_end;
const char *NT BND(__this, stabstr_end) stabstr;
const char *SNT stabstr_end;
} user_stab_data_t;
/* We used to check for a null terminating byte for the entire strings section
* (due to JOS, I think), but that's not what the spec says: only that all
* strings are null terminated. There might be random stuff tacked on at the
* end. I had some stabs that seemed valid (lookups worked), that did not have
* the entire table be null terminated. Still, something else might be jacked
* up. If it turns out that's the case, put the checks in here. */
static bool stab_table_valid(const char *stabstr, const char *stabstr_end)
{
if (stabstr_end <= stabstr)
return FALSE;
return TRUE;
}
// stab_binsearch(stabs, region_left, region_right, type, addr)
//
// Some stab types are arranged in increasing order by instruction
// address. For example, N_FUN stabs (stab entries with n_type ==
// N_FUN), which mark functions, and N_SO stabs, which mark source files.
//
// Given an instruction address, this function finds the single stab
// entry of type 'type' that contains that address.
//
// The search takes place within the range [*region_left, *region_right].
// Thus, to search an entire set of N stabs, you might do:
//
// left = 0;
// right = N - 1; /* rightmost stab */
// stab_binsearch(stabs, &left, &right, type, addr);
//
// The search modifies *region_left and *region_right to bracket the
// 'addr'. *region_left points to the matching stab that contains
// 'addr', and *region_right points just before the next stab. If
// *region_left > *region_right, then 'addr' is not contained in any
// matching stab.
//
// For example, given these N_SO stabs:
// Index Type Address
// 0 SO f0100000
// 13 SO f0100040
// 117 SO f0100176
// 118 SO f0100178
// 555 SO f0100652
// 556 SO f0100654
// 657 SO f0100849
// this code:
// left = 0, right = 657;
// stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184);
// will exit setting left = 118, right = 554.
//
static void
stab_binsearch(const stab_t *BND(__this, stab_end) stabs,
const stab_t *SNT stab_end,
int *region_left, int *region_right,
int type, uintptr_t addr)
{
int l = *region_left, r = *region_right, any_matches = 0;
while (l <= r) {
int true_m = (l + r) / 2, m = true_m;
// search for earliest stab with right type
while (m >= l && stabs[m].n_type != type)
m--;
if (m < l) { // no match in [l, m]
l = true_m + 1;
continue;
}
// actual binary search
any_matches = 1;
if (stabs[m].n_value < addr) {
*region_left = m;
l = true_m + 1;
} else if (stabs[m].n_value > addr) {
*region_right = m - 1;
r = m - 1;
} else {
// exact match for 'addr', but continue loop to find
// *region_right
*region_left = m;
l = m;
addr++;
}
}
if (!any_matches)
*region_right = *region_left - 1;
else {
// find rightmost region containing 'addr'
for (l = *region_right;
l > *region_left && stabs[l].n_type != type;
l--)
/* do nothing */;
*region_left = l;
}
}
// debuginfo_eip(addr, info)
//
// Fill in the 'info' structure with information about the specified
// instruction address, 'addr'. Returns 0 if information was found, and
// negative if not. But even if it returns negative it has stored some
// information into '*info'.
//
int
debuginfo_eip(uintptr_t addr, eipdebuginfo_t *NONNULL info)
{
const stab_t *SNT stab_end;
const stab_t *BND(__this,stab_end) stabs;
const char *SNT stabstr_end;
const char *NT BND(__this,stabstr_end) stabstr;
int lfile, rfile, lfun, rfun, lline, rline;
// Initialize *info
info->eip_file = "<unknown>";
info->eip_line = 0;
info->eip_fn_name = "<unknown>";
info->eip_fn_namelen = 9;
info->eip_fn_addr = addr;
info->eip_fn_narg = 0;
// Find the relevant set of stabs
if (addr >= ULIM) {
stab_end = __STAB_END__;
stabs = __STAB_BEGIN__;
stabstr_end = __STABSTR_END__;
stabstr = __STABSTR_BEGIN__;
} else {
/* TODO: short circuiting this, til our user space apps pack stab data
* the kernel knows about */
return -1;
#if 0
// The user-application linker script, user/user.ld,
// puts information about the application's stabs (equivalent
// to __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__, and
// __STABSTR_END__) in a structure located at virtual address
// USTABDATA.
const user_stab_data_t *usd = (const user_stab_data_t *COUNT(1))TC(USTABDATA);
// Make sure this memory is valid.
// Return -1 if it is not. Hint: Call user_mem_check.
// LAB 3: Your code here.
stab_end = usd->stab_end;
stabs = usd->stabs;
stabstr_end = usd->stabstr_end;
stabstr = usd->stabstr;
// Make sure the STABS and string table memory is valid.
// LAB 3: Your code here.
#endif
}
if (!stab_table_valid(stabstr, stabstr_end))
return -1;
// Now we find the right stabs that define the function containing
// 'eip'. First, we find the basic source file containing 'eip'.
// Then, we look in that source file for the function. Then we look
// for the line number.
// Search the entire set of stabs for the source file (type N_SO).
lfile = 0;
rfile = (stab_end - stabs) - 1;
stab_binsearch(stabs, stab_end, &lfile, &rfile, N_SO, addr);
if (lfile == 0)
return -1;
// Search within that file's stabs for the function definition
// (N_FUN).
lfun = lfile;
rfun = rfile;
stab_binsearch(stabs, stab_end, &lfun, &rfun, N_FUN, addr);
if (lfun <= rfun) {
// stabs[lfun] points to the function name
// in the string table, but check bounds just in case.
if (stabs[lfun].n_strx < stabstr_end - stabstr)
info->eip_fn_name = stabstr + stabs[lfun].n_strx;
info->eip_fn_addr = stabs[lfun].n_value;
addr -= info->eip_fn_addr;
// Search within the function definition for the line number.
lline = lfun;
rline = rfun;
} else {
// Couldn't find function stab! Maybe we're in an assembly
// file. Search the whole file for the line number.
info->eip_fn_addr = addr;
lline = lfile;
rline = rfile;
}
// Ignore stuff after the colon.
info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name;
// Search within [lline, rline] for the line number stab.
// If found, set info->eip_line to the right line number.
// If not found, return -1.
//
// Hint:
// There's a particular stabs type used for line numbers.
// Look at the STABS documentation and <inc/stab.h> to find
// which one.
// Your code here.
stab_binsearch(stabs, stab_end, &lline, &rline, N_SLINE, addr);
if (lline <= rline)
// stabs[lline] points to the line number
info->eip_line = stabs[lline].n_value;
else
return -1;
// Search backwards from the line number for the relevant filename
// stab.
// We can't just use the "lfile" stab because inlined functions
// can interpolate code from a different file!
// Such included source files use the N_SOL stab type.
while (lline >= lfile
&& stabs[lline].n_type != N_SOL
&& (stabs[lline].n_type != N_SO || !stabs[lline].n_value))
lline--;
if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr)
info->eip_file = stabstr + stabs[lline].n_strx;
// Set eip_fn_narg to the number of arguments taken by the function,
// or 0 if there was no containing function.
// Your code here.
info->eip_fn_narg = 0;
if (lfun <= rfun) {
lfun++;
while (stabs[lfun++].n_type == N_PSYM)
info->eip_fn_narg++;
}
return 0;
}
/* Returns a function pointer for a function name matching the given string. */
void *debug_get_fn_addr(char *fn_name)
{
const struct stab *SNT stab_end = __STAB_END__;
const struct stab *BND(__this,stab_end) stabs = __STAB_BEGIN__;
const char *SNT stabstr_end = __STABSTR_END__;
const char *NT BND(__this,stabstr_end) stabstr = __STABSTR_BEGIN__;
static int first_fn_idx = 0;
int i = first_fn_idx;
int len;
const char *stab_fn_name = 0;
void *retval = 0;
if (!stab_table_valid(stabstr, stabstr_end))
return 0;
for (/* i set */; &stabs[i] < stab_end; i++) {
if (stabs[i].n_type != N_FUN)
continue;
first_fn_idx = first_fn_idx ? first_fn_idx : i;
/* broken stab, just keep going */
if (!(stabs[i].n_strx < stabstr_end - stabstr))
continue;
stab_fn_name = stabstr + stabs[i].n_strx;
len = strfind(stab_fn_name, ':') - stab_fn_name;
if (!len)
continue;
/* we have a match. */
if (!strncmp(stab_fn_name, fn_name, len)) {
printd("FN name: %s, Addr: %p\n", stab_fn_name, stabs[i].n_value);
retval = (void*)stabs[i].n_value;
break;
}
}
return retval;
}
size_t backtrace_list(uintptr_t pc, uintptr_t fp, uintptr_t *pcs,
size_t nr_slots)
{
size_t nr_pcs = 0;
while (fp && nr_pcs < nr_slots) {
/* could put some sanity checks in here... */
pcs[nr_pcs++] = pc;
printd("PC %p FP %p\n", pc, fp);
if (!is_kaddr((void*)fp))
break;
/* PC becomes the retaddr - 1. the -1 is to put our PC back inside the
* function that called us. this was necessary in case we called as the
* last instruction in a function (would have to never return). not
* sure how necessary this still is. */
pc = *(uintptr_t*)(fp + sizeof(uintptr_t)) - 1;
fp = *(uintptr_t*)fp;
}
return nr_pcs;
}
void backtrace_frame(uintptr_t eip, uintptr_t ebp)
{
char *func_name;
#define MAX_BT_DEPTH 20
uintptr_t pcs[MAX_BT_DEPTH];
size_t nr_pcs = backtrace_list(eip, ebp, pcs, MAX_BT_DEPTH);
for (int i = 0; i < nr_pcs; i++) {
func_name = get_fn_name(pcs[i]);
printk("#%02d [<%p>] in %s\n", i + 1, pcs[i], func_name);
kfree(func_name);
}
}
void backtrace(void)
{
uintptr_t ebp, eip;
ebp = read_bp();
/* retaddr is right above ebp on the stack. we subtract an additional 1 to
* make sure the eip we get is actually in the function that called us.
* i had a couple cases early on where call was the last instruction in a
* function, and simply reading the retaddr would point into another
* function (the next one in the object) */
eip = *(uintptr_t*)(ebp + sizeof(uintptr_t)) - 1;
/* jump back a frame (out of backtrace) */
ebp = *(uintptr_t*)ebp;
printk("Stack Backtrace on Core %d:\n", core_id());
backtrace_frame(eip, ebp);
}
/* Assumes 32-bit header */
void print_fpu_state(struct ancillary_state *fpu)
{
printk("fcw: 0x%04x\n", fpu->fp_head_n64.fcw);
printk("fsw: 0x%04x\n", fpu->fp_head_n64.fsw);
printk("ftw: 0x%02x\n", fpu->fp_head_n64.ftw);
printk("fop: 0x%04x\n", fpu->fp_head_n64.fop);
printk("fpu_ip: 0x%08x\n", fpu->fp_head_n64.fpu_ip);
printk("cs: 0x%04x\n", fpu->fp_head_n64.cs);
printk("fpu_dp: 0x%08x\n", fpu->fp_head_n64.fpu_dp);
printk("ds: 0x%04x\n", fpu->fp_head_n64.ds);
printk("mxcsr: 0x%08x\n", fpu->fp_head_n64.mxcsr);
printk("mxcsrm: 0x%08x\n", fpu->fp_head_n64.mxcsr_mask);
for (int i = 0; i < sizeof(struct ancillary_state); i++) {
if (i % 20 == 0)
printk("\n");
printk("%02x ", *((uint8_t*)fpu + i));
}
printk("\n");
}