chouett0's note

掃きだめ的なsomething

glibcを利用しないコンパイル方法

はじめに

今回の検証を行うに至った動機としてリンカやローダといった、一般的にコンパイラとして一括りにされているソフトウェアについて勉強する過程でライブラリを利用せずにコンパイルを行うにはどうすればいいのか、という疑問が生まれました。
そこで、glibcを用いずにコンパイルを行ってみることにしました。

glibcを利用したコンパイル

ライブラリを用いないコンパイルを行う前に、比較を行えるようglibcを用いた一般的なコンパイルを行ったバイナリを生成しました。
そのソースコードは以下になります。

#include <stdio.h>

int main() {
   printf("Hello World!\n");
   return 0;
}

このソースコードコンパイルし実行すると以下のようになります。

> gcc -o hello hello.c
> ./hello
Hello World!
>

特に問題なく動作しています。
次に、このバイナリのもつセクション情報と逆アセンブル、サイズを調査しました。

> objdump -h hello

hello:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000400238  0000000000400238  00000238  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  0000000000400254  0000000000400254  00000254  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000400274  0000000000400274  00000274  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .gnu.hash     0000001c  0000000000400298  0000000000400298  00000298  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynsym       00000060  00000000004002b8  00000000004002b8  000002b8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynstr       0000003d  0000000000400318  0000000000400318  00000318  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version  00000008  0000000000400356  0000000000400356  00000356  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version_r 00000020  0000000000400360  0000000000400360  00000360  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rela.dyn     00000018  0000000000400380  0000000000400380  00000380  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.plt     00000030  0000000000400398  0000000000400398  00000398  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .init         0000001a  00000000004003c8  00000000004003c8  000003c8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .plt          00000030  00000000004003f0  00000000004003f0  000003f0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .plt.got      00000008  0000000000400420  0000000000400420  00000420  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .text         00000182  0000000000400430  0000000000400430  00000430  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .fini         00000009  00000000004005b4  00000000004005b4  000005b4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 15 .rodata       00000011  00000000004005c0  00000000004005c0  000005c0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .eh_frame_hdr 00000034  00000000004005d4  00000000004005d4  000005d4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .eh_frame     000000f4  0000000000400608  0000000000400608  00000608  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 18 .init_array   00000008  0000000000600e10  0000000000600e10  00000e10  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 19 .fini_array   00000008  0000000000600e18  0000000000600e18  00000e18  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .jcr          00000008  0000000000600e20  0000000000600e20  00000e20  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .dynamic      000001d0  0000000000600e28  0000000000600e28  00000e28  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .got          00000008  0000000000600ff8  0000000000600ff8  00000ff8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .got.plt      00000028  0000000000601000  0000000000601000  00001000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 24 .data         00000010  0000000000601028  0000000000601028  00001028  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 25 .bss          00000008  0000000000601038  0000000000601038  00001038  2**0
                  ALLOC
 26 .comment      00000034  0000000000000000  0000000000000000  00001038  2**0
                  CONTENTS, READONLY
> objdump -d hello

hello:     file format elf64-x86-64


Disassembly of section .init:

00000000004003c8 <_init>:
  4003c8:       48 83 ec 08             sub    $0x8,%rsp
  4003cc:       48 8b 05 25 0c 20 00    mov    0x200c25(%rip),%rax        # 600ff8 <_DYNAMIC+0x1d0>
  4003d3:       48 85 c0                test   %rax,%rax
  4003d6:       74 05                   je     4003dd <_init+0x15>
  4003d8:       e8 43 00 00 00          callq  400420 <__libc_start_main@plt+0x10>
  4003dd:       48 83 c4 08             add    $0x8,%rsp
  4003e1:       c3                      retq

Disassembly of section .plt:

00000000004003f0 <puts@plt-0x10>:
  4003f0:       ff 35 12 0c 20 00       pushq  0x200c12(%rip)        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  4003f6:       ff 25 14 0c 20 00       jmpq   *0x200c14(%rip)        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  4003fc:       0f 1f 40 00             nopl   0x0(%rax)

0000000000400400 <puts@plt>:
  400400:       ff 25 12 0c 20 00       jmpq   *0x200c12(%rip)        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
  400406:       68 00 00 00 00          pushq  $0x0
  40040b:       e9 e0 ff ff ff          jmpq   4003f0 <_init+0x28>

0000000000400410 <__libc_start_main@plt>:
  400410:       ff 25 0a 0c 20 00       jmpq   *0x200c0a(%rip)        # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
  400416:       68 01 00 00 00          pushq  $0x1
  40041b:       e9 d0 ff ff ff          jmpq   4003f0 <_init+0x28>

Disassembly of section .plt.got:

0000000000400420 <.plt.got>:
  400420:       ff 25 d2 0b 20 00       jmpq   *0x200bd2(%rip)        # 600ff8 <_DYNAMIC+0x1d0>
  400426:       66 90                   xchg   %ax,%ax

Disassembly of section .text:

0000000000400430 <_start>:
  400430:       31 ed                   xor    %ebp,%ebp
  400432:       49 89 d1                mov    %rdx,%r9
  400435:       5e                      pop    %rsi
  400436:       48 89 e2                mov    %rsp,%rdx
  400439:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  40043d:       50                      push   %rax
  40043e:       54                      push   %rsp
  40043f:       49 c7 c0 b0 05 40 00    mov    $0x4005b0,%r8
  400446:       48 c7 c1 40 05 40 00    mov    $0x400540,%rcx
  40044d:       48 c7 c7 26 05 40 00    mov    $0x400526,%rdi
  400454:       e8 b7 ff ff ff          callq  400410 <__libc_start_main@plt>
  400459:       f4                      hlt
  40045a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

