Solaris学习笔记(4)
作者: Badcoffee
Email: blog.oliver@gmail.com
Blog: http://blog.youkuaiyun.com/yayong
2007年2月
很久没有写blog了,工作越来越忙,生活中琐事也不少,一天中的时间所剩无几了。当初很想写出精品的技术文章来,到后来才发现,要写一篇精品真的很难。从基础理论到实际案例,想深入潜出又要有一定水准,真的是太难了,这中间得花费写作者的多少心智?现在才能体会到成为一名出色的技术作家,需要的不仅仅是技术功底而已。所以,还是先做好工程师这份没有前途的职业再说吧:)
关键词:RISC/CISC/SPARC/ABI/MDB/KMDB/Solaris/OpenSolaris
本文介绍Solaris SPARC系统上Crashdump分析所需的基础知识:寄存器窗口,ABI等;Solaris的内核调试工具已经可以很好地读取出函数的入口参数,但是,在一些情况下,仍旧需要通过读取线程栈的内容来确定函数参数的值。再次说明,本文仅用于学习交流目的,错误再所难免,如果有勘误或疑问请与作者联系。
RISC 与 CISC
关于RISC与CISC的争论由来以久,相关文章到处都是,不明白可以Google一下。似乎两者已经互相借鉴,互相融合了,如果有时间,应该深入了解一下CPU和体系结构方面的知识。
SPARC体系结构的文档,可以从下面的网站得到,本文只涉及到crash dump分析的基础。
SPARC的特点
1. 精简指令集;
2. 指令和操作基于寄存器,内存访问需要专门的load/store操作;
3. 有很多寄存器(几百个),通过寄存器窗口来访问;
4. 函数参数传递也通过寄存器,而不是通过栈;
5. 指令编码定长;
6. 地址对齐,不对齐的地址会引发bad trap;例如,各种数据类型必须与其自然边界对齐;
7. 字节序缺省是大端的(也可支持小端,需要页表项的特殊设置);
SPARC寄存器
Processor Status Register (PSR):用来指示kernel/user mode, condition codes, CPU priority level等。
Program Counter (PC): 当前正在执行的指令地址;
next Program Counter (nPC):下条要执行指令的地址;
General-purpose registers:通用寄存器,实际上SPARC有数百个寄存器,但对每个程序仅可见32个,这是通过寄存器窗口实现的。
寄存器窗口中的通用寄存器分类
General-purpose registers %r0 … %r31
1. 全局寄存器(8个) - 对所有程序可见
General-purpose global registers %g0 … %g7 Same as %r0 … %r7
2. 输出寄存器(8个) -函数返回值,输出寄存器是下一个窗口的输入寄存器
General-purpose out registers %o0 … %o7 Same as %r8 … %r15
3. 局部寄存器(8个) - 仅本函数可见
General-purpose local registers %l0 … %l7 Same as %r16 … %r23
4. 输入寄存器(8个) -本函数的输入参数,来自于上一窗口的输出寄存器
General-purpose in registers %i0 … %i7 Same as %r24 … %r31
寄存器窗口
SPARCv9寄存器窗口的数量范围从 2 到 32,具体取决于处理器实现。每个寄存器窗口对应一个函数,当每个函数被调用时,函数开始会调用save指令分配相应的栈空间;
函数的调用必然引起一次窗口的切换和一次栈空间的预先分配,但寄存器在下列情况时才被保存在栈上,
1. 寄存器窗口用尽时
2. 上下文切换
3. flushw指令
这时会引发一个硬件的溢出trap,将寄存器窗口中的16个寄存器(8个局部寄存器和8个输入寄存器)保存到栈上;
寄存器窗口还有underflow trap,可以把保存在栈里的值恢复;restore指令用于切回上级调用的寄存器窗口,并取释放栈空间;
相关的指令
1. 函数调用:
call address
或者
jmpl address, %o7
2. 函数入场:
save %sp, -0x78, %sp
3. 函数返回:
jmpl %i7+8, %g0
restore
或者
ret
restore
SPARC的ABI
函数的调用者传递参数到本窗口的o寄存器,被掉函数从本窗口的i寄存器读到;
%o0到%o5 - 用于函数返回值或者传递给下一个函数参数,第一个参数是%o0 ,参数多于6个还会用栈传递;
%o6 - %sp栈顶指针;
%o7 -函数返回地址;
%i6 -%fp栈框架指针(SFP)
%i0到%i5 -入口参数,第一个参数是%i0,来自于上一窗口的输出参数;
%g0永远是0,特殊用途;
SPARC V9的 stack bias:
%o6 is still referred to as the stack pointer register %sp, and %i6 is the frame pointer register %fp. However, the %sp and %fp registers are offset by a constant, known as the stack bias, from the actual memory location of the stack. The size of the stack bias is 2047 bytes.
栈框架的定义
Solaris中,stack frame的格式定义在体系结构相关的头文件中:
http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/sparc/sys/frame.h#36
* Definition of the sparc stack frame (when it is pushed on the stack).
*/
struct frame {
long fr_local[ 8 ]; /* saved locals */
long fr_arg[ 6 ]; /* saved arguments [0 - 5] */
struct frame * fr_savfp; /* saved frame pointer */
long fr_savpc; /* saved program counter */
#if !defined(__sparcv9)
char * fr_stret; /* struct return addr */
#endif /* __sparcv9 */
long fr_argd[ 6 ]; /* arg dump area */
long fr_argx[ 1 ]; /* array of args past the sixth */
};
实际验证
在console上激活kmdb,用$
头文件验证栈内寄存器窗口值的保存是与上面定义一致的。之所以产生一个crash dump来验证,
是为防止寄存器窗口还没有保存在栈里。
000002a100347111 abort_seq_softintr + 0x94 ( 1814400 , 18cc800, 300077d4000, 3f70, 1 , 1870400 )
% l0 -% l3: 1821d40 22d3188da0893 0 0
% l4 -% l7: 0 18cc800 191bc00 1010184
intr_thread + 0x2c0 : jmpl % o5, % o7
000002a1003472d1 intr_thread + 0x2c0 ( 0 , 1859ff8, 180c000, 1b, 10b5fd8, 1817c00)
% l0 -% l3: 1008468 a 4414001602 80a
% l4 -% l7: 0 0 0 2a10030f980
mdb: failed to read instruction at 0 : no mapping for address
000002a10030f221 0 (1817c00, 0 , 300077d4000, ffffffffffffffff, 2 , 1816800 )
% l0 -% l3: 60000128fe8 1b 0 ffffffffffffffff
% l4 -% l7: 60000128fe8 ffffffffffffffff 1859ff8 106b174
thread_start + 4 : jmpl % i7, % o7
000002a10030f2d1 thread_start + 4 ( 0 , 0 , 0 , 0 , 0 , 0 )
% l0 -% l3: 0 0 0 0
% l4 -% l7: 0 0 0 0
> ::regs
% g0 = 0x0000000000000000 % l0 = 0x000006000013c200
% g1 = 0x0000000000000000 % l1 = 0x0000000000000001
% g2 = 0x0000000000000000 % l2 = 0x0000000000000001
% g3 = 0x0000000001853400 cpunodes + 0x1140 % l3 = 0x00000600002b4a10
% g4 = 0x0000000000000000 % l4 = 0x000000000191c040
pci_intr_dma_sync
% g5 = 0x000000000109a800 % l5 = 0x0000000000000001
% g6 = 0x0000000000000010 % l6 = 0x0000000000000002
% g7 = 0x000002a100347cc0 % l7 = 0x0000000000000001
% o0 = 0x0000000000000000 % i0 = 0x0000000000000000
% o1 = 0x0000000000020040 % i1 = 0x000000000000000a
% o2 = 0x0000000001851000 lgrp_stats + 0x12f8 % i2 = 0x000000000000000a
% o3 = 0x0000000000000001 % i3 = 0x0000000001814400
% o4 = 0x00000000013446a4 kmdbmod`kaif_enter % i4 = 0x0000000000000000
% o5 = 0x00000000018ea2b8 kmdbmod`kaif_dvec % i5 = 0x0000000001853400
cpunodes + 0x1140
% o6 = 0x000002a100347041 % i6 = 0x000002a100347111
% o7 = 0x000000000101028c debug_enter + 0x108 % i7 = 0x000000000100ff38
abort_seq_softintr + 0x94
% ccr = 0x00 xcc = nzvc icc = nzvc
% fprs = 0x00 fef = 0 du = 0 dl = 0
% asi = 0x00
% y = 0x0000000000000000
% pc = 0x0000000000000000
% npc = 0x0000000000000004
% sp = 0x000002a100347041 unbiased = 0x000002a100347840
% fp = 0x000002a100347111
% tick = 0x000006b00af1f9fe
% tba = 0x0000000000000000
% tt = 0x9
% tl = 0x0
% pil = 0xc
% pstate = 0x016 cle = 0 tle = 0 mm = TSO red = 0 pef = 1 am = 0 priv = 1 ie = 1 ag = 0
% cwp = 0x05 % cansave = 0x00
% canrestore = 0x00 % otherwin = 0x00
% wstate = 0x00 % cleanwin = 0x00
> 0x000002a100347111 + 0t2047, 24 / naJ
0x2a100347910 :
0x2a100347910 : 1821d40
0x2a100347918 : 22d3188da0893
0x2a100347920 : 0
0x2a100347928 : 0
0x2a100347930 : 0
0x2a100347938 : 18cc800
0x2a100347940 : 191bc00
0x2a100347948 : 1010184
0x2a100347950 : 1814400
0x2a100347958 : 18cc800
0x2a100347960 : 300077d4000
0x2a100347968 : 3f70
0x2a100347970 : 1
0x2a100347978 : 1870400
0x2a100347980 : 2a1003472d1
0x2a100347988 : 100c714
0x2a100347990 : 3000793e0b8
0x2a100347998 : 0
0x2a1003479a0 : 0
0x2a1003479a8 : 0
0x2a1003479b0 : 3000793e028
0x2a1003479b8 : 3000793e030
0x2a1003479c0 : 1fafdd
0x2a1003479c8 : 3000793e030
0x2a1003479d0 : 0
0x2a1003479d8 : 6000003b6e0
0x2a1003479e0 : 300347221
0x2a1003479e8 : 1fafdd
0x2a1003479f0 : 1297538
0x2a1003479f8 : 413
0x2a100347a00 : 1297518
0x2a100347a08 : 6000003b6e0
0x2a100347a10 : 13c200
0x2a100347a18 : 0
0x2a100347a20 : 0
0x2a100347a28 : 600002b4a10
> 2a1003472d1 + 0t2047, 24 / naJ
0x2a100347ad0 :
0x2a100347ad0 : 1008468
0x2a100347ad8 : a
0x2a100347ae0 : 4414001602
0x2a100347ae8 : 80a
0x2a100347af0 : 0
0x2a100347af8 : 0
0x2a100347b00 : 0
0x2a100347b08 : 2a10030f980
0x2a100347b10 : 0
0x2a100347b18 : 1859ff8
0x2a100347b20 : 180c000
0x2a100347b28 : 1b
0x2a100347b30 : 10b5fd8
0x2a100347b38 : 1817c00
0x2a100347b40 : 2a10030f221
0x2a100347b48 : 0
0x2a100347b50 : baddcafebaddcafe
0x2a100347b58 : baddcafebaddcafe
0x2a100347b60 : baddcafebaddcafe
0x2a100347b68 : baddcafebaddcafe
0x2a100347b70 : baddcafebaddcafe
0x2a100347b78 : baddcafebaddcafe
0x2a100347b80 : baddcafebaddcafe
0x2a100347b88 : baddcafebaddcafe
0x2a100347b90 : baddcafebaddcafe
0x2a100347b98 : baddcafebaddcafe
0x2a100347ba0 : baddcafebaddcafe
0x2a100347ba8 : baddcafebaddcafe
0x2a100347bb0 : baddcafebaddcafe
0x2a100347bb8 : baddcafebaddcafe
0x2a100347bc0 : baddcafebaddcafe
0x2a100347bc8 : baddcafebaddcafe
0x2a100347bd0 : baddcafebaddcafe
0x2a100347bd8 : baddcafebaddcafe
0x2a100347be0 : baddcafebaddcafe
0x2a100347be8 : baddcafebaddcafe