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

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>…

5.1 プロセスID

Linuxではカーネルがinitを起動する際、以下の順番でパス検索し、はじめに見つかったinitを起動する。 /sbin/init /etc/init /bin/init /bin/sh initのPIDは1。 PIDの最大値は32768(/proc/sys/kernel/pid_max)。 PIDの割当ては単調増加方式で、サイクリッ…

標準出力バッファリング

例えば、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>…

csvの空行・コメント行・コメント部分を除外して、配列に格納する

CSVファイルのコメント行・空行をスキップし、コメント部を削除して、各レコードを配列に格納するスクリプトです。 ・行"aaa,bbb ccc,ddd"を分割して、2列目の値「bbb ccc」がスペースで区切られないこと。←これは対応したが ・"aaa,,ccc"で、「ccc」が3列目…

csvの空行・コメント行・コメント部分を除外したフィールド数をカウントする

awkを使った、テキストファイルを処理するスクリプトです。csvファイル(sample.csv)から1行ずつ読み込み、そのフィールド数を標準出力する。 行頭の直後が行末である行は空行とする。 (半角スペースまたはタブ文字)のみで構成された行は空行とする。 行…

シェルにおける終了コードの扱い

シェルで扱うコマンドの終了コードは、その指定可能な値の範囲は8ビットの整数である。終了コードは、特殊変数「$?」にセットされる。負の整数を指定することも可能だが、その場合、当該値を2の補数表現として解釈した値が「$?」にセットされる。その場合の…

サブシェルとは

サブシェルとは、以下のサンプルコードで言うと、4〜7行目にわたって「()」で括られたリストを指します。「シェルスクリプトを実行する」とは、シェルが新たなシェルを子プロセスとしてメモリ上にロードし、その子プロセスが引数に指定されたスクリプトフ…

リニアアドレス→物理アドレスの変換方式

セグメントディスクリプタのリニアアドレスの算出式を疑似コードで表現すると、 QWORD segment_desc segment_desc := *(QWORD *)((gdtr AND 0xFFFFFFFF0000) + (segment_reg AND 0xFFF8))上で求めたセグメントディスクリプタの値(segment_desc)から、飛び…

論理アドレス空間上のLinuxカーネルの位置

Linuxカーネルは、4GBのアドレス空間上の「0xC0100000」にロードされます。これはおおよそ3Gの位置ですが、「0xC0100000 = 0xC0000000 + 0x100000」なので、正確には「3G+1M」の位置にロードされることになります。論理アドレス上では上記の通りですが、物理…

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

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>…

リンカによるシンボル解決(3)ファイル内関数のCALL

今回は、関数の呼び出し部分の実行コードについて、リンク前とリンク後で比較してみます。 d: e8 f3 ff ff ff call 5 80483c1: e8 f3 ff ff ff call 80483b9 ありゃ、該当のコード「e8 f3 ff ff ff」には、まったく変化なしですね。局所変数の場合、オブジェ…

リンカによるシンボル解決(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…