0000000000400460 <deregister_tm_clones>:
  400460:       b8 3f 10 60 00          mov    $0x60103f,%eax
  400465:       55                      push   %rbp
  400466:       48 2d 38 10 60 00       sub    $0x601038,%rax
  40046c:       48 83 f8 0e             cmp    $0xe,%rax
  400470:       48 89 e5                mov    %rsp,%rbp
  400473:       76 1b                   jbe    400490 <deregister_tm_clones+0x30>
  400475:       b8 00 00 00 00          mov    $0x0,%eax
  40047a:       48 85 c0                test   %rax,%rax
  40047d:       74 11                   je     400490 <deregister_tm_clones+0x30>
  40047f:       5d                      pop    %rbp
  400480:       bf 38 10 60 00          mov    $0x601038,%edi
  400485:       ff e0                   jmpq   *%rax
  400487:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  40048e:       00 00
  400490:       5d                      pop    %rbp
  400491:       c3                      retq
  400492:       0f 1f 40 00             nopl   0x0(%rax)
  400496:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40049d:       00 00 00

00000000004004a0 <register_tm_clones>:
  4004a0:       be 38 10 60 00          mov    $0x601038,%esi
  4004a5:       55                      push   %rbp
  4004a6:       48 81 ee 38 10 60 00    sub    $0x601038,%rsi
  4004ad:       48 c1 fe 03             sar    $0x3,%rsi
  4004b1:       48 89 e5                mov    %rsp,%rbp
  4004b4:       48 89 f0                mov    %rsi,%rax
  4004b7:       48 c1 e8 3f             shr    $0x3f,%rax
  4004bb:       48 01 c6                add    %rax,%rsi
  4004be:       48 d1 fe                sar    %rsi
  4004c1:       74 15                   je     4004d8 <register_tm_clones+0x38>
  4004c3:       b8 00 00 00 00          mov    $0x0,%eax
  4004c8:       48 85 c0                test   %rax,%rax
  4004cb:       74 0b                   je     4004d8 <register_tm_clones+0x38>
  4004cd:       5d                      pop    %rbp
  4004ce:       bf 38 10 60 00          mov    $0x601038,%edi
  4004d3:       ff e0                   jmpq   *%rax
  4004d5:       0f 1f 00                nopl   (%rax)
  4004d8:       5d                      pop    %rbp
  4004d9:       c3                      retq
  4004da:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

00000000004004e0 <__do_global_dtors_aux>:
  4004e0:       80 3d 51 0b 20 00 00    cmpb   $0x0,0x200b51(%rip)        # 601038 <__TMC_END__>
  4004e7:       75 11                   jne    4004fa <__do_global_dtors_aux+0x1a>
  4004e9:       55                      push   %rbp
  4004ea:       48 89 e5                mov    %rsp,%rbp
  4004ed:       e8 6e ff ff ff          callq  400460 <deregister_tm_clones>
  4004f2:       5d                      pop    %rbp
  4004f3:       c6 05 3e 0b 20 00 01    movb   $0x1,0x200b3e(%rip)        # 601038 <__TMC_END__>
  4004fa:       f3 c3                   repz retq
  4004fc:       0f 1f 40 00             nopl   0x0(%rax)

0000000000400500 <frame_dummy>:
  400500:       bf 20 0e 60 00          mov    $0x600e20,%edi
  400505:       48 83 3f 00             cmpq   $0x0,(%rdi)
  400509:       75 05                   jne    400510 <frame_dummy+0x10>
  40050b:       eb 93                   jmp    4004a0 <register_tm_clones>
  40050d:       0f 1f 00                nopl   (%rax)
  400510:       b8 00 00 00 00          mov    $0x0,%eax
  400515:       48 85 c0                test   %rax,%rax
  400518:       74 f1                   je     40050b <frame_dummy+0xb>
  40051a:       55                      push   %rbp
  40051b:       48 89 e5                mov    %rsp,%rbp
  40051e:       ff d0                   callq  *%rax
  400520:       5d                      pop    %rbp
  400521:       e9 7a ff ff ff          jmpq   4004a0 <register_tm_clones>

0000000000400526 <main>:
  400526:       55                      push   %rbp
  400527:       48 89 e5                mov    %rsp,%rbp
  40052a:       bf c4 05 40 00          mov    $0x4005c4,%edi
  40052f:       e8 cc fe ff ff          callq  400400 <puts@plt>
  400534:       b8 00 00 00 00          mov    $0x0,%eax
  400539:       5d                      pop    %rbp
  40053a:       c3                      retq
  40053b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000400540 <__libc_csu_init>:
  400540:       41 57                   push   %r15
  400542:       41 56                   push   %r14
  400544:       41 89 ff                mov    %edi,%r15d
  400547:       41 55                   push   %r13
  400549:       41 54                   push   %r12
  40054b:       4c 8d 25 be 08 20 00    lea    0x2008be(%rip),%r12        # 600e10 <__frame_dummy_init_array_entry>
  400552:       55                      push   %rbp
  400553:       48 8d 2d be 08 20 00    lea    0x2008be(%rip),%rbp        # 600e18 <__init_array_end>
  40055a:       53                      push   %rbx
  40055b:       49 89 f6                mov    %rsi,%r14
  40055e:       49 89 d5                mov    %rdx,%r13
  400561:       4c 29 e5                sub    %r12,%rbp
  400564:       48 83 ec 08             sub    $0x8,%rsp
  400568:       48 c1 fd 03             sar    $0x3,%rbp
  40056c:       e8 57 fe ff ff          callq  4003c8 <_init>
  400571:       48 85 ed                test   %rbp,%rbp
  400574:       74 20                   je     400596 <__libc_csu_init+0x56>
  400576:       31 db                   xor    %ebx,%ebx
  400578:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40057f:       00
  400580:       4c 89 ea                mov    %r13,%rdx
  400583:       4c 89 f6                mov    %r14,%rsi
  400586:       44 89 ff                mov    %r15d,%edi
  400589:       41 ff 14 dc             callq  *(%r12,%rbx,8)
  40058d:       48 83 c3 01             add    $0x1,%rbx
  400591:       48 39 eb                cmp    %rbp,%rbx
  400594:       75 ea                   jne    400580 <__libc_csu_init+0x40>
  400596:       48 83 c4 08             add    $0x8,%rsp
  40059a:       5b                      pop    %rbx
  40059b:       5d                      pop    %rbp
  40059c:       41 5c                   pop    %r12
  40059e:       41 5d                   pop    %r13
  4005a0:       41 5e                   pop    %r14
  4005a2:       41 5f                   pop    %r15
  4005a4:       c3                      retq
  4005a5:       90                      nop
  4005a6:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4005ad:       00 00 00

