altanative マクロで学ぶ gas
入門
kosaki@ ふじつう ?
今日はなんの話?
• x86 の smp_mb(), smp_wmb(), smp_rmb() など
の実装に使われている altanative マクロの解説
• ・・・をサカナにして、 gas の独特の記法とか
カーネルでアヤシゲなセクションが出てきたら
まずどこから調べるべきか。とか、そういう入
門話をするつもり
• 最近レベルが上がりすぎているカーネル読書会
にへたれな発表をお届け
• 発表者の敷居を下げるのにちょっと貢献?
あなたは誰?
• さあ?
• 最近自分でも専門が分からない
• 昔 XML とかやってて、データ放送関係に引っ張り込ま
れ、ブラウザを三回ぐらいイチから作ったりしているう
ちにカーネルとか X とかアセンブラとかメモリバリアと
か low level な話に詳しくなった。
• その後、今の会社に転職。面談で甘い言葉をいろいろと
囁かれたので
• え、現在の仕事ですか? いやあ、あんまり悪口いえな
いんですよ。
• 最近、レガシーエンコーディング界隈もウロウロと
• 人見知りなのでお手柔らかに (  ̄ ω  ̄ ;) ゞ
なんで出てきたの?
• 1月ぐらいから Blog をつけてみた。
もちろん (?) ギャグとネタとお笑いがメイン
• ある時、ブログに spinlock という単語が入って
”いたら spinlock ”トイレ というアヤシイ検索ワ
ードでたどり着く人が・・・
• 特殊な使命感にかられ、トイレ以外のロックを
説明する
• カーネルの話は誰も反応くれないので、最近飽
き気味
• 自分にカツを入れるために発表するか! ← おい
では始めましょう
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 にも似たような
コードがあるらしい・・
定義
#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")
なにこの暗号?
てゆーか、ジャンプ命令どこ?
これを見た瞬間理解できる人は少ない
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 */
この一文は、後で超重要なヒントに
むりやりアセンブラを展開して
みる
テストプログラムにむりやり貼っつける
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();
}
むりやりアセンブラを展開して
みる $ 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つ出てきた
なんかコードっぽい
こっちはデータかな?
セクションって何?
• バイナリのカタマリの単位
• 最低でもコードとデータの2つのセクシ
ョンが出来るのが一般的( OS 問わず)
• elf は作ろうと思えば好きなだけセクショ
ンを作れる仕様
• 作って意味があるかは別の話ww
OS や標準ライブラリ・コマンドが使わない情報の意味って・・・
ここまで来てから、
やっと、
gas のマニュアルを読む
なんちゃって理解重要!
セクション切り替え
.section [section_name], “[FLAGS]” セクションを切り替える
section_name:
”任意の文字列。先頭は .” (ドット)で始めるのが礼儀作法みたい
FLAGS:
‘a’ allocatable, 実行時にメモリにロードされる。
これが OFF なセクションってほとんど見ない
‘ x’ executable, 実行可能
.previous 1つ前の .section 命令をキャンセルする。
663:
mfence
664:
.section .altinstr_replacement,"ax"
.previous
むむ、テキストセクションじゃない
セクションにコードを入れてますな
ローカルなラベル
661:
lock; addl $0,0(%esp)
662:
同じ関数内に2つ以上あってもエラーにならないラベル
ラベル名が数値だけ
661:
lock; addl $0,0(%esp)
662:
どうやって参照するのかは次ページで
ローカルなラベル
ラベルを参照するときは必ず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 ってインラインアセンブラ前提の拡張多い
アライン整列 擬似命令
• align [argument]
後続の命令・データを argument byte にア
ライメントさせる。
• んー、説明いるのかな?
即値埋め込み擬似命令
.byte number
.long number
引数の number をその場所に埋め込む。
単純。
普通は定数データを埋め込むのに使う。
マシン語さえ分かっていれば、これだけで何でも
出来る
すごくダメな例
• カーネルの 32bit 移行直後
.byte 0x66 0xea
code32: .long 0x1000
.word 0x10
ここ、受け売りなので、質問禁止
16bit アセンブラのファイルにむりやり 32bit ジャンプ命令を入れるぜ。
↓ こんなコード
0x66 0xea 0x1000 0x10
次の命令
32bit 命令ね
jmp
オフセット
セレクタ
即値埋め込み擬似命令
• .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
アドレスが入るっぽげ
コード長が入るっぽげ
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 中でのセクションのファイルオフセットとサイズが分かるので・・・
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
へのポインタ へのポインタ
実は
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 };
実は以下のデータ構造を
むりやり作ってるんです。
むりやりまとめる(ここまでの
結論)
• alternative マクロは、 oldinstr 引数だけを
実行コードとして生成する。
• newinstr 引数、 feature 引数を使っ
て、 .altinstr_replacement と
.altinstructions
というなんだかよく分からないデータ構
造を作っている。
oldinstr しか実行されない!!
まあ、おちつけ
そんなわけ無いから
リンカスクリプトを見よう
• セクションを作った以上どこかで使ってるはず
• リンカスクリプトで使わずどこで使えると?
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 言語でいうグローバル変数)を作ってるぞ!!
それぞれ、先頭と
最後をさすポインタ
__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
見る
カーネル起動時に
呼ばれる
書き換えだぜ!
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 でコード上書き
結論
• altanative マクロは if-else をジャンプ命令を使わず実現したいとき
に使う自己書き換えマクロである
• smp_wmb() などはシーケンスロックとか RCU Update とかの軽量
ロックで使われるので性能重要
• Pentium4 なのに lock addl するのも、ジャンプ命令走るのはイヤイ
ヤ
( ゚ Д ゚ ;≡; ゚ Д ゚ )
ハックするか!
という思考回路が働いたと思われる
本日主張したかったこと
• マクロ中でインラインアセンブラされると C とアセンブラが混じっ
てとても読み肉い
• gas の拡張はドキュメント少なくてとても困る
そんな時
kosaki メソッド 誕生!!
• とりあえず動かしてみる。
• ついでにバイナリダンプとかしてみる
• バイナリは嘘つかない !
もちろん
ネタです
信じないでくださ
い
ギャグはおいといて
本日、本当に主張したかったこ
と
• gcc 拡張とか gas 拡張とか慣れないとよく分からなくて
、カーネル初心者に敷居が高くなってると思うんですよ
• でも、やってみると以外に分かったりする。
• ソースコードをいきなり読んで分からない所があるのは
当たり前
• それで挫折した気になるなんて挫折プロのオイラからみ
たら10年早いっすよ
やってみようよ!!
ご清聴ありがとうございまし
た!!

Altanative macro

  • 1.
  • 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.
  • 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.
  • 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 0408 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 1318static 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.

Editor's Notes

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