ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
![ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道 ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道](http://ecx.images-amazon.com/images/I/41KHt9YEIyL._SL160_.jpg)
ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
- 作者: 青木峰郎
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2005/07/27
- メディア: 単行本
- 購入: 28人 クリック: 347回
- この商品を含むブログ (134件) を見る
現在、この本を読んでいます。153ページまで進みました。
昨日の帰り、本屋に(閉店10分前に)駆け込んで購入してきました。
shimookaさんに背中を後押しされたので、今回購入する事に。どうもありがとうございました。<(_ _)>
まだ最初の方なので何とも言えませんが、確かに良書の予感。。。非常に読みやすい。
(Cプログラミングの入門書ではなくて)Linux Cプログラミングの入門書という位置づけですが、PHPとか何か一つの言語を知っている人だったら本書は読めそうな予感。(個人的には)非常に丁寧に書かれていると感じました。最初のとっかかりにしたい場合は間違いなく正解。
適度に「これは〜だから使っちゃ駄目」とか補足があって、つまずく事なく先に進めそうな感じです。前に読んだ本が少し独特だった(癖が強かった)?(w
全部で440ページくらいあったので(今の調子で行くと)来週の水曜日くらいに読み終わりそうな感じ。先は長いなぁ。
最近読んでる本が結構デカくなってきているので、電車の中で読んでると恥ずかしい。。。(w
(一応)カバーは外して読んでいるのですが、中にはそれでも目立つヤツがあったりするので。。。家で読むのを許してくれよな。_| ̄|○
無理して出力しようとするのは良くないと思うアレコレ
以前、「未定義の定数の扱い」というエントリで気に入らない挙動を何とかしたのですが、その続き。
どういった挙動が気に入らないかは、最初の実行結果を確認して頂くという事で。
まずは、玉(アーカイブ)を展開してディレクトリを移動。その後、バイナリをビルド。
% cd /usr/local/src % gzip -dc php-4.4.4.tar.gz | tar xf - % cd php-4.4.4 % ./configure --disable-all --enable-debug % make
ビルドが完了したら、処理を実行。
% sapi/cli/php -r ' \ $PTR = fopen( "configure", "r" ); \ echo null; echo "\n"; \ echo false; echo "\n"; \ echo true; echo "\n"; \ echo $PTR; echo "\n"; \ echo new stdClass; echo "\n"; \ echo array( ); echo "\n"; '; 結果 ---- 1 Resource id #4 Object Array
うーん。。。やっぱり、あり得ない!。。。配列をechoしたヤツの考えも。。。きぃ。
デバッグ用途に用いるなら(まだ)理解はできるのですが。本番サービスで、この挙動はあり得ないだろう。。。という訳で。
今回は、この挙動を変えてみる(何も出力しない)事に挑戦してみます。今回も内部の動作を確認しつつ進行します。
(多分、該当しないんだろうけど)まずはgdbを起動してブレーク・ポイントの設定。
% gdb sapi/cli/php (gdb) b print Function "print" not defined. Make breakpoint pending on future shared library load? (y or [n]) (gdb) b echo Function "echo" not defined. Make breakpoint pending on future shared library load? (y or [n]) (gdb) q
(予想通り)ブレーク・ポイントが見つからないので、バイナリに対して「print」キーワードで検索。
% strings sapi/cli/php | egrep print vfprintf php_info_print_box_start zif_print_r print_class vspprintf zend_printf php_sprintf zend_print_zval_r ap_php_snprintf zif_vprintf php_info_print_style zend_print_zval php_print_credits zend_print_zval_ex php_info_print_css php_print_info php_info_print_table_start _php_stream_printf zif_user_sprintf ap_php_vsnprintf php_info_print_table_row zend_print_zval_r_ex zend_make_printable_zval php_info_print_table_end php_printf php_info_print_box_end php_info_print_table_header zend_sprintf zif_quoted_printable_decode zend_do_print zif_user_printf zend_locale_sprintf_double zif_vsprintf php_info_print_hr php_info_print_table_colspan_header php_print_info_htmlhead zend_print_variable print sprintf printf vprintf vsprintf quoted_printable_decode print_r /usr/local/src/php-4.4.4/ext/standard/formatted_print.c /usr/local/src/php-4.4.4/ext/standard/quot_print.c Illegal length modifier specified '%c' in s[np]printf call Illegal length modifier specified '%c' in s[np]printf call /usr/local/src/php-4.4.4/main/spprintf.c
かなりの量で見る気が失せる。。。キーワードを「echo」にして再挑戦。
% strings sapi/cli/php | egrep echo zend_do_echo echo_expr_list
非常にスッキリ。こっちで進行する事にします。
gdbを起動して検索結果から得られた情報の中から、それっぽいヤツ(zend_do_echo)をブレーク・ポイントに設定。
% gdb sapi/cli/php (gdb) b zend_do_echo Breakpoint 1 at 0x80f7322: file /usr/local/src/php-4.4.4/Zend/zend_compile.c, line 317. (gdb) r -r 'echo "Hello World.\n";' Starting program: /usr/local/src/php-4.4.4/sapi/cli/php -r 'echo "Hello World.\n";' Breakpoint 1, zend_do_echo (arg=0xbfffe890) at /usr/local/src/php-4.4.4/Zend/zend_compile.c:317 317 zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); (gdb) bt #0 zend_do_echo (arg=0xbfffe890) at /usr/local/src/php-4.4.4/Zend/zend_compile.c:317 #1 0x080ebe63 in zendparse () at Zend/zend_language_parser.c:3350 #2 0x080ef15a in compile_string (source_string=0xbffff790, filename=0x814b254 "Command line code") at Zend/zend_language_scanner.c:3187 #3 0x080fea26 in zend_eval_string (str=0xbffffbc7 "echo \"Hello World.\\n\";", retval_ptr=0x0, string_name=0x814b254 "Command line code") at /usr/local/src/php-4.4.4/Zend/zend_execute_API.c:635 #4 0x08122e12 in main (argc=3, argv=0xbffffa74) at /usr/local/src/php-4.4.4/sapi/cli/php_cli.c:871 (gdb) r -r '$A = "Hello World.\n"; echo $A;' The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /usr/local/src/php-4.4.4/sapi/cli/php -r '$A = "Hello World.\n"; echo $A;' Breakpoint 1, zend_do_echo (arg=0xbfffe880) at /usr/local/src/php-4.4.4/Zend/zend_compile.c:317 317 zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); (gdb) bt #0 zend_do_echo (arg=0xbfffe880) at /usr/local/src/php-4.4.4/Zend/zend_compile.c:317 #1 0x080ebe63 in zendparse () at Zend/zend_language_parser.c:3350 #2 0x080ef15a in compile_string (source_string=0xbffff780, filename=0x814b254 "Command line code") at Zend/zend_language_scanner.c:3187 #3 0x080fea26 in zend_eval_string (str=0xbffffbbe "$A = \"Hello World.\\n\"; echo $A;", retval_ptr=0x0, string_name=0x814b254 "Command line code") at /usr/local/src/php-4.4.4/Zend/zend_execute_API.c:635 #4 0x08122e12 in main (argc=3, argv=0xbffffa64) at /usr/local/src/php-4.4.4/sapi/cli/php_cli.c:871 (gdb) q The program is running. Exit anyway? (y or n) y
文字リテラルそのものを出力する方法と、(文字リテラルを変数に代入してから)変数を出力する方法を実行してみた。
バックトレースの内容を見る限りでは、どちらも差が無い事が分かります。やっぱりバックトレースは非常に便利。
「zend_eval_string」関数や「compile_string」関数でコンパイル作業でもやってるのかな。。。と思いつつ、ここは本題ではないので(また別の機会にでも)。
バックトレースの情報から関数の実体は「Zend/zend_compile.c」の中にある事が分かったので、中身を覗いてみる。
% less -N Zend/zend_compile.c 〜 省略 〜 302 void zend_do_print(znode *result, znode *arg TSRMLS_DC) 303 { 304 zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); 305 306 opline->result.op_type = IS_TMP_VAR; 307 opline->result.u.var = get_temporary_variable(CG(active_op_array)); 308 opline->opcode = ZEND_PRINT; 309 opline->op1 = *arg; 310 SET_UNUSED(opline->op2); 311 *result = opline->result; 312 } 313 314 315 void zend_do_echo(znode *arg TSRMLS_DC) 316 { 317 zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); 318 319 opline->opcode = ZEND_ECHO; 320 opline->op1 = *arg; 321 SET_UNUSED(opline->op2); 322 }
非常にショボイ。。。直ぐ上に「zend_do_print」関数があるね。
これだけでは情報が少ないので「ZEND_ECHO」のキーワードでディレクトリ内を検索。
% find -type f | xargs egrep -l ZEND_ECHO ./Zend/zend_execute.c ./Zend/zend_compile.c ./Zend/zend_compile.h
手始めに「Zend/zend_execute.c」の中を覗いてみる。
% less -N Zend/zend_execute.c 〜 省略 〜 1040 ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) 1041 { 〜 省略 〜 1097 zend_clean_garbage(TSRMLS_C); 1098 1099 switch(EX(opline)->opcode) { 1100 case ZEND_ADD: 1101 EG(binary_op) = add_function; 1102 goto binary_op_addr; 1103 case ZEND_SUB: 1104 EG(binary_op) = sub_function; 1105 goto binary_op_addr; 1106 case ZEND_MUL: 1107 EG(binary_op) = mul_function; 1108 goto binary_op_addr; 1109 case ZEND_DIV: 1110 EG(binary_op) = div_function; 1111 goto binary_op_addr; 1112 case ZEND_MOD: 1113 EG(binary_op) = mod_function; 1114 goto binary_op_addr; 〜 省略 〜 1263 case ZEND_PRINT: 1264 zend_print_variable(get_zval_ptr(&EX(opline)->op1, EX(Ts), &EG(free_op1), BP_VAR_R)); 1265 EX(Ts)[EX(opline)->result.u.var].tmp_var.value.lval = 1; 1266 EX(Ts)[EX(opline)->result.u.var].tmp_var.type = IS_LONG; 1267 FREE_OP(EX(Ts), &EX(opline)->op1, EG(free_op1)); 1268 NEXT_OPCODE(); 1269 case ZEND_ECHO: 1270 zend_print_variable(get_zval_ptr(&EX(opline)->op1, EX(Ts), &EG(free_op1), BP_VAR_R)); 1271 FREE_OP(EX(Ts), &EX(opline)->op1, EG(free_op1)); 1272 NEXT_OPCODE(); 1273 case ZEND_FETCH_R: 1274 zend_fetch_var_address(EX(opline), EX(Ts), BP_VAR_R TSRMLS_CC); 1275 AI_USE_PTR(EX(Ts)[EX(opline)->result.u.var].var); 1276 NEXT_OPCODE(); 1277 case ZEND_FETCH_W: 1278 zend_fetch_var_address(EX(opline), EX(Ts), BP_VAR_W TSRMLS_CC); 1279 NEXT_OPCODE();
おっ!いきなり大当たりっぽい。(w
1263行目の「ZEND_PRINT」にしても1269行目の「ZEND_ECHO」にしても「zend_print_variable」関数を呼んでいる事が分かります。名前から察するに、間違いないだろう。。。きっと。
という訳で「zend_print_variable」関数をブレーク・ポイントに設定。
% gdb sapi/cli/php (gdb) b zend_print_variable Breakpoint 1 at 0x810692b: file /usr/local/src/php-4.4.4/Zend/zend_variables.c, line 151. (gdb) r -r 'echo "Hello World.\n";' Starting program: /usr/local/src/php-4.4.4/sapi/cli/php -r 'echo "Hello World.\n";' Breakpoint 1, zend_print_variable (var=0x8181bdc) at /usr/local/src/php-4.4.4/Zend/zend_variables.c:151 151 return zend_print_zval(var, 0); (gdb) bt #0 zend_print_variable (var=0x8181bdc) at /usr/local/src/php-4.4.4/Zend/zend_variables.c:151 #1 0x0811a0f2 in execute (op_array=0x81819e4) at /usr/local/src/php-4.4.4/Zend/zend_execute.c:1270 #2 0x080fea77 in zend_eval_string (str=0xbffffbc7 "echo \"Hello World.\\n\";", retval_ptr=0x0, string_name=0x814b254 "Command line code") at /usr/local/src/php-4.4.4/Zend/zend_execute_API.c:647 #3 0x08122e12 in main (argc=3, argv=0xbffffa74) at /usr/local/src/php-4.4.4/sapi/cli/php_cli.c:871 (gdb) q The program is running. Exit anyway? (y or n) y
一応、バックトレースの確認もしておく。
「zend_do_echo」関数をブレークポイントに設定した時は「zend_eval_string」関数の次は「compile_string」関数だったのですが、「zend_print_variable」関数をブレークポイントに設定した時は「zend_eval_string」関数の次は「execute」関数になってますね(その後に「zend_print_variable」関数が登場)。
バックトレースの情報から関数の実体は「Zend/zend_variables.c」の中にある事が分かったので、中身を覗いてみる。
% less -N Zend/zend_variables.c 〜 省略 〜 149 ZEND_API int zend_print_variable(zval *var) 150 { 151 return zend_print_zval(var, 0); 152 }
。。。こっちもショボい。_| ̄|○
めげずに「zend_print_zval」関数をブレーク・ポイントに設定。
% gdb sapi/cli/php (gdb) b zend_print_zval Breakpoint 1 at 0x8106e19: file /usr/local/src/php-4.4.4/Zend/zend.c, line 192. (gdb) r -r 'echo "Hello World.\n";' Starting program: /usr/local/src/php-4.4.4/sapi/cli/php -r 'echo "Hello World.\n";' Breakpoint 1, zend_print_zval (expr=0x8181bdc, indent=0) at /usr/local/src/php-4.4.4/Zend/zend.c:192 192 return zend_print_zval_ex(zend_write, expr, indent); (gdb) bt #0 zend_print_zval (expr=0x8181bdc, indent=0) at /usr/local/src/php-4.4.4/Zend/zend.c:192 #1 0x0810693e in zend_print_variable (var=0x8181bdc) at /usr/local/src/php-4.4.4/Zend/zend_variables.c:151 #2 0x0811a0f2 in execute (op_array=0x81819e4) at /usr/local/src/php-4.4.4/Zend/zend_execute.c:1270 #3 0x080fea77 in zend_eval_string (str=0xbffffbc7 "echo \"Hello World.\\n\";", retval_ptr=0x0, string_name=0x814b254 "Command line code") at /usr/local/src/php-4.4.4/Zend/zend_execute_API.c:647 #4 0x08122e12 in main (argc=3, argv=0xbffffa74) at /usr/local/src/php-4.4.4/sapi/cli/php_cli.c:871 (gdb) q The program is running. Exit anyway? (y or n) y
バックトレースの情報から関数の実体は「Zend/zend.c」の中にある事が分かったので、中身を覗いてみる。
% less -N Zend/zend.c 〜 省略 〜 49 ZEND_API zend_write_func_t zend_write; 〜 省略 〜 142 ZEND_API void zend_make_printable_zval(zval *expr, zval *expr_copy, int *use_copy) 143 { 144 if (expr->type==IS_STRING) { 145 *use_copy = 0; 146 return; 147 } 148 switch (expr->type) { 149 case IS_NULL: 150 expr_copy->value.str.len = 0; 151 expr_copy->value.str.val = empty_string; 152 break; 153 case IS_BOOL: 154 if (expr->value.lval) { 155 expr_copy->value.str.len = 1; 156 expr_copy->value.str.val = estrndup("1", 1); 157 } else { 158 expr_copy->value.str.len = 0; 159 expr_copy->value.str.val = empty_string; 160 } 161 break; 162 case IS_RESOURCE: 163 expr_copy->value.str.val = (char *) emalloc(sizeof("Resource id #")-1 + MAX_LENGTH_OF_LONG); 164 expr_copy->value.str.len = sprintf(expr_copy->value.str.val, "Resource id #%ld", expr->value.lval); 165 break; 166 case IS_ARRAY: 167 expr_copy->value.str.len = sizeof("Array")-1; 168 expr_copy->value.str.val = estrndup("Array", expr_copy->value.str.len); 169 break; 170 case IS_OBJECT: 171 expr_copy->value.str.len = sizeof("Object")-1; 172 expr_copy->value.str.val = estrndup("Object", expr_copy->value.str.len); 173 break; 174 case IS_DOUBLE: 175 *expr_copy = *expr; 176 zval_copy_ctor(expr_copy); 177 zend_locale_sprintf_double(expr_copy ZEND_FILE_LINE_CC); 178 break; 179 default: 180 *expr_copy = *expr; 181 zval_copy_ctor(expr_copy); 182 convert_to_string(expr_copy); 183 break; 184 } 185 expr_copy->type = IS_STRING; 186 *use_copy = 1; 187 } 188 189 190 ZEND_API int zend_print_zval(zval *expr, int indent) 191 { 192 return zend_print_zval_ex(zend_write, expr, indent); 193 } 194 195 196 ZEND_API int zend_print_zval_ex(zend_write_func_t write_func, zval *expr, int indent) 197 { 198 zval expr_copy; 199 int use_copy; 200 201 zend_make_printable_zval(expr, &expr_copy, &use_copy); 202 if (use_copy) { 203 expr = &expr_copy; 204 } 205 if (expr->value.str.len==0) { /* optimize away empty strings */ 206 if (use_copy) { 207 zval_dtor(expr); 208 } 209 return 0; 210 } 211 write_func(expr->value.str.val, expr->value.str.len); 212 if (use_copy) { 213 zval_dtor(expr); 214 } 215 return expr->value.str.len; 216 }
ふーん。ふーん。ふーん。そうですか、にやり。勝利は目前だっ!
「zend_make_printable_zval」関数で気に入らない文字列を成形している事が分かったので、149行目にある「IS_NULL」の条件分岐内容に従って出力したくない箇所に適用してみます。
実際にパッチを作成。
% vi php-4.4.4_Zend-no-output.patch ここから >>--------------------------------------------------------------------- --- Zend/zend.c,orig 2006-01-01 22:46:49.000000000 +0900 +++ Zend/zend.c 2006-10-27 11:43:35.789616350 +0900 @@ -149,27 +149,27 @@ case IS_NULL: expr_copy->value.str.len = 0; expr_copy->value.str.val = empty_string; + zend_error(E_NOTICE,"Can not output NULL"); break; case IS_BOOL: - if (expr->value.lval) { - expr_copy->value.str.len = 1; - expr_copy->value.str.val = estrndup("1", 1); - } else { - expr_copy->value.str.len = 0; - expr_copy->value.str.val = empty_string; - } + expr_copy->value.str.len = 0; + expr_copy->value.str.val = empty_string; + zend_error(E_NOTICE,"Can not output boolean"); break; case IS_RESOURCE: - expr_copy->value.str.val = (char *) emalloc(sizeof("Resource id #")-1 + MAX_LENGTH_OF_LONG); - expr_copy->value.str.len = sprintf(expr_copy->value.str.val, "Resource id #%ld", expr->value.lval); + expr_copy->value.str.val = empty_string; + expr_copy->value.str.len = 0; + zend_error(E_NOTICE,"Can not output Resource"); break; case IS_ARRAY: - expr_copy->value.str.len = sizeof("Array")-1; - expr_copy->value.str.val = estrndup("Array", expr_copy->value.str.len); + expr_copy->value.str.len = 0; + expr_copy->value.str.val = empty_string; + zend_error(E_NOTICE,"Can not output Array"); break; case IS_OBJECT: - expr_copy->value.str.len = sizeof("Object")-1; - expr_copy->value.str.val = estrndup("Object", expr_copy->value.str.len); + expr_copy->value.str.len = 0; + expr_copy->value.str.val = empty_string; + zend_error(E_NOTICE,"Can not output Object"); break; case IS_DOUBLE: *expr_copy = *expr; ---------------------------------------------------------------------<< ここまで
ついでに「NULL」「BOOL」「RESOURCE」「ARRAY」「OBJECT」を出力しようとした場合は「E_NOTICE」を発生させるようにしてみた。
作成したら、パッチを当ててバイナリを再ビルド。
% patch -p0 < php-4.4.4_Zend-no-output.patch patching file Zend/zend.c % make distclean % ./configure --disable-all --enable-debug % make
ビルドが完了したら、再度、処理を実行。
% sapi/cli/php -r ' \ $PTR = fopen( "configure", "r" ); \ echo null; echo "\n"; \ echo false; echo "\n"; \ echo true; echo "\n"; \ echo $PTR; echo "\n"; \ echo new stdClass; echo "\n"; \ echo array( ); echo "\n"; '; 結果 ----
わ〜い!これで、また一つ理解を深める事ができました。めでたし、めでたし。
所要時間は20分くらいですかね。段々と縮まってきたかな?