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, S3 j; O/ O2 d5 K
打算trace一下 (以ARM為例子)
8 P( \& E1 {- k" R- m9 }看看能不能多了解一下kernel booting時候的一些動作 2 K' @5 l# x, q: @; d  K
一些文章提到是從/arch/arm/boot/bootp/init.S開始4 w) L: D) h* ]6 _1 ~* Q
所以節錄了一些下來( M# S  \7 ^9 J
; f# g  C1 [- T# Q
     19         .section .start,#alloc,#execinstr% C: v% U. s( E/ ]" a, m+ x/ x9 `4 E: L
     20         .type   _start, #function$ V" x! ]: C# ]
     21         .globl  _start
: G% X; o3 M- k- P# J     226 q2 V7 H3 C* I
     23 _start:     add lr, pc, #-0x8       @ lr = current load addr4 X! j) F( f' D' \+ p% f2 [
     24         adr r13, data5 h  B& e) c* N5 n  k' S
     25         ldmia   r13!, {r4-r6}       @ r5 = dest, r6 = length! g% o! m" [7 S! F7 \2 ~9 R; N
     26         add r4, r4, lr      @ r4 = initrd_start + load addr
" ~% f- ^+ T/ w" G     27         bl  move            @ move the initrd/ \" |# v: Y2 d% u+ o/ Z! K) k
     .....
