Exercise12
要求
在11的基础上打印出当前eip所指向地址的文件信息,行号,距离上一个eip的地址距离。
K> backtrace
Stack backtrace:
ebp f010ff78 eip f01008ae args 00000001 f010ff8c 00000000 f0110580 00000000
kern/monitor.c:143: monitor+106
ebp f010ffd8 eip f0100193 args 00000000 00001aac 00000660 00000000 00000000
kern/init.c:49: i386_init+59
ebp f010fff8 eip f010003d args 00000000 00000000 0000ffff 10cf9a00 0000ffff
kern/entry.S:70: +0
K>
分析
可以根据eip的地址在symbol table中获取这些信息。简称为stab。
symbol table(符号表): 在编译程序中符号表用来存放语言程序中出现的有关标识符的属性信息,这些信息集中反映了标识符的语义特征属性。
Stab汇编指导命令有3种格式:'.stabs'(string), '.stabn'(number)和'.stabd'(dot)。 在MIPS机器上, GCC采用 '.stabn' 输出源程序语句行号的 Stab 调试信息, 而未使用 '.stabd' , 因此, 在MIPS机器上, GCC生成的带有 Stab 调试信息的汇编代码中只含 '.stabs' 和 '.stabn' 两种汇编指导命令, '.stabs' 和 '.stabn' 命令格式如下:
.stabs "STRING", TYPE, OTHER, DESC, VALUE
.stabn TYPE, OTHER, DESC, VALUE
TYPE类型:
#define N_GSYM 0x20 // global symbol
#define N_FNAME 0x22 // F77 function name
#define N_FUN 0x24 // procedure name
#define N_STSYM 0x26 // data segment variable
#define N_LCSYM 0x28 // bss segment variable
#define N_MAIN 0x2a // main function name
#define N_PC 0x30 // global Pascal symbol
#define N_RSYM 0x40 // register variable
#define N_SLINE 0x44 // text segment line number
#define N_DSLINE 0x46 // data segment line number
#define N_BSLINE 0x48 // bss segment line number
#define N_SSYM 0x60 // structure/union element
#define N_SO 0x64 // main source file name
#define N_LSYM 0x80 // stack variable
#define N_BINCL 0x82 // include file beginning
#define N_SOL 0x84 // included source file name
#define N_PSYM 0xa0 // parameter variable
#define N_EINCL 0xa2 // include file end
#define N_ENTRY 0xa4 // alternate entry point
#define N_LBRAC 0xc0 // left bracket
#define N_EXCL 0xc2 // deleted include file
#define N_RBRAC 0xe0 // right bracket
#define N_BCOMM 0xe2 // begin common
#define N_ECOMM 0xe4 // end common
#define N_ECOML 0xe8 // end common (local name)
#define N_LENG 0xfe // length of preceding entry
使用Exercise11中的例子,加上--gstabs参数,可以得出源代码中的stab信息,其中100代表0x64,对应type的#define N_SO 0x64 // main source file name
代表是源文件的名字。
.stabs "test_stack.c",100,0,2,.Ltext0
68代表0x44,对应#define N_SLINE 0x44 // text segment line number
代表bar的行号,其中DESC = 3就代表bar函数在文件中的第三行。
bar:
.stabn 68,0,3,.LM0-.LFBB1
在程序中stab其实就是一个数据结构,在inc/stab.h下
// Entries in the STABS table are formatted as follows.
struct Stab {
uint32_t n_strx; // index into string table of name
uint8_t n_type; // type of symbol
uint8_t n_other; // misc info (usually empty)
uint16_t n_desc; // description field
uintptr_t n_value; // value of symbol
};
在程序中是一个stabs[]数组。
在kern/kdebug.c的stab_binsearch函数中,根据eip的地址addr和类型type使用二分查找,找到对应的stabs,然后获取对应的行号的文件信息存入到Eipdebuginfo的数据结构中。
struct Eipdebuginfo {
const char *eip_file; // Source code filename for EIP
int eip_line; // Source code linenumber for EIP
const char *eip_fn_name; // Name of function containing EIP
// - Note: not null terminated!
int eip_fn_namelen; // Length of function name
uintptr_t eip_fn_addr; // Address of start of function
int eip_fn_narg; // Number of function arguments
};
代码
在kern/kdebug.c的debuginfo_eip中添加获取行号的代码
stab_binsearch(stabs, &lline, &rline, N_SLINE ,addr);
if (lline > rline)
return -1;
info->eip_line = stabs[rline].n_desc;
在kern/monitor.c中添加信息打印
while(ebp) {
struct Eipdebuginfo debug_info;
eip = *(ebp+1);
debuginfo_eip(eip, &debug_info);
cprintf("ebp %x eip %x args", ebp, eip);
uint32_t* args = ebp+2;
for(int i = 0; i < 5; ++i) {
uint32_t argv = args[i];
cprintf(" %08x ", argv);
}
cprintf("\n");
cprintf("\t%s:%d: %.*s+%d\n",
debug_info.eip_file, debug_info.eip_line, debug_info.eip_fn_namelen,
debug_info.eip_fn_name, eip - debug_info.eip_fn_addr);
ebp = (uint32_t*)*ebp;
}
参考
http://caobeixingqiu.is-programmer.com/posts/12361.html
https://github.com/shishujuan/mit6.828-2017/blob/master/docs/lab1-exercize.md