Linux Version : 2.6.29
1. start_kernel-->setup_arch-->early_trap_init
1:
memcpy((void
*)vectors, __vectors_start, __vectors_end - __vectors_start);
2:
memcpy((void
*)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
3:
memcpy((void
*)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
对于第一行:
__vectors_start 和 __vectors_end 定义在 arch/arm/kernel/entry-armv.S , 它们之间保存了中断向量表。
1:
.globl __vectors_start
2:
__vectors_start:
3:
swi SYS_ERROR0
4:
b vector_und + stubs_offset
5:
ldr pc, .LCvswi + stubs_offset
6:
b vector_pabt + stubs_offset
7:
b vector_dabt + stubs_offset
8:
b vector_addrexcptn + stubs_offset
9:
b vector_irq + stubs_offset
10:
b vector_fiq + stubs_offset
11:
12:
.globl __vectors_end
13:
__vectors_end:
vectors 的地址为CONFIG_VECTORS_BASE , 在.config中定义为0xffff0000
所以 第1行就是把中断向量表拷贝到0xffff0000
对于第二行:
vector_stub是一个带参数的宏,第一个是name,第二个是arm excepiton mode,第三个是为了得到返回地址,lr需要减去的偏移
1:
.macro vector_stub, name, mode, correction=0
2:
.align 5
3:
4:
vector_/name:
5:
.if
/correction
6:
sub lr, lr, #/correction @得到正确的返回地址
7:
.endif
8:
9:
@
10:
@ Save r0, lr_(parent PC) and spsr_
11:
@ (parent CPSR)
12:
@
13:
stmia sp, {r0, lr} @ save r0, lr
14:
mrs lr, spsr
15:
str lr, [sp, #8] @ save spsr
16:
17:
@
18:
@ Prepare for
SVC32 mode. IRQs remain disabled.
19:
@
20:
mrs r0, cpsr
21:
eor r0, r0, #(/mode ^ SVC_MODE) @把cpsr内容与(mode^SVC_mode)异或,即r0里为SVC_MODE
22:
msr spsr_cxsf, r0 @把r0的值写入整个spsr寄存器(cxsf表示要往哪个字节写入)
23:
24:
@
25:
@ the branch table must immediately follow this
code
26:
@
27:
and lr, lr, #0x0f @lr为spsr_的值,此语句取到进入异常前的mode
28:
mov r0, sp @
29:
ldr lr, [pc, lr, lsl #2] @lr=pc+mode*4,其中pc为紧接着30的指令,即vector_stub后的第一条指令
30:
movs pc, lr @ movs会把spsr的值赋给cpsr,所以branch to handler in
SVC mode
31:
ENDPROC(vector_/name)
32:
.endm
再来看下vector 跳转表
1:
.long
__irq_usr @ 0 (USR_26 / USR_32)
2:
.long
__irq_invalid @ 1 (FIQ_26 / FIQ_32)
3:
.long
__irq_invalid @ 2 (IRQ_26 / IRQ_32)
4:
.long
__irq_svc @ 3 (SVC_26 / SVC_32)
5:
.long
__irq_invalid @ 4
6:
.long
__irq_invalid @ 5
7:
.long
__irq_invalid @ 6
8:
.long
__irq_invalid @ 7
9:
.long
__irq_invalid @ 8
10:
.long
__irq_invalid @ 9
11:
.long
__irq_invalid @ a
12:
.long
__irq_invalid @ b
13:
.long
__irq_invalid @ c
14:
.long
__irq_invalid @ d
15:
.long
__irq_invalid @ e
16:
.long
__irq_invalid @ f
这里只有usr 和svc 有入口,而其他都是invalid ,是因为linux只会从usr(application) 和svc(kernel)两种mode跳转到exception来
__stubs_start 和 __stubs_end 之间的代码简化后为:
1:
__stubs_start:
2:
vector_irq: @vector_stub irq, IRQ_MODE, 4
3:
vector_dabt: @vector_stub dabt, ABT_MODE, 8
4:
vector_pabt: @vector_stub pabt, ABT_MODE, 4
5:
vector_und: @vector_stub und, UND_MODE
6:
vector_fiq:
7:
vector_addrexcptn:
8:
.LCvswi:
9:
__stubs_end:
由此可以知道 __stubs_start 和 __stubs_end 之间定义了各种异常的入口
我们再来看为什么异常入口是“b vector_und + stubs_offset”, 同时为什么stubs_offset 的定义如下
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
arm 的跳转指令b 是跳转到相对于PC的一个偏移地址( offset ),汇编器在编译时会对label 减去PC 得到offset,同时vector 拷贝后是如下排列的
__vectors_start
B vector_
__vectors_end
+0x200
__stubs_start
vector_
__stubs_end
因此,"b vector_" 的label –PC = offset, 而offset 为 b 指令与vector的offset,即
vector_-__stubs_start + ( 0x200 – ( PC_old – __vectors_start ) )
= vector_+ __vectors_start + 0x200 – __stubs_start – PC_old
所以异常入口为“b vector_und + stubs_offset”, 同时stubs_offset= __vectors_start + 0x200 – __stubs_start
我们可以通过objdump反汇编来验证:
00000060 :
.globl __stubs_start
__stubs_start: /*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4
60 : e24ee004 sub lr, lr, #4 ; 0x4
64: e88d4001 stm sp, {r0, lr}
…
1d4: e1a00000 .word 0xe1a00000
1d8: e1a00000 .word 0xe1a00000
1dc: e1a00000 .word 0xe1a00000
000001e0 :
/*
* Undef instr entry dispatcher
* Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*/
…
__vectors_start:
swi SYS_ERROR0
284: ef9f0000 svc 0x009f0000
b vector_und + stubs_offset
288: ea0000dd b 604 ldr pc, .LCvswi + stubs_offset
28c: e59ff410 ldr pc, [pc, #1040] ; 6a4 b vector_pabt + stubs_offset
290: ea0000bb b 584 b vector_dabt + stubs_offset
294: ea00009a b 504 b vector_addrexcptn + stubs_offset
298: ea0000fa b 688 b vector_irq + stubs_offset
29c: ea000078 b 484 b vector_fiq + stubs_offset
2a0: ea0000f7 b 684
0x1e0 – 0x60 + 0x200 – ( 0x288 + 8 ) – 0x284 = 0xdd*4
|