00000000004005b0 <__libc_csu_fini>:
  4005b0:       f3 c3                   repz retq

Disassembly of section .fini:

00000000004005b4 <_fini>:
  4005b4:       48 83 ec 08             sub    $0x8,%rsp
  4005b8:       48 83 c4 08             add    $0x8,%rsp
  4005bc:       c3                      retq
> size hello
   text    data     bss     dec     hex filename
   1183     552       8    1743     6cf hello
> ldd hello 
        linux-vdso.so.1 =>  (0x00007fffe999f000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff8480c0000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff848600000)
>

この時点ではこのようになっているのだな、程度の調査のみを行い次に行うglibcを利用せずに生成したバイナリを調査する際に比較を行います。

glibcを利用しないコンパイル

次に、glibcを利用せずにコンパイルを行います。まず初めに、コンパイルが正常にできるかを確認するために以下のソースコードを用います。

int main() {
   char *text = "Hello World!\n";
   return 0;
}

最初にコンパイルを行ったソースコードではライブラリを利用することができたので「stdio.h」というヘッダファイルをインクルードしていましたが、今回は利用しないので文字列を「text」という変数に初期値設定しています。
また、glibcを利用しない場合は「 -nostdlib 」というオプションを追加します。

ubnt@ubuntu:~$ gcc -nostdlib -o hello_no_libc hello.c 
/usr/bin/ld: 警告: エントリシンボル _start が見つかりません。デフォルトとして 0000000000400144 を使用します

どうやらそのままではコンパイルができず、エラー内容を読んでみると「_start」というエントリシンボルが存在していないようでした。
このエントリシンボルとはエントリポイントのことであり、このシンボルが存在していないため実行できないということでした。
ということで、エントリポイントを記述します。

.global _start

_start:
    call main
    mov $60, %eax
    xor %rdi, %rdi
    syscall

これでコンパイルを行うために必要な材料はそろったので実際にコンパイルを行ってみます。

ubnt@ubuntu:~$ gcc -nostdlib stubstart.s -o hello_no_libc hello.c 
ubnt@ubuntu:~$ ./hello_no_libc 
ubnt@ubuntu:~$ 

問題なく動作することがわかりました。しかし、これでは比較を行えないのでインクルードファイルからprintf関数の基となるwriteシステムコールラッパを探そうと思ったのですが、システムコールに関する処理はあらかじめコンパイルされてアセンブラとなっているのではないか、という結論に至りインラインアセンブラを用いてwriteシステムコールを呼び出す処理を追加しました。以下はそのソースコードです。

#define WRITE_SYS_NUM 4
#define STDOUT_FILENO 1

int main() {
        char *str = "Hello World, I am chouett0.\nnice to meet you!!1!\n";
        int len=0, err;
        while (str[len] != '\0') {
                len++;
        }

        asm volatile("mov %0, %%eax" : : "i"(WRITE_SYS_NUM));
        asm volatile("mov %0, %%ebx" : : "i"(STDOUT_FILENO));
        asm volatile("mov %0, %%ecx" : : "m"(str));
        asm volatile("mov %0, %%edx" : : "m"(len));
        asm volatile("int $0x80"          : "=a"(err));

        return 0;
}

特に複雑な部分もなく、インラインアセンブラにてwriteシステムコールを呼び出すシステムコール番号とstdoutを意味する「1」,表示する文字列、文字列の長さを各レジスタに格納してint 0x80でシステムコールを呼び出します。その結果は以下のようになり正常に動作することがわかります。

ubnt@ubuntu:~$ gcc -nostdlib stubstart.s -o hello_nolibc hello_nolibc.c 
ubnt@ubuntu:~$ ./hello_nolibc 
Hello World, I am chouett0.
nice to meet you!!1!
ubntubuntu:~$ 

次に、このバイナリの各セクション情報、逆アセンブラ、サイズを確認します。

ubnt@ubuntu:~$ objdump -h hello_nolibc

hello_nolibc:     ファイル形式 elf64-x86-64

セクション:
索引名          サイズ      VMA               LMA               File off  Algn
  0 .note.gnu.build-id 00000024  0000000000400120  0000000000400120  00000120  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         0000005c  0000000000400144  0000000000400144  00000144  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rodata       00000032  00000000004001a0  00000000004001a0  000001a0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .eh_frame_hdr 00000014  00000000004001d4  00000000004001d4  000001d4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .eh_frame     00000038  00000000004001e8  00000000004001e8  000001e8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .comment      00000034  0000000000000000  0000000000000000  00000220  2**0
                  CONTENTS, READONLY
