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_
vector_
= vector_
所以异常入口为“b vector_und + stubs_offset”, 同时stubs_offset= __vectors_start + 0x200 – __stubs_start
我们可以通过objdump反汇编来验证:
00000060 … |
|
1d4: e1a00000 .word 0xe1a00000 000001e0 /* … |
|
__vectors_start: |
0x1e0 – 0x60 + 0x200 – ( 0x288 + 8 ) – 0x284 = 0xdd*4