Programming C

ファイルローカル変数の再配置情報

static int s1 = 0x7fffffff; static int s2 = 0x12345678; int main(int argc, char **argv) { s1 = 0x7f7f7f7f; s2 = 0x87654321; return 0; } .symtabは、シンボルに関する属性情報(そのシンボルがメモリ上に実体を持つセクションのセクション番号と、当…

【汎整数拡張】第6回 対応表

処理系による。 型 数学的値 内部表現 汎整数拡張後 char -1 0xFF 0xFFFFFFFF unsigned char 255 0xFF 0x000000FF int -1 0xFFFFFFFF − unsigned int 4294967295 0xFFFFFFFF − 比較関係 左辺↓右辺→ (char)-1 (unsigned char)255 (int)-1 (unsigned int)42949…

データ型モデル・規格Cごとの接尾語なし10進定数の型の違い

個人的メモです。データ型モデル char short int long long long void * ILP32 8 16 32 32 64 64 LP64 8 16 32 64 64 64 規格Cごとの接尾語なしの10進定数の型について。 以下のうち、その定数がオーバーフローしないような最小の型が選ばれる*1。 C89 int, …

オペランドのsigned/unsigned判定マクロ

算術変換の検証をする上で、対象の整数値がsignedかunsignedかを判定するマクロを作成してみました。 #define <limits.h> #define IS_SIGNED(x) ( \ (( (x)>>1)&(1L<<(sizeof(x)*CHAR_BIT-1)))!= \ ((~(x)>>1)&(1L<<(sizeof(x)*CHAR_BIT-1))) ) というものです。このマ</limits.h>…

【汎整数拡張】第5回 算術シフトと論理シフト

ついに来ました、ビットシフト。みんな大好きビットシフト。signed型の負数に対する右ビットシフトの結果は処理系依存なので、以下の話はgcc限定ととらえて下さい。まずは、unsigned char型のビットシフトから。 unsigned型のビットシフトは解説するところが…

【汎整数拡張】第4回 (2^n)による剰余とビットマスク(2^n)-1によるAND演算に等価性はあるか

整数型の値に対して、その下位nビットを取り出す場合、 x & ((1<<n)-1) と書いたりしますが、これって2を法とする剰余(2^nで割った余り)でも表現できないか?と考えたことはあると思います。まず、char/unsigned char型変数の値127に対して、(2^n)-1でマスクをかけてみます。 > char : 127 0111 1111 > char -> int 0000 0000 0000 0000 0000 0000 0111 1111 > c & 0x0F 0000 0000 0000 0000 0000 0000 0000 1111 > unsigned char : …</n)-1)>

【汎整数拡張】第3回 汎整数拡張の意外な落とし穴

汎整数拡張、俗にいう「暗黙のキャスト」はソースコードに出てこないところの挙動なので、C言語上級者でもなかなかに引っかかりやすいトラップだと思います。先日、組込み系の記事で見かけた例ですが…以下コードは、unsigned char型変数をビット反転し、その…

【汎整数拡張】第2回 単項変換

前回は、以下のことを確認しました。 基本形が同じならば、signed/unsigendでキャストしようが、その内部表現(ビット列)は変化しない。 内部表現が同じであっても、その数学的値が同じとは限らない。 今回は、char/unsigned charをint/unsigned intにそれ…

【汎整数拡張】第1回 内部表現と数学的値

「C言語はポインタが難しい」というのが定説ですが、ポインタは四天王の中でも最弱だと思っています。ポインタ以上に難解なのが、この「汎整数拡張」。インテグラル・プロモーション(integral promotion)とも言います。汎整数拡張の挙動を確認する上で、ビ…

malloc(3)に負の値を渡すと

「詳説Cポインタ」p43より。 2.2.1 malloc関数の使い方 (略) void* malloc(size_t); malloc関数はsize_t型の引数を1つ取ります。この型は1章で扱いました。引数に値を指定する際には、 注意が必要です。負の値を指定した場合には、問題となります。システ…

配列とは結局こういうこと

配列の添字指定は「人間にとっての分かりやすい表現」であって、配列概念など知ったこっちゃない処理系は「ベースアドレス(先頭要素へのポインタ値)+要素サイズ*要素番号」という計算式で、配列の各要素にアクセスしているのです。配列が「先頭要素へのポ…

ポインタについて語ってみた