ubnt@ubuntu:~$ objdump -d hello_nolibc

hello_nolibc:     ファイル形式 elf64-x86-64


セクション .text の逆アセンブル:

0000000000400144 <_start>:
  400144:   e8 0a 00 00 00          callq  400153 <main>
  400149:   b8 3c 00 00 00          mov    $0x3c,%eax
  40014e:   48 31 ff                xor    %rdi,%rdi
  400151:   0f 05                   syscall 

0000000000400153 <main>:
  400153:   55                      push   %rbp
  400154:   48 89 e5                mov    %rsp,%rbp
  400157:   48 c7 45 f0 a0 01 40    movq   $0x4001a0,-0x10(%rbp)
  40015e:   00 
  40015f:   c7 45 ec 00 00 00 00    movl   $0x0,-0x14(%rbp)
  400166:   eb 09                   jmp    400171 <main+0x1e>
  400168:   8b 45 ec                mov    -0x14(%rbp),%eax
  40016b:   83 c0 01                add    $0x1,%eax
  40016e:   89 45 ec                mov    %eax,-0x14(%rbp)
  400171:   48 8b 55 f0             mov    -0x10(%rbp),%rdx
  400175:   8b 45 ec                mov    -0x14(%rbp),%eax
  400178:   48 98                   cltq   
  40017a:   48 01 d0                add    %rdx,%rax
  40017d:   0f b6 00                movzbl (%rax),%eax
  400180:   84 c0                   test   %al,%al
  400182:   75 e4                   jne    400168 <main+0x15>
  400184:   b8 04 00 00 00          mov    $0x4,%eax
  400189:   bb 01 00 00 00          mov    $0x1,%ebx
  40018e:   8b 4d f0                mov    -0x10(%rbp),%ecx
  400191:   8b 55 ec                mov    -0x14(%rbp),%edx
  400194:   cd 80                   int    $0x80
  400196:   89 45 fc                mov    %eax,-0x4(%rbp)
  400199:   b8 00 00 00 00          mov    $0x0,%eax
  40019e:   5d                      pop    %rbp
  40019f:   c3                      retq   
ubnt@ubuntu:~$ size hello_nolibc
   text    data     bss     dec     hex filename
    254       0       0     254      fe hello_nolibc
an3m0ne@an3m0ne:~$ ldd hello_nolibc
    動的実行ファイルではありません
ubnt@ubuntu:~$ 

比較するまでもなく、各項目で明らかに違いがあることがわかります。
まず、セクション情報ではglibcを利用していないので無駄なものが無く変数の値が変化することもないのでtextセクション内で完結することによりbssセクションも必要がなくなります。(文字列をconstで宣言することでさらに無駄が省ける?)また、関数を利用せず直接システムコールを実行しているのでPLT領域も必要なくなります。
次に、逆アセンブラ情報では先ほども述べたPLTに関係する関数やGOTなどが存在せず_startとmainでのみ構成されていることがわかります。その結果、glibcを利用している時には1743byteあったバイナリがglibcを利用していない場合では254byteにまで縮小され明らかにサイズが変化していることがわかります。また、共有ライブラリを一切利用していないこともわかりました。

まとめ

今回の検証でライブラリの便利さや、リンカがどのようにバイナリを生成していくのかを少し理解できたと思います。また、ライブラリの冗長さを改めて理解でき大規模なソフトウェアであれば変わりはないものの今回のような小さなバイナリではその冗長さが速度の低下につながり、そういった部分がパフォーマンスチューニングにつながるのだろうか、と感じました。

参考文献

nopipi.hatenablog.com

tkmr.hatenablog.com

softwaretechnique.jp

nopipi.hatenablog.com

Chrome使用時にフリーズする問題の簡易的な解決法について

はじめに

Chromeを使用しているときになぜかフリーズしてしまう問題があり、その問題の根本的なものではないにせよ
簡易的に解決したいと思い簡易的なツールを作成したのでその経緯とちょっとした説明の記事です。 以下からDLして任意の場所に保存するだけで使用できます。

github.com

動機

先ほども書きましたが、Chromeを使用しているときになぜかフリーズしてしまう問題があり特に私のラップトップではChrome起動状態でスリープ復帰するとほぼ必ずフリーズしてしまいます。 タスクマネージャ立ち上げてプロセスを落とせばよいのですがタスクマネージャを立ち上げてプロセス探して落とす、という動作をいちいち行うのは面倒なので単純動作で行えるよ宇にしたいと考えて作成したのが「Ch0romeKill3er」です。

実装

仕組みというほどではありませんが内部的には
- 特定のプロセス名からプロセスIDを取得 - プロセスのハンドラを取得 - プロセス強制終了 という処理を行っています。
一応拡張性を持たせたいと思いmain関数で呼び出すKillProcess関数に任意のプロセス名を与えるとKillできるようになっています。

まとめ

幼稚園児でも実装できるレベルのツール && n番煎じ

Apple製品のゼロ幅非結合子処理の際に生じるクラッシュについて

初めに

つい先日、最新のものを含むiOSOS X,Watch OSに特定の文字列を送信することでクラッシュするバグが発見されました。  

gigazine.net

詳細についてはこちらを参照していただくとして、今回はこの現象を実際に検証してみた結果となります。
本来は画像を張りたかったのですがスクショを取り忘れていたので文字のみでの解説となります。
なお、今回の検証は個人の端末を用いてとなりますので第三者へ向けて行っていません。 またこの記事での内容を実際に行うと最悪の場合端末が利用できなくなる場合がありますのであくまで自己責任でお願いします。

