누군가 다음 어셈블리 코드의 기능을 설명 할 수 있습니까?
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 명령어 세트에 존재하는 바로 많은 지시 사항 중 하나입니다 (실제로 어셈블리 언어 표현 (또는 나는 그것을 '연상')라고한다). 이 지침에 대한 자세한 내용은 여기 에서 찾을 수있는 인텔의 자체 설명서에서도 찾을 수 있습니다 .
PDF에서 요약하면 :
INT n / INTO / INT 3— 인터럽트 프로 시저 호출
INT n 명령어는 대상 피연산자로 지정된 인터럽트 또는 예외 처리기에 대한 호출을 생성합니다. 대상 피연산자는 8 비트 부호없는 중간 값으로 인코딩 된 0에서 255 사이의 벡터를 지정합니다. INT n 명령어는 인터럽트 핸들러에 대한 소프트웨어 생성 호출을 실행하기위한 일반적인 니모닉입니다.
보시다시피 0x80 은 질문 의 대상 피연산자 입니다. 이 시점에서 CPU는 커널에있는 일부 코드를 실행해야한다는 것을 알고 있지만 어떤 코드입니까? 이는 Linux의 인터럽트 벡터에 의해 결정됩니다.
가장 유용한 DOS 소프트웨어 인터럽트 중 하나는 인터럽트 0x21입니다. 레지스터의 다른 매개 변수 (대부분 ah 및 al)로 호출하면 다양한 IO 작업, 문자열 출력 등에 액세스 할 수 있습니다.
대부분의 Unix 시스템 및 파생 제품은 시스템 호출에 사용되는 인터럽트 0x80을 제외하고 소프트웨어 인터럽트를 사용하지 않습니다. 이는 커널 함수에 해당 하는 32 비트 값을 프로세서의 EAX 레지스터에 입력 한 다음 INT 0x80을 실행하여 수행됩니다.
인터럽트 처리기 테이블에서 사용 가능한 다른 값이 표시되는 곳을 살펴보십시오.
표에서 볼 수 있듯이 CPU는 시스템 호출을 실행합니다. 여기 에서 Linux 시스템 호출 테이블을 찾을 수 있습니다 .
따라서 값 0x1을 EAX 레지스터로 이동하고 프로그램에서 INT 0x80을 호출하여 현재 실행중인 프로세스 (Linux, x86 Intel CPU)를 중지 (종료)하는 커널의 코드를 프로세스가 실행하도록 할 수 있습니다.
하드웨어 인터럽트를 소프트웨어 인터럽트와 혼동해서는 안됩니다. 다음은 이 점에 아주 좋은 대답이다.
이것은 또한 좋은 소스입니다.
int 0x80
I386 리눅스 시스템 호출 ABI는 DOS 매우 유사하다 int 0x21
ABI. 레지스터에 호출 번호 (DOS의 경우 AH, Linux의 경우 EAX)를 입력하고 다른 레지스터에 다른 인수를 입력 한 다음 소프트웨어 인터럽트 명령을 실행합니다. 주요 차이점은 시스템 호출을 통해 수행 할 수있는 작업 (리눅스가 아닌 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 업스트림 . 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
: 실행 중지프로세서가 address에서 첫 번째 핸들러를 찾는 방법 0
과 두 번째 핸들러를 찾는 방법에 유의하십시오 4
. 이는 IVT 라는 핸들러 테이블 이며 각 항목에는 4 바이트가 있습니다.
핸들러를 표시하기 위해 일부 IO 를 수행하는 최소 예제입니다 .
최소 보호 모드 예
최신 운영 체제는 소위 보호 모드에서 실행됩니다.
이 모드에서는 핸들링에 더 많은 옵션이 있으므로 더 복잡하지만 정신은 동일합니다.
핵심 단계는 처리기를 설명하는 메모리 내 데이터 구조 (인터럽트 설명자 테이블)의 주소를 가리키는 LGDT 및 LIDT 명령어를 사용하는 것입니다.
int 0x80은 x86 (즉, Intel 호환) 프로세서의 Linux에서 시스템 호출을 호출하는 데 사용되는 어셈블리 언어 명령어입니다.
"int"명령은 인터럽트를 발생시킵니다.
간단한 답변 : 간단히 말해 인터럽트는 CPU를 중단하고 특정 작업을 실행하도록 지시하는 이벤트입니다.
자세한 답변 :
CPU에는 메모리에 저장된 인터럽트 서비스 루틴 (또는 ISR) 테이블이 있습니다. 리얼 (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을 수신 할 때 호출되는 0x80의 인터럽트 핸들러를 포함하도록 IDT를 설정했습니다. 그런 다음 커널은 인수를 읽고 그에 따라 커널 함수를 호출합니다. EAX / EBX에 반품을 저장할 수 있습니다. 시스템 호출은 크게 의해 대체되었습니다 sysenter
및 sysexit
(또는 syscall
과 sysret
링 0으로 빠른 진입을 허용 지침, AMD에).
이 인터럽트는 다른 OS에서 다른 의미를 가질 수 있습니다. 설명서를 확인하십시오.
eax
syscall 번호 에만 사용됩니다. 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를 참조 하세요.