unless’s blog

日々のちょっとした技術的なことの羅列

オブジェクトファイルと戯れてみる Pert.2

echoコマンドのオブジェクトファイルと戯れてみる第二弾
第一弾はこちら

unless.hatenablog.jp

共有ライブラリの依存関係

objdump で依存関係を見てみる

ja.wikipedia.org

共有ライブラリが何かという情報はELFの[Dynamic Section]のNEEDEDに記載されている

# objdump -p /bin/echo

/bin/echo:     file format elf64-x86-64

Program Header:
    PHDR off    0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3
         filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x
  INTERP off    0x0000000000000238 vaddr 0x0000000000400238 paddr 0x0000000000400238 align 2**0
         filesz 0x000000000000001c memsz 0x000000000000001c flags r--
    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
         filesz 0x0000000000005ecc memsz 0x0000000000005ecc flags r-x
    LOAD off    0x0000000000006da8 vaddr 0x0000000000606da8 paddr 0x0000000000606da8 align 2**21
         filesz 0x0000000000000478 memsz 0x00000000000005d8 flags rw-
 DYNAMIC off    0x0000000000006e08 vaddr 0x0000000000606e08 paddr 0x0000000000606e08 align 2**3
         filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-
    NOTE off    0x0000000000000254 vaddr 0x0000000000400254 paddr 0x0000000000400254 align 2**2
         filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off    0x00000000000051cc vaddr 0x00000000004051cc paddr 0x00000000004051cc align 2**2
         filesz 0x000000000000023c memsz 0x000000000000023c flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
   RELRO off    0x0000000000006da8 vaddr 0x0000000000606da8 paddr 0x0000000000606da8 align 2**0
         filesz 0x0000000000000258 memsz 0x0000000000000258 flags r--

Dynamic Section:
  NEEDED               libc.so.6
  INIT                 0x0000000000401050
  FINI                 0x000000000040442c
  INIT_ARRAY           0x0000000000606da8
  INIT_ARRAYSZ         0x0000000000000008
  FINI_ARRAY           0x0000000000606db0
  FINI_ARRAYSZ         0x0000000000000008
  GNU_HASH             0x0000000000400298
  STRTAB               0x0000000000400810
  SYMTAB               0x00000000004002b8
  STRSZ                0x000000000000025d
  SYMENT               0x0000000000000018
  DEBUG                0x0000000000000000
  PLTGOT               0x0000000000607000
  PLTRELSZ             0x0000000000000498
  PLTREL               0x0000000000000007
  JMPREL               0x0000000000400bb8
  RELA                 0x0000000000400b40
  RELASZ               0x0000000000000078
  RELAENT              0x0000000000000018
  VERNEED              0x0000000000400ae0
  VERNEEDNUM           0x0000000000000001
  VERSYM               0x0000000000400a6e

Version References:
  required from libc.so.6:
    0x0d696913 0x00 06 GLIBC_2.3
    0x09691974 0x00 05 GLIBC_2.3.4
    0x06969194 0x00 04 GLIBC_2.14
    0x0d696914 0x00 03 GLIBC_2.4
    0x09691a75 0x00 02 GLIBC_2.2.5

readelfコマンドの場合は下記

# readelf -d /bin/echo

Dynamic section at offset 0x6e08 contains 24 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x401050
 0x000000000000000d (FINI)               0x40442c
 0x0000000000000019 (INIT_ARRAY)         0x606da8
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x606db0
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x400298
 0x0000000000000005 (STRTAB)             0x400810
 0x0000000000000006 (SYMTAB)             0x4002b8
 0x000000000000000a (STRSZ)              605 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x607000
 0x0000000000000002 (PLTRELSZ)           1176 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x400bb8
 0x0000000000000007 (RELA)               0x400b40
 0x0000000000000008 (RELASZ)             120 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x400ae0
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x400a6e
 0x0000000000000000 (NULL)               0x0

/bin/echoにはlibc.so.6という共有ライブラリが必要だということがわかる
が、実際に必要なのはこの一つのみではなくその共有ライブラリが必要としている別の共有ライブラリも必要

NEEDEDに記載されているのはSONAMEなのでSONAMEから実際のファイルを探す必要がある
特になにも設定されてない場合は/usr/lib及び/libSONAMEに対応するファイルが存在すればそれが対象の共有ライブラリとなる
環境変数LD_LIBRARY_PATHがあればそこを参照し、/etc/ld.so.cacheに情報があればそれを参照する

lddコマンドで依存関係を見てみる

lddコマンドを使うと共有ライブラリのSONAMEとパス名の一覧が表示される

# ldd /bin/echo
    linux-vdso.so.1 =>  (0x00007ffca93e5000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f88dde02000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f88de1cf000)
