Tôi đã làm điều này nhiều lần và tiếp tục làm điều này. Trong trường hợp mục tiêu chính của bạn là đọc và không viết trình dịch hợp ngữ, tôi cảm thấy điều này áp dụng.
Viết trình tháo gỡ của riêng bạn. Không nhằm mục đích chế tạo bộ tháo lắp tuyệt vời nhất tiếp theo, cái này hoàn toàn dành cho bạn. Mục đích là học tập hướng dẫn. Cho dù tôi đang học trình lắp ráp trên một nền tảng mới, hãy nhớ trình lắp ráp cho một nền tảng mà tôi từng biết. Bắt đầu chỉ với một vài dòng mã, thêm các thanh ghi chẳng hạn, và chơi bóng bàn giữa việc tháo rời đầu ra nhị phân và thêm các hướng dẫn ngày càng phức tạp hơn ở phía đầu vào, bạn:
1) tìm hiểu bộ hướng dẫn cho bộ xử lý cụ thể
2) tìm hiểu các sắc thái của cách viết mã trong lắp ráp cho bộ xử lý nói trên để bạn có thể sử dụng từng bit opcode trong mọi hướng dẫn
3) bạn học tập hướng dẫn tốt hơn mà hầu hết các kỹ sư sử dụng tập hướng dẫn đó để kiếm sống
Trong trường hợp của bạn, có một số vấn đề, tôi thường khuyên bạn nên bắt đầu tập lệnh ARM, ngày nay có nhiều sản phẩm dựa trên ARM được xuất xưởng hơn bất kỳ sản phẩm nào khác (bao gồm cả máy tính x86). Nhưng có khả năng bạn đang sử dụng ARM bây giờ và không biết đủ trình hợp dịch để nó viết mã khởi động hoặc các quy trình khác khi biết ARM có thể có hoặc có thể không giúp ích cho những gì bạn đang cố gắng thực hiện. Lý do thứ hai và quan trọng hơn đối với ARM đầu tiên là vì độ dài lệnh có kích thước cố định và được căn chỉnh. Việc tháo rời các hướng dẫn có độ dài thay đổi như x86 có thể là một cơn ác mộng khi là dự án đầu tiên của bạn và mục tiêu ở đây là tìm hiểu tập lệnh không phải để tạo một dự án nghiên cứu. ARM thứ ba là một tập lệnh được thực hiện tốt, các thanh ghi được tạo ra bằng nhau và không có các sắc thái đặc biệt riêng lẻ.
Vì vậy, bạn sẽ phải tìm ra bộ xử lý bạn muốn bắt đầu với. Tôi đề xuất msp430 hoặc ARM đầu tiên, sau đó là ARM đầu tiên hoặc thứ hai rồi đến sự hỗn loạn của x86. Bất kể nền tảng nào, bất kỳ nền tảng nào đáng sử dụng đều có bảng dữ liệu hoặc hướng dẫn sử dụng lập trình viên tham khảo miễn phí từ nhà cung cấp, bao gồm tập lệnh cũng như mã hóa các mã opcodes (các bit và byte của ngôn ngữ máy). Với mục đích tìm hiểu trình biên dịch làm gì và cách viết mã mà trình biên dịch không phải vật lộn với nó, tốt hơn là bạn nên biết một vài tập lệnh và xem cách thực hiện cùng một mã cấp cao trên mỗi tập lệnh với mỗi trình biên dịch với mỗi tối ưu hóa. cài đặt. Bạn không muốn đi vào tối ưu hóa mã của mình chỉ để thấy rằng bạn đã làm cho nó tốt hơn cho một trình biên dịch / nền tảng nhưng tệ hơn nhiều cho mọi nền tảng khác.
Ồ để tháo rời các tập lệnh có độ dài thay đổi, thay vì chỉ bắt đầu từ đầu và tháo rời tuyến tính từng từ bốn byte thông qua bộ nhớ như bạn làm với ARM hoặc mỗi hai byte như msp430 (msp430 có các lệnh có độ dài thay đổi nhưng bạn vẫn có thể nhận được bằng cách đi tuyến tính qua bộ nhớ nếu bạn bắt đầu tại các điểm vào từ bảng vectơ ngắt). Đối với độ dài thay đổi, bạn muốn tìm điểm vào dựa trên bảng vectơ hoặc kiến thức về cách bộ xử lý khởi động và làm theo mã theo thứ tự thực thi. Bạn phải giải mã hoàn toàn từng lệnh để biết có bao nhiêu byte được sử dụng, sau đó nếu lệnh đó không phải là một nhánh vô điều kiện, giả sử byte tiếp theo sau lệnh đó là lệnh khác. Bạn cũng phải lưu trữ tất cả các địa chỉ chi nhánh có thể có và giả sử đó là các địa chỉ byte bắt đầu để có thêm hướng dẫn. Một lần tôi đã thành công, tôi đã thực hiện một số lần chuyển qua nhị phân. Bắt đầu từ điểm nhập, tôi đánh dấu byte đó là phần bắt đầu của một lệnh sau đó được giải mã tuyến tính thông qua bộ nhớ cho đến khi chạm vào một nhánh vô điều kiện. Tất cả các mục tiêu nhánh đã được gắn thẻ là địa chỉ bắt đầu của một lệnh. Tôi đã thực hiện nhiều lần chuyển qua nhị phân cho đến khi tôi không tìm thấy mục tiêu nhánh mới. Nếu bất kỳ lúc nào bạn tìm thấy lệnh 3 byte nhưng vì lý do nào đó bạn đã gắn thẻ byte thứ hai là phần đầu của lệnh thì bạn gặp sự cố. Nếu mã được tạo bởi trình biên dịch cấp cao, điều này sẽ không xảy ra trừ khi trình biên dịch đang làm điều gì đó xấu xa, nếu mã có trình hợp dịch viết tay (giống như một trò chơi arcade cũ), rất có thể sẽ có các nhánh có điều kiện không bao giờ có thể xảy ra như r0 = 0 theo sau là một bước nhảy nếu không phải là 0. Bạn có thể phải chỉnh sửa thủ công những thứ đó ngoài nhị phân để tiếp tục. Đối với mục tiêu trước mắt của bạn mà tôi cho rằng sẽ là trên x86, tôi không nghĩ rằng bạn sẽ gặp vấn đề.
Tôi khuyên bạn nên sử dụng các công cụ gcc, mingw32 là một cách dễ dàng để sử dụng các công cụ gcc trên Windows nếu x86 là mục tiêu của bạn. Nếu không mingw32 plus msys là một nền tảng tuyệt vời để tạo trình biên dịch chéo từ các nguồn binutils và gcc (nói chung là khá dễ dàng). mingw32 có một số ưu điểm so với cygwin, như các chương trình nhanh hơn đáng kể và bạn tránh được địa ngục cygwin. gcc và binutils sẽ cho phép bạn viết bằng C hoặc trình hợp dịch và tháo rời mã của bạn và có nhiều trang web hơn bạn có thể đọc chỉ cho bạn cách thực hiện bất kỳ một hoặc cả ba. Nếu bạn định làm điều này với tập lệnh có độ dài thay đổi, tôi thực sự khuyên bạn nên sử dụng một bộ công cụ bao gồm bộ tháo rời. Ví dụ, một bên thứ ba tháo gỡ cho x86 sẽ là một thách thức để sử dụng vì bạn không bao giờ thực sự biết liệu nó đã được tháo rời một cách chính xác hay chưa. Một số điều này cũng phụ thuộc vào hệ điều hành, mục tiêu là biên dịch các mô-đun sang định dạng nhị phân chứa các hướng dẫn đánh dấu thông tin từ dữ liệu để trình tháo gỡ có thể thực hiện công việc chính xác hơn. Lựa chọn khác của bạn cho mục tiêu chính này là có một công cụ có thể biên dịch trực tiếp sang trình hợp dịch để bạn kiểm tra, sau đó hy vọng rằng khi nó biên dịch sang định dạng nhị phân, nó sẽ tạo ra các hướng dẫn tương tự.
Câu trả lời ngắn gọn (hơi ngắn HƠN) cho câu hỏi của bạn. Viết một trình tháo gỡ để tìm hiểu một tập lệnh. Tôi sẽ bắt đầu với thứ gì đó RISCy và dễ học như ARM. Khi bạn biết một tập lệnh, những tập lệnh khác sẽ trở nên dễ dàng hơn nhiều, thường là trong vài giờ, đến tập hướng dẫn thứ ba, bạn có thể bắt đầu viết mã gần như ngay lập tức bằng cách sử dụng biểu dữ liệu / hướng dẫn tham khảo cho cú pháp. Tất cả các bộ xử lý đáng sử dụng đều có biểu dữ liệu hoặc sổ tay tham khảo mô tả các hướng dẫn đến từng bit và byte của mã quang. Tìm hiểu bộ xử lý RISC như ARM và CISC như x86 đủ để cảm nhận sự khác biệt, những thứ như phải thông qua các thanh ghi cho mọi thứ hoặc có thể thực hiện các hoạt động trực tiếp trên bộ nhớ với ít hơn hoặc không có thanh ghi. Ba hướng dẫn toán hạng so với hai, v.v. Khi bạn điều chỉnh mã cấp cao của mình, biên dịch cho nhiều bộ xử lý và so sánh kết quả đầu ra. Điều quan trọng nhất bạn sẽ học được là cho dù mã cấp cao được viết tốt như thế nào thì chất lượng của trình biên dịch và các lựa chọn tối ưu hóa được thực hiện tạo ra sự khác biệt rất lớn trong các hướng dẫn thực tế. Tôi đề xuất llvm và gcc (với binutils), không sản xuấtmã tuyệt vời , nhưng chúng là đa nền tảng và đa mục tiêu và cả hai đều có trình tối ưu hóa. Và cả hai đều miễn phí và bạn có thể dễ dàng xây dựng các trình biên dịch chéo từ các nguồn cho các bộ xử lý mục tiêu khác nhau.