Your SlideShare is downloading. ×
Altanative macro
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Altanative macro

1,011
views

Published on

To explain how to work alternative() macro in Linux kernel.

To explain how to work alternative() macro in Linux kernel.

Published in: Technology

0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,011
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
9
Comments
0
Likes
7
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • 昔ごとむさんの発表をみて感動した。というのもある
  • インラインアセンブラだと、戻すべきセクションが自明じゃない事が多い
  • Cで普通に作ると名前がいるからな
  • Transcript

    • 1. altanative マクロで学ぶ gas 入門 kosaki@ ふじつう ?
    • 2. 今日はなんの話? • x86 の smp_mb(), smp_wmb(), smp_rmb() など の実装に使われている altanative マクロの解説 • ・・・をサカナにして、 gas の独特の記法とか カーネルでアヤシゲなセクションが出てきたら まずどこから調べるべきか。とか、そういう入 門話をするつもり • 最近レベルが上がりすぎているカーネル読書会 にへたれな発表をお届け • 発表者の敷居を下げるのにちょっと貢献?
    • 3. あなたは誰? • さあ? • 最近自分でも専門が分からない • 昔 XML とかやってて、データ放送関係に引っ張り込ま れ、ブラウザを三回ぐらいイチから作ったりしているう ちにカーネルとか X とかアセンブラとかメモリバリアと か low level な話に詳しくなった。 • その後、今の会社に転職。面談で甘い言葉をいろいろと 囁かれたので • え、現在の仕事ですか? いやあ、あんまり悪口いえな いんですよ。 • 最近、レガシーエンコーディング界隈もウロウロと • 人見知りなのでお手柔らかに (  ̄ ω  ̄ ;) ゞ
    • 4. なんで出てきたの? • 1月ぐらいから Blog をつけてみた。 もちろん (?) ギャグとネタとお笑いがメイン • ある時、ブログに spinlock という単語が入って ”いたら spinlock ”トイレ というアヤシイ検索ワ ードでたどり着く人が・・・ • 特殊な使命感にかられ、トイレ以外のロックを 説明する • カーネルの話は誰も反応くれないので、最近飽 き気味 • 自分にカツを入れるために発表するか! ← おい
    • 5. では始めましょう altanative マクロを使っている例 #define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM) いわゆる if – else マクロ if( X86_FEATURE_XMM ){ ← SSE 搭載 CPU か return "asm(sfence)" }else { return "asm(lock; addl $0,0(%%esp))" } のように動作する(x86のみ存在するマクロ) ・・・・なら、そう書けばいいじゃん! なんでそうしないの? スタックポインタの指すメモリに 0を足す (もちろん何もおきない) 実は PPC にも似たような コードがあるらしい・・
    • 6. 定義 #define alternative(oldinstr, newinstr, feature) asm volatile ("661:nt" oldinstr "n662:n" ".section .altinstructions,"a"n" " .align 4n" " .long 661bn" /* label */ " .long 663fn" /* new instruction */ " .byte %c0n" /* feature bit */ " .byte 662b-661bn" /* sourcelen */ " .byte 664f-663fn" /* replacementlen */ ".previousn" ".section .altinstr_replacement,"ax"n" "663:nt" newinstr "n664:n" /* replacement */ ".previous" :: "i" (feature) : "memory") なにこの暗号? てゆーか、ジャンプ命令どこ? これを見た瞬間理解できる人は少ない
    • 7. altanative マクロのコメント 288 /* 289 * Alternative instructions for different CPU types or capabilities. 290 * 291 * This allows to use optimized instructions even on generic binary 292 * kernels. 293 * 294 * length of oldinstr must be longer or equal the length of newinstr 295 * It can be padded with nops as needed. 296 * 297 * For non barrier like inlines please define new variants 298 * without volatile and memory clobber. 299 */ この一文は、後で超重要なヒントに
    • 8. むりやりアセンブラを展開して みる テストプログラムにむりやり貼っつける main.c #define alternative(oldinstr, newinstr, feature) asm volatile ("661:nt" oldinstr “n662:n" ".section .altinstructions,"a“n" " .align 4n" " .long 661bn" /* label */ " .long 663fn" /* new instruction */ " .byte %c0n" /* feature bit */ " .byte 662b-661bn" /* sourcelen */ " .byte 664f-663fn" /* replacementlen */ ".previousn" ".section .altinstr_replacement,"ax“n" "663:nt" newinstr “n664:n" /* replacement */ ".previous" :: "i" (feature) : "memory") #define X86_FEATURE_XMM2 57 /* dummy */ #define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2) main(){ mb(); }
    • 9. むりやりアセンブラを展開して みる $ gcc -S main.c -o main.s 661: lock; addl $0,0(%esp) 662: .section .altinstructions,"a" .align 4 .long 661b .long 663f .byte 57 .byte 662b-661b .byte 664f-663f .previous .section .altinstr_replacement,"ax" 663: mfence 664: .previous なんかセクションが 3つ出てきた なんかコードっぽい こっちはデータかな?
    • 10. セクションって何? • バイナリのカタマリの単位 • 最低でもコードとデータの2つのセクシ ョンが出来るのが一般的( OS 問わず) • elf は作ろうと思えば好きなだけセクショ ンを作れる仕様 • 作って意味があるかは別の話ww OS や標準ライブラリ・コマンドが使わない情報の意味って・・・
    • 11. ここまで来てから、 やっと、 gas のマニュアルを読む なんちゃって理解重要!
    • 12. セクション切り替え .section [section_name], “[FLAGS]” セクションを切り替える section_name: ”任意の文字列。先頭は .” (ドット)で始めるのが礼儀作法みたい FLAGS: ‘a’ allocatable, 実行時にメモリにロードされる。 これが OFF なセクションってほとんど見ない ‘ x’ executable, 実行可能 .previous 1つ前の .section 命令をキャンセルする。 663: mfence 664: .section .altinstr_replacement,"ax" .previous むむ、テキストセクションじゃない セクションにコードを入れてますな
    • 13. ローカルなラベル 661: lock; addl $0,0(%esp) 662: 同じ関数内に2つ以上あってもエラーにならないラベル ラベル名が数値だけ 661: lock; addl $0,0(%esp) 662: どうやって参照するのかは次ページで
    • 14. ローカルなラベル ラベルを参照するときは必ずfかbサフィックスをつける 参照位置から一番近いラベルを 参照 fは前向きに( forward) 、 bは後ろ向きに ( backword) サーチして一番近いもの 661: lock; addl $0,0(%esp) 662: jmp 661f 661: lock; addl $0,0(%esp) 662: jmp 661b マクロで内緒でインラインアセンブラしてる時(今回のケース)では 人間がラベルぶつからないように調整できないから。 ほんと gas ってインラインアセンブラ前提の拡張多い
    • 15. アライン整列 擬似命令 • align [argument] 後続の命令・データを argument byte にア ライメントさせる。 • んー、説明いるのかな?
    • 16. 即値埋め込み擬似命令 .byte number .long number 引数の number をその場所に埋め込む。 単純。 普通は定数データを埋め込むのに使う。 マシン語さえ分かっていれば、これだけで何でも 出来る
    • 17. すごくダメな例 • カーネルの 32bit 移行直後 .byte 0x66 0xea code32: .long 0x1000 .word 0x10 ここ、受け売りなので、質問禁止 16bit アセンブラのファイルにむりやり 32bit ジャンプ命令を入れるぜ。 ↓ こんなコード 0x66 0xea 0x1000 0x10 次の命令 32bit 命令ね jmp オフセット セレクタ
    • 18. 即値埋め込み擬似命令 • .byte や .long の引数は単純な四則演算を受け付ける • .long 661b と書けばラベル 661 を前方参照してそのアドレスを埋め 込む • .byte 662b-661b と書けば、と書けばラベル 662 のアドレス引く 661 の アドレス、つまりその範囲のコードの長さが埋め込まれ る。.section .altinstructions,"a" .align 4 .long 661b .long 663f .byte 57 .byte 662b-661b .byte 664f-663f .previous アドレスが入るっぽげ コード長が入るっぽげ
    • 19. objdump ( readelf でも可) セクション : 索引名 サイズ VMA LMA File off Algn (中略) 12 .altinstr_replacement 00000006 0804841c 0804841c 0000041c 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 13 .fini 0000001a 08048424 08048424 00000424 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 14 .rodata 00000008 08048440 08048440 00000440 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 15 .altinstructions 00000017 08048448 08048448 00000448 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA さっきの、なんちゃって main.c をコンパイルして objdump a.out 中でのセクションのファイルオフセットとサイズが分かるので・・・
    • 20. altinstructions 部分を od してみ る 50 83 04 08 1c 84 04 08 39 06 03 00 56 83 04 08 1f 84 04 08 39 06 03 ---------------- --------------- oldinstr newinstr pad (4byte align に するため) mfence 命令の opcode は 0F AE /6 の 3 バイト add mem imm8 で 5 バイト lock prefix で +1 feature(X86_FEATURE_XMMを ダミーで 0x39 でごまかしたから ) od -A x -t x1 -j 0x448 -N 0x17 a.out .section .altinstructions,"a" .align 4 .long 661b .long 663f .byte 57 .byte 662b-661b .byte 664f-663f .previous へのポインタ へのポインタ
    • 21. 実は 50 83 04 08 1c 84 04 08 39 06 03 00 56 83 04 08 1f 84 04 08 39 06 03 oldinstr newinstr linux/include/asm-i386/system.h の 278 struct alt_instr { 279 __u8 *instr; /* original instruction */ 280 __u8 *replacement; 281 __u8 cpuid; /* cpuid bit set for replacement */ 282 __u8 instrlen; /* length of original instruction */ 283 __u8 replacementlen; /* length of new instruction, <= instrlen */ 284 __u8 pad; 285 }; 実は以下のデータ構造を むりやり作ってるんです。
    • 22. むりやりまとめる(ここまでの 結論) • alternative マクロは、 oldinstr 引数だけを 実行コードとして生成する。 • newinstr 引数、 feature 引数を使っ て、 .altinstr_replacement と .altinstructions というなんだかよく分からないデータ構 造を作っている。 oldinstr しか実行されない!!
    • 23. まあ、おちつけ そんなわけ無いから
    • 24. リンカスクリプトを見よう • セクションを作った以上どこかで使ってるはず • リンカスクリプトで使わずどこで使えると? linux/arch/i386/kernel/vmlinux.lds.S 86 . = ALIGN(4); 87 __alt_instructions = . ; 88 .altinstructions : { *(.altinstructions) } 89 __alt_instructions_end = . ; 90 .altinstr_replacement : { *(.altinstr_replacement) } __alt_instructions と __alt_instructions_end という シンボル( C 言語でいうグローバル変数)を作ってるぞ!! それぞれ、先頭と 最後をさすポインタ
    • 25. __alt_instructions で grep linux/arch/i386/kernel/setup.c 1318 static int no_replacement __initdata = 0; 1319 1320 void __init alternative_instructions(void) 1321 { 1322 extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; 1323 if (no_replacement) 1324 return; 1325 apply_alternatives(__alt_instructions, __alt_instructions_end); 1326 } 1327 1328 static int __init noreplacement_setup(char *s) 1329 { 1330 no_replacement = 1; 1331 return 0; 1332 } 1333 1334 __setup("noreplacement", noreplacement_setup); 型はどこから? と思ってはいけない。 リンカスクリプトに 型はないので 宣言したもの勝ち 次のページで apply_alternatives 見る カーネル起動時に 呼ばれる
    • 26. 書き換えだぜ! 1291 void apply_alternatives(void *start, void *end) 1292 { 1293 struct alt_instr *a; 1294 int diff, i, k; 1295 unsigned char **noptable = intel_nops; (中略) 1302 for (a = start; (void *)a < end; a++) { 1303 if (!boot_cpu_has(a->cpuid)) 1304 continue; 1305 BUG_ON(a->replacementlen > a->instrlen); 1306 memcpy(a->instr, a->replacement, a->replacementlen); 1307 diff = a->instrlen - a->replacementlen; 1308 /* Pad the rest with nops */ 1309 for (i = a->replacementlen; diff > 0; diff -= k, i += k) { 1310 k = diff; 1311 if (k > ASM_NOP_MAX) 1312 k = ASM_NOP_MAX; 1313 memcpy(a->instr + i, noptable[k], k); 1314 } 1315 } 1316 } linux/include/asm-i386/system.h の 278 struct alt_instr { 279 __u8 *instr; 280 __u8 *replacement; 281 __u8 cpuid; 282 __u8 instrlen; 283 __u8 replacementlen; 284 __u8 pad; 285 }; CPU がサポートしていな ければスキップ memcpy でむりやりコード書き換え 新旧でコードの長さが不一致の 時は残りを NOP で埋める 1~7バイトの多彩な NOP 命令を 生かして出来る限り少ない NOP 命令で埋める 作ったデータ構造を生かして featre 引数を満たしていれば newinstr でコード上書き
    • 27. 結論 • altanative マクロは if-else をジャンプ命令を使わず実現したいとき に使う自己書き換えマクロである • smp_wmb() などはシーケンスロックとか RCU Update とかの軽量 ロックで使われるので性能重要 • Pentium4 なのに lock addl するのも、ジャンプ命令走るのはイヤイ ヤ ( ゚ Д ゚ ;≡; ゚ Д ゚ ) ハックするか! という思考回路が働いたと思われる
    • 28. 本日主張したかったこと • マクロ中でインラインアセンブラされると C とアセンブラが混じっ てとても読み肉い • gas の拡張はドキュメント少なくてとても困る そんな時 kosaki メソッド 誕生!! • とりあえず動かしてみる。 • ついでにバイナリダンプとかしてみる • バイナリは嘘つかない ! もちろん ネタです 信じないでくださ い
    • 29. ギャグはおいといて
    • 30. 本日、本当に主張したかったこ と • gcc 拡張とか gas 拡張とか慣れないとよく分からなくて 、カーネル初心者に敷居が高くなってると思うんですよ • でも、やってみると以外に分かったりする。 • ソースコードをいきなり読んで分からない所があるのは 当たり前 • それで挫折した気になるなんて挫折プロのオイラからみ たら10年早いっすよ やってみようよ!!
    • 31. ご清聴ありがとうございまし た!!