fromnandの日記

完全自分用のメモでございます。

【gdb】startとrunの違い

startはブレイクポイントがmain関数に仕掛けられているのと同じ動作をする。

つまり、break mainしたつもりがなくてもmainで処理が止まる。

 

runはmain関数も貫通して処理を実行する。

 

start arg1 arg2やrun arg1 arg2などとすると、引数を取ることが可能。

【configure・make・make install】Linuxにおけるアプリのインストールについて

configure - シェルスクリプト。実行した環境に適応したmakefileを作成する。

make - makefileに書かれたデフォルトの動作を行う。

make install - makefileの中のinstallというラベルに書かれた処理から開始する。

 

細かいことは「https://qiita.com/chihiro/items/f270744d7e09c58a50a5」におまかせ。

【関数呼び出しのABI】「OSのABI」と「アーキテクチャのABI」の違い

なんだかABIには2種類あるみたいなので、ここでまとめておく。

1つ目は「OSにおけるABI」、2つ目は「アーキテクチャ(コンパイラ)におけるABI」である。

 

前者はOSカーネルによって定義されるもので、主にシステムコールの呼び出し方法を規定しているらしい。

Linux/x86カーネルでは、システムコールを呼び出す引数にはレジスタを経由する」などといったものだ。

これはあるシステム上でビルドしたアプリケーションを他のシステムでも動作させるために必要だ。

 

後者はアーキテクチャによって定義されるもので、主に関数呼び出しの引数の渡し方は戻り値の返し方といったものを指している。

他にも構造体のメモリでの配置方法やプログラム中でのレジスタの使い方など、主にコンパイラにおける規定がされている。

これは、複数の異なるコンパイラで作成されたプログラムをリンクできるようにするための規定だ。

x86におけるC言語の呼び出し規約について

詳しくはここ→https://ja.wikipedia.org/wiki/%E5%91%BC%E5%87%BA%E8%A6%8F%E7%B4%84

 

x86では、C言語(C++)に対して「cdecl」という呼び出し規約が使用されています。

  1. 引数は右から左に積んでいく
  2. 関数の戻り値はeaxに保存する
  3. eax・ecx・edxの値は関数の中で破壊しても良い
  4. 上記のレジスタを破壊してほしくない場合は、呼び出し側の関数でスタックにpushしておく
  5. 引数の分のスタックポインタの処理は呼び出し側で行う

 

windowsなどでは「stdcall」というものが使われたりもするらしいのですが、これも「cdecl」とほぼ同様です。

唯一違うのは5の部分で、「引数の分のスタックポインタの処理は呼び出された側の関数で行う」だけです。

【gdb】ステップオーバー・ステップイン・ステップアウトについて

gdbにおける「ステップオーバー・ステップイン・ステップアウト」についてまとめておきます

 

高級言語のソースレベル】

ステップオーバー = n (next)

ステップイン   = s (step)

ステップアウト  = fin

 

機械語レベル】

ステップオーバー = ni (nexti)

ステップイン   = si (stepi)

ステップアウト  = fin

【Linuxカーネル】システムコールがどのように呼び出されるのか?

システムコール4番のwriteを例に考えます。

まず、C言語側からはwrite関数がAPIとして提供されているので3つの引数をセットして呼び出します。

 

このプログラムをgdbで追ってみると、write関数の中でebxに第一引数、ecxに第二引数、edxに第三引数がコピーされます。

つまり、次のようなプログラムが書かれているわけです。

write関数 :

    pushl %ebx

    movl 0x10(%esp), %edx  //第3引数

    movl 0xc(%esp), %ecx    //第2引数

    movl 0x8(%esp), %ebx   //第1引数

    movl $4, %eax                //?????

    call *0x80d6750              //int 0x80が置かれている場所にジャンプする

    //プログラムはまだまだ続きます...

 

write関数を更に覗いていくと、eaxに4という数字をセットするのが見えます(上の?????の部分です)

これはシステムコールの番号のことで、int 0x80の中で割り込み関数テーブルの添字を指定するのに使用されます。

 

さて、最終的にはint 0x80を行いますが、この内部ではebx・ecx・edxの値をスタックにつむSAVE_ALLというマクロが使われています。

SAVE_ALLをめちゃんこ簡単に :

    cld

    pushl %edx

    pushl %ecx

    pushl %ebx

 

僕の環境(他でも??)では、sys_call_tableという割り込み関数テーブルが用意されています。

上のプログラムで「call *0x80d6750」を行った後にint 0x80を行いますが、int 0x80の中ではこのsys_call_tableの添字にeaxを使用することによって、writeに対応する関数を呼び出しています。

具体的には「call *sys_call_table(, %eax, 4)」のようにしています。

ここで呼び出される関数はC言語で記述されているので、上のSAVE_ALLで保存された引数を受け取ります。

【GAS】関数アドレステーブルのn番目の関数を呼び出すアセンブラコードについて

割り込みプログラミングなどをする際に、関数のアドレスを配列にして保存することがあると思います。

その際に、どのようなアセンブラが生成されるのかを調べてみました。

 

interrupt_func_tableが関数アドレステーブルだとすると、32bit環境におけるn番目の関数のアドレスは次のようにして呼び出すことができます。

call *interrupt_func_table(, %eax, 4) 

 

callの後に付いている「*」は、そのメモリに存在する値を調べるという意味。

c言語ではポインタの参照といったりします。

 

(, %eax, 4)という部分がありますが、この部分は(a, b, c)と一般化できます。

この値は「a + b * c」で計算できるようです。