タイトル/名前 | 更新者 | 更新日 |
---|---|---|
日記/2010/02/11/VC++に手を出した理由 | msakamoto-sf | 2010-02-12 00:11:17 |
C言語系/memos/VC++/02, 共通系リンカオプション | msakamoto-sf | 2010-02-11 14:16:17 |
C言語系/memos/VC++ | msakamoto-sf | 2010-02-11 12:35:44 |
抄訳メモ/Calling Conventions Demystified | msakamoto-sf | 2010-02-07 16:30:41 |
抄訳メモ/unixwiz.net/Using Win32 calling conventions | msakamoto-sf | 2010-02-07 14:09:52 |
抄訳メモ/unixwiz.net/Intel x86 Function-call Conventions - Assembly View | msakamoto-sf | 2010-02-07 14:09:36 |
抄訳メモ | msakamoto-sf | 2010-02-07 14:09:18 |
C言語系/memos/gcc/関数の属性機能("__attribute__") | msakamoto-sf | 2010-02-05 15:11:39 |
C言語系/「デーモン君のソース探検」読書メモ/A04, isatty(3) | msakamoto-sf | 2010-02-03 21:18:04 |
C言語系/「デーモン君のソース探検」読書メモ/A03, ttyname(3) | msakamoto-sf | 2010-02-03 19:35:05 |
呼び出し規約調べてた。
→「あれ?もしかして "__fastcall" ってBorlandとMSVC++とで違う? "__msfastcall"というのがBorlandにはあるっぽいけど・・・」
→「Borlandのコンパイラ弄りたいけど、フリーのBorland C++ Compilerだとアセンブラコード生成できねー。TurboDebugger使う気にも今更ならねーし・・・」
→「こうなったら・・・2004年に買って放置してたOllyDbg使った解析入門書あったから、あれ読み直して、Borlandで生成した実行ファイルをOllyDbgで解析して "__fastcall" 周り含めて調べるか・・・」
→「OllyDbg使った解析入門書、書いてある内容をちょっと確かめたいんだけど組み込みアセンブラとか出てきちゃったよ・・・Borland C++ Compilerじゃ無理だし・・・DJGPPはGNUだから出来て当たり前でつまんないし・・・」
→「どうせDLLとかその周りも絡んでくるんだから、ちょっとVC++に寄り道してみるか。」
.NETフレームワークとかCLRとかの仕組みが浸透して、随分様変わりして戸惑ってる。
ただ、自分がそれでもMS頑張ってるなぁ、と思うのがMSDNの充実。少なくともgccやbinutilのinfoコマンドよりは日本語訳や検索の使い勝手、目次の構成、うまく出来てると思う。しかもそれをONLINE、あるいはローカルにDLして自由に閲覧できるのも有り難い。サンプルコードも充実しているし。
2000年前後の、高校~大学時代とはエライ違いだ。
というわけで、暫くはWindows上でのプログラミングを弄ってます。
で、VC++とBorland C++ Compilerで一通りのサンプル作れるようになりつつOllyDbgの入門書2冊読み倒し、OllyDbg使って呼び出し規約を弄り倒す、と。長い回り道だ・・・。
それが終わったらUNIXやるか、アセンブラとOSの境目に戻るか、yacc周りに再戦を挑むか・・・。あ、Binary HacksとDebug Hacksも攻略しないと。でもそこまで辿り着けば、目指すところまで大分近づくんだけど。
よく使うかも知れないリンカオプションのまとめ。
対象:Visual C++ 2008 Express Edition
> cl Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. > link Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved.
まだリンカオプションを駆使するような場面に遭遇していないので、極々簡単で良く使いそうなものだけをまとめた。
DLL関連については別ページでまとめておく。
Microsoft VisualC++ でのメモ。主にVisualC++ 2008 Express Edition上でのメモになります。
原文:
※原文は2001/9月に書かれ、VC6 + Win2kをベースに記述されています。日付や環境は古いですが、内容自体は2010/2月時点でのMSDNの呼び出し規約に関する説明と同じです。
※なお記事中のアセンブラコードについては、原文のものをそのまま載せています。こちらについては、2010年現在も同じ保証がありません。また記事中のコードはデバッグモードでコンパイルしたもので、最適化は施されていないようです。
Web上の技術記事の抄訳(*1)メモ集
※msakamoto-sf個人が、主に勉強の為に翻訳しています。誤訳や内容自体の誤り等ありましたら、 FrontPage に記載のメールアドレスまで御連絡頂けると幸いです。
GCCで提供されている関数の"__attribute__" 機能について極々簡単に紹介する。基本的に unixwiz.net の "Using GNU C __attribute__" のざっくりとした要約になっている。なお、ここで紹介している機能を有効にするには-Wallオプションを指定する必要がある。
※動作確認はCentOS5.2上のgcc-4.1.2, binutils-2.17.50.0.6 上で行っている。
printfやscanfのような、可変長引数群を書式指定文字列に埋め込んだりする関数で、書式指定文字列と可変長引数群の組み合わせを「コンパイル時に」チェックできるようになる。
format (archetype, string-index, first-to-check)
"archetype"は "printf", "scanf" のようにチェック時に参考にする関数名を指定する。gcc側で選択肢は決められており、GCC4の場合は次の4つのいずれかとなる。
printf, scanf, strftime, strfmon
"string-index"は書式指定文字列が引数の何番目かを指定する(1始まり)。
"first-to-check"は書式に埋め込む・あるいは関連する可変長引数が何番目から始まるかを指定する(1始まり)。
printfの例を簡単に紹介する。
format_test.c:
// p1,p2,p3は適当に挟み込んだ引数。 // arhetype = printf形式 // string-index : 3番目の引数、const char *formatを書式指定文字列としてチェック // first-to-check : p3をスキップし、5番目の引数からformatとの組み合わせをチェック extern void myprintf(int p1, int p2, const char *format, int p3, ...) __attribute__ ((format(printf, 3, 5))); void foo() { myprintf(1, 2, "s = %s\n", -1, 5); // "%s"なのにintの5 myprintf(1, 2, "n = %d, %d, %d\n", -1, 1, 2); // 引数が足りない }
まず"-Wall"無しでコンパイルすると、エラーも警告も表示されず、コンパイルは正常に終了する。
$ gcc -c format_test.c
続いて "-Wall" をつけるとformatとのミスマッチについて警告が表示されるようになる。
$ gcc -Wall -c format_test.c format_test.c: In function ‘foo’: format_test.c:6: 警告: format ‘%s’ expects type ‘char *’, but argument 5 has type ‘int’ format_test.c:7: 警告: フォーマットへの引数が少なすぎます
abort(3)やexit(3)は、呼んだ関数から戻らない。GCCはこれらについては自動的に判別する。それ以外の、ユーザーが定義した関数で呼び出し元へ戻らない場合は noreturn を使うことで警告表示を消すことが出来る。
文章だけでは分かりづらいので、実際の例を示す。まず警告が表示されるパターンを示す。
noreturn_test.c:
extern void exitnow(); int foo(int n) { if (0 < n) { exitnow(); // returnせずにfoo()が終了する } else { return 0; } }
"-Wall"付でコンパイルすると、exitnow()のルートでreturn無しでfoo()が終了する為警告が表示される。
$ gcc -Wall -c noreturn_test.c noreturn_test.c: In function ‘foo’: noreturn_test.c:10: 警告: control reaches end of non-void function
__attribute__( (noreturn) ) を付けて実行すると、警告は表示されなくなる。
extern void exitnow() __attribute__((noreturn)); (以下同じ)
コンパイルしてみる。
$ gcc -Wall -c noreturn_test.c $
exit(3)やabort(3)の宣言を確認してみると、同様にnoreturnが指定されていることが確認出来た(ただし、"__noreturn__"とアンダースコア付になっている)。
/usr/include/stdlib.h:
extern void abort (void) __THROW __attribute__ ((__noreturn__)); extern void exit (int __status) __THROW __attribute__ ((__noreturn__));
副作用が無く引数のみで処理が完結し、引数のポインタの内容を見ない関数の場合に、gcc側でキャッシュなど最適化処理を行うようになる。stdlib.hではabs(3)やdiv(3)などで使われている。
より制限が緩いものに pure 属性もあり、こちらはstrlen(3)などポインタの内容を見る場合にも適用出来る。
/usr/include/stdlib.h:
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur; extern div_t div (int __numer, int __denom) __THROW __attribute__ ((__const__)) __wur;
/usr/include/string.h:
extern size_t strlen (__const char *__s) __THROW __attribute_pure__ __nonnull ((1));
複数の"__attribute__"を組み合わせることも出来る。"__attribute__"を連続して続けることも出来るし、一つの"__attribute__"の中にまとめることも出来る。
例:
extern void die1(const char *format, ...) __attribute__((noreturn)) __attribute__((format(printf, 1, 2))); extern void die2(const char *format, ...) __attribute__((noreturn, format(printf, 1, 2))); int foo(int n) { switch (n) { case 1: die1("n = %d\n", n); case 2: die2("n = %d\n", n); default: return n; } }
__attribute__機能はGCC以外のコンパイラでは無視されても問題ないよう、注意深く設計されている。空のマクロに展開することで、GCC以外のコンパイラに対応出来る。__attribute__は複数のパラメータを取るようになっているが、これまでの例のように2重括弧でくくることで一つの引数としてマクロで扱えるようになる。
#ifndef __GNUC__ # define __attribute__(x) /*NOTHING*/ #endif
最後に、その他の注意点として関数属性は宣言時にのみ指定できる点に注意する。
関数の定義部では使えない。
このため、関数属性を使う時は必ずプロトタイプ宣言を用意してそちらに"__attribute__"を指定する必要がある。
お題:isatty(3)が端末上で動作しているか判定する仕組みを調査せよ
※この章は「デーモン君のソース探検」に載っていませんが、msakamoto-sf自身が個人的に興味を持って調べ、"Appendix"として読書メモシリーズに入れてありますのでご注意下さい。
ソースファイルの場所を把握:
$ locate isatty /usr/share/man/cat3/isatty.0 /usr/share/man/man3/isatty.3 /usr/src/gnu/dist/libf2c/libU77/isatty_.c /usr/src/gnu/dist/toolchain/libf2c/libU77/isatty_.c /usr/src/lib/libc/gen/isatty.c
ソースコード:
int isatty(fd) int fd; { struct termios t; return(tcgetattr(fd, &t) != -1); }
tcgetattr(3)のソースファイル:
$ locate tcgetattr /usr/share/man/cat3/tcgetattr.0 /usr/share/man/man3/tcgetattr.3 /usr/src/lib/libc/termios/tcgetattr.c
ソースコード:
int tcgetattr(fd, t) int fd; struct termios *t; { _DIAGASSERT(t != NULL); return (ioctl(fd, TIOCGETA, t)); }
以上より、最終的にはioctl(2)を使ってデバイスドライバ経由で情報を取得出来れば端末上で動作していると判定していることが分かった。
今回のお題については、ここまで。
お題:ttyname(3)が端末のデバイスファイル名を取得する仕組みを調査せよ
※この章は「デーモン君のソース探検」に載っていませんが、msakamoto-sf自身が個人的に興味を持って調べ、"Appendix"として読書メモシリーズに入れてありますのでご注意下さい。