인터럽트 프레임이란 무엇인지, 그리고 do_iret 에서는 어떤 일을 하는 건지 이제서야 조금 알 것 같아 별도로 글을 작성한다.
do_iret을 이해하려면 인터럽트 프레임 구조체의 크기와, 인터럽트 프레임 구조체 내의 gp_registers 구조체, 그리고 나머지 멤버변수들의 크기를 알고있어야 한다.
각각의 사이즈를 모두 출력해보았다. (뭘 굳이 찍어봐야 아나 싶은 것들도 있지만 난 아직도 데이터 크기가 어렵다 😭 )
printf( "인터럽트 프레임 사이즈: %d \\n", sizeof(struct intr_frame));
printf( "gp_registers 사이즈: %d \\n", sizeof(struct gp_registers));
printf( "uint16_t 사이즈: %d \\n", sizeof(uint16_t));
printf( "uint32_t 사이즈: %d \\n", sizeof(uint32_t));
printf( "uintptr_t 사이즈: %d \\n", sizeof(uintptr_t));
이렇게 찍으면 아래와 같이 출력된다. 이 정보들을 잘 기억하고 있자.
인터럽트 프레임 사이즈: 192
gp_registers 사이즈: 120
uint16_t 사이즈: 2
uint32_t 사이즈: 4
uintptr_t 사이즈: 8
인터럽트 프레임 구조체는 아래와 같이 정의되어있다. 위에서 확인한 바와 같이 이 구조체는 192 바이트 크기를 가지고 있고, 첫번째 멤버변수는 gp_registers 라는 구조체는 120 바이트 크기를 가진다.
struct intr_frame {
/* Pushed by intr_entry in intr-stubs.S.
These are the interrupted task's saved registers. */
struct gp_registers R; // 범용 레지스터 - 120 바이트
uint16_t es;
uint16_t __pad1;
uint32_t __pad2;
uint16_t ds; // 세그먼트 관리
uint16_t __pad3;
uint32_t __pad4;
/* Pushed by intrNN_stub in intr-stubs.S. */
uint64_t vec_no; /* Interrupt vector number. 인터럽트 종류 */
/* Sometimes pushed by the CPU,
otherwise for consistency pushed as 0 by intrNN_stub.
The CPU puts it just under `eip', but we move it here. */
uint64_t error_code;
/* Pushed by the CPU.
These are the interrupted task's saved registers. */
uintptr_t rip; // pc
uint16_t cs; // 세그먼트 관리
uint16_t __pad5;
uint32_t __pad6;
uint64_t eflags;// cpu 상태를 나타내는 정보
uintptr_t rsp; // 스택 포인터
uint16_t ss;
uint16_t __pad7;
uint32_t __pad8;
} __attribute__((packed));
gp_registers 는 아래와 같이 정의된다.
/* Interrupt stack frame. */
struct gp_registers {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rsi;
uint64_t rdi;
uint64_t rbp;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
} __attribute__((packed));
자리에 리버싱 책이 있는 우리 반 신 교수님이 인터럽트 프레임 구조체의 모습을 그려놓은 게 있어서 허락을 받고 가져와봤다.