追記: 2/21に公開されたiOS11.2.6へアップデートする事で今回のバグが修正されました

検証環境

今回の検証では
* iPhone5 iOS10.2 * iPhone6 iOS11.1.2 この2台で行い、文字列の送信・生成はPCとAndroid端末から行いました。
文字列を受信、通知させるため方法としてYahooメールアカウントを登録したiOS標準のメールクライアントとSMSにて行いました。
本来はTwitterを用いて行う予定でしたが、すでに対策がなされているようでWeb・モバイルともにバグを引き起こす文字列は送信できなくなっていました。

検証方法

まず初めにYahooメールを用いた検証について記述する前に、検証で用いる文字列について説明したいと思います。
今回問題となっている文字列は「テルグ語」と呼ばれる言語のテルグ文字という文字によるものであり、この文字を表現する際に必要な「ゼロ幅非結合子(以下ZWNJ)」という制御文と特定の組み合わせの際にのみ発火する文字のようです。
詳しい説明は

japanese.engadget.com

こちらに掲載されているので重要な部分のみを抜粋すると、今回のバグは「子音1、ヴィラーマ(発音区別符号)、子音2、ZWNJ 、母音」という組み合わせの際に発火するようです。
ということで、初めに検証のために利用したのがおそらくほとんどの記事で扱われている「U+0c1cU+0c4dU+0c1eU+200cU+0c3e」というUnicodeで表現される文字列です。当然といえば当然ですが、クラッシュを引き起こす文字列をニュース記事でそのまま記載することはまずないと思われますので、まずはこのUnicodeを実際の文字列に直していく作業です。

Unicodeから可読文字列に変換する方法はたくさんあると思われますが、私は

https://www.marbacka.net/msearch/tool.php

http://www.eva.hi-ho.ne.jp/cgi-bin/user/zxcv/decodeUTF8.cgi?req=oct&oct=343+222+242+343+202+244

この2つを用いて変換を行いました。

検証

それでは実際に検証を行う手順を記述していきます。
まずメールですが、あらかじめ検証機にメールの設定を入れておきます。
今回は標準メールクライアントを用いたので、まずはメールクライアントを開き「Yahooメール」を選択し、
メールアドレスとPWを入れて完了です。
送信する際はPCからブラウザ上で行いました。
一応件名と本文のどちらが表示されても発火するように両方に文字列を挿入しました。
両方の端末でメールクライアントを起動した状態で実際に送信してみると、正常にアプリケーションがクラッシュして再起動しました。
次に、バナーでの発火の検証をしてみるとバナーが表示される瞬間にSpringBoardがクラッシュして再起動していたことから
今回のバグの危険度がわかります。

メールソフトでの発火は確認できたので、その次にSMSを用いた検証を行いました。
この検証ではSIMを挿入したAndroid端末から同じくSIMを挿入したiPhone6にバグを引き起こす文字列を送信しました。
この検証でもメールのときと同じ結果で、アプリケーションを開いた状態ではアプリケーションがクラッシュし、バナーに表示させるとシステムがクラッシュしてしまいました。

ここまでの検証は記事にある内容の模範に過ぎず、あまり面白みはありませんでした。
そこで、私の先輩からの疑問でほかの組み合わせや少ない文字列では発火しないのかを検証してみました。
あまり長い文章だと読みにくくなってしまうので概要だけ説明すると、まず、今回の検証で用いた文字列から1文字分削った
文字列を送信してみましたが発火することはなく、次に長さは変わらずZWNJを別の場所に移して見たところやはり発火することはありませんでした。
次に、検証方法のところで記述した特定の組み合わせに従い最後の母音を別の文字に置き換えてみたところ正常に発火していました。
これらのことを踏まえると、やはり特定の組み合わせの文字列をZWNJを利用して処理する際にバグが発生していることがわかりました。

まとめ

今回の検証はただバグを引き起こす文字列を送信してクラッシュすることを確認するだけで詳しい原因やほかの組み合わせまでは調査できませんでした。
また、検証で用いた検証機が2台だけだったためそのうちより低いバージョンの端末やMac,Apple Watchでも発火するのかを検証してみたいなと思います。
ここからは完全に余談となりますが、今回メールとSMSを用いたのには理由があり本来であればTwitterを利用したかったのですが、どうやらweb・mobile両方で既に対策が行われているようで投稿はもちろん名前にすら入力できないようにされていました。
しかし、某緑のSNSアプリケーションでは対策がなされておらず、

本来検証する予定ではないプライベート機で発火してしまう事態になってしまいました。
さらに、連携させているApple Watchにも最悪の場合通知が行ってしまいクラッシュする羽目になりかけたり復旧する際に
MacBook Proで無意識にLINEを起動しようとして発火することを思い出したりと散々な思いをしたので早急にiOS11.3がリリースされることを祈ります。

割り込み処理について

初めに

今回の記事も前回同様、技術的な内容ではなく私が理解を深めるためのメモであり
特に面白みのないものとなっています。
また、誤字脱字、間違っている点も多くあると思われますのでご了承ください。

割り込み

  • 割り込みとは、ハードウェアから特定の処理を要求されたときに、一時的にソフトウェアの処理を中断させて要求された処理を実行することをいう。
    例)キーボードコントローラ->キーが押されたときにCPU割り混みを信号を出し、CPUはどのキーが押されたのかをキーボードコントローラから読み出す処理を行う
  • 割り込みを行うことでCPUがハードウェアを常に監視する必要性がなくなる

  • 割り込みの種類

    • 例外(Exception)
    • 割り込み要求(IRQ: Interrupt ReQuest)
    • ソフトウェア割り込み

