Upgrade to Pro — share decks privately, control downloads, hide ads and more …

StackExchangeで見たシステムプログラミング案件

 StackExchangeで見たシステムプログラミング案件

システムプログラミング会
http://connpass.com/event/34995/

YAEGASHI Takeshi

July 02, 2016
Tweet

More Decks by YAEGASHI Takeshi

Other Decks in Programming

Transcript

  1. StackExchangeで見た
    システムプログラミング案件
    八重樫 剛史
    https://twitter.com/hogegashi
    https://github.com/yaegashi

    View Slide

  2. 自己紹介
    ● 八重樫 剛史 (やえがし たけし)
    ● ゲーム会社勤務のエンジニア、だがゲームプログラマではない
    ● GNU/Linux と組み込みシステムが興味・関心の中心

    View Slide

  3. 仕事とか
    ● いろんな組み込みシステム向けに GNU/Linux や Android の
    実行環境・開発環境をブートストラップしてきた
    ● 版権ものをよく扱うのでコンテンツ保護機構を
    考案・実装したりもする
    ● 最近の案件は RasPi が MQTT を Go したら IoT した
    みたいなやつ

    View Slide

  4. 趣味とか
    ● 競技プログラミング (下手の横好き)
    ○ TopCoder を 2 年続けてようやくイエローコーダー
    ○ Google Code Jam 2016 は順当に Round 1 敗退
    ● Ingress (ひきこもりの L16)
    ● 電子工作・FPGA (時間がない)
    ● StackExchange 回答陣 (ほぼ引退)

    View Slide

  5. StackExchange 見てますか?
    ● 海外の巨大 Q&A サイト
    ● 特定ジャンル向けの姉妹サイトがたくさんある
    ○ Stack Overflow stackoverflow.com
    ○ Server Fault serverfault.com
    ○ Ask Ubuntu ask.ubuntu.com
    ○ スタック・オーバーフロー ja.stackoverflow.com ← 日本語版
    ● ぐぐるとよく引っかかるのでお世話になったことがある人は多いと思います

    View Slide

  6. StackExchange 効能
    ● ゲーミフィケーションのさじ加減がよくできている
    ● 参加者がみな熱心で、質問・回答ともに質が高いものが多く、勉強になる
    ● Reputation や Badge を稼ぐことで英語の読み書き能力がどんどん向上する
    ● 自分の興味のあるジャンル・サイトに参加して腕を磨こう!

    View Slide

  7. 私の主戦場: Unix & Linux
    ● unix.stackexchange.com
    ● 低レベルから高レベルまで様々なレイヤの話題
    ● マイナーな処理系にやたら詳しい人がいる
    ○ マイナー商用 Unix (AIX とか HP-UX とか)
    ○ マイナーシェル (ksh93 とか yash とか fish とか)
    ○ マイナーシェル機能 (coproc とか)
    ● GNU/Linux 環境に慣れきって Posix とか忘れてしまってることを実感する
    ○ シェルスクリプトの回答は #!/bin/sh でなく #!/bin/bash で始めておいたほうが無難

    View Slide

  8. 私の戦績: Unix & Linux
    ● 2015年6月に始めたところ妙にはまってしまい、
    3 ヶ月あまりで200以上の回答を投稿し5000以上のreputationを稼いだ
    ● その後は飽きた

    View Slide

  9. U&L システムプログラミング案件
    各質問にはタグがついており、自分が回答した質問もタグで検索できる

    View Slide

  10. U&L システムプログラミング案件
    むかし自分が回答した /dynamic-linking タグの質問

    View Slide

  11. U&L システムプログラミング案件
    Q: LD_LIBRARY_PATH environment variable
    拙い語彙の英語でも通じるし upvote や accept がもらえます

    View Slide

  12. U&L システムプログラミングおもしろ案件
    Q: 実行中にシステムコールを全く行わない unix コマンドってある?

    View Slide

  13. U&L システムプログラミングおもしろ案件
    Q: 実行中にシステムコールを全く行わない unix コマンドってある?
    A: Unix プロセスって終了するのにも exit() を呼んで
    システムコールしないといけないから、ないよ。
    ・・・本当だろうか?

    View Slide

  14. Q: システムコールを使わずに終了するプログラム書ける?
    int main()
    {
    return 0;
    }
    安直な回答

    View Slide

  15. Q: システムコールを使わずに終了するプログラム書ける?
    $ echo 'int main(){return 0;}' | gcc -static -x c -
    $ strace ./a.out
    execve("./a.out", ["./a.out"], [/* 56 vars */]) = 0
    uname({sysname="Linux", nodename="hf", ...}) = 0
    brk(NULL) = 0xeec000
    brk(0xeed1c0) = 0xeed1c0
    arch_prctl(ARCH_SET_FS, 0xeec880) = 0
    readlink("/proc/self/exe", "/home/yaegashi/a.out", 4096) = 20
    brk(0xf0e1c0) = 0xf0e1c0
    brk(0xf0f000) = 0xf0f000
    access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or
    directory)
    exit_group(0) = ?
    +++ exited with 0 +++
    実際には libc ランタイムが main() の前後でたくさんシステムコール呼んでる

    View Slide

  16. Q: システムコールを使わずに終了するプログラム書ける?
    $ echo 'int _start(){return 0;}' | gcc -x c - -nostdlib -nostartfiles
    $ strace ./a.out
    execve("./a.out", ["./a.out"], [/* 56 vars */]) = 0
    --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x1} ---
    +++ killed by SIGSEGV (core dumped) +++
    Segmentation fault (core dumped)
    libc ランタイムなしで 0 を返す関数だけのプログラムを作って実行すると…

    View Slide

  17. いまのところの結論
    ● Linux カーネルではプロセスが終了するには
    自分で exit_group() というシステムコールを呼ぶ必要がある
    ● さもなければ、誰かにシグナルを送ってもらって止めてもらうしかない
    ● シグナルで終了したプロセスは exit code が シグナル番号+128 になる
    ● ということは、システムコールを使わずにプログラムを正常終了
    つまり exit code 0 でプロセス終了する方法はない?
    Q: システムコールを使わずに終了するプログラム書ける?

    View Slide

  18. ところでシグナルって何種類あるのか?
    ・・・システムコールを使わずに、何種類のシグナルを出せるだろうか?
    とりあえず Linux on x86_64 という条件でチャレンジしてみよう!
    $ kill -l
    1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
    6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
    11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
    16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
    21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
    26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
    31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
    38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
    43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
    48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
    53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
    58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
    63) SIGRTMAX-1 64) SIGRTMAX

    View Slide

  19. SIGSEGVをシステムコールなしで出す
    もっとも簡単?
    $ echo 'void _start(){}' | gcc -x c - -nostdlib -nostartfiles
    $ strace ./a.out
    execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0
    --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x1} ---
    +++ killed by SIGSEGV (core dumped) +++
    Segmentation fault (core dumped)

    View Slide

  20. SIGSEGVをシステムコールなしで出す
    ちなみに空の関数を最適化つきでコンパイルすると、
    GCCは repz retq という見慣れないコードを吐く
    $ echo 'void _start(){}' | gcc -O -x c - -nostartfiles -nostdlib
    $ objdump -d
    a.out: file format elf64-x86-64
    Disassembly of section .text:
    0000000000400144 <_start>:
    400144: f3 c3 repz retq
    http://repzret.org というサイトに解説がある

    View Slide

  21. SIGFPEをシステムコールなしで出す
    整数ゼロ除算などで発生する
    $ echo 'int _start(){return 1/0;}' | gcc -x c - -nostartfiles -nostdlib
    : In function '_start':
    :1:22: warning: division by zero [-Wdiv-by-zero]
    $ strace ./a.out
    execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0
    --- SIGFPE {si_signo=SIGFPE, si_code=FPE_INTDIV, si_addr=0x40014f} ---
    +++ killed by SIGFPE (core dumped) +++
    Floating point exception (core dumped)

    View Slide

  22. SIGFPEをシステムコールなしで出す
    あれ、でも 1/0 って定数・即値じゃないのかね?
    $ echo 'int _start(){return 1/0;}' | gcc -O -x c - -nostartfiles -nostdlib
    : In function '_start':
    :1:22: warning: division by zero [-Wdiv-by-zero]
    $ objdump -d
    a.out: file format elf64-x86-64
    Disassembly of section .text:
    0000000000400144 <_start>:
    400144: b8 01 00 00 00 mov $0x1,%eax
    400149: b9 00 00 00 00 mov $0x0,%ecx
    40014e: 99 cltd
    40014f: f7 f9 idiv %ecx
    400151: c3 retq

    View Slide

  23. SIGFPEをシステムコールなしで出す
    Clang は GCC とはまた違ったコードを吐くぞ?
    $ echo 'int _start(){return 1/0;}' |
    clang -O -x c - -nostartfiles -nostdlib
    :1:22: warning: division by zero is undefined [-Wdivision-by-zero]
    int _start(){return 1/0;}
    ^~
    1 warning generated.
    $ objdump -d
    a.out: file format elf64-x86-64
    Disassembly of section .text:
    0000000000400150 <_start>:
    400150: c3 retq

    View Slide

  24. SIGILLをシステムコールなしで出す
    このへんからアセンブリ言語を使う必要がでてくる
    $ echo '.global _start;_start:mov %ax,%cs' |
    gcc -x assembler - -nostdlib -nostartfiles
    $ strace ./a.out
    execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0
    --- SIGILL {si_signo=SIGILL, si_code=ILL_ILLOPN, si_addr=0x4000d4} ---
    +++ killed by SIGILL (core dumped) +++
    Illegal instruction (core dumped)

    View Slide

  25. SIGTRAPをシステムコールなしで出す
    有名問題 (BINARY HACKS にも書かれている)
    $ echo 'int3' | gcc -x assembler - -nostdlib -nostartfiles
    $ strace ./a.out
    execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0
    --- SIGTRAP {si_signo=SIGTRAP, si_code=SI_KERNEL} ---
    +++ killed by SIGTRAP (core dumped) +++
    Trace/breakpoint trap (core dumped)

    View Slide

  26. SIGBUSをシステムコールなしで出す
    ● 伝統的に x86 ではアラインメントされないメモリアクセスができてしまう
    ● じゃあ x86 で SIGBUS ってどうやって出すのか?
    ● システムコールを使っていいならファイルを mmap() して
    ファイルサイズ外のオフセットのアドレスをアクセスすれば出る

    View Slide

  27. SIGTRAPをシステムコールなしで出す
    実は x86 は EFLAGS の AC ビットというのを立てると
    アラインメントなしのメモリアクセスで例外を出すようになるらしい
    $ echo 'pushf;orl $0x40000,(%rsp);popf;movl $0,1' |
    gcc -x assembler - -nostdlib -nostartfiles
    $ strace ./a.out
    execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0
    --- SIGBUS {si_signo=SIGBUS, si_code=BUS_ADRALN, si_addr=0} ---
    +++ killed by SIGBUS (core dumped) +++
    Bus error (core dumped)
    stackoverflow.com: How to get a “bus error”?

    View Slide

  28. SIGKILLをシステムコールなしで出す
    ちょっとずるい方法
    無限ループするプログラムを作って CPU time に limit を設定して殺してもらう
    $ echo 'jmp .' | gcc -O -x assembler - -nostdlib -nostartfiles
    $ ulimit -t 1
    $ strace -rt ./a.out
    0.000000 execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0
    0.997027 +++ killed by SIGKILL +++
    Killed

    View Slide

  29. SIGXCPUをシステムコールなしで出す
    おまけ
    CPU time に soft limit を設定すると SIGKILL の前に SIGXCPU を送ってもらえる
    $ echo 'jmp .' | gcc -O -x assembler - -nostdlib -nostartfiles
    $ ulimit -t 10
    $ ulimit -S -t 1
    $ strace -rt ./a.out
    0.000000 execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0
    0.997119 --- SIGXCPU {si_signo=SIGXCPU, si_code=SI_KERNEL} ---
    0.118958 +++ killed by SIGXCPU (core dumped) +++
    CPU time limit exceeded (core dumped)

    View Slide

  30. システムコールなしで出せるシグナルはまだあるだろうか?
    他にもうまい方法を考案された方はぜひ教えてください!!
    $ kill -l
    1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
    6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
    11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
    16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
    21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
    26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
    31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
    38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
    43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
    48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
    53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
    58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
    63) SIGRTMAX-1 64) SIGRTMAX

    View Slide

  31. まとめ
    ● StackExchange のシステムプログラミング案件を紹介しました
    ● システムコールを使わずにシグナルをもらう方法を考えました
    ● システムコールを使わずに正常終了するプログラムを書く方法を募集中

    View Slide

  32. おわり

    View Slide