! t3 E& [/ L( Z2 H     76         .type   data,#object
2 ]" ]. U2 u1 }. e$ y+ _     77 data:       .word   initrd_start        @ source initrd address
8 ~1 @+ \) f  C. D% a     78         .word   initrd_phys     @ destination initrd address
/ Q* T% L/ {% a4 n! o' Y     79         .word   initrd_size     @ initrd size2 J$ R  z2 [/ K! l& s& a9 l/ L9 b
     80
+ w  [/ H- u7 K4 t- c2 T0 k# v6 ^  r' M% O( s" }3 K; C
line 19,宣告了叫做.start的section
9 A7 K& _" r% R3 V# r' sline 20,21宣告了一個叫做_start的function
: w) A+ X0 r; ^) B9 S/ X程式碼似乎從line 23開始
5 b$ q+ Q+ @$ l% Aline 23, 『add lr, pc, #-0x8』
0 e& |  H1 n/ m+ Vadd就是將pc+(-0x8)的結果放到lr之中,pc和lr都是ARM裡頭暫存器
$ I4 C( g1 O5 K7 z/ R1 O5 ]pc就是program counter,CPU用來指著目前要執行的指令,執行完CPU就會自動把PC+1& E$ b2 {; T, P0 J1 Y
這樣就會拿到下一道指令,lr通常是第14個register, r14,常被用來繼續function
/ G, I0 z0 K5 O! b& E0 w5 mreturn時候的返回位置。奇怪的是為什麼要-0x8??
3 c' l1 f3 F' i5 [2 W1 i原因應該是ARM本身有pipeline的設計,prefetch->decode->execution,當指令被執行
& @  A' |9 g% P6 M, j4 y/ H的時候,其實已經預先去偷偷抓下一道,所以PC值不是真的指在目前執行的地方,三級/ S0 r4 I0 ]8 k, _3 K% m" O
pipeline剛好多了兩個cycle所以4 bytes x 2必須要-8才是正在執行指令的位址。
* Q$ f3 F5 ^: Y
/ v* S) b* R, H1 C$ ^0 {* E' Uline 24, 『adr r13, data』: v* |  n3 h: ^; S% J( r+ Z  S
adr會去讀取data所在的位址當作值,寫道r13裡頭。r13通常是用來放stack pointer,
' L3 d7 d' B* ]4 K# W, f. ]( {常縮寫成sp,所以現在r13就指到data所在的位置上去。
$ `. x5 p1 m2 C( s1 Z
. k% T( T8 X* Y+ H* w8 oline 25, 『ldmia   r13!, {r4-r6}』
1 f. Q' Q& v5 tldmia, load multiple increment after,顧名思義就是可以做很多次load的動作,每此
8 V( g5 `/ q7 _* v0 t  R) f# [, kload完就把位址+1,執行完之後$ t5 C, J4 D- p  [  Q$ N) t/ {
r4 = initrd_start5 x* p. c, S6 _* d; R/ V% g$ \
r5 = initrd_phys& B  @" k! m7 f# k% G
r6 = initrd_size
7 O: u, S; x1 i2 G& N2 }9 H) L
' [+ q: y5 L, A0 i  E# vline 26, 『add r4, r4, lr』
; o5 y' H( U6 X. |: qr4 = r4+lr, lr是剛剛我們算過的,指到一開始執行指令的地方,看程式碼的解釋,被當
- F, ?$ `7 F0 d  m5 v$ N成載入的位址,所以執行完 r4 = initrd_start+程式被載入的位置,用途不明,看看之
1 ?# a3 K- M4 P! h後有沒有被用到。* I. a0 _3 B' p( p; Y

$ Y0 R, h$ b' U: ]1 c9 Lline 27, 『bl  move』- c4 {+ z/ C/ g' D' v
bl, b是branch,相當於C語言goto的動作,l就是goto之前,先把目前位置存放到lr裡面,
5 J6 L$ C% t, H0 N所以bl除了goto之外,也改寫了lr,應該是有利於等一下返回的時候,可以用lr的值。
. `$ }9 Y* N& \  |( |
1 R2 w6 Q6 [5 i5 |; o# u3 C以上,好像還沒開始什麼重點,有空再繼續,有想錯的地方或是需要補充的地方,多多指教~
作者: gogojesse    時間: 2008-8-6 06:23 PM
上面的 code 裡面出現了一些還沒定義的symbol
7 o3 a" v, l* m  W$ o& c/ p例如 initrd_start, initrd_size等等  I* p7 e# @4 ~( ]& R3 L2 b8 v
其實是被定義在另外一個檔 ./arch/arm/boot/bootp/initrd.S
! J, w; w6 a5 L% Z8 d% B, d# S  i$ L, z! f1 y
      1     .type   initrd_start,#object
* L4 P- k/ U% U      2     .globl  initrd_start9 ?* S' N3 w1 O' w
      3 initrd_start:
: u: F* J, W3 O0 f" Z! x: n      4     .incbin INITRD( t, l9 {3 a/ p5 r- H$ W$ k
      5     .globl  initrd_end
$ t! V. F! e5 b: [      6 initrd_end:$ W  j8 S% A' L9 O3 R
- t. N6 u! d/ s6 r
line 2, 3, 5, 6定義了兩個symbol initrd_start和initrd_end
  T3 I  t' [6 {中間還用.incbin INITRD 將 ramdisk 的 image include進來1 |+ M9 G* }: n+ }9 k5 s2 Z( d
這邊有點複雜
2 W" r; Q) W4 l) ]$ s. n$ W  |假如compiler kernel的時候
2 y" Q/ f6 n" f6 A5 }" ?: G5 A% [有選擇使用ramdisk當成boot device的話
! Q7 {5 _! K. i4 S* N會對從環境變數去設置 INITRD ( v. i' h$ N( A. z% P- o
這個檔名會被帶入到MAKEFILE
: l, ]% t- {+ V/ K並且在做assembler動作的丟進來
+ F# x0 b% w* M3 M; W4 C) S假如沒有使用initrd. Q' x/ [9 f' M
那就.incbin應該就包不到東西
! ^$ i% |2 z+ d2 Pinitrd_start 和 initrd_end 就會相等  A0 U% }; I& U# t8 l1 A9 L

* l* `7 v0 s% S/ M$ h* o另外有個 script ./arch/arm/boot/bootp/bootp.lds+ J$ C0 Y* P4 h: B5 ~3 _" L7 K1 x
它規範了所有link起來的object code裡面的section要怎麼編排+ C! @8 x( d6 O. B
這樣撰寫kernel的時候7 d' g6 O; k" b' @7 G! Z8 T
可以到特定的section取得想要的資料或是計算某個section的大小
0 {8 t, U+ b2 _; g
" x* S" m# J% T; t* J" U- J     10 OUTPUT_ARCH(arm)1 e- H% \6 F; z2 x3 F
     11 ENTRY(_start)
5 m7 U' n6 C# i     12 SECTIONS6 X  Q" a; R" u4 L# C8 A
     13 {- H! @! t. o: ?
     14   . = 0;* }3 m4 t8 e& \, s2 Y; r! I
     15   .text : {
9 W% W5 `/ \# L: G: y& d     16    _stext = .;
+ v6 A# b7 l$ d, N1 x7 D     17    *(.start)
  }  H4 \3 B, @  @7 n7 C; m0 D0 I     18    *(.text)* M, A, i! g7 L$ {
     19    initrd_size = initrd_end - initrd_start;% E+ e4 p( C6 D* o9 Q3 c, S$ t
     20    _etext = .;
/ @! H5 m) @, Q% Z9 C$ m' H& J- i/ S4 E     21   }& I$ m' u7 s$ _; `! v" I! r/ W
     22
6 q. J' }6 o' \# C& N0 g/ E     23   .stab 0 : { *(.stab) }& V( x+ h4 f/ U, Z  j: ]
     24   .stabstr 0 : { *(.stabstr) }: S3 R2 T8 i- L& ~& F/ {
     25   .stab.excl 0 : { *(.stab.excl) }$ j, C4 T" @- A( c
     26   .stab.exclstr 0 : { *(.stab.exclstr) }# R" h2 T2 B; \* E
     27   .stab.index 0 : { *(.stab.index) }! A% h! T4 Z6 p7 \# X( W
     28   .stab.indexstr 0 : { *(.stab.indexstr) }8 J* \; y  @3 [' K; ?- C
     29   .comment 0 : { *(.comment) }; ?7 X6 D% P( J1 [/ Y
     30 }! }2 ^9 Y4 ]9 u' T  h1 q

9 F* I$ ]  r; v9 a- j- d對於object file的格式不熟悉的話. r& o2 A) ?# q$ |  A
可以參考ELF format的相關文件
作者: gogojesse    時間: 2008-8-6 07:50 PM
接著繼續看 init.S
7 ^9 B5 x$ y4 ]9 D/ J* U之前的code已經goto到move這邊來* A+ R% k# j2 Y2 `+ ~' R8 @
所以貼一些move的程式碼/ X+ r6 F& R! a* g4 m

/ J9 _* \+ _6 [9 D, D: V2 |     66 move:       ldmia   r4!, {r7 - r10}     @ move 32-bytes at a time
0 k3 K8 c: ]5 l0 f     67         stmia   r5!, {r7 - r10}
; c* G; B3 d9 u6 U     68         ldmia   r4!, {r7 - r10}8 f  P7 |" U0 F" q: M( L- x) A9 r
     69         stmia   r5!, {r7 - r10}, O- a0 c6 Q+ {
     70         subs    r6, r6, #8 * 46 u1 k" |0 ~5 }' g& Y# O0 O. U) K7 N
     71         bcs move
7 W' a, k6 V& W- i* N3 W% B1 t     72         mov pc, lr2 ?, E) p5 ^, F; c2 P# L
: G# |* Y! d* [9 i" b8 Y
line 66, 將r4所指到的位置,分別將值讀出來放到r7, r8, r9, r10, 可以發現剛剛計算過的r4這邊被; ^8 J  S  M# r8 D2 E8 m# S
用到了,但是為什麼r4不是用initrd_start,卻還要加上load addr??, e7 k) _. a, q) f+ S" ^
原因應該是bootp.lds的14行『. = 0;』表示最後被link好的address會從0x0開始6 c) h6 P4 y9 `) \# A
所以 initrd_start 所記錄的位置可以當成是offset
4 L. [) g3 u( L& i% T' C9 s加上load到DRAM或是擺在flash上的位址後+ y9 f. C/ ?5 `
就剛好是initrd所在的地方
$ n& b4 _0 w+ V% I$ E& z# R* b  U7 {8 y% H. @( B9 D6 y# `$ f2 h
line 67, 『stmia   r5!, {r7 - r10}』" W+ F6 @: i9 Q& v
stmia, store multiple increment after, 和ldmia動作相同,只是用來寫資料。
, P; Q! m$ @$ l+ N' R( Ur5是存放著initrd要擺放的位置 3 z# ~4 F2 r5 V, O
猜測應該是為了一開始image放在flash上,但是可以將initrd拷貝到DRAM上5 o- \/ ?+ C1 e5 N7 U% }. [
r7寫到r5指到的位置
, w; S4 S6 J) }* or8->r5+15 u# a- N+ H4 _  f/ F! [" W
r9->r5+2, N0 Z+ B& n" b; a% c* {5 S; _4 D
r10->r5+3 + ]! U! k9 B( F7 ^/ l$ l
所以我們發現,66,67行就是將r4所指的東西搬到r5。/ Y3 S( f- q- k6 ?; e- O8 O/ E

- W) O( W, |7 o& wline 68, 69也是一樣copy了4x4bytes,一共是32bytes。) w" J6 L+ D! w8 E7 F: W9 g: N
line 70,『subs    r6, r6, #8 * 4』,將length - 32bytes
% p' W; m  m6 I4 {, L  q3 Fline 71,『bcs move』,b是branch的意思,cs是表示condition的條件,要是條件符合的話,
0 \% w" d& b( S# o! B% g# L就做branch的動作,這邊的用意是判斷前一個length是不是已經到0,如果不為零就繼續copy。3 a9 c' b; }4 w! D" Z1 y1 @, h: J6 |
line 72,『mov pc, lr』# \6 r: I9 |6 ~) E3 @
接著就把剛剛bl指令預先存放好的lr 填入pc,這樣CPU就會跳回去原本的return address。
+ I) P& b5 y* `4 F6 K( o
. o2 _( M  c! q' X6 N以上的動作,慢慢看得出來有在做些什麼事) s1 n$ T; t- p# R! S' w
1. 找出initrd的所在位置
7 Q) ]8 h+ {/ ^& W( y2. 將它copy到一個指定的destination去
作者: gogojesse    時間: 2008-8-7 11:25 AM
程式返回之後
4 u4 @+ ?7 L$ T$ |我們接著看下一行
  1.      33         ldmia   r13, {r5-r9}        @ get size and addr of initrd5 e, b" F. F% h& ~5 F* \5 u5 W
  2.      34                         @ r5 = ATAG_CORE4 c+ d& e' A; _/ Z; X
  3.      35                         @ r6 = ATAG_INITRD2
    ' q) K( k/ v( ~
  4.      36                         @ r7 = initrd start
    * p3 i) C# ?  w2 V2 B. U, V* f* _
  5.      37                         @ r8 = initrd end( R1 n* ]/ e/ C3 d' W3 d
  6.      38                         @ r9 = param_struct address- s. ?, d, W: S2 ], j  {) p+ Y& C
  7.      39
    5 w* {* J, S& l/ Y* `, E
  8.      40         ldr r10, [r9, #4]       @ get first tag# A5 g2 [6 ]% q( l3 p, l" l6 K
  9.      41         teq r10, r5         @ is it ATAG_CORE?
複製代碼
line 33, 繼續從r13的地方取出資料到r5, r6, r7 ,r8, r9,註解的說明有提到各個資料" N% m, A( R& |5 T: K# t
的意義,注意一下這邊的r7是initrd的destination address不是source address。  [( W& S+ a9 m: z6 E  B

1 c5 H1 y% k/ tline 40, 讀入第一個tag,這邊的tag是指bootloader丟給kernel的一個boot arguments,
6 v6 k6 E9 y* r, u會被用一個叫做ATAG的structure包起來,並且放到系統的某個地方。然後kernel跑init.S,: M( I+ ?- X) M" G5 j; ]
的時候就會去這個地方拿ATAG的資料,這些資訊包括記憶體要使用多大,螢幕的解析度多大等等。
( t! z! w9 A! \. _  l, q, T9 I; L
line 41, t是test, eq是equal, 判斷拿到的第一個tag是不是等於atag core. 應該是看 ; e( W0 d  v  Q3 ?* e0 H) n& ^
atag list 是不是成立的。% L% ~3 c  A9 a# z9 h6 Y3 K8 B! k

  `2 K3 i/ r( i9 r9 N$ j$ m6 Q繼續接著看
  1.      45         movne   r10, #0         @ terminator
    ) B5 j* Y' r0 K- q
  2.      46         movne   r4, #2          @ Size of this entry (2 words)
    " \8 V, _/ y, C
  3.      47         stmneia r9, {r4, r5, r10}   @ Size, ATAG_CORE, terminator
複製代碼
發現45, 46, 47的指令都帶有condition "ne", not equal,表示是剛剛 line 41發現atag不成立! D" F4 s$ h% Q% K! O" _- P& X
所做的事情,注釋是寫『If we didn't find a valid tag list, create a dummy ATAG_CORE entry.』1 D% n4 G. _# t
所以以上三行就是用來創造一個假的entry,假設一切順利這三行指令會bypass過去不會被執行到。
# z* U1 X. a: ?& b$ D) P5 i( f. H
接著來看init.S最後一段程式碼 (終於~)
  1.      54 taglist:    ldr r10, [r9, #0]       @ tag length' N' I7 z6 K* V
  2.      55         teq r10, #0         @ last tag (zero length)?5 k: X8 p- d8 {5 P" J4 h4 P& b
  3.      56         addne   r9, r9, r10, lsl #2
    ! f. y( \: C( j/ U# K
  4.      57         bne taglist
    8 ^4 _5 O9 b6 M& Z
  5.      58
    . \8 V: ]1 [8 |8 x% `  d
  6.      59         mov r5, #4          @ Size of initrd tag (4 words): I. W2 M8 {  F5 s) q
  7.      60         stmia   r9, {r5, r6, r7, r8, r10}5 N( ^6 A( G6 f2 r( l
  8.      61         b   kernel_start        @ call kernel
複製代碼
line 54, 將r9指到的位址的offset 0x0的值載入到r10。看註解是tag length,所以這邊得要去翻翻atag的規範
! G6 }& Y! u6 Y, e* D3 q這邊有個文章有提到 http://www.simtec.co.uk/products ... ooting_article.html ,一開+ E* R9 w& \% z  v1 V, d
始應該是去讀atag_header所看第一個欄位,確認一下是size,應該沒問題。
  1. struct atag_header {- F$ D# \- I9 U* _
  2.         u32 size; /* legth of tag in words including this header */. O/ E5 S& @. Z, E
  3.         u32 tag;  /* tag value */
    ; R) k+ ]( Q8 B8 `$ |9 t
  4. };
複製代碼
line 55,測試一下size是不是0。
" z2 q) Z! C. p& C: |line 56, 57也有condition ne,表示是不為0的時候做的。將拿到的length(r10)乘以4,這邊的lsl是將r10往
$ k2 m. Q2 ~  Q+ M. y左shift的意思,因為一個欄位是4bytes,所以乘4之後就跳到下一個tag,一直跳到最後沒東西。
7 I4 K* G( n7 `; K2 X1 V; l) ]! @% o# g& W. x5 v) q  k
line 59, 將r5設成45 s, Y; }, s. }; P) u7 O
line 60, 將r5, r6, r7, r8 ,r10存到r9所指到的位置,應該就是跟在atag list的後面。* A" o, V4 v4 u  f3 m$ p
line 61, jump 到 kernel_start ,注意這邊是用b而不是bl,因為跳過去kernel就不需要返回了。BL會用到9 X/ n, m* B" f7 r, C
lr紀錄返回位置。$ C4 c1 T8 T. ^8 g- D1 k" }

4 l. c( ]) C2 I1 q, Z! c以上,走過一整個init.S,接著會跳到./arch/arm/boot/compressed/head.S。' l  b) j( ]# I: W; L

