「?:」演算子(続きの続きの続きの続き)
少し時間が空いてしまいましたが、まだまだモチベーションは下がっていないようで一安心です。先日の続きです。
今回は(前回に宣言した)変数の変更(改竄)あたりをやってみたいと思います。
どの関数を対象にしようか迷いましたが、適当にround関数をターゲットにしてみます。。。って、特に深い事をやっている訳ではないのでhnwさんに登場されるとマジで焦ります。(w
以前「strings」コマンドを使って関数の実体を探した事を思い出したので、今回その方法でやってみました。
% cd $HOME/php-5.3-dev % strings ./php-cli | grep 'round' zif_round round body {background-color: #ffffff; color: #000000;} a:link {color: #000099; text-decoration: none; background-color: #ffffff;} .e {background-color: #ccccff; font-weight: bold; color: #000000;} .h {background-color: #9999cc; font-weight: bold; color: #000000;} .v {background-color: #cccccc; color: #000000;} .vr {background-color: #cccccc; text-align: right; color: #000000;} hr {width: 600px; background-color: #cccccc; border: 0px; height: 1px; color: #000000;}
なる程。「zif_round」か「round」のどっちかだね。
gdbを起動してブレーク・ポイントに設定してみる。
% gdb ./php-cli (gdb) b round Function "round" not defined. Make breakpoint pending on future shared library load? (y or [n]) (gdb) b zif_round Breakpoint 1 at 0x81580b5: file $HOME/php-5.3-dev/work/php5.3-200808250230/ext/standard/math.c, line 184. (gdb) i breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x081580b5 in zif_round at $HOME/php-5.3-dev/work/php5.3-200808250230/ext/standard/math.c:184
「zif_round」が正解。それでは実際に実行してみる。
(gdb) r -r 'echo round( 5.045, 2 );' Starting program: $HOME/php-5.3-dev/php-cli -r 'echo round( 5.045, 2 );' Failed to read a valid object file image from memory. Breakpoint 1, zif_round (ht=2, return_value=0xb7decee0, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1) at $HOME/php-5.3-dev/work/php5.3-200808250230/ext/standard/math.c:184 184 int places = 0; (gdb) bt #0 zif_round (ht=2, return_value=0xb7decee0, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1) at $HOME/php-5.3-dev/work/php5.3-200808250230/ext/standard/math.c:184 #1 0x0823655b in zend_do_fcall_common_helper_SPEC (execute_data=0xb7d75048) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:315 #2 0x0823aa3d in ZEND_DO_FCALL_SPEC_CONST_HANDLER (execute_data=0xb7d75048) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:1574 #3 0x08235a49 in execute (op_array=0xb7deca10) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:104 #4 0x081ffe56 in zend_eval_string (str=0xbfbcb9a6 "echo round( 5.045, 2 );", retval_ptr=0x0, string_name=0x834a4dc "Command line code") at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_execute_API.c:1112 #5 0x08200011 in zend_eval_string_ex (str=0xbfbcb9a6 "echo round( 5.045, 2 );", retval_ptr=0x0, string_name=0x834a4dc "Command line code", handle_exceptions=1) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_execute_API.c:1147 #6 0x08293ecc in main (argc=3, argv=0xbfbca404) at $HOME/php-5.3-dev/work/php5.3-200808250230/sapi/cli/php_cli.c:1169 (gdb) l 179 /* {{{ proto float round(float number [, int precision]) 180 Returns the number rounded to specified precision */ 181 PHP_FUNCTION(round) 182 { 183 zval **value; 184 int places = 0; 185 long precision = 0; 186 double return_val; 187 188 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|l", &value, &precision) == FAILURE) { (gdb) 189 return; 190 } 191 192 if (ZEND_NUM_ARGS() == 2) { 193 places = (int) precision; 194 } 195 convert_scalar_to_number_ex(value); 196 197 switch (Z_TYPE_PP(value)) { 198 case IS_LONG: (gdb) c Continuing. 5.05 Program exited normally.
結果は「5.05」となりました。期待通り。
前準備はこれくらいにしておいて、今回はround関数の第2引数を変更(改竄)してみたいと思います。
最終的には変数「places」で処理されているっぽいので、変数「places」へ代入している位置をブレーク・ポイントに設定してみる。
(gdb) d 1 (gdb) b $HOME/php-5.3-dev/work/php5.3-200808250230/ext/standard/math.c:193 Breakpoint 2 at 0x81580f4: file $HOME/php-5.3-dev/work/php5.3-200808250230/ext/standard/math.c, line 193. (gdb) i breakpoints Num Type Disp Enb Address What 2 breakpoint keep y 0x081580f4 in zif_round at $HOME/php-5.3-dev/work/php5.3-200808250230/ext/standard/math.c:193 (gdb) r -r 'echo round( 5.045, 2 );' Starting program: $HOME/php-5.3-dev/php-cli -r 'echo round( 5.045, 2 );' Failed to read a valid object file image from memory. Breakpoint 2, zif_round (ht=2, return_value=0xb7dd9ee0, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1) at $HOME/php-5.3-dev/work/php5.3-200808250230/ext/standard/math.c:193 193 places = (int) precision;
止まった。ここからが本題。
「pt(ptype)type_name」で指定した「type_name」の型が分かる、と。
「p(print)variable_name」で指定した「variable_name」の値が分かる、と。
「s(set)variable_name = expression」で指定した「variable_name」の値を変更できる、と。
「expression」には値だけではなくて式も指定できるみたいなんだけど、現時点では「何のこっちゃ?」って感じ。
いづれ分かるさ。。。きっと。_| ̄|○
気を取り直して内容と中身を確認。
(gdb) pt places type = int (gdb) p places $1 = 0
この時点では(まだ)初期値のママなので「n(next)」で次へ進んでみる。
(gdb) n 195 convert_scalar_to_number_ex(value); (gdb) p places $2 = 2
おぉっ!第2引数で指定した値に変わってる。
それでは、今回の目的である変更(改竄)をやってみます。
(gdb) s places = 1 197 switch (Z_TYPE_PP(value)) { (gdb) p places $3 = 1 (gdb) c Continuing. 5 Program exited normally.
おぉっ!変更(改竄)した値で正しく処理されています。結果も「5」となり、期待通り。すげぇ。
ただ、気になるのは変数を変更(改竄)しただけなのに次へ処理が進んでいるのは何故なんだろうか。。。こういう仕様なの?
使いづらいような。。。全然分かりません。ヘタれっぷりは(いつまでも)健在です。_| ̄|○
ここからは(若干)話が逸れますが「zval **value」の変更(改竄)に挑戦。
ブレーク・ポイントの設定は残したママで再実行。
(gdb) r -r 'echo round( 5.045, 2 );' Starting program: $HOME/php-5.3-dev/php-cli -r 'echo round( 5.045, 2 );' Failed to read a valid object file image from memory. Breakpoint 2, zif_round (ht=2, return_value=0xb7d5bee0, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1) at $HOME/php-5.3-dev/work/php5.3-200808250230/ext/standard/math.c:193 193 places = (int) precision;
止まったので、内容を確認。
(gdb) pt value type = struct _zval_struct { zvalue_value value; zend_uint refcount__gc; zend_uchar type; zend_uchar is_ref__gc; } **
うへ。複雑。中身を確認。
(gdb) p value $4 = (zval **) 0xb7ce40a8 (gdb) p *value $5 = (zval *) 0xb7d5b31c (gdb) p **value $6 = {value = {lval = 2061584302, dval = 5.0449999999999999, str = {val = 0x7ae147ae <Address 0x7ae147ae out of bounds>, len = 1075064340}, ht = 0x7ae147ae, obj = {handle = 2061584302, handlers = 0x40142e14}}, refcount__gc = 1, type = 2 '\002', is_ref__gc = 0 '\0'}
pointerのpointerなので、中身を確認するには「*」が二つ必要、と。
それでは変更(改竄)に挑戦!
(gdb) s **value.value = 4.0449999999999999 Attempt to take contents of a non-pointer value. (gdb) p **value $7 = {value = {lval = 2061584302, dval = 5.0449999999999999, str = {val = 0x7ae147ae <Address 0x7ae147ae out of bounds>, len = 1075064340}, ht = 0x7ae147ae, obj = {handle = 2061584302, handlers = 0x40142e14}}, refcount__gc = 1, type = 2 '\002', is_ref__gc = 0 '\0'} (gdb) s *value.value = 4.0449999999999999 Attempt to take contents of a non-pointer value. (gdb) p **value $8 = {value = {lval = 2061584302, dval = 5.0449999999999999, str = {val = 0x7ae147ae <Address 0x7ae147ae out of bounds>, len = 1075064340}, ht = 0x7ae147ae, obj = {handle = 2061584302, handlers = 0x40142e14}}, refcount__gc = 1, type = 2 '\002', is_ref__gc = 0 '\0'} (gdb) s value.value = 4.0449999999999999 Value can't be converted to integer. (gdb) p **value $9 = {value = {lval = 2061584302, dval = 4.0449999999999999, str = {val = 0x7ae147ae <Address 0x7ae147ae out of bounds>, len = 1074802196}, ht = 0x7ae147ae, obj = {handle = 2061584302, handlers = 0x40102e14}}, refcount__gc = 1, type = 2 '\002', is_ref__gc = 0 '\0'} (gdb) c Continuing. 4.05 Program exited normally. (gdb) q %
うーん。。。全然分かりません。_| ̄|○
実行結果を見る限りでは「4.05」になっているので(一応)変更されたっぽいけど、警告っぽいのも出ているので、この方法は間違っている気がする。。。けど、そもそも使い方を理解していないので、何が正しいのかすらサッパリ分かりません。
全然駄目スギ。。。久々に涙目。_| ̄|○
そろそろ本でも購入すべきかな。。。と思いつつ、今日はここまで。
次は条件付きブレーク・ポイントの設定や関数呼び出しあたりを重点的に勉強してみたい。
。。。次からはタイトルを変更しよう。意味不明スギ。_| ̄|○