ポインタ・配列についての「分かりやすい説明」を目指すことは、ネットの解説でも多くみかけますが、ポインタと配列は、根本的に日常とはかけ離れたアクロバティックな頭の使い方を求められるので、日常的直観との距離を縮める比喩的説明(住所を書いたメモ…

二次元配列をダンプする

頭の体操として、二次元配列の各要素を参照するコードを、いろんな書き方で書いてみようと思います。いまさら二次元配列?と思うなかれ。こういう当たり前のコードを、1ステップずつ意味を理解した上で、高速でコーディングできるのも、ひとつの技術力だと思…

sizeof演算子のまとめ(仮引数の多次元配列)

関数の引数に配列型変数を指定した場合、関数内の当該仮引数はポインタ型変数に型調整される(正確には、配列の先頭要素へのポインタ値が格納されたポインタ型変数に型調整される)。これが多次元配列になると、どうなるだろう。二次元配列とは、T型配列のT…

sizeof演算子のまとめ(配列の型調整)

sizeof演算子の続きです。 関数のプロトタイプ宣言で配列型を指定された引数は、関数の仮引数ではポインタ型として扱われる、というお話です。 1 /* sizeof_func.c */ 2 #include <stdio.h> 3 4 void func(char buf[]){ 5 printf("sizeof(buf)@func = %zu\n", sizeof(</stdio.h>…

sizeof演算子のまとめ

sizeof演算子について、いろいろ試してみました。アセンブリコードを見てみると、sizeofオペランドの式はコンパイル時に定数に置換されるか、sizeofの計算に影響を与えない数式の場合、式自体が翻訳時に捨てられているのが分かります。 00000000004004e3 <main>: i</main>…

標準出力バッファリング

例えば、1秒ごとに「.」(ドット)を表示するプログレスバー的なものを考えてみます。 1 /* progress.c */ 2 #include <stdio.h> 3 #include <unistd.h> 4 5 int main(int argc, char *argv[]) 6 { 7 int i; 8 9 for(i=0; i<10; i++){ 10 printf("."); 11 sleep(1); 12 } 13 14 </unistd.h></stdio.h>…

セグメント・ディスクリプタ

63 56 55 54 53 52 51 48 47 46 44 43 41 40 39 32 +----------------+---+---+---+---+--------------------+---+---+---+------+---+-------------------+ |ベース(上位8bit)| G | D |res|AVL| リミット(上位4bit) | P |DPL| S |タイプ| A | ベース(中8ビ…

セグメント・セレクタ

セグメント・セレクタとは、ディスクリプタ・テーブル上、現在どのディスクリプタを使用しているかを示す「セレクタ値」のことです。セレクタ値は、セグメント・レジスタに格納されます。 15 3 2 1 0 +---------------+--+---+ | Index |TI|RPL| +----------…

GDTR(Global Descriptor Table Register)

GDTRについて。 GDTR(Global Descriptor Table Register)とは、論理アドレス→物理アドレスのマッピングテーブルの先頭アドレスを格納するレジスタである。 47 16 15 0 +---------------------+-------------+ | Linear Base Address | Table Limit | +----…

リンカによるシンボル解決(4)文字列リテラル

文字列リテラルのシンボル解決について見ていきます。 1 #include <stdio.h> 2 3 const char *dummy_string; 4 const char *string; 5 6 int main() 7 { 8 dummy_string = "123456"; 9 string = "ABCDEF"; 10 11 printf("string = %p\n", string); 12 13 return 0; 14</stdio.h>…

リンカによるシンボル解決(2)ファイル内変数のシンボル解決(後編)

変数の論理アドレスが実行コード中に埋め込まれる場所は、以下の.rel.text(再配置テーブル)で管理されます。 Relocation section '.rel.text' at offset 0x410 contains 2 entries: Offset Info Type Sym.Value Sym. Name 00000005 00000401 R_386_32 0000…

リンカによるシンボル解決(1)ファイル内変数のシンボル解決(前編)

以下は、あるオブジェクトファイルの機械語コードのダンプです。 $ gcc -c sample.c -Wall $ objdump -d sample.o -M intel (略) 00000000 <main>: 0: 55 push ebp 1: 89 e5 mov ebp,esp 3: 8b 15 04 00 00 00 mov edx,DWORD PTR ds:0x4 9: a1 08 00 00 00 mov e</main>…

自己参照構造体のエイリアス定義

自己参照構造体のエイリアスを定義するとき、構造体のタグ名とエイリアスを重複させないように、エイリアスの先頭に「_」(アンダースコア)をつけたものをタグ名とする定義を見かけますが・・・ /* ×悪い例 */ typedef struct _NODE{ int node_id; struct _…

【ELF形式】.dynsym セクション

セクションヘッダおよびセクションの内容をダンプしてみます。 # # .dynsym # # セクションヘッダ $ od -x -A x -j0x1214 -N0x28 sample 001214 004e 0000 000b 0000 0002 0000 81cc 0804 001224 01cc 0000 0040 0000 0006 0000 0001 0000 001234 0004 0000 …

【ELF形式】.gnu.hash セクション

セクションヘッダの内容をダンプしてみます。 # # .gnu.hash # # セクションヘッダ $ od -x -A x -j0x11ec -N0x28 sample 0011ec 0044 0000 fff6 6fff 0002 0000 81ac 0804 0011fc 01ac 0000 0020 0000 0005 0000 0000 0000 00120c 0004 0000 0004 0000 0012…

【ELF形式】.note.gnu.build-id セクション

「.note.*」セクションは、「注釈セクション」というものらしいです。「ORACLE Documentation リンカーとライブラリ 第7章オブジェクトファイル 注釈セクション」 http://docs.oracle.com/cd/E19620-01/805-5821/6j5ga47bq/index.html#chapter6-18048オブジ…

【ELF形式】.note.ABI-tagセクション

徹夜でやってて、たまに寝落ちが入ってきたので、そろそろ投げやりモードですが(笑)セクションヘッダのダンプ。 $ od -x -A x -w16 -j0x119c -N0x28 sample 00119c 0023 0000 0007 0000 0002 0000 8168 0804 0011ac 0168 0000 0020 0000 0000 0000 0000 00…

【ELF形式】ELFヘッダ(1)

ELF形式について、徹底的に調べ尽くしてみます。ELFヘッダの構造体の定義は、「/usr/include/elf.h」に、データ型の定義は「/usr/include/stdint.h」でそれぞれ定義されています。 /* /usr/include/elf.h */ 26 /* Standard ELF types. */ 27 28 #include <stdint.h> 2</stdint.h>…

【ELF形式】.interpセクション

前回は、.shstrtabセクションからセクション名を抽出し、セクションヘッダ一覧に代入しました。 オフセット 内容 サイズ 管理セクション名 0x0000114c セクションヘッダ0 28h(40d)バイト "\0" 0x00001174 セクションヘッダ1 28h(40d)バイト ".interp" 0x0000…