Skip to content

Instantly share code, notes, and snippets.

@AnaTofuZ
Last active February 19, 2021 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AnaTofuZ/9a4465f24a6dd4090a69915d039ae2e5 to your computer and use it in GitHub Desktop.
Save AnaTofuZ/9a4465f24a6dd4090a69915d039ae2e5 to your computer and use it in GitHub Desktop.

自己紹介

  • 八雲アナグラ(@AnaTofuZ)
  • (3月まで)沖縄の(3月まで)学生
    • Perlで修論を通した
    • 4月からは社会に放出されます
    • 本州にも放出されます

LT

Perl処理系

  • C言語で実装されている
  • Cが読めれば読める(それはそう)

Perl処理系の読み方

  • GitHubでCのソースコードを読むのはつらい
  • 処理をトレースしながら読むと読みやすい

Perl処理系を読むのに必要なツール

  • どこのご家庭にもあるPerl5のソースコード
    • デバッグビルドをする必要がある
  • C言語のデバッガ
    • macOS
      • lldb
    • linux
      • gdb
  • いい感じのgrep & findツール
    • 個人的にはRust制ツール
      • ripgrep
      • fd

今日読む内容

  • 最近入った連鎖比較(Chained comparisons)!!
  • こういうやつ
if ( 10 < $n <= 20 ) {...}

Perlのデバッグビルド

  • まずConfigureを実行する

./Configure -DDEBUGGING=-g -Doptimize=-O0 -de -Dusedevel -Dversiononly

  • -DDEBUGGING=-g

    • -gでCデバッガでデバッグ可能なバイナリを生成
  • -Doptimize=-O0

    • 最適化を無効
  • -de

    • Configureの対話処理をスキップ
  • Dusevel

    • 安定番じゃなくてもビルドできるようにするおまじない
    • 新鮮なPerl5をビルドするときに必須
  • あとは気合

    • $make -j
    • $make install

新鮮なPerl5で構築しました

-rw-r--r--  1 anatofuz anatofuz  2600992 Feb 19 17:11 regcomp.o
-rwxr-xr-x  1 anatofuz anatofuz  6464640 Feb 19 17:11 miniperl*
-rw-r--r--  1 anatofuz anatofuz   280024 Feb 19 17:11 perl.o
-rw-r--r--  1 anatofuz anatofuz    46320 Feb 19 17:11 DynaLoader.o
-rw-r--r--  1 anatofuz anatofuz 10025810 Feb 19 17:11 libperl.a
drwxr-xr-x 64 anatofuz anatofuz     4096 Feb 19 17:11 lib/
-rw-r--r--  1 anatofuz anatofuz      550 Feb 19 17:12 extra.pods
drwxr-xr-x  2 anatofuz anatofuz    12288 Feb 19 17:12 pod/
drwxr-xr-x  2 anatofuz anatofuz     4096 Feb 19 17:12 utils/
-rw-r--r--  1 anatofuz anatofuz     4722 Feb 19 17:12 perlmain.c
-rw-r--r--  1 anatofuz anatofuz    15256 Feb 19 17:12 perlmain.o
-rwxr-xr-x  1 anatofuz anatofuz  6592080 Feb 19 17:12 perl*

テストコード

#!/usr/bin/env perl
if (0 < $ARGV[0] < 10 ) {
  print "true!\n";
}

Perlの処理(ざっくり)

  • 一種のアセンブラみたいなものに変換して実行している
    • バイトコードインタプリタ
    • プロセスVMとも
  • Perlの内部命令 == Cの関数に対応している

B moduleであてをつける

  • B::Terseを使うと見やすい
    • この命令はCだとPerl_pp_ + 命令名の関数になる
      • Perl_pp_const
      • Perl_pp_cmpchain_dup
