1.最も簡単なアセンブリ
例18.1.最も簡単なアセンブリ
このプログラムをファイル
次に、ターゲットファイル
なぜアセンブリで機械指令に翻訳してもだめなのか、リンクの手順が必要なのか.リンクには主に2つの役割があります.1つは、ターゲットファイルの情報を変更し、アドレスを再配置し、5.2節の「実行可能ファイル」で詳細に説明することです.2つ目は、複数のターゲットファイルを1つの実行可能ファイルに統合し、2節の「
今このプログラムを実行して、それはただ1件の事をして退出して、退出の状態は4で、第2節の“カスタマイズの関数”はShellの中で特殊な変数の
したがって、このアセンブリコードは、Cプログラムの
次に、このアセンブリプログラムを行ごとに分析します.まず、
アセンブリプログラムの
ここでは
これは、CPU内部にデジタル1を生成し、
前の命令と同様に、即時数4を生成し、
前の2つの命令はすべてこの命令のために準備して、この命令を実行する時以下の動作が発生します:
x 86アセンブリには2つの異なる構文が存在し、intelの公式ドキュメントではintel構文を使用し、Windowsではintel構文を使用し、UNIXプラットフォームのアセンブリではAT&T構文を使用しているため、本書ではAT&T構文を使用しています.
x 86を紹介する本は多く、UNIXプラットフォームの本はAT&T文法を採用している.例えば[GroudUp]、他の本は一般的にintel文法を採用している.例えば[x 86 Assembly].
1、この節の例の
#PURPOSE: Simple program that exits and returns a
# status code back to the Linux kernel
#
#INPUT: none
#
#OUTPUT: returns a status code. This can be viewed
# by typing
#
# echo $?
#
# after running the program
#
#VARIABLES:
# %eax holds the system call number
# %ebx holds the return status
#
.section .data
.section .text
.globl _start
_start:
movl $1, %eax # this is the linux kernel command
# number (system call) for exiting
# a program
movl $4, %ebx # this is the status number we will
# return to the operating system.
# Change this around and it will
# return different things to
# echo $?
int $0x80 # this wakes up the kernel to run
# the exit command
このプログラムをファイル
hello.s
(アセンブリプログラムは通常.s
をファイル名の接尾辞とする)に保存し、アセンブリ(Assembler)as
でアセンブリプログラム中のアシストをマシン命令に翻訳し、ターゲットファイルhello.o
を生成する.$ as hello.s -o hello.o
次に、ターゲットファイル
ld
を、リンク(Linker、またはLink Editor)hello.o
で実行可能ファイルhello
にリンクする.$ ld hello.o -o hello
なぜアセンブリで機械指令に翻訳してもだめなのか、リンクの手順が必要なのか.リンクには主に2つの役割があります.1つは、ターゲットファイルの情報を変更し、アドレスを再配置し、5.2節の「実行可能ファイル」で詳細に説明することです.2つ目は、複数のターゲットファイルを1つの実行可能ファイルに統合し、2節の「
main
関数と起動ルーチン」で詳細に説明することです.この例では、ターゲットファイルは1つしかありませんが、実行可能ファイルになるにはリンクが必要です.今このプログラムを実行して、それはただ1件の事をして退出して、退出の状態は4で、第2節の“カスタマイズの関数”はShellの中で特殊な変数の
$?
で前の命令の退出の状態を得ることができることを話しました:$ ./hello
$ echo $?
4
したがって、このアセンブリコードは、Cプログラムの
main
関数のreturn 4;
に相当する.どうして相当するの?第2節「main
関数および起動ルーチン」で詳細に説明する.次に、このアセンブリプログラムを行ごとに分析します.まず、
#
号は、C言語の//
注釈に類似した単行注釈を表す. .section .data
アセンブリプログラムの
.
で始まる名前は、命令の補助記号ではなく、機械命令に翻訳されるのではなく、アセンブリ指示(Assembler Directive)または擬似操作(Pseudo-operation)と呼ばれ、本物の命令ではないので「擬似」の字を付ける特殊な指示である..section
は、コードをいくつかのセグメントに分割することを示し、プログラムがオペレーティングシステムによってロードされて実行されると、各セグメントが異なるアドレスにロードされ、オペレーティングシステムは異なるページに対して異なる読み取り、書き込み、実行権限を設定する..data
段はプログラムのデータを保存し、Cプログラムのグローバル変数に相当する読み書き可能である.本プログラムには定義データがないので、.data
セグメントは空です. .section .text
.text
セグメントはコードを保存し、読み取り専用で実行可能であり、後続の命令は.text
セグメントに属する. .globl _start
_start
はシンボル(Symbol)であり、シンボルはアセンブリプログラムでアドレスを表し、命令で使用することができ、アセンブリプログラムはアセンブリの処理を経て、すべてのシンボルがその代表するアドレス値に置き換えられる.C言語では変数名で変数にアクセスします.実は、あるアドレスを読み書きするメモリユニットです.私たちは関数名で関数を呼び出します.実は、この関数の最初の命令があるアドレスにジャンプします.だから、変数名と関数名は記号で、本質的にはメモリアドレスを表しています..globl
は、このシンボルがリンクによって使用されることをアセンブリに示すので、ターゲットファイルのシンボルテーブルにグローバルシンボルであることをマークする(5.1節「ターゲットファイル」の詳細な説明)._start
はCプログラムの_start
関数のように特殊で、プログラム全体のエントリであり、リンクはリンク時にターゲットファイルのmain
シンボルが表すアドレスを検索し、プログラム全体のエントリアドレスに設定するので、各アセンブリプログラムは_start
シンボルを提供し、_start
で宣言しなければならない.1つのシンボルが.globl
で宣言されていない場合、このシンボルはリンクに使用されないことを示します._start:
ここでは
.globl
シンボルが定義され、アセンブリはアセンブリを翻訳する際に各データオブジェクトと各命令のアドレスを計算し、このようなシンボル定義を見ると、その後ろの命令のアドレスをこのシンボルが表すアドレスとする._start
という記号は特殊で、その代表するアドレスはプログラム全体のエントリアドレスであるため、次の命令_start
はプログラムの中で最初に実行される命令となっている. movl $1, %eax
これは、CPU内部にデジタル1を生成し、
movl $1, %eax
レジスタに保存するように要求されるデータ転送命令である.eax
の接尾辞lはlongを表し、32ビットの転送命令であることを示す.このコマンドはCPUにメモリを読み込む必要はありません.1この数はCPU内部で発生し、即時数(Immediate)と呼ばれます.アセンブリでは、シンボル名と区別するために、即時数の前に$を追加し、レジスタ名の前に%を追加します.mov
命令には他にもいくつかの形式がありますが、データ転送方向は同じで、最初のオペランドは常にソースオペランドで、2番目のオペランドは常にターゲットオペランドです. movl $4, %ebx
前の命令と同様に、即時数4を生成し、
mov
レジスタに保存する. int $0x80
前の2つの命令はすべてこの命令のために準備して、この命令を実行する時以下の動作が発生します:
ebx
命令はソフト割り込み命令と呼ばれ、この命令で故意に異常を発生させることができ、前章で述べたように、異常の処理と割り込みは類似しており、CPUはユーザモードから特権モードに切り替え、カーネルコードにジャンプして異常処理プログラムを実行する.int
命令の即時数0 x 80はパラメータであり、異常処理プログラムではこのパラメータに基づいてどのように処理するかを決定し、Linuxカーネルではint
という異常をシステムコール(System Call)と呼ぶ.カーネルはユーザプログラムのために多くのシステムサービスを提供しているが、これらのシステムサービスはライブラリ関数(例えばint $0x80
)のように呼び出すことができず、ユーザプログラムを実行する際にCPUがユーザモードにあり、直接カーネル関数を呼び出すことができないため、システム呼び出しによってCPUモードを切り替え、異常処理プログラムを介してカーネルに入る必要があり、ユーザプログラムはレジスタを通じていくつかのパラメータしか伝達できない.その後、カーネル設計されたコードルートに従って歩き、ユーザープログラムが思うように、どのカーネル関数を調整したいのか、どのカーネル関数を調整することはできません.これにより、システムサービスが安全に呼び出されることを保証できます.呼び出しが終了した後、CPUは再びユーザモードに戻り、printf
の次の命令を実行し続け、ユーザプログラムから見れば関数呼び出しと戻りのように見える.int $0x80
およびeax
の値は、システム呼び出しに渡される2つのパラメータである.ebx
の値はシステム呼び出し番号であり、Linuxの各種システム呼び出しはeax
命令によって開始され、カーネルはint $0x80
を通じてユーザーがどのシステム呼び出しを呼び出すかを判断する必要があり、eax
のシステム呼び出し番号は1である._exit
の値は、ebx
に渡されるパラメータであり、終了状態を示す.ほとんどのシステム呼び出しが完了すると、ユーザ空間に戻って後続の命令を実行し続けますが、_exit
システム呼び出しは比較的特殊で、ユーザ空間に戻って実行を続けるのではなく、現在のプロセスを終了します.x 86アセンブリの2つの文法:intel文法とAT&T文法コメント
x 86アセンブリには2つの異なる構文が存在し、intelの公式ドキュメントではintel構文を使用し、Windowsではintel構文を使用し、UNIXプラットフォームのアセンブリではAT&T構文を使用しているため、本書ではAT&T構文を使用しています.
_exit
この命令はintel構文で書けばmovl %edx,%eax
であり、レジスタ名に%番号を付けず、ソース操作数とターゲット操作数の位置が入れ替わり、文字長も命令の接尾辞lで表すのではなく別の方法で表される.本書ではこの2つの文法の違いを詳しく論じないが,読者は[AssemblyHOWTO]を参考にすることができる.x 86を紹介する本は多く、UNIXプラットフォームの本はAT&T文法を採用している.例えば[GroudUp]、他の本は一般的にintel文法を採用している.例えば[x 86 Assembly].
練習問題はコメントをお願いします
1、この節の例の
mov eax,edx
の命令を削除して、アセンブリ、リンクも通過できますが、実行中にセグメントエラーが発生しました.その原因を説明できますか.