例外(Exception)

  • CPUが異常を検出した際に発生する。
  • ページングで割り当てられたページがない時などに異常を検出して例外処理を行う。
  • 主にソフトウェアでの処理で異常を検出する
    例) 存在しない配列要素へのアクセス

割り込み要求

  • CPUの周辺のハードウェアから要求があった場合に発生する信号
  • CPUのピンとハードウェアは電気的にワイヤで接続されており、ハードウェアは割り込み要求を出すときにそのワイヤへ電気信号を出すことでCPUに伝達する
  • CPUはワイヤの信号が変化したことを検出すると要求にあった割り込みハンドラを呼び出す。
  • この信号はソフトウェアの動作に関係なくいつ信号が送信されるかはわからない
  • CPUはPIC(Programmable Interrupt Controller)とわいやでIRQラインが結ばれている。
  • PICはフロッピーディスクコントローラやDMAコントローラなどの割り込み要求を一括してCPUに伝える。

ソフトウェア割り込み

  • 例外とは異なり、意図的にソフトウェアが発生させることができる割り込み。
  • INT命令がソフトウェア割り込みでを発生させるめいれいであり、主にユーザプログラムがシステムコールを呼び出すときに使用する。

割り込みベクタと割り込みハンドラ

  • 割り込みが発生したときにどのハンドラを呼び出すのかを記述したものを割り込みハンドラという。
  • 割り込みベクタには割り込みハンドラのアドレスが順番に並んでいるようなテーブルのイメージで番号がつけられている。
  • 割り込みが発生したときに、その割り込みに対応した番号のハンドラは呼び出されることで、割り込み処理が行われる。
  • 割り込みベクタのうち、例外の部分はCPU仕様によって決まっている。

GDT(Global Descriptor Table)

  • 64bitを一つのデータとした配列
  • 一つの要素である64bitので0他派セグメントディスクリプタといい、セグメントの開始位置、セグメントの崔伊豆アクセス権限などを細かく設定できる。
  • このセグメントディスクリプタは最大で8192個まで持つことができる。

IDT(Interrupt Descriptor Table)

IDTR

  • CPUが持っている特殊なレジスタを指す。
  • LIDT命令を使用してIDTRにIDTのサイズとアドレスを格納する。
  • IDTRにIDTのサイズとアドレスが格納されている際にわりこみが発生することでCPUはIDTを参照し、対応する割り込みハンドラの処理を行う。

LDT(Local Descriptor Table)

参考文献

softwaretechnique.jp

VyOS

# 宣伝
harekaze.com


# 初めに
Harekaze Advent calendar 14日目の記事となります。
adventar.org


本来は技術的な記事にしたかったのですが、進捗が思ったように出なかったので
今回はVyOSにて家庭内ネットワークを再構築しようとした際に気づいたことのまとめを書いていこうと思います。

# VyOSについて
VyOSとは、Vyattaと呼ばれるLinuxベースのネットワークOSの無償版であるVyatta CoreというものからフォークされたOSです。
UNIXLinuxに近いと考えるとわかりやすいかもしれません(実際は全然違いますが)
このVyOSですが、実機にインストールできるだけでなくVBox等でVMとしても動作させることができる非常に面白いOSです。

# Edge Router
Edge Routerとは、先ほどのVyOSをベースに作られたOSを搭載した小型の高機能ルータのことで、一般的なルータの機能はもちろんのこと
CiscoYamahaなどの企業向けルータでできることは大体できるにもかかわらず10k以内で購入できる非常に素晴らしいルータのことです。
実際の我が家の家庭内ネットワークではこのルータを利用していて、小さいながらもなかなか頑張ってくれています。

# 気づいた点
## VyOSではTrackやTrigger shutdownと呼ばれる機能がない
まず、TrackやTrigger Shutdownとは、VRRPという冗長化機能を使用する際に設定されるもので

[ インターネット ]
/ \
/ \
[ Router ] [ Router ]
| |
[ L2SW ]
|
[ PC ]

例としてPC側にVRRPを設定してこのような構成を組んでいるときに、インターネット側が切断されてしまったと想定すると
PC側の回線は接続されている状態なのでVRRPのみでは障害検知ができません。
そこで、先ほどの機能を設定することで対向の回線がDownした際にはPC側もDownするので冗長化構成が効率的に気のすることになります。
そのTrackやTrigger shutdownと呼ばれる機能ですが、私の調べた限りではVyOSに存在しないようです。
とても残念ではありますが、VRRP構成を組んでいるルータ間で渡となる回線を作り、OSPFで自動的にルーティングさせることで回避できます。

# VRRPの仕様
先ほども取り上げたVRRPですが、私はどうしてもこの構成をインターネット側に取り入れたかったのですが、某comのONUの仕様で
MACアドレスの一致した機器以外は接続できないためVRRPは構成できない状態でした。
そこで、VRRPを構成する機器のMACアドレスを同一にすれば問題が解決するのではないかと考えて実際に行ってみました。
結果としては成功したものの、不安定でありなかなかうまいようには行きませんでした。
しかし、私はこの際に両機器がVRRPの切り替わりを正常に行えていることに疑問を抱きました。
なぜかというと、MACアドレスを同一にしているのであればお互いで疎通をとることはできないはずなのにもかかわらず
瞬時に切り替わりを行えていたからです。その原因を探ってみると、VRRPはペアとなる機器に直接パケットを送るのではなく
masterとなるルータがHelloパケットと呼ばれるパケットを定期的にユニキャストしていたことが原因のようです。
この結果、MACアドレスが同一でもbackupルータはHelloを正常に確認できるので、VRRPも正常に切り替えを行えていたようです。