$./perl -MO=Terse,-exec,-src japanpm.pl
OP (0x5625eefc80a0) enter
# 2: if (0 < $ARGV[0] < 10 ) {
COP (0x5625eefc8110) nextstate
SVOP (0x5625eefc6388) const  IV (0x5625eefbe4e8) 0
SVOP (0x5625eefc6350) aelemfast  GV (0x5625eefbdea0) *ARGV
UNOP (0x5625eefc83a0) cmpchain_dup
BINOP (0x5625eefc6260) lt
LOGOP (0x5625eefc8330) cmpchain_and
    SVOP (0x5625eefc6228) const  IV (0x5625eefbe650) 10
    BINOP (0x5625eefc61e8) lt
LOGOP (0x5625eefc81a8) and
    OP (0x5625eefc82c8) pushmark
    SVOP (0x5625eefc82f8) const  PV (0x5625eefbe5a8) "true!\n"
    LISTOP (0x5625eefc8288) print
LISTOP (0x5625eefc80d0) leave [1]

gdbでトレース

  • 引数与えて実行
    • $gdb --args ./perl japanpm.pl

命令で止める

  • grepしながらそれっぽいのを探す
    • PP()はマクロ
  • Perl_pp_ + 命令名の関数で止める
    • いったんmainくらいまで動かすとロード可能になる

型を見る

  • svtypeでキャストして0xffとANDをとるとなんか数値が出る
    • ここでsv.hを見る
    • 実態はenumで定義しているPerlの内部型の番号
(gdb) p ((svtype)(right)->sv_flags & 0xff)
$23 = 0
(gdb) p ((svtype)(left)->sv_flags & 0xff)
$21 = 1
  • 0はNULLの値
    • Perlの内部だと0系は特別扱いしているっぽい
(gdb) p (svtype)*left
$19 = 1436120072
(gdb) p (svtype)*right
$20 = SVt_NULL

値をみる

  • p *(XPVIV*)right->sv_anyとかすると見える
    • 興奮しますね

スタック操作

  • Perlは内部命令をするときに必要なデータをスタックにいれる
    • popとかpushとかの操作で各命令で出し入れする
  • スタックポインタspで操作できる
    • @spみたいな感じ
  • (lldb) p *(XPVIV*)(*(sp))->sv_any
  • spから1を引くと、スタックの中身をたどっていける
(lldb)  p *(XPVIV*)(*(right)).sv_any
(XPVIV) $35 = {
  xmg_stash = 0x0000800900000004
  xmg_u = {
    xmg_magic = 0x0000000101204340
    xmg_hash_index = 4313858880
  }
  xpv_cur = 4320283672
  xpv_len_u = {
    xpvlenu_len = 18695992639489
    xpvlenu_rx = 0x0000110100000001
  }
  xiv_u = {
    xivu_iv = 5
    xivu_uv = 5
    xivu_namehek = 0x0000000000000005
    xivu_eval_seen = true
  }
}
(lldb)  p *(XPVIV*)(*(left)).sv_any
(XPVIV) $36 = {
  xmg_stash = 0x0000110100000001
  xmg_u = {
    xmg_magic = 0x0000000000000005
    xmg_hash_index = 5
  }
  xpv_cur = 4320283696
  xpv_len_u = {
    xpvlenu_len = 576760923272773633
    xpvlenu_rx = 0x0801110100000001
  }
  xiv_u = {
    xivu_iv = 1
    xivu_uv = 1
    xivu_namehek = 0x0000000000000001
    xivu_eval_seen = true
  }
}
  • leftには1, rightには5が入っています。

  • そしてスタックポインタspの値を確認します。

