Ai đó có thể giải thích những gì mã lắp ráp sau đây làm gì?
int 0x80
Ai đó có thể giải thích những gì mã lắp ráp sau đây làm gì?
int 0x80
Câu trả lời:
Nó vượt qua điều khiển để ngắt vectơ 0x80
Xem http://en.wikipedia.org/wiki/Interrupt_vector
Trên Linux, hãy xem điều này : nó được sử dụng để xử lý system_call
. Tất nhiên trên một hệ điều hành khác, điều này có thể có nghĩa là một cái gì đó hoàn toàn khác.
int 0x80
như một kiểu đặc biệt của call
một hàm trong hạt nhân (được chọn bởi eax
).
int
nghĩa là ngắt, và number 0x80
là số ngắt. Một ngắt chuyển luồng chương trình cho bất kỳ ai đang xử lý ngắt đó, ngắt 0x80
trong trường hợp này. Trong Linux, 0x80
trình xử lý ngắt là hạt nhân và được sử dụng để thực hiện các cuộc gọi hệ thống tới hạt nhân bởi các chương trình khác.
Kernel được thông báo về lệnh gọi hệ thống mà chương trình muốn thực hiện, bằng cách kiểm tra giá trị trong thanh ghi %eax
(cú pháp AT&T và EAX trong cú pháp Intel). Mỗi lệnh gọi hệ thống có các yêu cầu khác nhau về việc sử dụng các thanh ghi khác. Ví dụ: giá trị 1
in %eax
có nghĩa là một lệnh gọi hệ thống exit()
và giá trị trong %ebx
giữ giá trị của mã trạng thái cho exit()
.
Hãy ghi nhớ rằng 0x80
=80h
=128
Bạn có thể thấy ở đây mà INT
chỉ là một trong rất nhiều hướng dẫn (thực sự là đại diện hội Ngôn ngữ (hay tôi nên nói 'ghi nhớ') của nó) mà tồn tại trong tập lệnh x86. Bạn cũng có thể tìm thêm thông tin về hướng dẫn này trong sách hướng dẫn riêng của Intel tại đây .
Tóm tắt từ PDF:
INT n / INTO / INT 3 — Thủ tục gọi đến ngắt
Lệnh INT n tạo ra một cuộc gọi tới trình xử lý ngắt hoặc ngoại lệ được chỉ định với toán hạng đích. Toán hạng đích chỉ định một vectơ từ 0 đến 255, được mã hóa dưới dạng giá trị trung gian 8 bit không dấu. Lệnh INT n là cách ghi nhớ chung để thực hiện lệnh gọi do phần mềm tạo ra tới trình xử lý ngắt.
Như bạn có thể thấy 0x80 là toán hạng đích trong câu hỏi của bạn. Tại thời điểm này, CPU biết rằng nó sẽ thực thi một số mã nằm trong Kernel, nhưng mã nào? Điều đó được xác định bởi Vectơ ngắt trong Linux.
Một trong những ngắt phần mềm DOS hữu ích nhất là ngắt 0x21. Bằng cách gọi nó với các tham số khác nhau trong thanh ghi (chủ yếu là ah và al), bạn có thể truy cập các hoạt động IO khác nhau, đầu ra chuỗi và hơn thế nữa.
Hầu hết các hệ thống Unix và các dẫn xuất không sử dụng ngắt phần mềm, ngoại trừ ngắt 0x80, được sử dụng để thực hiện các cuộc gọi hệ thống. Điều này được thực hiện bằng cách nhập giá trị 32-bit tương ứng với một chức năng hạt nhân vào thanh ghi EAX của bộ xử lý và sau đó thực thi INT 0x80.
Vui lòng xem điều này, nơi các giá trị có sẵn khác trong bảng trình xử lý ngắt được hiển thị:
Như bạn có thể thấy bảng chỉ CPU để thực hiện lệnh gọi hệ thống. Bạn có thể tìm thấy bảng Cuộc gọi Hệ thống Linux tại đây .
Vì vậy, bằng cách di chuyển giá trị 0x1 vào thanh ghi EAX và gọi INT 0x80 trong chương trình của bạn, bạn có thể làm cho quá trình thực thi mã trong Kernel sẽ dừng (thoát) quá trình đang chạy hiện tại (trên Linux, CPU Intel x86).
Không được nhầm lẫn ngắt phần cứng với ngắt phần mềm. Đây là một câu trả lời rất tốt về vấn đề này.
Đây cũng là một nguồn tốt.
int 0x80
i386 hệ thống Linux gọi ABI là cực kỳ giống với DOS int 0x21
ABI. Đặt một số cuộc gọi vào một thanh ghi (AH cho DOS, EAX cho Linux) và các args khác trong các thanh ghi khác, sau đó chạy một lệnh ngắt phần mềm. Sự khác biệt chính là ở những gì mà các lệnh gọi hệ thống cho phép bạn thực hiện (truy cập trực tiếp vào phần cứng trong DOS chứ không phải Linux) chứ không phải ở cách bạn gọi chúng.
/usr/include/x86_64-linux-gnu/asm/unistd_64.h
Ví dụ về lệnh gọi hệ thống Linux có thể chạy tối thiểu
Linux thiết lập trình xử lý ngắt để 0x80
nó thực hiện các lệnh gọi hệ thống, một cách để các chương trình vùng người dùng giao tiếp với hạt nhân.
.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
Biên dịch và chạy với:
as -o main.o main.S
ld -o main.out main.o
./main.out
Kết quả: chương trình in ra stdout:
hello world
và thoát ra ngoài sạch sẽ.
Bạn không thể đặt trình xử lý ngắt của riêng mình trực tiếp từ vùng người dùng vì bạn chỉ có vòng 3 và Linux ngăn bạn làm như vậy .
GitHub ngược dòng . Đã thử nghiệm trên Ubuntu 16.04.
Các lựa chọn thay thế tốt hơn
int 0x80
đã được thay thế bởi các lựa chọn thay thế tốt hơn để thực hiện các cuộc gọi hệ thống: đầu tiên sysenter
, sau đó là VDSO.
x86_64 có một syscall
hướng dẫn mới .
Xem thêm: "int 0x80" hay "syscall" thì tốt hơn?
Ví dụ tối thiểu 16 bit
Trước tiên, hãy tìm hiểu cách tạo một hệ điều hành bootloader tối thiểu và chạy nó trên QEMU và phần cứng thực như tôi đã giải thích ở đây: https://stackoverflow.com/a/32483545/895245
Bây giờ bạn có thể chạy ở chế độ thực 16 bit:
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
Điều này sẽ làm theo thứ tự:
Do 0.
Do 1.
hlt
: ngừng thực hiệnLưu ý cách bộ xử lý tìm kiếm trình xử lý đầu tiên tại địa chỉ 0
và trình xử lý thứ hai tại 4
: đó là một bảng trình xử lý được gọi là IVT và mỗi mục nhập có 4 byte.
Ví dụ tối thiểu thực hiện một số IO để hiển thị các trình xử lý.
Ví dụ về chế độ bảo vệ tối thiểu
Hệ điều hành hiện đại chạy ở chế độ được gọi là được bảo vệ.
Việc xử lý có nhiều lựa chọn hơn trong chế độ này, vì vậy nó phức tạp hơn, nhưng tinh thần là như nhau.
Bước quan trọng là sử dụng các lệnh LGDT và LIDT, các lệnh này trỏ đến địa chỉ của cấu trúc dữ liệu trong bộ nhớ (Bảng mô tả ngắt) mô tả các trình xử lý.
int 0x80 là lệnh hợp ngữ được sử dụng để gọi các lệnh gọi hệ thống trong Linux trên bộ xử lý x86 (tức là tương thích với Intel).
Lệnh "int" gây ra ngắt.
Câu trả lời đơn giản: Nói một cách đơn giản, ngắt là một sự kiện làm ngắt CPU và yêu cầu nó chạy một tác vụ cụ thể.
Câu trả lời chi tiết :
CPU có một bảng các Quy trình Dịch vụ Ngắt (hoặc ISR) được lưu trữ trong bộ nhớ. Trong Chế độ thực (16-bit), điều này được lưu trữ dưới dạng IVT hoặc I nterrupt V ector T có thể. IVT thường được đặt tại 0x0000:0x0000
(địa chỉ thực0x00000
), và nó là một loạt các địa chỉ bù phân đoạn trỏ đến ISR. Hệ điều hành có thể thay thế các mục IVT đã có từ trước bằng ISR của riêng nó.
(Lưu ý: Kích thước của IVT được cố định ở 1024 (0x400) byte.)
Trong Chế độ được bảo vệ (32-bit), CPU sử dụng IDT. IDT là một cấu trúc có độ dài thay đổi bao gồm các bộ mô tả (còn được gọi là các cổng), cho CPU biết về các bộ xử lý ngắt. Cấu trúc của các bộ mô tả này phức tạp hơn nhiều so với các mục bù phân đoạn đơn giản của IVT; nó đây:
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 có thể có kích thước thay đổi, nhưng nó phải tuần tự, tức là nếu bạn khai báo IDT của mình là từ 0x00 đến 0x50, bạn phải có mọi ngắt từ 0x00 đến 0x50. Hệ điều hành không nhất thiết phải sử dụng tất cả chúng, vì vậy bit Hiện tại cho phép CPU xử lý đúng các ngắt mà hệ điều hành không có ý định xử lý.
Khi một ngắt xảy ra (hoặc bởi một bộ kích hoạt bên ngoài (ví dụ như một thiết bị phần cứng) trong IRQ hoặc bởi int
lệnh từ một chương trình), CPU sẽ đẩy EFLAGS, sau đó là CS và sau đó là EIP. (Chúng được tự động khôi phục bằng iret
lệnh trả về ngắt.) Hệ điều hành thường lưu trữ thêm thông tin về trạng thái của máy, xử lý ngắt, khôi phục trạng thái máy và tiếp tục.
Trong nhiều HĐH * NIX (bao gồm cả Linux), các lệnh gọi hệ thống dựa trên ngắt. Chương trình đặt các đối số cho lệnh gọi hệ thống trong các thanh ghi (EAX, EBX, ECX, EDX, v.v.) và gọi ngắt 0x80. Kernel đã thiết lập IDT để chứa một trình xử lý ngắt trên 0x80, được gọi khi nó nhận được ngắt 0x80. Sau đó, hạt nhân đọc các đối số và gọi một hàm nhân tương ứng. Nó có thể lưu trữ lợi nhuận trong EAX / EBX. Các lệnh gọi hệ thống phần lớn đã được thay thế bằng sysenter
và sysexit
(hoặc syscall
vàsysret
trên AMD), cho phép vào vòng 0 nhanh hơn.
Ngắt này có thể có một ý nghĩa khác trong một hệ điều hành khác. Hãy chắc chắn để kiểm tra tài liệu của nó.
eax
được sử dụng cho số syscall. asm.sourceforge.net/intro/hello.html
Như đã đề cập, nó làm cho điều khiển nhảy tới vectơ ngắt 0x80. Trong thực tế, điều này có nghĩa là gì (ít nhất là trong Linux) là một lệnh gọi hệ thống được gọi ra; lệnh gọi hệ thống chính xác và các đối số được xác định bởi nội dung của các thanh ghi. Ví dụ: exit () có thể được gọi bằng cách đặt% eax thành 1, theo sau là 'int 0x80'.
Nó yêu cầu cpu kích hoạt vector ngắt 0x80, trên hệ điều hành Linux là ngắt lệnh gọi hệ thống, được sử dụng để gọi các chức năng hệ thống như open()
đối với tệp, v.v.
int không là gì khác ngoài một sự gián đoạn, tức là bộ xử lý sẽ đặt quá trình thực thi hiện tại của nó để giữ.
0x80 không là gì ngoài một lệnh gọi hệ thống hoặc lệnh gọi hạt nhân. tức là chức năng hệ thống sẽ được thực thi.
Cụ thể, 0x80 đại diện cho rt_sigtimedwait / init_module / restart_sys, nó thay đổi theo từng kiến trúc.
Để biết thêm chi tiết, hãy tham khảo https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md