GDBのコマンド出力をファイルに保存する方法
GDBで実行しているPGのファイルの出力は、リダイレクトなどを 使って簡単にファイルに保存することが可能です。
例えば"helloworld"をただ出力するだけのプログラム の出力結果,"helloworld"という文字列をファイルに保存するのは簡単です。
しかし、GDBのコマンド(メモリダンプなど)を行った時の出力結果を保存するのに少し手間がかかったのでここで紹介したいと思います。
GDBの出力するファイルを指定する
以下のコマンドで出力先ファイルの設定、出力開始を指定できます。
set logging file <ファイル名> set logging on
ファイルへの出力をやめるときは以下のコマンドです
set logging off
http://www.geocities.jp/harddiskdive/gdb/gdb_13.html
ページネーションを無効化する
たくさんメモリダンプした時など、一気に出さずに 続けて出すならEnter押してね~というメッセージが出ます
--Type <return> to continue, or q <return> to quit--- / “debugging --
これはデフォルトでONになっており、邪魔ですが以下のコマンドで無効化できます。
set pagination off
これは.gdbinitファイルに加えたほうがいいかもしれません。
まとめて見ると意外と簡単ですが、これを探し出すのにかなり時間がかかりました。
GDBのコマンド
GDBの動かし方について。
オンラインで試せるGDBがあったので遊び倒して見た。
GDB online Debugger | Compiler - Code, Compile, Run, Debug online C, C++
よく使う(使った)コマンドなどをまとめた。
シンボルファイル関連は後日やりたい。
コマンド | 省略形 | 意味 |
---|---|---|
set disassembly-flavor intel |
- | アセンブラの表示をintelのものにできる |
list [<行数>|<関数名>] |
l |
関数または行数から10行周辺のソースを表示 |
break [<行数>|<関数名>] |
b |
関数、または行数の位置にブレークポイントを置く |
clear [<行数>|<関数名>] |
関数、または行数の位置のブレークポイントを削除する | |
dlete bnums ... |
指定されたブレークポイント番号のブレークポイントを削除する | |
step |
s |
シンボルファイルで紐付いたプログラムを1行実行(関数の中に入る) |
stepi |
si |
単一の機械語を一命令実行 |
next |
n |
シンボルファイルで紐付いたプログラムを1行実行(関数は中に入らない) |
nexti |
ni |
単一の機械語を一命令実行、サブルーチンはリターンするまで実行する |
backtrace |
bt |
関数の呼び出しトレースを表示 |
print <変数名> |
p |
変数の内容を表示。& をつけてアドレスを表示したり* を付けて値参照したりできる。 |
メモリダンプのコマンド
x/<量><単位><表示形式> <アドレス>
起動時に -ex <ファイルまたは''囲いのコマンド>
オプションをつける事で起動時にgdbにコマンドを実行させられる。
自分の場合はqemuで1234ポートでgdbサーバーを起動した後、クライアント側のgdbはgdb -ex 'target remote localhost:1234'
とコマンドを打つ事ですぐさまデバッグが始められる。*1
他メモ
見やすいGDBのUI。良さそう
.gdbinitの設定方法
gdbで初期設定をAT&T記法からIntel記法に変更する方法 | サラリーマンがハッカーを真剣に目指す
*1:もちろん起動時に行いたい長ったらしい処理をファイル外だしにして引数に与えることも可能
QEMUにGDBを繋げてhariboteOSをデバッグする方法
QEMUにGDBを繋げてhariboteOSを調査したいと思います。
とりあえず初期設定っぽいことはできました。
主に観測したい事
ブレイクポイントを適当に設定して以下のものを観測したい
QEMU × GDBについて
https://en.wikibooks.org/wiki/QEMU/Debugging_with_QEMU
-S -gdb stdio
:使えなかった-S -gdb tcp::9000
:使えた-s -S
:使えた
-S
はデバッガのコマンドまでCPUを起動させないオプション。-s
は-gdb tcp::1234.
の意味
z_tools/qemu/Makefile
でgdbを使うように-s -S
を書き足す
編集前
QEMU = qemu-system-i386 QEMU_ARGS = -L . -m 32 -localtime -vga std -fda fdimage0.bin -monitor stdio default: $(QEMU) $(QEMU_ARGS)
編集後
QEMU = qemu-system-i386 QEMU_ARGS = -L . -m 32 -localtime -vga std -fda fdimage0.bin -monitor stdio -s -S default: $(QEMU) $(QEMU_ARGS)
この状態でmake run. すると画面が黒い状態で止まる(-SオプションでCPUを止めているため)
別ウインドウを開き、gdb起動後にコンソールに以下を打ち込む
target remote localhost:1234
デバッガが接続される。この状況でcontinue
と打ち込むと...
CPUが動きだしhariboteOSが起動する(break pointをまだ設定していないため特にデバッグっぽいことはできない)
主に観測したい事に関しては次回以降。。。
- 作者: 川合秀実
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2006/03/01
- メディア: 単行本
- 購入: 36人 クリック: 735回
- この商品を含むブログ (299件) を見る
C言語復習ラスト。ライブラリについて
ライブラリは今までよく知らなかったので、復習ではない気がする。
ライブラリとは
このプロセスの中の .o
ファイルをまとめてライブラリを作ることができる
$ ar -r mylib.a a.o b.o ar: creating archive mylib.a $ ls -alt -rw-r--r-- 1 staff 1768 7 17 00:07 mylib.a -rw-r--r-- 1 staff 748 7 16 19:13 b.o -rw-r--r--@ 1 staff 748 7 16 19:13 a.o $ ar -t mylib.a __.SYMDEF SORTED a.o b.o
- 関数を使うにはプロトタイプ宣言が必要
#include<stdio.h>
とmain関数の間にある関数の宣言のやつ- main関数の手前に関数を定義してしまえばプロトタイプ宣言を書かなくて良いのではないだろうか?(手抜き)
- この方法を採用すると、呼び出し関係を把握して、関数の依存性を整理して順番に定義する必要がある(非常に手間)
- main関数の手前に関数を定義してしまえばプロトタイプ宣言を書かなくて良いのではないだろうか?(手抜き)
#include<stdio.h> //このプロトタイプ宣言を入れとけば関数の依存関係は気にしなくて良くなる。 //void a(); //void b(); void a(){ b();//関数bを呼び出しているが、この呼び出しより上に定義されていないためコンパイルエラー } void b(){ puts("test"); } int main(){ a(); }
コンパイルエラー other/prototype.c:4:3: warning: implicit declaration of function 'b' is invalid in C99 [-Wimplicit-function-declaration] b(); ^ other/prototype.c:6:6: error: conflicting types for 'b' void b(){ ^ other/prototype.c:4:3: note: previous implicit declaration is here b(); ^
- 少し規模の大きいプログラムなら、プロトタイプ宣言としてmain関数の前にとりあえず宣言しておいたほうが楽。
- というわけでC言語では慣例的にプロトタイプ宣言をすることになっている
- もう一度基礎からC言語 第15回 関数の宣言~ライブラリとヘッダファイル プロトタイプ宣言の省略
- ライブラリを利用するたびに自分で書くのは面倒なのでヘッダファイル
.h
を作ってプリプロセッサに書かせる
mylib.hの内容
void a(); void b();
libtest.cの内容
#include "mylib.h" main() { a(); b(); }
- コンパイルコマンド
$ gcc libtest.c mylib.a
もちろんライブラリを利用するプログラムは.h
ファイルを展開するプリプロセッサを使わなくても
自分でライブラリの中の関数をプロトタイプ宣言に追記すれば利用することができる。ただし、こういうことは面倒なので、基本的には.h
ファイルを用意して#include
すべき
void a(); void b(); main() { a(); b(); }
#include
プリプロセッサの利用有無による結果の違い
gcc -E
コマンドの結果も大して変わらない
#include
のプリプロセッサ利用
$ gcc -E libtest.c mylib.a clang: warning: mylib.a: 'linker' input unused [-Wunused-command-line-argument] # 1 "libtest.c" # 1 "<built-in>" 1 # 1 "<built-in>" 3 # 341 "<built-in>" 3 # 1 "<command line>" 1 # 1 "<built-in>" 2 # 1 "libtest.c" 2 # 1 "./mylib.h" 1 void a(); void b(); # 2 "libtest.c" 2 main() { a(); b(); }
#include
のプリプロセッサ不使用
$ gcc -E libtest.c mylib.a clang: warning: mylib.a: 'linker' input unused [-Wunused-command-line-argument] # 1 "libtest.c" # 1 "<built-in>" 1 # 1 "<built-in>" 3 # 341 "<built-in>" 3 # 1 "<command line>" 1 # 1 "<built-in>" 2 # 1 "libtest.c" 2 void a(); void b(); main() { a(); b(); }
まとめ
ライブラリを作ったなら.h
ファイルも忘れず作ろう!
おしまい。
プログラミング学習シリーズ C言語改訂版 2 はじめて学ぶCの仕組み
- 作者: 倉薫
- 出版社/メーカー: 翔泳社
- 発売日: 2009/02/13
- メディア: 大型本
- 購入: 2人 クリック: 6回
- この商品を含むブログ (3件) を見る
C言語復習その6。プリプロセッサ、リンカについて
本書ラストのライブラリまで行けると思ったが、時間がなくなった。 もっと有意義に3連休を過ごせなかったのだろうか。(反省)
プログラミング学習シリーズ C言語改訂版 2 はじめて学ぶCの仕組み
- 作者: 倉薫
- 出版社/メーカー: 翔泳社
- 発売日: 2009/02/13
- メディア: 大型本
- 購入: 2人 クリック: 6回
- この商品を含むブログ (3件) を見る
ソースコードから実行ファイル(exe,elf)などができるまで
の処理で処理される。
- コンパイラは自動的に1,3の処理を呼んでいる
- 本書の想定だとそれぞれcpp.exe,gcc.exe,ld.exeを使っているようだ
- cppってC++用のコンパイラだと思っていたのだが...
- C Preprocessorの略らしい
プリプロセッサ
元ソース
#include<stdio.h> //defineで値を定義 #define bignumber 1000 //マクロを定義 #define max(a,b) (a >b)? a: b int main(){ int a = bignumber; printf("%d is larger\n", max(100,10)); printf("%d\n",a); printf("%d\n",bignumber); }
gcc -Eの出力(※stdio.hの展開が巨大すぎるのでそれは略している)
・・・(stdio.hの展開略) int main(){ int a = 1000; printf("%d is larger\n", (100 >10)? 100: 10); printf("%d\n",a); printf("%d\n",1000); }
- マクロの注意点
- 関数と違って「型」の情報がない
- 演算子の優先順位でハマる可能性がある
- 例えば
int a=5,b=4; printf("%d\n",max(a, ++b));//6が表示される
- これは++bが2回行われるため
- 以下はプリプロセッサの処理結果
int a=5,b=4; printf("%d\n",(a >++b)? a: ++b);
- 例2:以下は大きい方に+100された値を期待しているが。。。
printf("larger + 100=%d\n",max(20,10)+100);
のソースがprintf("larger + 100=%d\n",(20 >10)? 20: 10 +100);
と展開される- この3項演算子は
(20>10)?10:110
となるため評価結果は20
となる
- この3項演算子は
- 出力結果は
larger + 100=20
となる。
undef
で#define
で定義した値を取り下げる- 例
# define max 5 printf("%d\n",max); # undef max #define max 10 printf("%d\n",max);
- `gcc -E`で以下のように展開される
printf("%d\n",5); printf("%d\n",10);
- ifdefでdefineの定義に応じてコードを切り替えられる
- 昔nmapのソースを読もうと思ったことがあったが、コードに
#ifdef DEBUG
みたいなのを散見した - 多分テストやデバッグ時のコードをプロダクションのバイナリに含まされなくない、というようなことをやっている
- 昔nmapのソースを読もうと思ったことがあったが、コードに
- 組み込みマクロというものもある。
リンカ
- ソース -> 実行ファイルの変換の大トリのやつ。
- コンパイラは
.c
ファイルから.o
ファイルにコンパイルするところまでしかやらない。 - 実際に
.o
を組み合わせて実行ファイルを作っているのはリンカ。 .o
ファイルは実行ファイル作成時に消されてしまう。- gcc に-cオプションをつけることで、リンカの処理を実行しないようにできる
リンカ適応度前適応後のバイナリを比べてみる
gcc test.c a.c b.c -o test
gcc -c test.c a.c b.c
$ ls -alt total 72 -rw-r--r-- 1 staff 748 7 16 19:13 b.o -rw-r--r-- 1 staff 748 7 16 19:13 a.o -rw-r--r-- 1 staff 672 7 16 19:13 test.o -rwxr-xr-x 1 staff 8488 7 16 19:13 test
test.o:8488Byte
のサイズはa.o + b.o + test.o = 2168Byte
を大きくうわ待っている
$ hexdump -C test 00000000 cf fa ed fe 07 00 00 01 03 00 00 80 02 00 00 00 |................| 00000010 0f 00 00 00 b0 04 00 00 85 00 20 00 00 00 00 00 |.......... .....| 00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE| 00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............| 00000040 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000060 00 00 00 00 00 00 00 00 19 00 00 00 d8 01 00 00 |................| 00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| 00000080 00 00 00 00 01 00 00 00 00 10 00 00 00 00 00 00 |................| 00000090 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 |................| 000000a0 07 00 00 00 05 00 00 00 05 00 00 00 00 00 00 00 |................| 000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| 000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| 000000d0 20 0f 00 00 01 00 00 00 5d 00 00 00 00 00 00 00 | .......].......| 000000e0 20 0f 00 00 04 00 00 00 00 00 00 00 00 00 00 00 | ...............| 000000f0 00 04 00 80 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000100 5f 5f 73 74 75 62 73 00 00 00 00 00 00 00 00 00 |__stubs.........| 00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| 00000120 7e 0f 00 00 01 00 00 00 06 00 00 00 00 00 00 00 |~...............| 00000130 7e 0f 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |~...............| 00000140 08 04 00 80 00 00 00 00 06 00 00 00 00 00 00 00 |................| 00000150 5f 5f 73 74 75 62 5f 68 65 6c 70 65 72 00 00 00 |__stub_helper...| 00000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| 00000170 84 0f 00 00 01 00 00 00 1a 00 00 00 00 00 00 00 |................| 00000180 84 0f 00 00 02 00 00 00 00 00 00 00 00 00 00 00 |................| 00000190 00 04 00 80 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001a0 5f 5f 63 73 74 72 69 6e 67 00 00 00 00 00 00 00 |__cstring.......| 000001b0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| 000001c0 9e 0f 00 00 01 00 00 00 14 00 00 00 00 00 00 00 |................| 000001d0 9e 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001e0 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001f0 5f 5f 75 6e 77 69 6e 64 5f 69 6e 66 6f 00 00 00 |__unwind_info...| 00000200 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| 00000210 b4 0f 00 00 01 00 00 00 48 00 00 00 00 00 00 00 |........H.......| 00000220 b4 0f 00 00 02 00 00 00 00 00 00 00 00 00 00 00 |................| 00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000240 19 00 00 00 e8 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| 00000250 00 00 00 00 00 00 00 00 00 10 00 00 01 00 00 00 |................| 00000260 00 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 |................| 00000270 00 10 00 00 00 00 00 00 07 00 00 00 03 00 00 00 |................| 00000280 02 00 00 00 00 00 00 00 5f 5f 6e 6c 5f 73 79 6d |........__nl_sym| 00000290 62 6f 6c 5f 70 74 72 00 5f 5f 44 41 54 41 00 00 |bol_ptr.__DATA..| 000002a0 00 00 00 00 00 00 00 00 00 10 00 00 01 00 00 00 |................| 000002b0 10 00 00 00 00 00 00 00 00 10 00 00 03 00 00 00 |................| 000002c0 00 00 00 00 00 00 00 00 06 00 00 00 01 00 00 00 |................| 000002d0 00 00 00 00 00 00 00 00 5f 5f 6c 61 5f 73 79 6d |........__la_sym| 000002e0 62 6f 6c 5f 70 74 72 00 5f 5f 44 41 54 41 00 00 |bol_ptr.__DATA..| 000002f0 00 00 00 00 00 00 00 00 10 10 00 00 01 00 00 00 |................| 00000300 08 00 00 00 00 00 00 00 10 10 00 00 03 00 00 00 |................| 00000310 00 00 00 00 00 00 00 00 07 00 00 00 03 00 00 00 |................| 00000320 00 00 00 00 00 00 00 00 19 00 00 00 48 00 00 00 |............H...| 00000330 5f 5f 4c 49 4e 4b 45 44 49 54 00 00 00 00 00 00 |__LINKEDIT......| 00000340 00 20 00 00 01 00 00 00 00 10 00 00 00 00 00 00 |. ..............| 00000350 00 20 00 00 00 00 00 00 28 01 00 00 00 00 00 00 |. ......(.......| 00000360 07 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000370 22 00 00 80 30 00 00 00 00 20 00 00 08 00 00 00 |"...0.... ......| 00000380 08 20 00 00 18 00 00 00 00 00 00 00 00 00 00 00 |. ..............| 00000390 20 20 00 00 10 00 00 00 30 20 00 00 40 00 00 00 | ......0 ..@...| 000003a0 02 00 00 00 18 00 00 00 78 20 00 00 06 00 00 00 |........x ......| 000003b0 e8 20 00 00 40 00 00 00 0b 00 00 00 50 00 00 00 |. ..@.......P...| 000003c0 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 |................| 000003d0 04 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 |................| 000003e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000003f0 d8 20 00 00 04 00 00 00 00 00 00 00 00 00 00 00 |. ..............| 00000400 00 00 00 00 00 00 00 00 0e 00 00 00 20 00 00 00 |............ ...| 00000410 0c 00 00 00 2f 75 73 72 2f 6c 69 62 2f 64 79 6c |..../usr/lib/dyl| 00000420 64 00 00 00 00 00 00 00 1b 00 00 00 18 00 00 00 |d...............| 00000430 01 a0 6d a0 dc d8 3b 93 b9 e4 c3 d8 18 65 02 c8 |..m...;......e..| 00000440 24 00 00 00 10 00 00 00 00 0d 0a 00 00 0d 0a 00 |$...............| 00000450 2a 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 |*...............| 00000460 28 00 00 80 18 00 00 00 20 0f 00 00 00 00 00 00 |(....... .......| 00000470 00 00 00 00 00 00 00 00 0c 00 00 00 38 00 00 00 |............8...| 00000480 18 00 00 00 02 00 00 00 04 32 e4 04 00 00 01 00 |.........2......| 00000490 2f 75 73 72 2f 6c 69 62 2f 6c 69 62 53 79 73 74 |/usr/lib/libSyst| 000004a0 65 6d 2e 42 2e 64 79 6c 69 62 00 00 00 00 00 00 |em.B.dylib......| 000004b0 26 00 00 00 10 00 00 00 70 20 00 00 08 00 00 00 |&.......p ......| 000004c0 29 00 00 00 10 00 00 00 78 20 00 00 00 00 00 00 |).......x ......| 000004d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000f20 55 48 89 e5 b0 00 e8 15 00 00 00 b0 00 e8 2e 00 |UH..............| 00000f30 00 00 31 c0 5d c3 90 90 90 90 90 90 90 90 90 90 |..1.]...........| 00000f40 55 48 89 e5 48 83 ec 10 48 8d 3d 4f 00 00 00 e8 |UH..H...H.=O....| 00000f50 2a 00 00 00 89 45 fc 48 83 c4 10 5d c3 90 90 90 |*....E.H...]....| 00000f60 55 48 89 e5 48 83 ec 10 48 8d 3d 38 00 00 00 e8 |UH..H...H.=8....| 00000f70 0a 00 00 00 89 45 fc 48 83 c4 10 5d c3 90 ff 25 |.....E.H...]...%| 00000f80 8c 00 00 00 4c 8d 1d 7d 00 00 00 41 53 ff 25 6d |....L..}...AS.%m| 00000f90 00 00 00 90 68 00 00 00 00 e9 e6 ff ff ff 61 20 |....h.........a | 00000fa0 63 61 6c 6c 65 64 00 62 28 29 20 63 61 6c 6c 65 |called.b() calle| 00000fb0 64 00 00 00 01 00 00 00 1c 00 00 00 00 00 00 00 |d...............| 00000fc0 1c 00 00 00 00 00 00 00 1c 00 00 00 02 00 00 00 |................| 00000fd0 20 0f 00 00 34 00 00 00 34 00 00 00 7e 0f 00 00 | ...4...4...~...| 00000fe0 00 00 00 00 34 00 00 00 03 00 00 00 0c 00 01 00 |....4...........| 00000ff0 10 00 01 00 00 00 00 00 00 00 00 01 00 00 00 00 |................| 00001000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00001010 94 0f 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00001020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00002000 11 22 10 51 00 00 00 00 11 40 64 79 6c 64 5f 73 |.".Q.....@dyld_s| 00002010 74 75 62 5f 62 69 6e 64 65 72 00 51 72 00 90 00 |tub_binder.Qr...| 00002020 72 10 11 40 5f 70 75 74 73 00 90 00 00 00 00 00 |r..@_puts.......| 00002030 00 01 5f 00 05 00 04 5f 6d 68 5f 65 78 65 63 75 |.._...._mh_execu| 00002040 74 65 5f 68 65 61 64 65 72 00 27 6d 61 69 6e 00 |te_header.'main.| 00002050 2b 61 00 30 62 00 35 02 00 00 00 03 00 a0 1e 00 |+a.0b.5.........| 00002060 03 00 c0 1e 00 03 00 e0 1e 00 00 00 00 00 00 00 |................| 00002070 a0 1e 20 20 00 00 00 00 02 00 00 00 0f 01 10 00 |.. ............| 00002080 00 00 00 00 01 00 00 00 16 00 00 00 0f 01 00 00 |................| 00002090 40 0f 00 00 01 00 00 00 19 00 00 00 0f 01 00 00 |@...............| 000020a0 60 0f 00 00 01 00 00 00 1c 00 00 00 0f 01 00 00 |`...............| 000020b0 20 0f 00 00 01 00 00 00 22 00 00 00 01 00 00 01 | .......".......| 000020c0 00 00 00 00 00 00 00 00 28 00 00 00 01 00 00 01 |........(.......| 000020d0 00 00 00 00 00 00 00 00 04 00 00 00 05 00 00 00 |................| 000020e0 00 00 00 40 04 00 00 00 20 00 5f 5f 6d 68 5f 65 |...@.... .__mh_e| 000020f0 78 65 63 75 74 65 5f 68 65 61 64 65 72 00 5f 61 |xecute_header._a| 00002100 00 5f 62 00 5f 6d 61 69 6e 00 5f 70 75 74 73 00 |._b._main._puts.| 00002110 64 79 6c 64 5f 73 74 75 62 5f 62 69 6e 64 65 72 |dyld_stub_binder| 00002120 00 00 00 00 00 00 00 00 |........|
$ hexdump -C test.o 00000000 cf fa ed fe 07 00 00 01 03 00 00 00 01 00 00 00 |................| 00000010 04 00 00 00 b0 01 00 00 00 20 00 00 00 00 00 00 |......... ......| 00000020 19 00 00 00 38 01 00 00 00 00 00 00 00 00 00 00 |....8...........| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000040 78 00 00 00 00 00 00 00 d0 01 00 00 00 00 00 00 |x...............| 00000050 78 00 00 00 00 00 00 00 07 00 00 00 07 00 00 00 |x...............| 00000060 03 00 00 00 00 00 00 00 5f 5f 74 65 78 74 00 00 |........__text..| 00000070 00 00 00 00 00 00 00 00 5f 5f 54 45 58 54 00 00 |........__TEXT..| 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000090 16 00 00 00 00 00 00 00 d0 01 00 00 04 00 00 00 |................| 000000a0 48 02 00 00 02 00 00 00 00 04 00 80 00 00 00 00 |H...............| 000000b0 00 00 00 00 00 00 00 00 5f 5f 63 6f 6d 70 61 63 |........__compac| 000000c0 74 5f 75 6e 77 69 6e 64 5f 5f 4c 44 00 00 00 00 |t_unwind__LD....| 000000d0 00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00 |................| 000000e0 20 00 00 00 00 00 00 00 e8 01 00 00 03 00 00 00 | ...............| 000000f0 58 02 00 00 01 00 00 00 00 00 00 02 00 00 00 00 |X...............| 00000100 00 00 00 00 00 00 00 00 5f 5f 65 68 5f 66 72 61 |........__eh_fra| 00000110 6d 65 00 00 00 00 00 00 5f 5f 54 45 58 54 00 00 |me......__TEXT..| 00000120 00 00 00 00 00 00 00 00 38 00 00 00 00 00 00 00 |........8.......| 00000130 40 00 00 00 00 00 00 00 08 02 00 00 03 00 00 00 |@...............| 00000140 00 00 00 00 00 00 00 00 0b 00 00 68 00 00 00 00 |...........h....| 00000150 00 00 00 00 00 00 00 00 24 00 00 00 10 00 00 00 |........$.......| 00000160 00 0d 0a 00 00 00 00 00 02 00 00 00 18 00 00 00 |................| 00000170 60 02 00 00 03 00 00 00 90 02 00 00 10 00 00 00 |`...............| 00000180 0b 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00 |....P...........| 00000190 00 00 00 00 01 00 00 00 01 00 00 00 02 00 00 00 |................| 000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000001d0 55 48 89 e5 b0 00 e8 00 00 00 00 b0 00 e8 00 00 |UH..............| 000001e0 00 00 31 c0 5d c3 00 00 00 00 00 00 00 00 00 00 |..1.]...........| 000001f0 16 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 |................| 00000200 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 |................| 00000210 01 7a 52 00 01 78 10 01 10 0c 07 08 90 01 00 00 |.zR..x..........| 00000220 24 00 00 00 1c 00 00 00 a8 ff ff ff ff ff ff ff |$...............| 00000230 16 00 00 00 00 00 00 00 00 41 0e 10 86 02 43 0d |.........A....C.| 00000240 06 00 00 00 00 00 00 00 0e 00 00 00 02 00 00 2d |...............-| 00000250 07 00 00 00 01 00 00 2d 00 00 00 00 01 00 00 06 |.......-........| 00000260 01 00 00 00 0f 01 00 00 00 00 00 00 00 00 00 00 |................| 00000270 0a 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000280 07 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000290 00 5f 6d 61 69 6e 00 5f 62 00 5f 61 00 00 00 00 |._main._b._a....| 000002a0
ふむふむなるほど(無能)
結局意味がわからないので特に何も書くことがない。 とりあえずcatで連結するような単純なものではないらしい。
感想
リンカは結構単純な置換をやっているだけに見えるが、 ruiさんが
Cプリプロセッサの実装はかなり手間がかかる。
と書かれていたり
コンパイラの入門書で作成対象外になっていたりなどで
ふつうのコンパイラをつくろう 言語処理系をつくりながら学ぶコンパイルと実行環境の仕組み
- 作者: 青木峰郎
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2017/03/30
- メディア: Kindle版
- この商品を含むブログを見る
本書では、この4つの段階のうちプリプロセスを除いた、コンパイル・アセンブル・リンクについてお話しします(普通のコンパイラを作ろうp8より)
作るとなると意外と難しいものなのかもしれない。
C言語の復習その6メモリについて
メモリ管理について学びました。malloc,free,reallocなど
コンパイルするときに、知識不足によるコンパイルエラーがかなり減りました。*1 またコンパイルエラー時に、何が間違っているかすぐわかるようになり、なぜ動いているのか・動かないのかわからないというソースがかなり減りました。
mallocについて
ローカル変数との違い
- ローカル変数は確保できる合計サイズに限りがある
- mallocの方が柔軟。
- staticをつけた変数はデータセグメントに格納される
- ではローカル変数は?
- スタック領域に確保されます
- ある程度の領域を抑えてスタックのベースアドレスから、
- 下に伸びる
- 上に伸びる
- みたいな感じで空き容量を埋めていく感じです。
- ある程度の領域を抑えてスタックのベースアドレスから、
- スタック領域に確保されます
mallocではヒープ領域にメモリを確保する
- heapの上限はない?
この「ヒープ領域」には上限が決められていません。p223より
- スタックはコンパイル、リンク時に大きさが決まるらしい。
- heapの上限はない?
mallocで戻ってきたアドレスを紛失しないこと
ローカル変数でmallocの返値アドレスを確保して、スコープを抜けるなどで噴出すると、 プログラム実行終了までに確保したアドレスは回収されない。
javaのようなメソッドエリア(静的領域とも)からの参照がなくなった参照を自動的に解放対象にする(ガベージコレクション)のような仕組みはない。
対策としては、
- アドレスを管理している変数を関数の返値として呼び出し元に返す
- グローバル変数で管理する
などがある。
free()について
- mallocで確保した引数を与えるとそこを解放する
void free(void *ptr);
- 宣言を見る限り
void*
->char
のキャストはもちろんできるが、char
->void*
のキャストも可能なのか。
- mallocで確保していないアドレスや解放済みのアドレスをfree()に渡すと実行時エラーになる
- コンパイル時にわかりそうなもんだが。。。
p = (char *) malloc(strlen(str)+1); void* vp = (void*) p; free(p); //free(vp); // 間違えて、同じアドレスを指している変数を2回freeしてしまった。 // その時のエラーを記念に残しておく // a.out(20725,0x7fffad00f380) malloc: *** error for object 0x7f8ebd402710: pointer being freed was not allocated // *** set a breakpoint in malloc_error_break to debug // Abort trap: 6 char localv = 'c'; //free(&localv); //ローカル変数をfreeした結果。 // a.out(20787,0x7fffad00f380) malloc: *** error for object 0x7ffee5ed7ab7: pointer being freed was not allocated // *** set a breakpoint in malloc_error_break to debug // Abort trap: 6
realloc
void *realloc(void *ptr, size_t size);
char *str1 = "hello"; char *str2 = " world"; p = malloc(strlen(str1)+1); strcpy(p,str1); puts(p);//hello //なぜか入れても抜いても同じ結果になる。 //p = realloc(p,strlen(str1)+ strlen(str2) +1); strcat(p,str2); puts(p);//hello world; free(p);
- 確保した領域の後ろのアドレスが空で、たまたま使えていたということ?
保証されていない動作?ということだろうか。
mallocを2回続けて、1回目の領域に大きなデータを書いてみたが、バグらせることができなかった。
reallocやらないとどうなるか。
複数回連続でmallocをして確保したアドレスを
printf()
してみたが、アドレスは綺麗に順番で確保されているわけではないようだ以下は複数char型のポインタ変数のアドレスを確保して実験したソース
char *vp1 = malloc(1); char *vp2 = malloc(1); char *vp3 = malloc(1); char *vp4 = malloc(1); char *vp5 = malloc(1); char *vp6 = malloc(1); //1バイトしか確保していない領域に複数バイト分の文字を書き込み //文字列リテラルはそれ自体が静的データへのアドレスなので以下のコードは実験としてふさわしくない //vp1 = "test"; strcpy(vp1,"testtesttesttesttesttesttesttest"); strcpy(vp2,"computercomputercomputercomputer"); strcpy(vp3,"abcdeabcdeabcdeabcdeabcdeabcde"); strcpy(vp4,"englishenglishenglishenglishenglish"); strcpy(vp5,"boyboyboyboyboyboyboyboyboyboy"); strcpy(vp6,"girlgirlgirlgirlgirlgirlgirlgirlgirl"); puts(vp1); puts(vp2); puts(vp3); puts(vp4); puts(vp5); puts(vp6); //結果:データ破壊ができた // bcdeabcdeabcde // computercomputerabcdeabcdeabcdeabcdeabcdeabcde // abcdeabcdeabcdeabcdeabcdeabcde // englishenglishenboyboyboyboyboybgirlgirlgirlgirlgirlgirlgirlgirlgirl // boyboyboyboyboybgirlgirlgirlgirlgirlgirlgirlgirlgirl // girlgirlgirlgirlgirlgirlgirlgirlgirl printf("vp1 addr is %p\n",vp1); printf("vp1 value is %c\n",*vp1); printf("vp2 addr is %p\n",vp2); printf("vp2 value is %c\n",*vp2); printf("vp3 addr is %p\n",vp3); printf("vp3 value is %c\n",*vp3); printf("vp4 addr is %p\n",vp4); printf("vp4 value is %c\n",*vp4); printf("vp5 addr is %p\n",vp5); printf("vp5 value is %c\n",*vp5); printf("vp6 addr is %p\n",vp6); printf("vp6 value is %c\n",*vp6); //出力 // vp1 addr is 0x7fc072402730 // vp1 value is b // vp2 addr is 0x7fc072402710 // vp2 value is c // vp3 addr is 0x7fc072402720 // vp3 value is a // vp4 addr is 0x7fc072402750 // vp4 value is e // vp5 addr is 0x7fc072402760 // vp5 value is b // vp6 addr is 0x7fc072402770 // vp6 value is g
- 値の欠損、上書きなどが起こったが、想定したものと微妙に違う。
- mallocは10バイト間隔を開けてアドレスを確保している
- mallocが確保するアドレスはアドレスの並び順通りではない
- 上の例だと
vp1 addr is 0x7fc072402730
vp2 addr is 0x7fc072402710
vp3 addr is 0x7fc072402720
vp4 addr is 0x7fc072402750
- 最近のコンパイラはバッファオーバーフロー対策でこういう細工がしてあるということを少し聞きかじったことがあるが、もしかしてそれなのでしょうか。
- コンパイラのオプションでこの細工を無効化できたはずだが。
realloc実践
確保した領域より大きい文字列を書き込む前に、reallocすることで適切な動作を実現できた。
char *vp1 = malloc(1); char *vp2 = malloc(1); char *vp3 = malloc(1); char *vp4 = malloc(1); char *vp5 = malloc(1); char *vp6 = malloc(1); char* str1 = "testtesttesttesttesttesttesttest"; char* str2 = "computercomputercomputercomputer"; char* str3 = "abcdeabcdeabcdeabcdeabcdeabcde"; char* str4 = "englishenglishenglishenglishenglish"; char* str5 = "boyboyboyboyboyboyboyboyboyboy"; char* str6 = "girlgirlgirlgirlgirlgirlgirlgirlgirl"; vp1 = realloc(vp1,strlen(str1)+1); vp2 = realloc(vp2,strlen(str2)+1); vp3 = realloc(vp3,strlen(str3)+1); vp4 = realloc(vp4,strlen(str4)+1); vp5 = realloc(vp5,strlen(str5)+1); vp6 = realloc(vp6,strlen(str6)+1); strcpy(vp1,str1); strcpy(vp2,str2); strcpy(vp3,str3); strcpy(vp4,str4); strcpy(vp5,str5); strcpy(vp6,str6); puts(vp1); puts(vp2); puts(vp3); puts(vp4); puts(vp5); puts(vp6); printf("vp1 addr is %p\n",vp1); printf("vp1 value is %c\n",*vp1); printf("vp2 addr is %p\n",vp2); printf("vp2 value is %c\n",*vp2); printf("vp3 addr is %p\n",vp3); printf("vp3 value is %c\n",*vp3); printf("vp4 addr is %p\n",vp4); printf("vp4 value is %c\n",*vp4); printf("vp5 addr is %p\n",vp5); printf("vp5 value is %c\n",*vp5); printf("vp6 addr is %p\n",vp6); printf("vp6 value is %c\n",*vp6); //出力 // testtesttesttesttesttesttesttest // computercomputercomputercomputer // abcdeabcdeabcdeabcdeabcdeabcde // englishenglishenglishenglishenglish // boyboyboyboyboyboyboyboyboyboy // girlgirlgirlgirlgirlgirlgirlgirlgirl // vp1 addr is 0x7fa623402770 // vp1 value is t // vp2 addr is 0x7fa6234027a0 // vp2 value is c // vp3 addr is 0x7fa6234027d0 // vp3 value is a // vp4 addr is 0x7fa623402810 // vp4 value is e // vp5 addr is 0x7fa6234027f0 // vp5 value is b // vp6 addr is 0x7fa623402710 // vp6 value is g
void型のポインタ
- mallocの結果はvoid型のポインタとして帰ってくる
void *malloc(size_t size);
- これは任意のポインタ型に代入(暗黙の型変換を伴う)可能になっている
- もちろん明示的にキャストすることも可能
char *p; char *str = "Getting memories!!"; p = (char *) malloc(strlen(str)+1); //キャストしなくてもOK //p = malloc(strlen(str)+1); void* vp = (void*) p; printf("void pointer type size is %d\n",sizeof vp); strcpy(p,str); puts(p); //vp型のポインタ変数もputsに渡せるようだ。 puts(vp); //putsは引数にchar型のポインタをとるので、渡った先で暗黙の型変換がされているものと思われる //int puts(char * s);
Linuxのmallocとmmapについて
liunuxではmalloc()
は以下のような動作になっている
- mmap()関数で似たようなことができる
Linuxの仕組みより
[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識
- 作者: 武内覚
- 出版社/メーカー: 技術評論社
- 発売日: 2018/02/23
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
hariboteOSでは?
memmann_alloc(カーネル用のmalloc)
こちらは MEMMAN_ADDRというmammann_allocで割り当てる領域の開始アドレスがdefineで(0x003c0000)に指定されています。
api_malloc(アプリ用のmalloc)
mallocで割り当てるアドレスがどこから始まるかをhariboteOS用の実行ファイル.hrbファイルに書きこまれています。 あまり自信がありませんが、これはデータセグメントのベースからのオフセットアドレスだと思います。
感想
メモリリークは大変だと思うが、 発生した際に時間をかければ、問題箇所の調査ぐらいはできるくらいの知識はついた気がする。
今まではメモリリークの調査など絶対できそうになかったが、最近は結構自信がついてきた。(まだやったことないけど)
ただし、調査に時間はすごくかかりそうだ。
プログラミング学習シリーズ C言語改訂版 2 はじめて学ぶCの仕組み
- 作者: 倉薫
- 出版社/メーカー: 翔泳社
- 発売日: 2009/02/13
- メディア: 大型本
- 購入: 2人 クリック: 6回
- この商品を含むブログ (3件) を見る
C言語の復習その5。関数ポインタについて
関数ポインタは「宣言方法」と「引数、返値があっていない適当に実装されたコードを読む」のがややこしいが、綺麗に書けばそこまで難解ではない。という認識です。
関数ポインタについて
- 宣言
void(*p)()
- 引数付き
void(*p)(char *str)
- 引数付き
*p
を囲ってる()
が若干気持ち悪い
引数や戻り値は暗黙の型変換が絡んで来るとややこしい
この記事を書いた当初、gccのコンパイルオプションを-W -Wall
と書くべきところを-w -Wall
と書いていまい、
警告を抑制するという馬鹿なことをやってしまいました。
※警告を最大限に出すようにオプション(-W -Wall
)を与えて*1、悪いプログラミングの癖は強制するべきだと思っています。
//引数なし、返値なしの関数の場合 void (*fp)(void) = func1;//この宣言代入がもっともふさわしい fp(); fp(2);//エラー void型の関数の引数にintを渡したため //関数ポインタの型を不適切なものにする int(*fp2)(int a) = func1;//警告がでる //関数ポインタの宣言時の引数の個数にふさわしく無い呼び出しをすると、 //fp2();//コンパイルエラー // error: too few arguments to function call, expected 1, // have 0 // fp2(); //関数ポインタの宣言時の引数にふさわしい引数を与えているが、代入した関数には引数はない //void func1(); //一見不正なことをしているように見えるが、そもそも引数の指定が無い関数には引数を与えて呼び出すこともできる func1(1);//ただし警告は出る // other/typeConvert.c:44:12: warning: too many arguments in call to 'func1' // func1(1); // ~~~~~ ^ //この挙動が気に入らないなら、関数ポインタの引数としてvoidを定義すれば良い。 //ちなみに関数ポインタの宣言で引数が決められている場合は、代入する関数の引数がvoidであっても、 //引数を与えて呼び出すことができてしまう。 //void voidf(void) void (*fp3)(int a) = voidf;//ただしこの代入は警告が出る // warning: incompatible pointer types initializing // 'void (*)(int)' with an expression of type 'void (void)' // [-Wincompatible-pointer-types] // void (*fp3)(int a) = voidf; // ^ ~~~~~ fp3(1); //防ぐには代入する関数の引数をvoidにして、関数ポインタの引数もvoidに、そしてコンパイラの警告オプションを最大にすることが重要。 ・・・ //代入されている関数達 void func1(){ puts("引数なしのfunc1が呼ばれました"); } void func2(char *str){ puts(str); } int intfunc(){ return 3; } int intfunc2(int i){ int result = i+3; printf("%d\n",result); return result; } void voidf(void){ puts("引数を渡すとエラーになる関数が呼ばれました"); }
- 暗黙の型変換を期待したコードでvoidが絡んでいると失敗する
大体のルールはここに記載されている。
関数ポインタと、ポインタ変数に代入する関数の
- 引数
- 戻り値
をきちんと一致するように宣言・代入し、コンパイルオプションで警告を細かく出せばこの問題に頭を悩ますことはないと思います。*2
関数ポインタの配列
宣言がちょっとわかりにくいけど、基本は同じなので特に解説なし。
//宣言 int(*p3[4])(char *output); //代入 p3[0] = func1; p3[1] = func2; p3[2] = intfunc; p3[3] = intfunc2; //誤った使用 //以下のコンパイルはすべて失敗する // p3[0](); // p3[1](); // p3[2](); // p3[3](); //コンパイル結果 // chap5/functionp.c:65:10: error: too few arguments to function call, expected 1, // have 0 // p3[0](); // ~~~~~ ^ // chap5/functionp.c:66:10: error: too few arguments to function call, expected 1, // have 0 // p3[1](); // ~~~~~ ^ // chap5/functionp.c:67:10: error: too few arguments to function call, expected 1, // have 0 // p3[2](); // ~~~~~ ^ // chap5/functionp.c:68:10: error: too few arguments to function call, expected 1, // have 0 // p3[3](); // ~~~~~ ^ //使用 p3[0]("123");//出力結果::引数なしのfunc1が呼ばれました p3[1]("123");//出力結果:123 p3[2]("123"); p3[3]("123");//出力結果:152838021 多分不正に数値にキャストした文字列に3を足した結果。 ・・・ //代入されている関数達 void func1(){ puts("引数なしのfunc1が呼ばれました"); } void func2(char *str){ puts(str); } int intfunc(){ return 3; } int intfunc2(int i){ int result = i+3; printf("%d\n",result); return result; }
補足、某所でいただいたアドバイスについて
引数なし、ではなくvoid型の引数というものがある。
DCL20-C. 引数を受け付けない関数の場合も必ず void を指定する
こちらを関数ポインタの型変数に宣言すれば、 引数をつけて読んだときにエラーになります。
//void引数と関数ポインタの実験 int (*fp1)(int a); //int voidf(void); fp1 = voidf;//代入可能 //fp1();//エラー fp1(1);//実行可能 //voidf(1);//コンパイルエラー int (*fp2)(void); fp2 = voidf; fp2(); //fp2(10);//コンパイルエラー
暗黙の型変換は、コンパイラ依存のものも結構あり、gccで動くからといって油断はしないこと
INT36-C. ポインタから整数への変換、整数からポインタへの変換
プログラミング学習シリーズ C言語改訂版 2 はじめて学ぶCの仕組み
- 作者: 倉薫
- 出版社/メーカー: 翔泳社
- 発売日: 2009/02/13
- メディア: 大型本
- 購入: 2人 クリック: 6回
- この商品を含むブログ (3件) を見る