CRuby (2) - らんだむな記憶 の続き。
cgdb --args ./ruby test.rb
して
(gdb) b process.c:4739 Breakpoint 1 at 0x133308: file process.c, line 4739. (gdb) r
し、ブレークしたところで、
(gdb) b string.c:815 Breakpoint 2 at 0x5579b7878d2d: file string.c, line 815.
に加えて
(gdb) b string.c:432 Breakpoint 3 at 0x5579b7877e88: file string.c, line 432.
する。これは https://github.com/ruby/ruby/blob/v3_0_1/string.c#L432 の部分であり、test.rb
の puts "Hello, ruby! (こんにちは、ruby!)"
部分でブレークさせたい狙いである。
何回か c
で進めると、
Breakpoint 3, rb_setup_fake_str (fake_str=0x7fffcf3ff510, name=0x5579b79c51d0 "puts", len=4, enc=0x5579b8c9389 0) at string.c:432 432 return setup_fake_str(fake_str, name, len, rb_enc_to_index(enc));
が出てくる。いい感じである。
n
で行を進めていくと、そのうち https://github.com/ruby/ruby/blob/v3_0_1/io.c#L7927 が見えてくる。なるほどこれが puts
の実装か。
ということで最初から
(gdb) b io.c:7927 Breakpoint 1 at 0x72e93: file io.c, line 7927.
で良いことが分かった。ここから流れを辿ると、
(gdb) b vm_eval.c:135 Breakpoint 2 at 0x564e7bbde121: file vm_eval.c, line 135.
という足がかりが得られることになる。そこから更に追跡すると、https://github.com/ruby/ruby/blob/v3_0_1/io.c#L7889 が見えてくるので、
(gdb) b io.c:7889 Breakpoint 1 at 0x72d14: file io.c, line 7889.
が特に(?)puts
の実装であることが分かる。この行でブレークすると、以下のように
(gdb) p rbimpl_rstring_getmem(argv[0]).as $1 = {heap = {len = 38, ptr = 0x5629e17a1310 "Hello, ruby! (\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257\343\200\201ruby!)", aux = {capa = 94737875647880, shared = 94737875647880}}, ary = "&\000\000\000\000\000\000\000\020\023z\341)V\000\000\210\275l\341)V\000"}
argv[0]
に "Hello, ruby! (こんにちは、ruby!)"
が来ていることが分かる。何故、こんな感じで p
すれば良いかは、後続の https://github.com/ruby/ruby/blob/v3_0_1/io.c#L7905 における
RSTRING_LEN(line)
の中にステップインして実装を見ると想像がつく。なお、文字列のエンコードされた部分は 8 進法表記になっているので、 Python でも使って以下のようにすると、UTF-8 で符号化されていることが分かる。
>>> b = b'\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257\343\200\201' >>> print(b.decode('utf_8')) こんにちは、 >>> b b'\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf\xe3\x80\x81' >>> f"{3*64+4*8+3:x}" 'e3'
なお、test.rb
の先頭を
# -*- coding: cp932 -*-
にして、cp932
として保存すると
(gdb) p rbimpl_rstring_getmem(argv[0]).as $1 = {heap = {len = 32, ptr = 0x55c23bdd10b0 "Hello, ruby! (\202\261\202\361\202\311\202\277\202\315\201Aruby!)", aux = {capa = 94292716566560, shared = 94292716566560}}, ary = "\000\000\000\000\000\000\000\260\020\335;\302U\000\0008\340;\302U\000"}
となって、もう一度 Python で 16 進数として見ると、
>>> b = b'\202\261\202\361\202\311\202\277\202\315\201' >>> b b'\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x81'
みんなが大好きな “2 バイト文字” の CP932 で符号化されていることが分かる。
最後に、argv[0]
は結局何であるかと言うと、https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/core/rstring.h#L73-L86 の構造体 struct RString
へのポインタである。
これは、https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/core/rstring.h#L122-L137 と https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/core/rstring.h#L35 と https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/cast.h#L33 から読み取れる rbimpl_rstring_getmem
の実装による。