# 〆
今回はあまり技術的な内容を書くことができず、何をしたのかも詳しく書けていないのでそのうち記事にしたいと思います。

UNIXとLinuxとBSDと色々

はじめに

この記事の内容はUNIXLinuxBSDとその他色々なOSの違いや成り立ちを忘れないよう、自分なりに理解するためのまとめです。新たな発見も無ければ間違いが多く含まれていると思われます。

UNIX

UNIXとは、言わずと知れた現在にUbuntuRHELなどのLinuxの誕生に影響を与えたOSです。1969年に米国のAT&Tベル研究所で開発が行われ、1971年に初めて公開されました。当時はアセンブラ言語での実装が一般的であったため、初期のUNIXアセンブラ言語で記述されていましたが後にC言語で改めて実装がなされました。オペレーションシステムを高級言語で記述するという試みは、先駆的であり他のプラットフォームへの移植をようにしました。
UNIX高級言語で記述された初めての言語」と言われますが、実は最初はアセンブラ言語で実装されていた、と言うのは初耳でした。さらに、当時では高級言語でOSが記述されるという事自体が一般的ではない(と言うよりもCの実装が今より使える言語ではなかった?)と言うことも意外でした。

UNIXの誕生にはさらに深い歴史があり、1960年代中ごろにマサチューセッツ工科大学ベル研究所、General Electric(GE)がGEのメインフレームコンピュータ用に開発した「Multics」というタイムシェアリングシステム、つまりは複数ユーザで様々な処理を行うことができるシステムを共同開発していました。しかし、革新的かつタイムシェアリングシステムという様々な処理を行うというコンセプトからわかるように、機能が増えていっていくに比例してその複雑さは増す一方となりついにはベル研究所Multics開発から離脱します。そこで、よりシンプルなシステムを開発しようとして生まれたのがUNIXです。つまりは、UNIXMulticsの複雑さを複雑さを解消する目的で開発されたということです。
このことから、UNIXMulticsの影響を受けて開発されたのだろうと解釈しました。また、「UNIX」という名称の由来も初期のUNIX、このころは「Unics」というシステムはMulticsと違いシングルタスクでのみしか動作できなかったのでMultiの逆で単一という意味の「Unit」から生まれたようです。 余談ですが、UNIXAT&Tの様々な事情によりソースコードが公開され、大学や企業に広まったようです。

GNU

初期のUNIXの公開から約10年後の1983年にリチャード・ストールマンによって、「フリーソフトウェアのみで構成されたUNIXの完全互換ソフトウェア」を開発するという目標の元作り出されたGNUは、ライブラリ、コンパイラテキストエディタUnixシェル、Windowシステムなどを備えていたものの、低水準の要素であるデーモンやカーネルデバイスドライバといったものは利用できなかったようです。後にLinuxを世に送り出すリーナス・トーバルズは、GNUカーネルが利用できなかったこともLinuxを開発した要因の一つであると述べています。
この記事を書くまでGNUについて詳しく調べてみたことがなかったのですが、GNUはOSでありLinuxとはまた違ったコンセプトで開発されたということがわかりました。さらに、LinuxUNIXを無料で利用するために誕生したという話を聞いていたので、意外なものが関係しているのだなと思いました。

BSD

1970年代後半から1980年代にかけて、カリフォルニア大学バクレー校のCSRGはUNIXの派生版としてBSDを開発しましたが、UNIXの利用者のライセンスを制限する、つまりは現代でいうところの著作権のようなものの関係がありBSDを公開することができなくなってしまいました。そこで、ライセンスに関わる部分を削り必要な部分を書き換えました。さらに、386で動かすコードを補って1992年にウィリアム・ジョリッツによってリリースされたのが386BSDであり、NetBSDFreeBSDの祖先となるシステムです。当時、現代的なマルチプロセスやメモリ保護などの機能が実装できる安価な32bitパーソナルコンピュータが普及され始めたこともあり、BSDのようなUNIXの移植が行われるようになりLinuxの開発にも影響しました。 BSDの成り立ちが私の中で一番あいまいで、BSDUNIXの関係性がイマイチ理解できていませんでしたが 、BSDLinuxの開発にも影響していたということは知らなかったのでなるほどなと思いました。また、この頃から今のようなPCが徐々に世に出ていくと考えるとなかなか面白くもあり時代が変わっていくのだなと感じます。

Linuxの誕生

