Hướng Dẫn Assembly 64bit (Tái Bản)
Bài viết được sự cho phép của tác giả Trần Thiện Khiêm
Nhiều bạn hỏi mình lập trình viên nên học ngôn ngữ gì, hôm nay mình xin giới thiệu một ngôn ngữ khá quen thuộc..
LỜI NÓI ĐẦU
Suốt hàng ngàn năm, con người đã sử dụng các ngôn ngữ lập trình để bắt máy tính phải “hiểu” mình và làm theo mệnh lệnh của mình. Các bạn đừng bị thị trường lừa dối, bởi vì máy tính vốn chỉ hiểu một ngôn ngữ duy nhất, đó là ngôn ngữ máy (machine code). Và trong khi các bạn diễn giải ý định của mình bằng java hay c# thì máy tính hoàn toàn không hiểu được. Và phải qua các bước biên dịch phức tạp thì máy mới hiểu được bạn muốn nói gì. Nhưng mà “tam sao thất bản”, chắc chắn máy tính sẽ không hiểu đúng 100% bạn ý định nói gì đâu. Mà coi như là máy tính hiểu bạn rồi, thì khi nào bạn sẽ hiểu máy tính?
Nếu bạn là người yêu máy tính, thích lập trình thì hãy nhanh chân học ngôn ngữ mà máy cũng hiểu và bạn cũng hiểu. Mình sẽ không bắt các bạn phải học mã máy, nhưng chí ít bạn nên học Assemb-Ly vì với máy tính, như thế là tuyệt lắm rồi. Assembly – Machine code nó tương đương 1-1 nên nếu biết assembly thì cũng tạm chấp nhận.
Đối với các bạn học bách khoa thì quả thật là may mắn vì các bạn đã được học assembly. Nhưng mà chắc là đã quên hết rồi nên trong bài này mình sẽ hướng dẫn lại căn bản cho các bạn.
Trong bài viết sử dụng MacOS và Xcode để lập trình nên bạn nào không có thì sử dụng trí tưởng tượng phong phú của mình.
CĂN BẢN ASSEMBLY
Data size: Các khái niệm cơ bản về kích thước của dữ liệu khi làm việc: byte (8 bit) -> word (16 bit) -> double words (32 bit) -> quad words 64 bit). Mình chỉ làm việc với 2 toán tử có kích thước tương đương.
Thanh ghi: Là các thanh nhớ đặc biệt dùng để tính toán. Khi tính toán thì chỉ thao tác với giá trị trên thanh ghi. (các thanh ghi general các bạn từng học như ax <=> al: (8 bit); ax: (16 bit); eax (32 bit); rax (64 bit))
Lưu trữ dữ liệu: Ngoài thanh ghi, có 2 vùng nhớ là stack và heap.
Lệnh: có các nhóm lệnh: đọc ghi dữ liệu / tính toán / điều khiển (lệnh nhảy – gọi hàm).
Nhãn (label): Mình có thể đánh dấu các câu lệnh, vùng nhớ bằng nhãn để có thể tham chiếu sau này.
Cú pháp: [<nhãn>:] <mã lệnh> nguồn[, đích]
Ví dụ:
movl $1, %eax; // gán eax = 1
move $1 to %eax (l nghĩa là kiểu long (4 bytes), b = bytes, w = word (2 bytes), q = quad words (8 bytes))
HELLO WORLD
Để bắt đầu, các bạn mở Xcode và tạo 1 dự án Command Line Tool, chọn ngôn ngữ là C. Bước tiếp theo, đổi tên main.c thành main.s và xoá hết code C trong main.s. .s là file chứa mã nguồn assembly.
Lúc biên dịch, bạn sẽ nhận được một thông báo lỗi như sau:
Undefined symbols for architecture x86_64:
“_main”, referenced from:
implicit entry/start for main executable
Thông báo lỗi này do chưa viết hàm main.
Chương trình assembly được chia thành nhiều đoạn (segment), cấu trúc căn bản như sau:
.section __DATA, __data // section data chứa data sử dụng trong chương trình .section __TEXT, __text // section text chứa code trong chương trình
Chương trình muốn chạy được thì phải có symbol _main, do đó ta thêm symbol _main vào trong chương trình.
.section __DATA, __data // section that stores data .section __TEXT, __text // section that stores executable code .global _main _main:
Chỉ thị global _main đăng kí với trình biên dịch symbol _main sẽ được public trong giai đoạn liên kết.
Bây giờ chúng ta đã biên dịch thành công, tuy nhiên lúc chạy chương trình sẽ gặp lỗi sau:
Thread 1: EXC_BAD_ACCESS
Hàm main chưa có gì thì làm sao mà chạy? làm sao mà chạy?
Lý do là hàm main hiện tại chúng ta chưa viết gì cả. Vậy hàm main sẽ làm gì? Chúng ta sẽ thử ra chỉ thị kết thúc chương trình.
_main: movl $0x2000001, %eax syscall
Nếu như trong MS DOS các bạn quen với interrupt (ngắt hệ thống) thì ở hệ thống 64 bit đó là syscall, các chương trình con của hệ thống. $0x2000001 là system exit(). Các bạn có thể tham khảo các chương trình con này ở
http://opensource.apple.com//source/xnu/xnu-1228.5.20/bsd/sys/syscall.h.
– Định nghĩa + tham số tương đương ở đây: http://opensource.apple.com//source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master
Như vậy chương trình của chúng ta đã chạy. Nhưng nó kết thúc ngay lập tức chưa kịp làm gì cả. Vậy làm sao để in ra chữ HelloWorld?
Nhìn trong file mình vưà gửi thì syscall 4 sẽ in viết một thông điệp ra dòng xuất chuẩn. Có 3 tham số : 1 là kí hiệu của dòng xuất (output stream) và 2 là thông điệp, 3 là kích thước của thông điệp
Ta chỉnh sửa lại chương trình như sau:
.section __DATA, __data helloMessage: .asciz "Hello World\n" .section __TEXT, __text .global _main _main: movl $0x2000004, %eax // the print syscall function movl $1, %edi // standard output = 1 movq helloMessage@GOTPCREL(%rip), %rsi // address to message movq $100, %rdx // size of the message syscall movl $0x2000001, %eax // the exit syscall function movl $0, %ebx // exit code == 0 syscall
Ta vừa thêm dữ liệu chứa dòng helloMessage vào segment data với nội dung là “Hello World\n”. Lúc biên dịch, nội dung này cũng sẽ được copy vào file thực thi, và lúc chạy sẽ hiện được tải vào data segment.
Để lấy địa chỉ của helloMessage, ta sử dụng cú pháp helloMessage@GOTPCREL(%rip), tương tự với toán tử lấy địa chỉ trong C: &helloMessage, sau đó ta gán vào tham số thứ 2, thanh ghi %rsi. Tham số thứ 3 là kích thước của thông điệp, ghi đại 0x100.
Sau khi gọi chương trình con số 4 của syscall, màn hình sẽ in ra dòng “Hello World”. Sau đó ta gọi chương trình con số 1 của syscall để kết thúc chương trình. Vậy là ta đã có chương trình “Hello World” đầu tiên. Xin chúc mừng!
!!!. Bây giờ bạn có thể yên tâm lên LinkedIn và cập nhật CV master Assembly.
Hẹn các bạn trong những bài viết tiếp theo về Assembly.
Bài viết gốc được đăng tải tại fanpage Khiem Tran – Programmer
Xem thêm:
- Stack Overflow Là Gì? Vì Sao Đệ Quy Lại Dễ Gây Tràn Stack
- Lập trình viên có nên học Assembly không?
- Coffee Talk: Anh Trần Thiện Khiêm – Từ Việt Nam đến GAFA
Đừng bỏ lỡ việc làm IT hàng đầu dành cho Top Devlopers tại TopDev!
- G Giải Quyết Bài Toán Kinh Doanh Bằng Big Data và AI
- B BenQ RD Series – Dòng Màn Hình Lập Trình 4k+ Đầu Tiên Trên Thế Giới
- F Framework nào tốt nhất cho dự án của bạn? – Checklist chi tiết
- K Kinh nghiệm xử lý responsive table hiệu quả
- S Stackoverflow là gì? Bí kíp tận dụng Stack Overflow hiệu quả
- 7 7 kinh nghiệm hữu ích khi làm việc với GIT trong dự án
- B Bài tập Python từ cơ bản đến nâng cao (có lời giải)
- B Bảo mật API là gì? Một số nguyên tắc và kỹ thuật cần biết
- H Hướng dẫn cài đặt và tự học lập trình Python cơ bản từ A-Z
- C Chinh Phục Phân Tích Dữ Liệu Với Pandas Trong Python: Hướng Dẫn Từng Bước