誰かが次のアセンブリコードが何をするのか説明できますか?
int 0x80
回答:
割り込みベクタ0x80に制御を渡します
http://en.wikipedia.org/wiki/Interrupt_vectorを参照してください
Linuxでは、見ていこれを:それは処理するために使用されましたsystem_call
。もちろん、別のOSでは、これはまったく異なることを意味する可能性があります。
int 0x80
に対する特別な種類のものと考える必要があります。call
eax
int
は割り込みを意味し、番号0x80
は割り込み番号です。割り込みは、その割り込みを処理している人(0x80
この場合は割り込み)にプログラムフローを転送します。Linuxでは、0x80
割り込みハンドラーはカーネルであり、他のプログラムによってカーネルへのシステムコールを行うために使用されます。
カーネルは、レジスターの値%eax
(AT&T構文、およびIntel構文のEAX)を調べることにより、プログラムが実行したいシステムコールについて通知されます。各システムコールには、他のレジスタの使用に関して異なる要件があります。たとえば、1
inの値%eax
はのシステムコールを意味し、exit()
in%ebx
の値はのステータスコードの値を保持しますexit()
。
ことを覚えておいてください0x80
= 80h
=128
あなたは見ることができ、ここでそのINT
x86命令セットに存在するだけで、多くの命令の1つである(実際にはアセンブリ言語表現(または私はそれを「ニーモニック」)と言う必要があります)。この手順の詳細については、こちらにあるIntel独自のマニュアルを参照してください。。
PDFから要約するには:
INT n / INTO / INT3-割り込みプロシージャの呼び出し
INT n命令は、デスティネーションオペランドで指定された割り込みまたは例外ハンドラへの呼び出しを生成します。デスティネーションオペランドは、0〜255のベクトルを指定し、8ビットの符号なし中間値としてエンコードされます。INT n命令は、ソフトウェアで生成された割り込みハンドラへの呼び出しを実行するための一般的なニーモニックです。
ご覧のとおり、0x80がデスティネーションオペランドです。質問のです。この時点で、CPUは、カーネルに存在するコードを実行する必要があることを認識していますが、どのコードですか?これは、Linuxの割り込みベクタによって決定されます。
最も有用なDOSソフトウェア割り込みの1つは、割り込み0x21でした。レジスタ内のさまざまなパラメータ(主にahとal)を使用して呼び出すことにより、さまざまなIO操作、文字列出力などにアクセスできます。
ほとんどのUnixシステムおよび派生物は、システムコールを行うために使用される割り込み0x80を除いて、ソフトウェア割り込みを使用しません。これは、カーネル関数に対応する32ビット値をプロセッサのEAXレジスタに入力してから、 INT0x80を実行することによって実現されます。
割り込みハンドラテーブルで利用可能な他の値が示されている場所で、これを見てください。
ご覧のとおり、表はシステムコールを実行するCPUを示しています。Linuxシステムコールの表はここにあります。
したがって、値0x1をEAXレジスタに移動し、プログラムでINT 0x80を呼び出すことにより、現在実行中のプロセス(Linux、x86 Intel CPU)を停止(終了)するカーネル内のコードをプロセスに実行させることができます。
ハードウェア割り込みをソフトウェア割り込みと混同しないでください。これは、この点に関して非常に良い答えです。
これも良い情報源です。
int 0x80
i386のLinuxシステムコールABIは、DOSに非常に似ているint 0x21
ABI。レジスタ(DOSの場合はAH、Linuxの場合はEAX)に呼び出し番号を入れ、他のレジスタに他の引数を入れてから、ソフトウェア割り込み命令を実行します。主な違いは、システムコールで実行できること(LinuxではなくDOSでハードウェアに直接アクセスすること)であり、呼び出す方法ではありません。
/usr/include/x86_64-linux-gnu/asm/unistd_64.h
最小限の実行可能な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
結果:プログラムはstdoutに出力します:
hello world
きれいに終了します。
リング3しかないため、ユーザーランドから直接独自の割り込みハンドラーを設定することはできません。Linuxでは設定できません。。
GitHubアップストリーム。Ubuntu16.04でテスト済み。
より良い選択肢
int 0x80
システムコールを行うためのより良い代替手段に取って代わられました:最初にsysenter
、次にVDSO。
x86_64に新しいsyscall
命令があります。
参照:「int0x80」または「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 0x80は、Linux on x86(つまり、Intel互換)プロセッサでシステムコールを呼び出すために使用されるアセンブリ言語命令です。
「int」命令は割り込みを引き起こします。
簡単な答え:簡単に言えば、割り込みはCPUに割り込みをかけ、特定のタスクを実行するように指示するイベントです。
詳細な回答:
CPUには、メモリに格納された割り込みサービスルーチン(またはISR)のテーブルがあります。REAL(16ビット)モードでは、これは次のように格納されているIVT、又はI nterrupt VエクターTができます。IVTは通常、0x0000:0x0000
(物理アドレス0x00000
)にあり、ISRを指す一連のセグメントオフセットアドレスです。OSは、既存のIVTエントリを独自のISRに置き換える場合があります。
(注:IVTのサイズは1024(0x400)バイトに固定されています。)
保護(32ビット)モードでは、CPUはIDTを使用します。IDTは、記述子で構成される可変長構造です。、割り込みハンドラーについて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ビットを使用すると、CPUはOSが処理する予定のない割り込みを適切に処理できます。
割り込みが発生したとき(IRQの外部トリガー(ハードウェアデバイスなど)によって、または int
プログラムから命令の)、CPUはEFLAGS、CS、EIPの順にプッシュします。(これらはによって自動的に復元されますiret
、割り込みリターン命令ます。)OSは通常、マシンの状態に関する詳細情報を格納し、割り込みを処理し、マシンの状態を復元して、続行します。
多くの* NIX OS(Linuxを含む)では、システムコールは割り込みベースです。プログラムは、システムコールの引数をレジスタ(EAX、EBX、ECX、EDXなど)に入れ、割り込み0x80を呼び出します。カーネルは、0x80に割り込みハンドラーを含むようにIDTを既に設定しています。これは、割り込み0x80を受信したときに呼び出されます。次に、カーネルは引数を読み取り、それに応じてカーネル関数を呼び出します。返品はEAX / EBXに保存される場合があります。システムコールの大部分はsysenter
and sysexit
(またはsyscall
andsysret
リング0に速く進入を可能命令、AMDの上)。
この割り込みは、OSによって意味が異なる可能性があります。必ずドキュメントを確認してください。
eax
システムコール番号にのみ使用されます。 asm.sourceforge.net/intro/hello.html
これは、CPUに割り込みベクトル0x80をアクティブにするように指示します。これは、Linux OSではシステムコール割り込みであり、open()
ファイルなどのシステム関数を呼び出すために使用されます。
intは中断に他なりません。つまり、プロセッサは現在の実行を保留にします。
0x80は、システムコールまたはカーネルコールに他なりません。つまり、システム機能が実行されます。
具体的には、0x80はrt_sigtimedwait / init_module / restart_sysを表し、アーキテクチャごとに異なります。
詳細については、https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.mdを参照してください 。