百日半狂乱

Shut the fuck up and write some code!!

nmコマンドでC/C++のシンボルテーブルを見る、C++の名前マングリング、"C"リンケージ、あるいはリンカに関するメモ

前回(g++)前々回(gcc)のサンプルコードを使ったC++のマングリングや"C"リンケージに関するメモ.

nmコマンドでサンプルコードのシンボルテーブルを覗いた後に、C++からCの関数を呼び出す場合のサンプルコードを示す.

特にリンケージ周りの説明は正確性を欠いているやもしれませんのでご容赦下さい.より詳細な説明は参考リンク等を参照して頂ければと思います.

nmコマンド

nm は、UNIXや類似のオペレーティングシステムに存在するコマンドであり、バイナリファイル(ライブラリ、実行ファイル、オブジェクトファイル)の中身を調べ、そこに格納されているシンボルテーブルなどの情報を表示する。デバッグに使われることが多く、識別子の名前の衝突問題やC++の名前修飾の問題を解決する際に補助として用いられる。

GNUプロジェクトでは、高機能の nm プログラムを GNU Binutils パッケージの一部として提供している。この nm コマンドは他のツールと同様に特定のコンピュータ・アーキテクチャとバイナリフォーマット向けにコンパイルされているので、セキュリティ専門家は疑わしいバイナリファイルを調査するためにネイティブでない nm コマンドを事前に取り揃えておくことが多い。

Wikipedia - nm(UNIX)

今回はこいつを使って、サンプルコードのコードのシンボルテーブルを見たり、C++からCの関数を呼び出す場合に起こる問題等について見ていく.

続きを読む

C/C++の関数プロトタイプ、暗黙ルールデータベースの話(その2)

前回の記事で公開したMakefileでは、gccコンパイルオプションに-Wallを付けていなかった.

極めて非人道的である.

現在Makefileを修正してコンパイル時に-Wallを付けるようにしたが、そのままmakeを叩くとコンパイラにしっかり警告を食らった.

run.c: In function ‘main’:
run.c:4:2: warning: implicit declaration of function ‘print_hello’ [-Wimplicit-function-declaration]

これは、前回のコードではmain関数でprint_hello()を呼び出しているが、それに先立って行うべき関数のプロトタイプ宣言を行っていないからである.

しかし、gccでは警告を食らうだけで、コンパイルは通ってしまう.

関数プロトタイプ(Function prototype)

Cで、もといgccで、関数プロトタイプがなくてもコンパイルが通るのは以下の理由による.

関数が事前に宣言されていない状況で、左括弧付きで式の中に現われた場合、その関数は暗黙のうちに int を返すものと判断され、引数については何の想定もなされない。

Wikipedia - 関数プロトタイプ

Wikipediaにも書いてある通り、書かない場合に問題が発生するので、基本的に関数プロトタイプを省略してはならない.

C++ではコンパイルが通らない

C++では、もといg++では、そもそもコンパイルが通らない.

前回のMakefile中の変数CCをg++にしてmakeを叩くと警告が出るだけでコンパイルが通ってしまうgccとは違って、コンパイラがエラーを吐く.

run.c: In function 'int main(int, char**)':
run.c:4:14: error: 'print_hello' was not declared in this scope
make: *** [run] Error 1

どうもg++では、関数プロトタイプが必須らしい.

g++でコンパイルが通るようにする

以下は、前回のコードをC++(g++)に合わせて若干変更したもの.

makeでライブラリ(.a)のビルドを管理するサンプル(g++.ver)

前回と違って.cファイルを.ccファイルとした上で、hello.hに関数プロトタイプを追加し、run.ccでhello.hを#includeするようにしている.

暗黙ルールデータベースの活用

上記のC++バージョンのコードは、Makefile前回のCのMakefileと少し違う.

ここではそれぞれのMakefileの記述が暗黙ルールデータベースにどのように作用しているかを比較してみることで、もう少し暗黙ルールデータベースに対する理解を深める.

続きを読む

makeを使ったライブラリのビルド管理と暗黙ルールデータベースの話

CでもC++でも良いんだけど、コードが大きくなってくるとファイルの分割を考え始める.

追記(204/1/30):このままコンパイラをg++にするとコンパイルが通らなかった.また、-Wallオプションを付けないというとんでもないことをしている.その辺のことをこちらの記事に書いたので併せて参照をば.

例えば以下のようなことをしたいと考えたとする.

  1. ライブラリとなるソースコード(hello.c)をコンパイルして、arコマンドで静的ライブラリ(hello.a)を作る.
  2. ライブラリを呼び出すソースコード(run.c)のコンパイル時にライブラリをリンクして実行ファイルを生成する.

これらを手でやろうと思うとこれだけでもう大変なのでmakeを使う.

makeでライブラリのビルドを管理する(サンプル)

これでおしまいにしようかと思ったけど、なんでこれで上手く行くのか毎回忘れるので簡単にまとめることにした.

今回はこのサンプルを使って、暗黙ルールデータベースを見てみる.

追記(2014/1/24):libhello.a: libhello.a(hello.o)というのは決して一般的な記法ではなく、アーカイブファイル用の記法なので注意(こちらを参照).

続きを読む

gistコマンドでターミナルからGistにコードを投稿する

Gistにコードを投稿するのにいちいちブラウザを介するのは煩わしいので、ターミナルからコマンド一発で投稿したい.

と思ったらあった.

https://github.com/defunkt/gist

インストール

$ sudo apt-get install ruby
$ sudo apt-get install gem
$ sudo gem install gist

手元のOSはDebian系ディストロです.

ヘルプ

$ gist -h
$ gist --help

ログイン

$ gist --login

一回ログイン完了させておけばOK.後は以下のコマンドを叩くだけでサクッと投稿できるようになる.

投稿

$ gist hoge.c
$ gist -d "description" hoge.c

説明文付きで投稿するなら-dオプションを使う.

使ってみた

$ gist -d "makeでライブラリのビルドを管理する(サンプル)" Makefile hello.*
$ gist run.c -u https://gist.github.com/8523429

このように複数のファイルを指定可能.もちろんワイルドカードも使える.

既存のgistにコードを追加したければ-uオプションを使う.

結果:makeでライブラリのビルドを管理する(サンプル)

既存のGistにあるコードを修正

Makefileが間違っていたので、これを修正した.

$ gist Makefile -u https://gist.github.com/8523429

単に修正したソースファイルをもう一度投稿するだけで上書き修正できる.ちゃんとリビジョンも更新されてdiffも確認できた.便利.

他にも標準入力やクリップボードからコードを投稿できたりして、物凄くカジュアルにコマンドを叩ける.

便利というかめちゃくちゃ簡単ですね.やっぱり何でもCLIでやれると気持ち良い.

参考:コマンドを使ってターミナルからGistに投稿する

二十五日半狂乱、7日目(の分)の記事