Chip123 科技應用創新平台
標題:
trace linux kernel source - ARM - 01
[打印本頁]
作者:
gogojesse
時間:
2008-8-6 02:33 PM
標題:
trace linux kernel source - ARM - 01
昨天下載了linux-2.6.26的kernel source
- n" o. r- a7 h, Y0 J) f
打算trace一下 (以ARM為例子)
/ a6 T# ^% Z# P- r) ^" `
看看能不能多了解一下kernel booting時候的一些動作
4 T$ y) p) h; D/ N
一些文章提到是從/arch/arm/boot/bootp/init.S開始
1 `$ v9 h# P" }+ b
所以節錄了一些下來
- Q9 F* p* I8 O5 Y$ f' ^
& F7 m) F4 R# A0 p
19 .section .start,#alloc,#execinstr
" G4 p7 J% j. Y3 Z2 P
20 .type _start, #function
( z+ t0 E; y* N
21 .globl _start
7 I) I5 }5 ^6 E: _+ h
22
. n% f/ v/ [* E
23 _start: add lr, pc, #-0x8 @ lr = current load addr
1 b9 a2 b- `/ P0 `' V$ E* }& X
24 adr r13, data
" ]& P$ J8 n! b _; D
25 ldmia r13!, {r4-r6} @ r5 = dest, r6 = length
1 @$ o- d9 _7 M% ?
26 add r4, r4, lr @ r4 = initrd_start + load addr
2 ^4 D2 Z# `9 ]: {4 v6 |
27 bl move @ move the initrd
! [/ Z" @! v* h, T. n
.....
* h4 z. E# a2 ]
76 .type data,#object
2 t. D( [2 B5 k. f5 ?
77 data: .word initrd_start @ source initrd address
) a. S( P9 u6 ~2 p& L( y0 [
78 .word initrd_phys @ destination initrd address
. L8 ?0 T: \$ p. G+ B% M
79 .word initrd_size @ initrd size
! Z$ k) i, s; J5 L- K, G g0 x
80
$ s; X& ?% m9 ]! \8 e9 h4 L3 \
% J: y3 C) V. f" V" g
line 19,宣告了叫做.start的section
! `) `, p6 M) I8 p5 y
line 20,21宣告了一個叫做_start的function
3 D) |/ W3 N7 H1 R4 Y8 ~5 d
程式碼似乎從line 23開始
, |% F( O% C1 W1 a. h, c8 w- }
line 23, 『add lr, pc, #-0x8』
- J+ k: J# G. i4 E. R2 _
add就是將pc+(-0x8)的結果放到lr之中,pc和lr都是ARM裡頭暫存器
6 N$ x4 p% C2 C( d$ S
pc就是program counter,CPU用來指著目前要執行的指令,執行完CPU就會自動把PC+1
! w! V* H, |1 s2 e
這樣就會拿到下一道指令,lr通常是第14個register, r14,常被用來繼續function
; {8 g5 v* @1 i+ F8 P) y) K t
return時候的返回位置。奇怪的是為什麼要-0x8??
3 _ A3 E" G6 M7 Q
原因應該是ARM本身有pipeline的設計,prefetch->decode->execution,當指令被執行
; t7 W! ^ B' t. k* h
的時候,其實已經預先去偷偷抓下一道,所以PC值不是真的指在目前執行的地方,三級
# x& E4 O* K! n. N4 \4 I6 w4 Q/ e
pipeline剛好多了兩個cycle所以4 bytes x 2必須要-8才是正在執行指令的位址。
; f5 n9 }+ R! G6 h6 C! @
( ]4 o. l$ j# V
line 24, 『adr r13, data』
- G0 {: @; }' s( C& S2 \0 p
adr會去讀取data所在的位址當作值,寫道r13裡頭。r13通常是用來放stack pointer,
) w3 x9 @' d4 D/ u: n/ L' a6 L W
常縮寫成sp,所以現在r13就指到data所在的位置上去。
8 ?) f& r3 X3 Y+ R
6 [% ]! C* u! i, o8 V: q3 n
line 25, 『ldmia r13!, {r4-r6}』
% W/ {& B, p8 G1 S7 q! @
ldmia, load multiple increment after,顧名思義就是可以做很多次load的動作,每此
# z) w+ x& n! Y, E
load完就把位址+1,執行完之後
+ D6 F& u6 V- B
r4 = initrd_start
8 i3 O/ {* F; V. d& z1 T/ K; s
r5 = initrd_phys
5 r0 D7 J! h9 I6 u# o
r6 = initrd_size
7 a* l# E( ~2 l( Q8 m/ f
: a' h: P" l+ B7 i- E
line 26, 『add r4, r4, lr』
5 ]! g7 o w! {- k
r4 = r4+lr, lr是剛剛我們算過的,指到一開始執行指令的地方,看程式碼的解釋,被當
$ C6 _- w6 A: Q
成載入的位址,所以執行完 r4 = initrd_start+程式被載入的位置,用途不明,看看之
9 a# j) ^! n9 {! X, k$ c
後有沒有被用到。
2 t* Y% V; h! y
! Z- m! a5 V# w8 r1 N
line 27, 『bl move』
& l$ j$ ?2 T# l4 b! Z2 S% b& q
bl, b是branch,相當於C語言goto的動作,l就是goto之前,先把目前位置存放到lr裡面,
. x( d# Z- Q. J- f! j
所以bl除了goto之外,也改寫了lr,應該是有利於等一下返回的時候,可以用lr的值。
P6 E+ D: k5 o# }, [2 r
. B/ t4 H$ U |- v! w# K$ n
以上,好像還沒開始什麼重點,有空再繼續,有想錯的地方或是需要補充的地方,多多指教~
作者:
gogojesse
時間:
2008-8-6 06:23 PM
上面的 code 裡面出現了一些還沒定義的symbol
Y6 D0 y- l4 V7 H+ e* y% i- ^0 f
例如 initrd_start, initrd_size等等
4 j& @8 h- E# B) {) e* z( ?
其實是被定義在另外一個檔 ./arch/arm/boot/bootp/initrd.S
5 z, M! U' ^: V2 B, A
3 S! C& C/ j9 D4 K7 s5 _
1 .type initrd_start,#object
& n1 \8 O2 I3 P' @* N3 i( {) q
2 .globl initrd_start
# H3 X" b& q3 `; D5 w. @- X
3 initrd_start:
7 G5 M) }5 H: ~; [" w
4 .incbin INITRD
) ~! |" w" {9 G- y9 u/ E" h" ^+ \2 N
5 .globl initrd_end
/ C1 q- k- Q+ U7 B! C( y
6 initrd_end:
* E5 Q1 ~9 n8 G# O1 _
+ l& {; C* R# u, j! b
line 2, 3, 5, 6定義了兩個symbol initrd_start和initrd_end
z4 o5 d) r9 _ x, K; n
中間還用.incbin INITRD 將 ramdisk 的 image include進來
0 H+ w, ]/ N8 x( I
這邊有點複雜
% O- z8 t; j; u6 E/ e$ c
假如compiler kernel的時候
+ N& N; @% }& L: O
有選擇使用ramdisk當成boot device的話
, a' f. l- [3 Q
會對從環境變數去設置 INITRD
. U: v) _6 O# s$ g: j6 l
這個檔名會被帶入到MAKEFILE
/ D9 {9 h) t* e0 t
並且在做assembler動作的丟進來
! w( H( c# Q" t# d: t' f
假如沒有使用initrd
8 ^; Y/ w+ M8 g+ L) G3 I" x
那就.incbin應該就包不到東西
( x- W1 G/ g8 |' V6 g% Z8 P) D
initrd_start 和 initrd_end 就會相等
$ C8 d6 V& ]7 A* l7 }. |
9 x% V5 s6 A7 }6 j6 p& w
另外有個 script ./arch/arm/boot/bootp/bootp.lds
( T ?: O- m9 N! b3 l
它規範了所有link起來的object code裡面的section要怎麼編排
* u. R+ Y$ Z# W% g5 i# C$ h
這樣撰寫kernel的時候
% Q- O. B5 ~7 e$ l- D1 h, L
可以到特定的section取得想要的資料或是計算某個section的大小
; T5 G. k' ^5 U- d! |) T3 [
# h7 N8 u2 Q% m; S) B% I
10 OUTPUT_ARCH(arm)
7 k# l2 ^) Y. V! r
11 ENTRY(_start)
- l/ u8 ?1 ~* o m( @4 @; R6 S7 J5 N
12 SECTIONS
! [4 Z: O9 L9 s) y! N ~# h9 e/ R
13 {
4 X2 ^, b$ ]& f( u
14 . = 0;
2 D' |6 T3 s' g4 S' d4 L
15 .text : {
/ [2 s1 q9 z5 o$ p* S( l5 k
16 _stext = .;
0 R. \& O/ c, z( l$ R3 i( r# g
17 *(.start)
& K2 c* [5 R8 P) t% P
18 *(.text)
; v2 D4 A G# u
19 initrd_size = initrd_end - initrd_start;
" n8 B8 A. `1 Y1 q2 x6 Y, U! S; ]
20 _etext = .;
' n' e4 K* `1 E: K5 c& Q
21 }
8 s8 R6 j. J. _% w; |; m/ g
22
' r& d) }/ u H& p
23 .stab 0 : { *(.stab) }
5 i) S$ W5 P, X9 p! N6 Q# }6 x
24 .stabstr 0 : { *(.stabstr) }
: c$ Z- V! f) ?3 N
25 .stab.excl 0 : { *(.stab.excl) }
' Y# }. Y; { `6 `
26 .stab.exclstr 0 : { *(.stab.exclstr) }
, N; s. X3 I4 E) s9 p
27 .stab.index 0 : { *(.stab.index) }
) @4 `% @9 C7 v7 x& M
28 .stab.indexstr 0 : { *(.stab.indexstr) }
# j& @+ r2 v" O4 S' n: E9 x$ J
29 .comment 0 : { *(.comment) }
R+ n9 s2 }) K1 E* @6 p! L1 I0 E
30 }
6 ?! o7 h& l( o& d( H3 l! a- v
5 F- V3 z8 c& o
對於object file的格式不熟悉的話
2 g. D# {# i; M5 S( D! \
可以參考ELF format的相關文件
作者:
gogojesse
時間:
2008-8-6 07:50 PM
接著繼續看 init.S
1 K. b6 ] U+ b# T
之前的code已經goto到move這邊來
( `1 |7 E! V! n; ^$ @; T
所以貼一些move的程式碼
# ~, A( g6 N9 q: H- u* l) d1 V
" P0 Y9 ^7 Y2 i7 g
66 move: ldmia r4!, {r7 - r10} @ move 32-bytes at a time
. A" i; x1 D) _, `
67 stmia r5!, {r7 - r10}
# t3 C9 ~6 E" O
68 ldmia r4!, {r7 - r10}
. y {1 u( _) p2 {4 r8 [- s! G
69 stmia r5!, {r7 - r10}
8 e' o: u4 l4 G! Y
70 subs r6, r6, #8 * 4
! p2 n9 U7 @# O2 u
71 bcs move
# [/ w) y2 K, \7 l6 R. W
72 mov pc, lr
6 X2 f, r& y& H
; A) W1 }: Y3 x3 g# c6 g
line 66, 將r4所指到的位置,分別將值讀出來放到r7, r8, r9, r10, 可以發現剛剛計算過的r4這邊被
; p) ~0 G4 r. d0 _9 E
用到了,但是為什麼r4不是用initrd_start,卻還要加上load addr??
1 L# Q6 Q- m H( |' b) U8 ^+ c: |! B1 t
原因應該是bootp.lds的14行『. = 0;』表示最後被link好的address會從0x0開始
3 f5 D3 k0 H& e) E3 S
所以 initrd_start 所記錄的位置可以當成是offset
' r9 x" R9 B* K. x
加上load到DRAM或是擺在flash上的位址後
( u# t" x$ G: c( `" K# P' ?! j
就剛好是initrd所在的地方
/ i% Z6 b- V/ I8 Y5 j
3 w; V! a7 c; o& a. M* ~
line 67, 『stmia r5!, {r7 - r10}』
. d3 ?4 P, a( t {# W' ]6 B
stmia, store multiple increment after, 和ldmia動作相同,只是用來寫資料。
/ ^: L! w+ o( O% q; y3 L4 W3 |
r5是存放著initrd要擺放的位置
) c$ P2 K) X; s! t) K9 j
猜測應該是為了一開始image放在flash上,但是可以將initrd拷貝到DRAM上
- k! W! v4 K: k2 G$ f8 N' T( F/ ?, M
r7寫到r5指到的位置
" j6 D+ o& }% C X5 P1 b2 J% E" T
r8->r5+1
4 ]5 X8 [& O7 y- k5 s. G) n9 P) x3 g
r9->r5+2
# n7 f E o! R- ~5 ?
r10->r5+3
$ r% n/ [3 t$ w' `5 | Y; |
所以我們發現,66,67行就是將r4所指的東西搬到r5。
$ W4 S0 T& \1 _3 G1 d
/ g9 d( K/ J" z$ S
line 68, 69也是一樣copy了4x4bytes,一共是32bytes。
$ M% D/ ?# u% d% d+ U- r
line 70,『subs r6, r6, #8 * 4』,將length - 32bytes
8 o( V6 `. p- b+ i- m# v
line 71,『bcs move』,b是branch的意思,cs是表示condition的條件,要是條件符合的話,
# i/ ~: q1 A9 L5 ?
就做branch的動作,這邊的用意是判斷前一個length是不是已經到0,如果不為零就繼續copy。
, s; l: N' h7 K1 n+ b, M) n+ G
line 72,『mov pc, lr』
% `: p7 A8 W" M* ]1 ^- N8 p1 s
接著就把剛剛bl指令預先存放好的lr 填入pc,這樣CPU就會跳回去原本的return address。
+ U; g) }( `! B
* a/ l: x9 s& f" h, ^& `
以上的動作,慢慢看得出來有在做些什麼事
$ f, L7 b" G8 Z
1. 找出initrd的所在位置
. T- {" `4 y2 i6 i
2. 將它copy到一個指定的destination去
作者:
gogojesse
時間:
2008-8-7 11:25 AM
程式返回之後
. V# n; T* Q& y1 g+ K
我們接著看下一行
33 ldmia r13, {r5-r9} @ get size and addr of initrd
5 ~; }( |, u" m# r# z5 `: [
34 @ r5 = ATAG_CORE
+ L1 C7 @! s0 }! B6 t$ B" u, K* H" J
35 @ r6 = ATAG_INITRD2
. U6 K {4 u" M" `4 v
36 @ r7 = initrd start
' p5 r7 z; z0 I# L8 b
37 @ r8 = initrd end
$ e! i2 D( j. W2 r
38 @ r9 = param_struct address
' w& _( u0 N+ {
39
/ |3 T# b6 {: [( ?# J5 ~
40 ldr r10, [r9, #4] @ get first tag
( c; O0 w$ ~" l; b! m
41 teq r10, r5 @ is it ATAG_CORE?
複製代碼
line 33, 繼續從r13的地方取出資料到r5, r6, r7 ,r8, r9,註解的說明有提到各個資料
* w0 _( [# `! w1 [2 Z
的意義,注意一下這邊的r7是initrd的destination address不是source address。
1 u$ M1 z+ v2 s- f' I$ K
+ l) Z' j. m6 @4 e9 ~$ e& O
line 40, 讀入第一個tag,這邊的tag是指bootloader丟給kernel的一個boot arguments,
/ s+ _6 f( |' _) b4 m
會被用一個叫做ATAG的structure包起來,並且放到系統的某個地方。然後kernel跑init.S,
$ Q5 F1 u! G9 g5 Z
的時候就會去這個地方拿ATAG的資料,這些資訊包括記憶體要使用多大,螢幕的解析度多大等等。
( M" v2 \, N3 ^0 {7 \ m
1 r9 d; _1 J/ X& z2 v9 z% t8 z
line 41, t是test, eq是equal, 判斷拿到的第一個tag是不是等於atag core. 應該是看
% a) a# `/ b& p) ^( x
atag list 是不是成立的。
1 Z" x, P, C7 N Y. W% p: k# v
5 K& k0 B) u) |5 P+ K) H1 F$ z
繼續接著看
45 movne r10, #0 @ terminator
3 ]+ b; z! P5 z$ @1 I a
46 movne r4, #2 @ Size of this entry (2 words)
9 {+ N. a( {4 O, i
47 stmneia r9, {r4, r5, r10} @ Size, ATAG_CORE, terminator
複製代碼
發現45, 46, 47的指令都帶有condition "ne", not equal,表示是剛剛 line 41發現atag不成立
s* H6 Q- j2 L/ C8 ~, {/ j
所做的事情,注釋是寫『If we didn't find a valid tag list, create a dummy ATAG_CORE entry.』
+ O2 R: \; r H8 a; M# h. n) C
所以以上三行就是用來創造一個假的entry,假設一切順利這三行指令會bypass過去不會被執行到。
& v( {" q% y+ P+ I$ J' x: q
& h: c3 q8 S5 {' U( L- N
接著來看init.S最後一段程式碼 (終於~)
54 taglist: ldr r10, [r9, #0] @ tag length
, e8 j1 k+ _3 B7 M. e& M1 ]
55 teq r10, #0 @ last tag (zero length)?
6 i& D- P+ P4 j& } W1 s
56 addne r9, r9, r10, lsl #2
5 B0 ^: K9 ~& e2 X5 l8 w, f& q
57 bne taglist
9 U; m& \$ L, }$ _ c) N* O7 @
58
+ g% P% t. G3 u% U
59 mov r5, #4 @ Size of initrd tag (4 words)
# Z2 m- \$ @, Q# U7 M, E. ?
60 stmia r9, {r5, r6, r7, r8, r10}
5 i! Z- V/ H' @1 h: G7 Z2 j
61 b kernel_start @ call kernel
複製代碼
line 54, 將r9指到的位址的offset 0x0的值載入到r10。看註解是tag length,所以這邊得要去翻翻atag的規範
, D( X2 E* n' o8 O2 Z+ J
這邊有個文章有提到
http://www.simtec.co.uk/products ... ooting_article.html
,一開
) g* u$ R, k/ L, E+ ^
始應該是去讀atag_header所看第一個欄位,確認一下是size,應該沒問題。
struct atag_header {
# f$ A: G0 u7 E8 a% ~1 @
u32 size; /* legth of tag in words including this header */
, ]9 J$ x0 L* O
u32 tag; /* tag value */
" K w- p6 t- t. G0 K* J
};
複製代碼
line 55,測試一下size是不是0。
) m) ]! k' P0 H4 N
line 56, 57也有condition ne,表示是不為0的時候做的。將拿到的length(r10)乘以4,這邊的lsl是將r10往
/ Z, p9 d7 E4 m/ j2 `. Q. [" l6 F
左shift的意思,因為一個欄位是4bytes,所以乘4之後就跳到下一個tag,一直跳到最後沒東西。
5 H/ P; L: _( w# Y4 t
4 ^" t+ S6 A: F0 Q$ T! p
line 59, 將r5設成4
+ g# u, w3 U! I. [7 O
line 60, 將r5, r6, r7, r8 ,r10存到r9所指到的位置,應該就是跟在atag list的後面。
4 V/ R1 R4 }, m7 l
line 61, jump 到 kernel_start ,注意這邊是用b而不是bl,因為跳過去kernel就不需要返回了。BL會用到
# R a: W9 s& b. i2 y- L
lr紀錄返回位置。
" q- z; t# K5 N! c$ }5 u
" N/ I' J9 D4 ]/ t$ K: L
以上,走過一整個init.S,接著會跳到./arch/arm/boot/compressed/head.S。
) M! f4 ~0 X, K" y. l5 D5 N5 h
/ `5 g2 o$ s2 G/ s3 i" g
kernel_start的定義方式跟initrd_start有點類似,中間有透過 kernel.S去用.incbin把kernel image包進來。
作者:
jacky002
時間:
2008-8-9 07:44 AM
好一段時間沒有碰程式了,看到你的分析讓我想起年輕時候的我 ~~~~ 還是不要透漏年齡
作者:
gogojesse
時間:
2008-8-9 11:31 AM
原帖由
jacky002
於 2008-8-9 07:44 AM 發表
* ]8 h3 |# n- Z7 D
好一段時間沒有碰程式了,看到你的分析讓我想起年輕時候的我 ~~~~ 還是不要透漏年齡
' u2 I2 L4 W2 e8 F+ z; h7 n' N
) q0 u q' l$ Z- S0 z+ j
' k4 z+ V, I1 S: v y! }
有些時候 東西是越陳越香啊~~
5 @( E* u. g* C2 r
...........
作者:
gogojesse
時間:
2009-7-16 03:26 PM
剛剛發現一個大陸網站的blog貼了我的文章
. s i( ], h6 v+ t9 {/ X0 v
但是沒表明出處 = =
$ n! p1 T( X; Z
還把標題改過,感覺像是他自己寫的
3 a* M" Q5 g% f+ M' h- z, L$ \* ~
真是麻煩~
% k! F @) ?% @& e r) u( E
) {" I( _% i% S4 M( `& @0 v3 Y& d' f
已經好幾次這樣的經驗
& H# X- H6 N& e1 N0 y* r. e8 U
以前幫忙有弄網站也是這樣
# g; l) `; w1 A& G
抄襲得很嚴重
7 C- L# j4 i( A: i d2 U
內地那邊到底有沒有在管啊!!
歡迎光臨 Chip123 科技應用創新平台 (http://www.chip123.com.tw/)
Powered by Discuz! X3.2