# ldd /lib64/libc.so.6
    /lib64/ld-linux-x86-64.so.2 (0x00007f7508b2c000)
    linux-vdso.so.1 =>  (0x00007fff0fd0d000)

ちなみにlddコマンド自体はshellで出来てる

# file /bin/ldd
/bin/ldd: Bourne-Again shell script, ASCII text executable

objdumpでオブジェクトファイルをダンプしてみる

前回はodコマンドでみたがobjdumpコマンドで特定のセクションだけdumpすることもできる

セクションの確認

# objdump -h /bin/echo

/bin/echo:     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       00000558  00000000004002b8  00000000004002b8  000002b8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynstr       0000025d  0000000000400810  0000000000400810  00000810  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
~~~(ry~~~

.dynstrをdump

# objdump -s -j .dynstr /bin/echo

/bin/echo:     file format elf64-x86-64

Contents of section .dynstr:
 400810 006c6962 632e736f 2e360066 666c7573  .libc.so.6.fflus
 400820 68007374 72637079 005f5f70 72696e74  h.strcpy.__print
 400830 665f6368 6b007365 746c6f63 616c6500  f_chk.setlocale.
 400840 6d627274 6f776300 7374726e 636d7000  mbrtowc.strncmp.
 400850 73747272 63687200 64636765 74746578  strrchr.dcgettex
 400860 74006572 726f7200 5f5f7374 61636b5f  t.error.__stack_
 400870 63686b5f 6661696c 00697377 7072696e  chk_fail.iswprin
 400880 74007265 616c6c6f 63006162 6f727400  t.realloc.abort.
 400890 5f657869 74007072 6f677261 6d5f696e  _exit.program_in
 4008a0 766f6361 74696f6e 5f6e616d 65005f5f  vocation_name.__
 4008b0 63747970 655f6765 745f6d62 5f637572  ctype_get_mb_cur
 4008c0 5f6d6178 0063616c 6c6f6300 7374726c  _max.calloc.strl
 4008d0 656e0075 6e676574 63006d65 6d736574  en.ungetc.memset
 4008e0 005f5f65 72726e6f 5f6c6f63 6174696f  .__errno_locatio
 4008f0 6e006d65 6d636d70 005f5f66 7072696e  n.memcmp.__fprin
 400900 74665f63 686b0073 74646f75 74006c73  tf_chk.stdout.ls
 400910 65656b00 6d656d63 70790066 636c6f73  eek.memcpy.fclos
 400920 65006d61 6c6c6f63 006d6273 696e6974  e.malloc.mbsinit
 400930 005f5f75 666c6f77 006e6c5f 6c616e67  .__uflow.nl_lang
 400940 696e666f 005f5f63 74797065 5f625f6c  info.__ctype_b_l
 400950 6f630067 6574656e 76005f5f 66726561  oc.getenv.__frea
 400960 64696e67 00737464 65727200 66736361  ding.stderr.fsca
 400970 6e660066 696c656e 6f006677 72697465  nf.fileno.fwrite
 400980 005f5f66 70656e64 696e6700 70726f67  .__fpending.prog
 400990 72616d5f 696e766f 63617469 6f6e5f73  ram_invocation_s
 4009a0 686f7274 5f6e616d 65006664 6f70656e  hort_name.fdopen
 4009b0 0062696e 64746578 74646f6d 61696e00  .bindtextdomain.
 4009c0 73747263 6d70005f 5f6c6962 635f7374  strcmp.__libc_st
 4009d0 6172745f 6d61696e 00667365 656b6f00  art_main.fseeko.
 4009e0 5f5f6f76 6572666c 6f770066 70757473  __overflow.fputs
 4009f0 5f756e6c 6f636b65 64006672 6565005f  _unlocked.free._
 400a00 5f70726f 676e616d 65005f5f 70726f67  _progname.__prog
 400a10 6e616d65 5f66756c 6c005f5f 6378615f  name_full.__cxa_
 400a20 61746578 6974005f 5f676d6f 6e5f7374  atexit.__gmon_st
 400a30 6172745f 5f00474c 4942435f 322e3300  art__.GLIBC_2.3.
 400a40 474c4942 435f322e 332e3400 474c4942  GLIBC_2.3.4.GLIB
 400a50 435f322e 31340047 4c494243 5f322e34  C_2.14.GLIBC_2.4
 400a60 00474c49 42435f32 2e322e35 00        .GLIBC_2.2.5.

objdumpでオブジェクトファイルを逆アセンブルしてみる

objdumpでオブジェクトファイルを逆アセンブル

-dオプションで逆アセンブルができるので下記のように実行

# objdump -d /bin/echo

/bin/echo:     file format elf64-x86-64


Disassembly of section .init:

0000000000401050 <.init>:
  401050:   48 83 ec 08             sub    $0x8,%rsp
  401054:   48 8b 05 85 5f 20 00    mov    0x205f85(%rip),%rax        # 606fe0 <__ctype_b_loc@plt+0x205c60>
  40105b:   48 85 c0                test   %rax,%rax
  40105e:   74 05                   je     401065 <__uflow@plt-0x1b>
  401060:   e8 2b 03 00 00          callq  401390 <__ctype_b_loc@plt+0x10>
  401065:   48 83 c4 08             add    $0x8,%rsp
  401069:   c3                      retq

Disassembly of section .plt:

0000000000401070 <__uflow@plt-0x10>:
  401070:   ff 35 92 5f 20 00       pushq  0x205f92(%rip)        # 607008 <__ctype_b_loc@plt+0x205c88>
  401076:   ff 25 94 5f 20 00       jmpq   *0x205f94(%rip)        # 607010 <__ctype_b_loc@plt+0x205c90>
  40107c:   0f 1f 40 00             nopl   0x0(%rax)
~~~(ry~~~

アドレスとバイト列もみたい場合は下記のようにオプションを指定すれば見れるようになる

# objdump -d --prefix-address --show-raw-insn /bin/echo

/bin/echo:     file format elf64-x86-64


Disassembly of section .init:
0000000000401050 <.init> 48 83 ec 08              sub    $0x8,%rsp
0000000000401054 <.init+0x4> 48 8b 05 85 5f 20 00     mov    0x205f85(%rip),%rax        # 0000000000606fe0 <__ctype_b_loc@plt+0x205c60>
000000000040105b <.init+0xb> 48 85 c0                 test   %rax,%rax
000000000040105e <.init+0xe> 74 05                    je     0000000000401065 <__uflow@plt-0x1b>
0000000000401060 <.init+0x10> e8 2b 03 00 00          callq  0000000000401390 <__ctype_b_loc@plt+0x10>
0000000000401065 <.init+0x15> 48 83 c4 08             add    $0x8,%rsp
0000000000401069 <.init+0x19> c3                      retq

Disassembly of section .plt:
0000000000401070 <__uflow@plt-0x10> ff 35 92 5f 20 00     pushq  0x205f92(%rip)        # 0000000000607008 <__ctype_b_loc@plt+0x205c88>
0000000000401076 <__uflow@plt-0xa> ff 25 94 5f 20 00      jmpq   *0x205f94(%rip)        # 0000000000607010 <__ctype_b_loc@plt+0x205c90>
000000000040107c <__uflow@plt-0x4> 0f 1f 40 00            nopl   0x0(%rax)
~~~(ry~~~

特定のセクションのみ逆アセンブルすることもできる

# objdump -d -j .dynstr /bin/echo

/bin/echo:     file format elf64-x86-64


Disassembly of section .dynstr:

0000000000400810 <.dynstr>:
  400810:   00 6c 69 62             add    %ch,0x62(%rcx,%rbp,2)
  400814:   63 2e                   movslq (%rsi),%ebp
  400816:   73 6f                   jae    400887 <__uflow@plt-0x7f9>
  400818:   2e 36 00 66 66          cs add %ah,%ss:0x66(%rsi)
  40081d:   6c                      insb   (%dx),%es:(%rdi)
  40081e:   75 73                   jne    400893 <__uflow@plt-0x7ed>
  400820:   68 00 73 74 72          pushq  $0x72747300
  400825:   63 70 79                movslq 0x79(%rax),%esi
~~~(ry~~~

ソースファイルとの対応を表示

デバッグ情報が含まれてるオブジェクトファイルの場合は-Sオプションでそのソースファイルがあれば ソースコードをその場に挿入してくれる

# cat hello.c
#include <stdio.h>

int main(void)
{
    printf("Hello World\n");
    return 0;
}
# gcc -g -o hello hello.c
# ll
total 16
-rwxr-xr-x 1 root root 9432 Mar 21 03:15 hello
-rw-r--r-- 1 root root   82 Mar 21 03:08 hello.c
# objdump -d -S hello
~~~~(ry~~~
000000000040051d <main>:
#include <stdio.h>

int main(void)
{
  40051d:   55                      push   %rbp
  40051e:   48 89 e5                mov    %rsp,%rbp
    printf("Hello World\n");
  400521:   bf d0 05 40 00          mov    $0x4005d0,%edi
  400526:   e8 d5 fe ff ff          callq  400400 <puts@plt>
    return 0;
  40052b:   b8 00 00 00 00          mov    $0x0,%eax
}
  400530:   5d                      pop    %rbp
  400531:   c3                      retq
  400532:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  400539:   00 00 00
  40053c:   0f 1f 40 00             nopl   0x0(%rax)
~~~(ry~~~

とりあえず今回は逆アセンブルまでしてみた
たぶん次回もあると思うが今回はここまで

参考

www.oreilly.co.jp

オブジェクトファイルと戯れてみる Pert.1

echoコマンドのオブジェクトファイルと戯れてみる

ELF

ELFとは実行可能バイナリやオブジェクトファイル等のフォーマットを規定したのもだそう

ja.wikipedia.org

echo コマンドのELFヘッダを見てみる

ELFヘッダとは

ファイルの先頭に存在し、ELF識別子、アーキテクチャ情報および、他の2つのヘッダへの情報を持つ。

ヘッダは下記で確認できる

# readelf -h /bin/echo
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x40192a
  Start of program headers:          64 (bytes into file)
  Start of section headers:          31144 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 30

ehco コマンドのプログラムヘッダを見てみる

プログラムヘッダとは

ファイル上のどの部分(セグメント)がどのような属性で何処に読み込まれるかを保持するヘッダであり、ファイルローダによって扱われる。
ELFヘッダに続いて実行時ディスクから何らかの形で読み込まれるセグメントの数だけ存在する。

プログラムヘッダは下記でみれる

# readelf -l /bin/echo

Elf file type is EXEC (Executable file)
Entry point 0x40192a
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000005ecc 0x0000000000005ecc  R E    200000
  LOAD           0x0000000000006da8 0x0000000000606da8 0x0000000000606da8
                 0x0000000000000478 0x00000000000005d8  RW     200000
  DYNAMIC        0x0000000000006e08 0x0000000000606e08 0x0000000000606e08
                 0x00000000000001d0 0x00000000000001d0  RW     8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x00000000000051cc 0x00000000004051cc 0x00000000004051cc
                 0x000000000000023c 0x000000000000023c  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
  GNU_RELRO      0x0000000000006da8 0x0000000000606da8 0x0000000000606da8
                 0x0000000000000258 0x0000000000000258  R      1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .jcr .data.rel.ro .dynamic .got .got.plt .data .bss
   04     .dynamic
   05     .note.ABI-tag .note.gnu.build-id
   06     .eh_frame_hdr
   07
   08     .init_array .fini_array .jcr .data.rel.ro .dynamic .got

ELFファイルタイプはEXEC(実行可能)でエントリポイントが0x40192a等の情報がわかる
また

Section to Segment mapping

以降の情報から最初のプログラムヘッダで示されるセグメントはPHDRでそれに含まれるセクションはなく
次がINTRTP。この中には.interpが含まれてることがわかる

セクションヘッダを見てみる

セクションヘッダとは

オブジェクトファイルの論理的な構造を記述する部分で、ヘッダと名前がついているが実際にはファイルの最後あたりに置かれていることが多い。
ここはリンカやデバッガによって参照されることがある。セクションはセクション名があるが、それは特殊なセクションに置かれ何番目の文字列かという指し方を行う。
このことによって、エントリそのものは固定長にしつつセクション名の長さ制限を取り払っている。

下記で確認できる

# readelf -S /bin/echo
There are 31 section headers, starting at offset 0x79a8:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000558  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400810  00000810
       000000000000025d  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400a6e  00000a6e
       0000000000000072  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400ae0  00000ae0
       0000000000000060  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400b40  00000b40
       0000000000000078  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000400bb8  00000bb8
       0000000000000498  0000000000000018  AI       5    25     8
  [11] .init             PROGBITS         0000000000401050  00001050
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000401070  00001070
       0000000000000320  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000401390  00001390
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         00000000004013a0  000013a0
       000000000000308a  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         000000000040442c  0000442c
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         0000000000404440  00004440
       0000000000000d8b  0000000000000000   A       0     0     32
  [17] .eh_frame_hdr     PROGBITS         00000000004051cc  000051cc
       000000000000023c  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000405408  00005408
       0000000000000ac4  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000606da8  00006da8
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000606db0  00006db0
       0000000000000008  0000000000000008  WA       0     0     8
  [21] .jcr              PROGBITS         0000000000606db8  00006db8
       0000000000000008  0000000000000000  WA       0     0     8
  [22] .data.rel.ro      PROGBITS         0000000000606dc0  00006dc0
       0000000000000048  0000000000000000  WA       0     0     32
  [23] .dynamic          DYNAMIC          0000000000606e08  00006e08
       00000000000001d0  0000000000000010  WA       6     0     8
  [24] .got              PROGBITS         0000000000606fd8  00006fd8
       0000000000000028  0000000000000008  WA       0     0     8
  [25] .got.plt          PROGBITS         0000000000607000  00007000
       00000000000001a0  0000000000000008  WA       0     0     8
  [26] .data             PROGBITS         00000000006071a0  000071a0
       0000000000000080  0000000000000000  WA       0     0     32
  [27] .bss              NOBITS           0000000000607220  00007220
       0000000000000160  0000000000000000  WA       0     0     32
  [28] .gnu_debuglink    PROGBITS         0000000000000000  00007220
       0000000000000010  0000000000000000           0     0     4
  [29] .gnu_debugdata    PROGBITS         0000000000000000  00007230
       0000000000000658  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  00007888
       000000000000011e  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

echoはELFヘッダを確認した時にストリングテーブルのindexが30だったので30番目のセクションヘッダがそのストリングテーブルを保持していることがわかる

[30] .shstrtab STRTAB 0000000000000000 00007888 000000000000011e 0000000000000000 0 0 1

これでsh_offsetが0x00007888でサイズが0x000000000000011eということがわかる

ストリングテーブルを見てみる

下記で見れる

# od --skip-bytes 0x00007888 --read-bytes 0x11e -t x1z /bin/echo
0074210 00 2e 73 68 73 74 72 74 61 62 00 2e 69 6e 74 65  >..shstrtab..inte<
0074230 72 70 00 2e 6e 6f 74 65 2e 41 42 49 2d 74 61 67  >rp..note.ABI-tag<
0074250 00 2e 6e 6f 74 65 2e 67 6e 75 2e 62 75 69 6c 64  >..note.gnu.build<
0074270 2d 69 64 00 2e 67 6e 75 2e 68 61 73 68 00 2e 64  >-id..gnu.hash..d<
0074310 79 6e 73 79 6d 00 2e 64 79 6e 73 74 72 00 2e 67  >ynsym..dynstr..g<
0074330 6e 75 2e 76 65 72 73 69 6f 6e 00 2e 67 6e 75 2e  >nu.version..gnu.<
0074350 76 65 72 73 69 6f 6e 5f 72 00 2e 72 65 6c 61 2e  >version_r..rela.<
0074370 64 79 6e 00 2e 72 65 6c 61 2e 70 6c 74 00 2e 69  >dyn..rela.plt..i<
0074410 6e 69 74 00 2e 70 6c 74 2e 67 6f 74 00 2e 74 65  >nit..plt.got..te<
0074430 78 74 00 2e 66 69 6e 69 00 2e 72 6f 64 61 74 61  >xt..fini..rodata<
0074450 00 2e 65 68 5f 66 72 61 6d 65 5f 68 64 72 00 2e  >..eh_frame_hdr..<
0074470 65 68 5f 66 72 61 6d 65 00 2e 69 6e 69 74 5f 61  >eh_frame..init_a<
0074510 72 72 61 79 00 2e 66 69 6e 69 5f 61 72 72 61 79  >rray..fini_array<
0074530 00 2e 6a 63 72 00 2e 64 61 74 61 2e 72 65 6c 2e  >..jcr..data.rel.<
0074550 72 6f 00 2e 64 79 6e 61 6d 69 63 00 2e 67 6f 74  >ro..dynamic..got<
0074570 2e 70 6c 74 00 2e 64 61 74 61 00 2e 62 73 73 00  >.plt..data..bss.<
0074610 2e 67 6e 75 5f 64 65 62 75 67 6c 69 6e 6b 00 2e  >.gnu_debuglink..<
0074630 67 6e 75 5f 64 65 62 75 67 64 61 74 61 00        >gnu_debugdata.<
0074646

.shstrtabの先頭からのoffsetがストリングテーブルでのindexとなる

シンボルテーブルを見てみる

シンブルテーブルはシンボルとその値を対応させるためのテーブル

ja.wikipedia.org

下記でみることができる

# readelf -s /bin/echo

Symbol table '.dynsym' contains 57 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __uflow@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getenv@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND __progname@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free@GLIBC_2.2.5 (2)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.2.5 (2)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.2.5 (2)
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strncmp@GLIBC_2.2.5 (2)
     8: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND stdout@GLIBC_2.2.5 (2)
     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _exit@GLIBC_2.2.5 (2)
    10: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcpy@GLIBC_2.2.5 (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __fpending@GLIBC_2.2.5 (2)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND textdomain@GLIBC_2.2.5 (2)
    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fclose@GLIBC_2.2.5 (2)
    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND bindtextdomain@GLIBC_2.2.5 (2)
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dcgettext@GLIBC_2.2.5 (2)
    16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __ctype_get_mb_cur_max@GLIBC_2.2.5 (2)
    17: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.2.5 (2)
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@GLIBC_2.4 (3)
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mbrtowc@GLIBC_2.2.5 (2)
    20: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __overflow@GLIBC_2.2.5 (2)
    21: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strrchr@GLIBC_2.2.5 (2)
    22: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND lseek@GLIBC_2.2.5 (2)
    23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memset@GLIBC_2.2.5 (2)
    24: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fscanf@GLIBC_2.2.5 (2)
    25: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND close@GLIBC_2.2.5 (2)
    26: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
    27: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memcmp@GLIBC_2.2.5 (2)
    28: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fputs_unlocked@GLIBC_2.2.5 (2)
    29: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND calloc@GLIBC_2.2.5 (2)
    30: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcmp@GLIBC_2.2.5 (2)
    31: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    32: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memcpy@GLIBC_2.14 (4)
    33: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND program_invocation_name@GLIBC_2.2.5 (2)
    34: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fileno@GLIBC_2.2.5 (2)
    35: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND malloc@GLIBC_2.2.5 (2)
    36: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fflush@GLIBC_2.2.5 (2)
    37: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND nl_langinfo@GLIBC_2.2.5 (2)
    38: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND ungetc@GLIBC_2.2.5 (2)
    39: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __freading@GLIBC_2.2.5 (2)
    40: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND realloc@GLIBC_2.2.5 (2)
    41: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fdopen@GLIBC_2.2.5 (2)
    42: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND setlocale@GLIBC_2.2.5 (2)
    43: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __printf_chk@GLIBC_2.3.4 (5)
    44: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND error@GLIBC_2.2.5 (2)
    45: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND __progname_full@GLIBC_2.2.5 (2)
    46: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND open@GLIBC_2.2.5 (2)
    47: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fseeko@GLIBC_2.2.5 (2)
    48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit@GLIBC_2.2.5 (2)
    49: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@GLIBC_2.2.5 (2)
    50: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fwrite@GLIBC_2.2.5 (2)
    51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __fprintf_chk@GLIBC_2.3.4 (5)
    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mbsinit@GLIBC_2.2.5 (2)
    53: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND program_invocation_short_@GLIBC_2.2.5 (2)
    54: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND iswprint@GLIBC_2.2.5 (2)
    55: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __ctype_b_loc@GLIBC_2.3 (6)
    56: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND stderr@GLIBC_2.2.5 (2)

セクションヘッダを見るとシンボルテーブルは下記.dynsmであることがわかる

[ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000558  0000000000000018   A       6     1     8

これをdumpしてみると

# od --skip-bytes 0x2b8 --read-bytes 0x558 -t x1z /bin/echo
0001270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
0001310 00 00 00 00 00 00 00 00 21 01 00 00 12 00 00 00  >........!.......<
0001330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
0001350 43 01 00 00 12 00 00 00 00 00 00 00 00 00 00 00  >C...............<
0001370 00 00 00 00 00 00 00 00 ef 01 00 00 11 00 00 00  >................<
0001410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
0001430 ea 01 00 00 12 00 00 00 00 00 00 00 00 00 00 00  >................<
0001450 00 00 00 00 00 00 00 00 7a 00 00 00 12 00 00 00  >........z.......<
0001470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
0001510 d1 00 00 00 12 00 00 00 00 00 00 00 00 00 00 00  >................<
0001530 00 00 00 00 00 00 00 00 38 00 00 00 12 00 00 00  >........8.......<
0001550 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
0001570 f7 00 00 00 11 00 00 00 00 00 00 00 00 00 00 00  >................<
~~~(ry~~~~~

シンボルテーブルの構造はこのようになっている

64bit

uint32_t          st_name;
unsigned char     st_info;
unsigned char     st_other;
uint16_t          st_shndx;
Elf64_Addr          st_value;
uint64_t          st_size;

0番目のsymble情報は空

0001270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<

進んでいってここの情報は

0001350 43 01 00 00 12 00 00 00 00 00 00 00 00 00 00 00 >C...............<

これはアドレス0001350から始まるこのバイト列は最初の43 01 00 00
つまりst_name=0x143に対応する
逆順になっているのは今回のアーキテクチャx86でリトルインディアンだから

.dynstrセクションを見るとストリングテーブルは先頭から0x810バイト目から始まっている

  [ 6] .dynstr           STRTAB           0000000000400810  00000810
       000000000000025d  0000000000000000   A       0     0     1

そこで0x810に0x143を足した0x953から始まる文字列を見てみる

# od --skip-bytes 0x953 --read-bytes 32 -t x1z /bin/echo
0004523 67 65 74 65 6e 76 00 5f 5f 66 72 65 61 64 69 6e  >getenv.__freadin<
0004543 67 00 73 74 64 65 72 72 00 66 73 63 61 6e 66 00  >g.stderr.fscanf.<
0004563

見た通り0x143に対応するシンボルはgetenvであることがわかった
これはreadelfのここに対応している

     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getenv@GLIBC_2.2.5 (2)

次回に続く

参考

www.oreilly.co.jp

RAIDのステータス確認コマンド

欲しいと思った時に忘れていて調べることが多いので自分用メモ

/opt/MegaRAID/MegaCli/MegaCli64 -LDInfo -Lall -aALL | grep State

status

  • Optimal
    • 正常
  • Degrated
    • RAIDが壊れてる。縮退状態
  • Partialy Degrated
    • RAIDが壊れてるが冗長性は保たれている

diskの特定

/opt/MegaRAID/MegaCli/MegaCli64 -PDList -aALL | grep -e Slot -e Error -e Firmware

Frimwareのstatus

  • Online, Spun Up
    • 正常
  • Failed
    • RAIDからDiskが外れてる
  • Rebuild

puppetで既に存在してるファイルを上書きする方法

puppetで既にファイルが存在している時に後から上書きする方法が
調べていてもパッと出てこなかったのでメモ

Resource Type: fileattribute: content
コンテンツをそのまま記載すれば上書きされる

puppet.com

また、自分でわざと指定していない限りはattribute: replaceもdefaultで
trueなはずなので特に問題なく上書きできるはず

$str = "echo 'Hello, world'"
file { '/foo/bar.txt':
  content => $str,
}

puppet.com

Redis keysコマンドの罠

概要

Redisに保存されている値を見たい時にkeysコマンドをつかって

$ redis-cli keys *

でkeyを取得してkeyを元にgetコマンドを作るって記事がわりとあるけど
実際にproductionとしてredisを運用しているとここに罠があるので紹介

Redis is 何?

公式サイト

redis.io

所謂、NoSQLとかKVSって言われているやつの代表格
特徴として

  • ネットワーク接続されたインメモリデータベース
  • キー・バリュー型データベース
  • オプションとして永続性を持つ
  • シングルスレッドで動作する

シングルスレッドで動作する

keysでサチる

Redisの説明でも書いたが
シングルスレッドで動作する (正確にはクエリを処理するスレッドは1つ)
ってところが罠なのだが、この仕組みによりAtomicな操作が可能になっている

redis.io

実際にproductionで本番運用させていくと
データ永続化可能なこともありkeyの数が膨大に増えていくことがあよくある

自分が実際に携わっていたアプリに関して言うと13万keyを軽く超えていた

そうなってくると
誰かがkeysコマンドを*(ワイルドカード)で叩くと
keyを取得するためだけにプロセスが使われてしまい
webサイトやAPI等からgetやsetされる処理が待ち状態になり
webサーバのプロセスが大量に増えていき
サーバ自体のリソースを食いつぶしていく
ということが起きる

結論

qiitaとかで調べてあまりRedisを理解していない段階で
本番環境とかにkeysを気軽に投げると死ぬことがあるよっていうこと
ちゃんと理解してから利用しましょう

[Perl] [GeoIP2] MaxMind::DB::Reader::XS と MaxMind::DB::Reader での速度比較

GeoIP2 とは

下記にあるようにipアドレスに紐づく地域やISP等が格納されているMaxMind社が提供するDatabase www.maxmind.com

概要

perlの公式APIのREADMEに下記のような記載があり本当に100倍も速度が違うのか気になったので速度を比較してみる

github.com

The XS implementation is approximately 100x faster than the pure Perl implementation.

環境準備

とりあえずdockerで速度を比較できる環境を準備する
ほんとこういう時にサクッと環境作れるdockerは偉大だなってつくづく思う

docker pull

sudo docker pull centos:centos6
centos6: Pulling from library/centos
~~~ (ry ~~~
Status: Downloaded newer image for centos:centos6
  • sentos6なことに特に意味はない
  • 一番慣れ親しんでるだけで今のご時世ならsentos7にすべきかなって思いながらサボった

docker run

docker run -ti -d --name geoip2 centos:centos6

perlのinstall

perl入ってなかったのでインストール

# yum info perl
Loaded plugins: fastestmirror, ovl
base                                                                                                                                                                                 | 3.7 kB     00:00
base/primary_db                                                                                                                                                                      | 4.7 MB     00:01
extras                                                                                                                                                                               | 3.4 kB     00:00
extras/primary_db                                                                                                                                                                    |  27 kB     00:00
updates                                                                                                                                                                              | 3.4 kB     00:00
updates/primary_db                                                                                                                                                                   | 3.0 MB     00:04
Available Packages
Name        : perl
Arch        : x86_64
Epoch       : 4
Version     : 5.10.1
Release     : 144.el6
Size        : 10 M
Repo        : base
Summary     : Practical Extraction and Report Language
URL         : http://www.perl.org/
License     : (GPL+ or Artistic) and (GPLv2+ or Artistic) and Copyright Only and MIT and Public Domain and UCD
Description : Perl is a high-level programming language with roots in C, sed, awk
            : and shell scripting.  Perl is good at handling processes and files,
            : and is especially good at handling text.  Perl's hallmarks are
            : practicality and efficiency.  While it is used to do a lot of
            : different things, Perl's most common applications are system
            : administration utilities and web programming.  A large proportion of
            : the CGI scripts on the web are written in Perl.  You need the perl
            : package installed on your system so that your system can handle Perl
            : scripts.
            :
            : Install this package if you want to program in Perl or enable your
            : system to handle Perl scripts.

とりあえず諸々install

# yum -y install perl gcc make patch
# curl -Lk  http://xrl.us/perlbrewinstall | bash
# echo " source ~/perl5/perlbrew/etc/bashrc " >> ~/.bashrc
source ~/perl5/perlbrew/etc/bashrc

perlbrewで使えるversionの確認

[root@c6cc159d9e64 /]# perlbrew available

   perl-5.29.7
   perl-5.28.1
   perl-5.28.0
   perl-5.26.3
   perl-5.26.2
   perl-5.26.1
   perl-5.26.0
   perl-5.24.4
   perl-5.24.3
~~~ (ry ~~~

とりあえずperl-5.28.1をinstall(結構な時間が掛かる)
この辺でなぜperlのimageを使わなかったのか激しく後悔したがここまできたのでそのまま進める

# perlbrew install perl-5.28.1
Installing /root/perl5/perlbrew/build/perl-5.28.1/perl-5.28.1 into ~/perl5/perlbrew/perls/perl-5.28.1

This could take a while. You can run the following command on another shell to track the status:

  tail -f ~/perl5/perlbrew/build.perl-5.28.1.log

perl-5.28.1 is successfully installed.

switch

# perlbrew switch perl-5.28.1

cpanm

# perlbrew install-cpanm

geoip2のinstall

database

# yum -y install wget gunzip openssl-devel
# mkdir gepip; cd gepip/
# wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz
# gunzip GeoLite2-Country.mmdb.gz
# wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz
# gunzip GeoLite2-City.mmdb.gz

perl module

# mkdir extlib; cd extlib
cpanm -L . GeoIP2

試しに実行

# perl -Mlocal::lib=/root/extlib/ -MGeoIP2::Database::Reader -E 'my $reader = GeoIP2::Database::Reader->new( file => "/root/gepip/GeoLite2-City.mmdb", locales => ["en"] ); my $city = $reader->city( ip => "24\.24\.24\.24" ); my $country = $city->country(); say $country->iso_code()'
US

libmaxminddb

下記に記載の通り、libmaxminddbMaxMind::DB::Reader::XSが必要なのでinstall github.com

epel repo の追加

# yum install epel-release

libmaxminddbのinstalll

# yum install libmaxminddb

速度比較用に別ディレクトリにGeoIP2とMaxMind::DB::Reader::XSをinstall

# mkdir extlib-xs; cd extlib-xs
cpanm -L . GeoIP2
cpanm -L . MaxMind::DB::Reader::XS

速度比較

簡易的なscriptを組んでbenchmarkを取ってみる

use strict;
use warnings;
use Benchmark qw(:all);
use GeoIP2::Database::Reader;

timethese (10000,
    {
        geoip => sub {
            my $reader = GeoIP2::Database::Reader->new( file => "/root/geoip/GeoLite2-City.mmdb", locales => ["en"] );
            my $city = $reader->city( ip => "24.24.24.24" );
            my $country = $city->country();
            $country->iso_code();
            $country->name();
        },
    }
);

実行結果

# perl -Mlocal::lib=/root/extlib benchmark.pl
Benchmark: timing 10000 iterations of geoip...
     geoip: 164 wallclock secs (141.50 usr + 21.71 sys = 163.21 CPU) @ 61.27/s (n=10000)
# perl -Mlocal::lib=/root/extlib-xs benchmark.pl
Benchmark: timing 10000 iterations of geoip...
     geoip:  6 wallclock secs ( 4.83 usr +  0.69 sys =  5.52 CPU) @ 1811.59/s (n=10000)

結論

100倍どころじゃなく速かったのではやりXSはすごいなぁって印象(当たり前だけども)

MySQLにおけるin句のitem数制限について

Oracle畑で育ってきた自分の中では

  • in句に指定できる要素数は1000個まで

というのが常識だったんだが、同僚からいままで気にしたことがなかったと言われてので調べてみた

のだがとくにそれらしい情報はなさそうであった。 どうやらMySQLには制限はなさそうだ

ちなみに制限はなくてもクエリ長自体に制限はあるっぽいのでこっちで引っかかるまでなのかもしれないね

show variables like 'max_allowed_packet'

dev.mysql.com