誰かが次のアセンブリコードの機能を説明できますか?
int 0x80
割り込みベクトル0x80に制御を渡します
http://en.wikipedia.org/wiki/Interrupt_vector を参照してください
Linuxでは、 this をご覧ください。system_call
。もちろん、別のOSでは、これはまったく異なるものを意味する場合があります。
int
は割り込みを意味し、番号_0x80
_は割り込み番号です。割り込みは、その割り込みを処理している人、この場合は割り込み_0x80
_にプログラムフローを転送します。 Linuxでは、_0x80
_割り込みハンドラーはカーネルであり、他のプログラムによるカーネルへのシステム呼び出しを行うために使用されます。
カーネルは、レジスター_%eax
_(ガス構文、およびIntel構文のEAX)の値を調べることにより、プログラムがどのシステム呼び出しを行うかについて通知されます。各システムコールには、他のレジスタの使用に関する異なる要件があります。たとえば、_1
_の_%eax
_の値はexit()
のシステムコールを意味し、_%ebx
_の値はexit()
。
0x80 = 80h = 128であることを忘れないでください
here を見ることができます[〜#〜] int [〜#〜]は多くの命令の1つにすぎません(実際にはアセンブリ言語表現(またはx86命令セットに存在する「ニーモニック」と言います)。また、 here にあるIntelのマニュアルで、この手順の詳細を確認することもできます。
PDFから要約するには:
INT n/INTO/INT 3-割り込み手順の呼び出し
INT n命令は、宛先オペランドで指定された割り込みハンドラーまたは例外ハンドラーへの呼び出しを生成します。宛先オペランドは、8ビットの符号なし中間値としてエンコードされた0〜255のベクトルを指定します。 INT n命令は、割り込みハンドラーへのソフトウェア生成呼び出しを実行するための一般的なニーモニックです。
ご覧のとおり、0x80は、質問の宛先オペランドです。この時点で、CPUはカーネルに存在するコードを実行する必要があることを認識していますが、どのコードですか?これは、Linuxの割り込みベクターによって決定されます。
最も有用なDOSソフトウェア割り込みの1つは、割り込み0x21でした。レジスタ内の異なるパラメーター(ほとんどがahとal)で呼び出すことにより、さまざまなIO操作、文字列出力などにアクセスできます。
ほとんどのUnixシステムおよび派生物は、システム呼び出しを行うために使用される割り込み0x80を除き、ソフトウェア割り込みを使用しません。これは、プロセッサのEAXレジスタにカーネル関数に対応する32ビット値を入力し、を実行してからINT 0x80を実行することで実現されます。
割り込みハンドラテーブルの他の利用可能な値が表示されている場所で、これを見てください:
表からわかるように、CPUはシステムコールを実行するようにポイントされています。 Linuxシステムコールテーブルは here にあります。
そのため、値0x1をEAXレジスタに移動し、プログラムでINT 0x80を呼び出すことにより、現在実行中のプロセス(Linux、x86 Intel CPU)を停止(終了)するカーネル内のコードをプロセスに実行させることができます。
ハードウェア割り込みとソフトウェア割り込みを混同しないでください。 here は、この点に関する非常に良い答えです。
This も良いソースです。
アクション here でint 80hを確認できます。
int 0x80は、x86(Intel互換)プロセッサ上のLinuxでシステムコールを呼び出すために使用されるアセンブリ言語命令です。
最小限の実行可能なLinuxシステムコールの例
Linuxは0x80
の割り込みハンドラーをセットアップして、システムコールを実装します。これは、ユーザーランドプログラムがカーネルと通信するための方法です。
.data
s:
.ascii "hello world\n"
len = . - s
.text
.global _start
_start:
movl $4, %eax /* write system call number */
movl $1, %ebx /* stdout */
movl $s, %ecx /* the data to print */
movl $len, %edx /* length of the buffer */
int $0x80
movl $1, %eax /* exit system call number */
movl $0, %ebx /* exit status */
int $0x80
コンパイルして実行:
as -o main.o main.S
ld -o main.out main.o
./main.out
結果:プログラムは標準出力に出力します:
hello world
そしてきれいに終了します。
リング3およびLinuxでは禁止されている しかないため、ユーザーランドから独自の割り込みハンドラーを直接設定することはできません。
GitHubアップストリーム 。 Ubuntu 16.04でテスト済み。
より良い代替案
int 0x80
は、システム呼び出しを行うためのより優れた代替手段に取って代わられました。最初のsysenter
、次にVDSOです。
x86_64には 新しいsyscall
命令 があります。
参照: 「int 0x80」または「syscall」の方が良いですか?
最小16ビットの例
最初に、ここで説明したように、最小限のブートローダーOSを作成し、QEMUおよび実際のハードウェアで実行する方法を学びます。 https://stackoverflow.com/a/32483545/895245
これで、16ビットリアルモードで実行できます。
movw $handler0, 0x00
mov %cs, 0x02
movw $handler1, 0x04
mov %cs, 0x06
int $0
int $1
hlt
handler0:
/* Do 0. */
iret
handler1:
/* Do 1. */
iret
これは順番に行います:
Do 0.
Do 1.
hlt
:実行を停止プロセッサがアドレス0
で最初のハンドラーを探し、4
で2番目のハンドラーを探す方法に注意してください。これは [〜#〜] ivt [〜#〜 ] 、各エントリは4バイトです。
IOを実行する最小限の例 ハンドラーを表示します。
最小保護モードの例
最新のオペレーティングシステムは、いわゆる保護モードで実行されます。
このモードでは処理のオプションが多いため、より複雑ですが、その精神は同じです。
重要なステップは、ハンドラーを記述するインメモリデータ構造(割り込み記述子テーブル)のアドレスを指すLGDTおよびLIDT命令を使用することです。
「int」命令により割り込みが発生します。
単純な答え:割り込みは、簡単に言えば、CPUに割り込み、特定のタスクを実行するように伝えるイベントです。
詳細な回答:
CPUには、メモリに格納された割り込みサービスルーチン(またはISR)のテーブルがあります。リアル(16ビット)モードでは、これは[〜#〜] ivt [〜#〜]、または[〜#〜] i [〜 #〜]nterrupt[〜#〜] v [〜#〜]ector[〜#〜] t [〜#〜]可能。 IVTは通常0x0000:0x0000
(物理アドレス0x00000
)にあり、ISRを指す一連のセグメントオフセットアドレスです。 OSは、既存のIVTエントリを独自のISRで置き換える場合があります。
(注:IVTのサイズは1024(0x400)バイトに固定されています。)
保護(32ビット)モードでは、CPUはIDTを使用します。 IDTは、descriptors(別名ゲートとも呼ばれる)で構成される可変長構造体であり、CPUに割り込みハンドラーについて通知します。これらの記述子の構造は、IVTの単純なセグメントオフセットエントリよりもはるかに複雑です。ここにあります:
bytes 0, 1: Lower 16 bits of the ISR's address.
bytes 2, 3: A code segment selector (in the GDT/LDT)
byte 4: Zero.
byte 5: A type field consisting of several bitfields.
bit 0: P (Present): 0 for unused interrupts, 1 for used interrupts.*
bits 1, 2: DPL (Descriptor Privilege Level): The privilege level the descriptor (bytes 2, 3) must have.
bit 3: S (Storage Segment): Is 0 for interrupt and trap gates. Otherwise, is one.
bits 4, 5, 6, 7: GateType:
0101: 32 bit task gate
0110: 16-bit interrupt gate
0111: 16-bit trap gate
1110: 32-bit interrupt gate
1111: 32-bit trap gate
* IDTは可変サイズの場合がありますが、連続している必要があります。つまり、IDTを0x00から0x50に宣言する場合、0x00から0x50のすべての割り込みが必要です。 OSは必ずしもそれらのすべてを使用するわけではないため、Presentビットを使用すると、OSが処理しない割り込みをCPUが適切に処理できます。
割り込みが発生すると(IRQの外部トリガー(ハードウェアデバイスなど)、またはプログラムからのint
命令)、CPUはEFLAGS、CS、EIPの順にプッシュします。 (これらは、割り込み戻り命令iret
によって自動的に復元されます。)OSは通常、マシンの状態に関する詳細情報を保存し、割り込みを処理し、マシンの状態を復元して、続行します。
多くの* NIX OS(Linuxを含む)では、システムコールは割り込みベースです。プログラムはシステムコールへの引数をレジスタ(EAX、EBX、ECX、EDXなど)に入れ、割り込み0x80を呼び出します。カーネルは、0x80の割り込みハンドラーを含むようにIDTをすでに設定しています。これは、割り込み0x80を受け取ったときに呼び出されます。その後、カーネルは引数を読み取り、それに応じてカーネル関数を呼び出します。 EAX/EBXにリターンを保存する場合があります。システムコールは、主にsysenter
およびsysexit
(またはAMDではsyscall
およびsysret
)命令に置き換えられ、リング0へのより高速なエントリが可能になりました。
この割り込みは、異なるOSでは異なる意味を持つ場合があります。必ずドキュメントを確認してください。
前述のように、制御により割り込みベクトル0x80にジャンプします。実際には(少なくともLinuxでは)これが意味することは、システムコールが呼び出されることです。正確なシステムコールと引数は、レジスタの内容によって定義されます。たとえば、%eaxを1に設定し、その後に「int 0x80」を設定すると、exit()を呼び出すことができます。
これは、CPUに割り込みベクトル0x80をアクティブにするように指示します。これは、Linux OSではシステムコール割り込みであり、ファイルなどのopen()
などのシステム関数を呼び出すために使用されます。