! K1 S) @+ X/ w. q  xkernel_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 發表
; ^$ K& J5 {3 o" H好一段時間沒有碰程式了,看到你的分析讓我想起年輕時候的我 ~~~~ 還是不要透漏年齡
+ S8 f- u) f! |9 ?, b6 l( s

5 Z/ o6 p) }3 m
% M3 V( h1 ^0 e$ R有些時候  東西是越陳越香啊~~
  J) y3 y5 h( b...........
作者: gogojesse    時間: 2009-7-16 03:26 PM
剛剛發現一個大陸網站的blog貼了我的文章
& e3 o1 a7 I7 i! H但是沒表明出處 = =: ^  w  U& y& N' m9 P4 w
還把標題改過,感覺像是他自己寫的/ G) i# q; i& N4 Q& _9 E
真是麻煩~( ?# H# N+ V3 R) J
4 |% \. v/ L* W. @% ]. e
已經好幾次這樣的經驗
2 B# q2 F  e' p7 I" |以前幫忙有弄網站也是這樣
! [' p8 j/ F1 f5 _抄襲得很嚴重
1 F2 Z& L) h6 {; |8 U內地那邊到底有沒有在管啊!!




歡迎光臨 Chip123 科技應用創新平台 (http://chip123.com/) Powered by Discuz! X3.2