上記BSDでもあるように、1990年代には比較的安価で買えるIntel 80386CPUを搭載した32bitPC/AT互換パーソナルコンピュータが普及し始めていました。その当時ヘルシン大学の学生であったリーナスはオペレーティングシステムについて強い好奇心を抱いていました。そこで彼はその安価なコンピュータを使用してUNIX互換の動作をするOSを動作させたいと考えますが、商業UNIXは高価であり、ここでは登場していませんがMINIXという教育用のOSは制限が多かったため、彼の思うようにはいきませんでした。そこで、自作のターミナルエミュレータファイルシステムなどのUNIX互換APIを実装して独自のOSカーネルを開発しました。これが後のLinuxカーネルになっていきます。
Linuxの開発はMINIX上で行われていたため、MINIXで動作するソフトウェアはLinuxでも動作可能でしたが後にLinux上でLinuxの開発が行えるようになると、MINIXコンポーネントGNUのプロダクトによって置き換わっていきました。
当時のLinuxの実装は単純であり他のUNIX互換のシステムと比べても見劣りするもので、機能面でLinuxは劣っていたものの自由なUNIX互換カーネルを利用でき、かつライセンスなどの問題が発生しないシステムは当時Linuxのみであった。
当時の、PCで動作するフリーのUNIX環境を求めるユーザたちの多くはMINIXを利用していたため、リーナスはMINIXメーリングリスト上でLinuxを公開しGPLの下で利用しました。ちょうど32bit PC/AT互換パーソナルコンピュータが普及し始めた時期であったためGPLでの改良が可能であったため、多くの開発者たちへ改良を促しました。その結果として開発者たちはLinuxカーネルをより良いものにしていくと同時に、GNUコンポーネントと統合する作業を行い、今のLinuxの形となりました。 今でこそ多くのOSの素となっているLinuxですが、当初は他のOSとは比較できないほど未熟であったのにも関わらずフリーかつLinuxの開発に影響を与えたGNUよりも早い段階で独自のカーネルを形にしていたというところを武器にして負けず劣らずな戦いを繰り広げていたというのはとても面白いですね。また、やはりOSSとしての利点として多くの開発者たちの手によってより良く改良されていったのも要因の一つなのかもしれませんね。さらに、上記BSDのライセンス問題を解消してリリースされたFreeBSDの公開が1994年11月であるのに対して、Linuxが公開されたのが1990年代前半というタイミングもLinuxの広まった要因の一つのように思えます。ここからは私の想像になってしまいますが、一般的に「Linux」と呼ばれているOSは、ここまで書いてきた「Linuxカーネル」を指すものでありRHELDebianは「Linuxカーネル」に独自のプログラムを追加したものなのではないかなと思います。

MINIX

Linuxでたびたび出てきたMINIXについても少しだけメモしておきたいと思います。
MINIXは、アンドリュー・タネンバウムによって作られたUNIX系のOSです。コンピュータ科学におけるオペレーティングシステムの教育という目的に重点を置いた設計で、企業ライセンスなどが無いように書かれたものでしたが、コンパイラにライセンスの問題がある、教育目的なので教科書として広く配布するために商業出版を必要とした関係もありライセンスには制限がありました。

まとめ

知っていたようで意外に知らないことも多く、特にUNIXが初めはアセンブラで書かれていたことに驚きました。
BSDの歴史やライセンスの問題、MINIXMulticsなどのあまり聞きなれないOSなど深く調べれば調べるほど様々な情報が得られ
よりコンピュータについての興味が生まれてくるので現代の技術も大切ですが歴史についても調べてみるのも良いかもしれないなと感じました。
特に、歴史の関連性などがまだ調べられていないのでいつか調べてみたいと思います。

参考文献

Unixの歴史 - Wikipedia

Linux - Wikipedia

ISUCON7で予選落ちした話

はじめに

先週の土日で行われたISUCON7に「インターネットは爆発しました」と言うチームで出場しました。
今回がISUCON初参加だったので参加しての感想とそのWriteupがメインの記事となります。

やったこと

僕の所属していたチームでは、3人中2人がサーバ担当、残りの1人でコードのチューンを行うと言う分担を行いました。
今回の競技ではメインで実行されていたアプリケーションはPythonのようでしたが、PHPに切り替えてチューンを行うことにしました。 補足として、PHPでのオリジナルコードのベンチマークスコアは「6000」でした。
やったこととして、まず始めに

パフォーマンス1000%UP!PHPでMySQLのDB処理を行うと重いときに行うパフォーマンス施策~基礎編~

この記事を参考にコード内のSQLクエリの命令をバッククォートで囲みました。
具体的には

SELECT UserName, Pass, ScreenName FROM UserList WHERE UserName = "hogehoge"

と言うようなクエリがあった場合

SELECT UserName, Pass, ScreenName FROM UserList WHERE UserName = "hogehoge"

と言ったように、テーブル名や要素名をクォートで囲むことで無駄な処理が減り、今回のように数千単位のデータを扱う場合では有効なようです。 さらに、今回のコードではいくつかの機能別に関数を定義しておきそれを別関数内で読んでいる構造をしているためなのか
DBからデータを取得する際に「*」で全フィールドを取ってきていたので、各関数で必要なフィールドを書き出して必要最低限の
データのみを処理するように変更しました。
ひとまずはこれでSQL部分のチューニングはいいだろうと考え、今度はコード全体を眺めていくと、「str_replace」と言う関数を使っている処理を発見しました。この関数でもまったく問題なく動作するのですが、どうやらこの関数はあまり速度的に速く無いようだったため
「strtr」と言う関数へ変更しました。

たったこれだけのことにかなりの時間をかけてしまいましたが、たったこれだけで最終スコアは20000代に跳ね上がり、約4倍の高速化を行うことができました。
それもサーバサイドのチューニングを一切行なっていなかったのでSQLがかなりのボトルネックになっていたのだろうと感じました。
もしこれでサーバサイド、そして記事内で触れていませんでしたが画像呼び出しがSQL内のバイナリを変換して画像化すると言う処理をしている影響で 見て分かるほど処理が重たくなっていたので画像の実ファイル化を行えていればもしかすると今回のボーダーの40000点も超えられたのかなと感じました。

まとめ

今回は予選敗退という結果に終わりましたが、初参戦ながらもそこそこ良い結果を出せたのかなと思いました。また、今までコードチューンを 行う機会がなかったので初めて聞いたことや初めて知ったことなどが多く久しぶりに真剣に取り組んだなぁと感じました。
最近はやたら周りがCTFで盛り上がっていて萎えr...勢いについていけなくなったのもあってか、ISUCON非常に熱いなと感じたので来年も参加したい。