(lldb)  p *(XPVIV*)(*(sp))->sv_any
(XPVIV) $38 = {
  xmg_stash = 0x0000110100000001
  xmg_u = {
    xmg_magic = 0x0000000000000005
    xmg_hash_index = 5
  }
  xpv_cur = 4320283696
  xpv_len_u = {
    xpvlenu_len = 576760923272773633
    xpvlenu_rx = 0x0801110100000001
  }
  xiv_u = {
    xivu_iv = 1
    xivu_uv = 1
    xivu_namehek = 0x0000000000000001
    xivu_eval_seen = true
  }
}
(lldb)  p *(XPVIV*)(*(sp-1))->sv_any
(XPVIV) $40 = {
  xmg_stash = 0x0000800900000004
  xmg_u = {
    xmg_magic = 0x0000000101204340
    xmg_hash_index = 4313858880
  }
  xpv_cur = 4320283672
  xpv_len_u = {
    xpvlenu_len = 18695992639489
    xpvlenu_rx = 0x0000110100000001
  }
  xiv_u = {
    xivu_iv = 5
    xivu_uv = 5
    xivu_namehek = 0x0000000000000005
    xivu_eval_seen = true
  }
}
(lldb)  p *(XPVIV*)(*(sp-2))->sv_any
error: Couldn't apply expression side effects : Couldn't dematerialize a result variable: couldn't read its memory

つまり現在spとleftは同じ場所を刺しており、sp-1の箇所に5があります。 perlで書くと 現在のスタックは(5, 1)という感じでしょうか。

ちなみにfreeしていないので、spに1を足すと、rightが見れます。

(lldb)  p *(XPVIV*)(*(sp+1))->sv_any
(XPVIV) $42 = {
  xmg_stash = 0x0000800900000004
  xmg_u = {
    xmg_magic = 0x0000000101204340
    xmg_hash_index = 4313858880
  }
  xpv_cur = 4320283672
  xpv_len_u = {
    xpvlenu_len = 18695992639489
    xpvlenu_rx = 0x0000110100000001
  }
  xiv_u = {
    xivu_iv = 5
    xivu_uv = 5
    xivu_namehek = 0x0000000000000005
    xivu_eval_seen = true
  }
}

ここで処理を進めてみます。

(lldb) n
Process 7847 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x00000001001bb193 perl`Perl_pp_lt at pp.c:2073:5
   2070     flags_and = SvFLAGS(left) & SvFLAGS(right);
   2071     flags_or  = SvFLAGS(left) | SvFLAGS(right);
   2072
-> 2073     SETs(boolSV(
   2074         ( (flags_and & SVf_IOK) && ((flags_or & SVf_IVisUV) ==0 ) )
   2075         ?    (SvIVX(left) < SvIVX(right))
   2076         : (flags_and & SVf_NOK)
Target 0: (perl) stopped.
(lldb) n
Process 7847 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x00000001001bb26d perl`Perl_pp_lt at pp.c:2080:5
   2077         ?    (SvNVX(left) < SvNVX(right))
   2078         : (do_ncmp(left, right) == -1)
   2079     ));
-> 2080     RETURN;
   2081 }
   2082
   2083 PP(pp_gt)
Target 0: (perl) stopped.

なにか計算が行われ、結果をRETURNするとこになりました。 この次点でのスタックの中身を確認します。

(lldb)  p *(XPVIV*)(*(sp))->sv_any
(XPVIV) $22 = {
  xmg_stash = 0x0000000000000000
  xmg_u = {
    xmg_magic = 0x0000000000000000
    xmg_hash_index = 0
  }
  xpv_cur = 1
  xpv_len_u = {
    xpvlenu_len = 0
    xpvlenu_rx = 0x0000000000000000
  }
  xiv_u = {
    xivu_iv = 1
    xivu_uv = 1
    xivu_namehek = 0x0000000000000001
    xivu_eval_seen = true
  }
}
(lldb)  p *(XPVIV*)(*(sp-1))->sv_any
(XPVIV) $23 = {
  xmg_stash = 0x0000800900000004
  xmg_u = {
    xmg_magic = 0x0000000101804cb0
    xmg_hash_index = 4320152752
  }
  xpv_cur = 4328597528
  xpv_len_u = {
    xpvlenu_len = 18695992639489
    xpvlenu_rx = 0x0000110100000001
  }
  xiv_u = {
    xivu_iv = 5
    xivu_uv = 5
    xivu_namehek = 0x0000000000000005
    xivu_eval_seen = true
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment