어셈블리 코드에서 "int 0x80"은 무엇을 의미합니까?


답변:


69

제어를 인터럽트 벡터 0x80으로 전달합니다.

http://en.wikipedia.org/wiki/Interrupt_vector 참조

Linux에서 이것을 보세요 : system_call. 물론 다른 OS에서는 완전히 다른 의미 일 수 있습니다.


5
짧게 짧게 말하면 지침은 DO IT 가 이전에 있었다는 것을 의미 합니다.
— Yuda Prawira 2011

2
@YudaPrawira : 이전 지침을 레지스터에 인수를 설정하는 것으로 생각해야 하며 커널의 함수에 int 0x80대한 특별한 종류 call(에서 선택 eax)로 생각해야합니다.
— Peter Cordes

왜 "사용 했어?"라고 말했습니까? 더 이상 사용되지 않습니까?
— Liga

129

int인터럽트를 의미하고 번호 0x80는 인터럽트 번호입니다. 인터럽트는 인터럽트를 처리하는 사람에게 프로그램 흐름을 전송합니다 0x80.이 경우에는 인터럽트 입니다. Linux에서 0x80인터럽트 처리기는 커널이며 다른 프로그램에서 커널에 대한 시스템 호출을 수행하는 데 사용됩니다.

커널은 레지스터의 값 %eax(AT & T 구문 및 Intel 구문의 EAX)을 검사하여 프로그램이 만들고자하는 시스템 호출에 대한 알림을받습니다 . 각 시스템 호출에는 다른 레지스터 사용에 대한 요구 사항이 다릅니다. 예를 들어 1in 값은 %eax의 시스템 호출을 의미하고 exit()in %ebx값은에 대한 상태 코드 값을 보유합니다 exit().


47

명심 0x80= 80h=128

당신은 볼 수 있습니다 여기에 그 INTx86 명령어 세트에 존재하는 바로 많은 지시 사항 중 하나입니다 (실제로 어셈블리 언어 표현 (또는 나는 그것을 '연상')라고한다). 이 지침에 대한 자세한 내용은 여기 에서 찾을 수있는 인텔의 자체 설명서에서도 찾을 수 있습니다 .

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)를 중지 (종료)하는 커널의 코드를 프로세스가 실행하도록 할 수 있습니다.

하드웨어 인터럽트를 소프트웨어 인터럽트와 혼동해서는 안됩니다. 다음은 이 점에 아주 좋은 대답이다.

이것은 또한 좋은 소스입니다.


4
리눅스 시스템 호출 테이블 링크 \ = 고장
— 미겔 안젤로에게

1
대부분의 유닉스 시스템과 파생물은 소프트웨어 인터럽트를 사용하지 않습니다 (int 0x80 제외) . int 0x80I386 리눅스 시스템 호출 ABI는 DOS 매우 유사하다 int 0x21ABI. 레지스터에 호출 번호 (DOS의 경우 AH, Linux의 경우 EAX)를 입력하고 다른 레지스터에 다른 인수를 입력 한 다음 소프트웨어 인터럽트 명령을 실행합니다. 주요 차이점은 시스템 호출을 통해 수행 할 수있는 작업 (리눅스가 아닌 DOS에서 하드웨어에 직접 액세스)이 아니라 호출 방식에 있습니다.
— Peter Cordes

다음은 깨지지 않은 syscall 테이블 링크입니다. syscalls.kernelgrok.com 상단에 모든 통화를 표시하려면 확장하기 만하면됩니다.
— ollien

Linux 64 비트를 사용하는 경우/usr/include/x86_64-linux-gnu/asm/unistd_64.h
— ton

11

최소 실행 가능한 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 명령어를 사용하는 것입니다.

최소한의 예



4

"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에서 다른 의미를 가질 수 있습니다. 설명서를 확인하십시오.


재미있는 사실 : FreeBSD의 i386 시스템 호출 ABI는 사용자 공간 스택에 인수를 전달합니다. eaxsyscall 번호 에만 사용됩니다. asm.sourceforge.net/intro/hello.html
— 피터 코르

2

언급했듯이 제어가 인터럽트 벡터 0x80으로 점프하도록합니다. 실제로 이것이 의미하는 것은 (적어도 Linux에서는) 시스템 호출이 호출된다는 것입니다. 정확한 시스템 호출과 인수는 레지스터의 내용에 의해 정의됩니다. 예를 들어, % eax를 1로 설정 한 다음 'int 0x80'을 설정하여 exit ()를 호출 할 수 있습니다.


1

CPU에게 인터럽트 벡터 0x80을 활성화하도록 지시합니다. Linux OS open()에서는 파일 등의 시스템 기능을 호출하는 데 사용되는 시스템 호출 인터럽트 입니다.


9
엄밀히 말하면 커널에게 알려주지 않습니다. CPU에 알려주고, IDT에서 핸들러를 조회하여 일부 커널 코드에 대한 포인터가됩니다.
— asveikau

진실. 더 나은 표현은 CPU에 벡터를 활성화하도록 지시하고 벡터 (커널의 일부)가 함수를 호출하는 것입니다.
— Amber

이는 다음 다음 간다 어떤이를 수행하는, 그 일을 끝을 어떤 inturn하는이, 일을 끝 혼동을 . : / 앰버는 .. understandable..thats 인 답이있다
— Afzaal 아마드 Zeeshan

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.