2012-06-26 37 views
5

Tôi hiểu giả lập làm gì, thay đổi một ngôn ngữ máy thành ngôn ngữ khác, thường là "Chỉ trong thời gian". Có thể một chương trình như vậy được xây dựng ở chỗ nó đọc trong một nhị phân được viết cho một kiến ​​trúc và lưu một nhị phân mới cho kiến ​​trúc khác. Sau khi quá trình hoàn tất, nó sẽ để người dùng với nhị phân sẵn sàng cho việc thực thi gốc trên kiến ​​trúc đã cho. Điều này sẽ đặc biệt hữu ích cho những người có các ứng dụng độc quyền đắt tiền cho một kiến ​​trúc cũ.Tại sao thi đua phải được thực hiện trong thời gian thực?

Có thể tạo một ứng dụng như vậy không? Biên dịch nhị phân không phải là một khái niệm mới, nhưng tôi chưa tìm thấy bất kỳ triển khai hữu ích nào như vậy.

Với sự giúp đỡ của một số người khác, tôi sẽ vui mừng khi bắt đầu viết mã trên một triển khai mã nguồn mở của một chương trình như vậy, nếu lập trình như vậy là một khả năng.

+1

Nó được thực hiện một vài lần. Ví dụ, FX's DEC! 32 biên dịch lại các x86 nhị phân để chạy trên DEC Alpha, thường nhanh hơn bất kỳ x86 nào trong thời gian có thể. Nó không đủ để bù đắp cho việc quản lý của DEC (mis), và Compaq/HP không quan tâm nhiều đến nó. –

+0

Tại sao bộ mô phỏng lại tồn tại nếu như vậy? Đây có phải là REALLY * đó * khó hơn nhiều so với viết một trình giả lập không? – user1483857

Trả lời

0

Bạn phải bắt đầu bằng cách đảm bảo mọi thư viện được tham chiếu đều được biên dịch lại bằng cách sử dụng tính năng này.

Có thể, nhưng thực hiện rất nhiều.

Cũng xem xét có thể có vấn đề cấp phép bằng cách thực hiện việc này; bạn đang tạo công việc phái sinh dựa trên phần mềm gốc. Hầu hết giấy phép cho phép bạn thực hiện việc này, cũng sẽ cho phép bạn có nguồn, vì vậy bạn có thể chỉ biên dịch lại hoặc chuyển nguồn, dễ dàng hơn.

+0

Vì vậy, điều này sẽ không được tính chính xác như kỹ thuật đảo ngược, phải không? Tôi rất muốn làm một con thú như vậy để có một số tiền lệ trong tòa án. Một ứng dụng hữu ích sẽ chạy các tệp nhị phân PPC nguồn đóng trên OS X Mountain Lion, nơi có nhiều lib cần thiết trong hệ điều hành hoặc có thể được chuyển từ Snow Leopard hoặc Lion. Khác sẽ được giao diện điều khiển thi đua, nơi mà hầu hết các lib là trong trò chơi. – user1483857

+1

Không thực sự, trình biên dịch lại của bạn sẽ: "read-> translate-> write new version". Điều này nghe có vẻ giống như nhiều công việc phái sinh hơn là kỹ thuật đảo ngược. Kỹ thuật đảo ngược là ** hiểu ** những gì nó làm, và viết mã mới thực hiện tương tự. – WhyNotHugo

+0

Một ứng dụng hữu ích sẽ chạy các tệp nhị phân PPC nguồn đóng trên OS X Mountain Lion, nơi có nhiều lib cần thiết trong hệ điều hành hoặc có thể được chuyển từ Snow Leopard hoặc Lion. Khác sẽ được giao diện điều khiển thi đua, nơi mà hầu hết các lib là trong trò chơi. Tôi có gặp rắc rối pháp lý không? – user1483857

3

Tôi tin rằng bạn đang tìm kiếm biên dịch tĩnh và động. Biên dịch lại động là những gì bạn mô tả như là mô phỏng "thời gian thực" hoặc hoàn thành. Mã được biên dịch lại theo các khối cho phép trình giả lập phản ánh chính xác môi trường thời gian chạy của mã ban đầu.

Tính toán lại tĩnh là những gì bạn đang hỏi nếu có thể. Có thể trong nhiều tình huống khác nhau như một số đã chỉ ra, tuy nhiên mã mong đợi các ràng buộc thời gian chạy rất cụ thể có thể không chạy thành công sau khi biên dịch lại tĩnh. Đây là lý do tại sao Corn, Trình giả lập N64 sử dụng tính toán lại tĩnh, chỉ có thể chạy một số trò chơi được tối ưu hóa rất cao, trong khi các trình giả lập N64 khác sử dụng tính năng biên dịch động lại có nhiều trò chơi hơn.

Việc biên dịch tĩnh thực sự có thể cho mã phức tạp và truyền thống hơn (ví dụ x86 đến PowerPC), tuy nhiên việc thực hiện như vậy sẽ cực kỳ tẻ nhạt, vì trình biên dịch sẽ phải sử dụng rất nhiều thủ thuật để có được mã nguồn được tạo ra để chạy đáng tin cậy trên máy mục tiêu. Recompilers động có thể làm điều này một cách nhanh chóng trong thời gian chạy ở một phần của nỗ lực phát triển, và cho một chi phí không đáng kể trong hoạt động.

+0

Ngoài ra tôi tin rằng có sự khác biệt giữa thực hiện điểm nổi trong kiến ​​trúc khác nhau nhưng tôi không chắc chắn. –

4

Biên dịch lại tĩnh là một cách hứa hẹn dịch các tệp nhị phân từ kiến ​​trúc nước ngoài sang kiến ​​trúc đích khác. Nó sẽ nhanh hơn Just-In-Time (JIT), bởi vì nó không phải biên dịch mã ngay trước khi chạy, và bởi vì thời gian biên dịch bổ sung nó có thể mất là hữu ích để tối ưu hóa mã tạo.

Tuy nhiên biên dịch JIT sử dụng phân tích chương trình động, trong khi biên dịch lại tĩnh dựa trên phân tích chương trình tĩnh (do đó tên).

Trong phân tích tĩnh, bạn không có thông tin thời gian chạy trên thực thi.

Một vấn đề lớn với điều này là do nhảy gián tiếp. Thuật ngữ này bao gồm mã có thể được tạo ra từ một số câu lệnh switch nhất định, từ việc sử dụng các con trỏ hàm hoặc từ đa hình thời gian chạy (nghĩ rằng bảng ảo). Tất cả nắm xuống một lệnh có dạng:

JMP reg_A 

Hãy nói rằng bạn biết địa chỉ bắt đầu của chương trình của bạn, và bạn quyết định bắt đầu biên dịch lại hướng dẫn từ thời điểm này. Khi bạn gặp phải một bước nhảy trực tiếp, bạn đi đến địa chỉ đích của nó, và bạn tiếp tục biên dịch lại từ đó. Khi bạn gặp phải một bước nhảy gián tiếp, bạn bị kẹt. Trong hướng dẫn lắp ráp này, nội dung của reg_A không được biết đến tĩnh. Vì vậy, chúng tôi không biết địa chỉ của lệnh kế tiếp. Lưu ý rằng trong biên dịch động, chúng tôi không có vấn đề này, bởi vì chúng tôi mô phỏng trạng thái ảo của thanh ghi và chúng tôi biết nội dung hiện tại của reg_A. Bên cạnh đó, trong biên dịch tĩnh, bạn quan tâm đến việc tìm kiếm tất cả giá trị có thể cho reg_A tại thời điểm này, bởi vì bạn muốn có tất cả các đường dẫn có thể được biên dịch. Trong phân tích động, bạn chỉ cần giá trị hiện tại để tạo đường dẫn mà bạn hiện đang thực thi, nên reg_A thay đổi giá trị của nó, bạn vẫn có thể tạo các đường dẫn khác. Trong một số trường hợp, phân tích tĩnh có thể tìm thấy một danh sách các ứng viên (nếu nó là switch thì phải có một bảng có thể bù đắp ở đâu đó), nhưng trong trường hợp chung chúng ta chỉ đơn giản là không biết.

Phạt tiền, bạn nói, hãy biên dịch lại tất cả các hướng dẫn trong tệp nhị phân!

Vấn đề ở đây là trong hầu hết các tệp nhị phân chứa cả mã và dữ liệu. Tùy thuộc vào kiến ​​trúc, bạn có thể không biết được cái nào.

Tệ hơn nữa, trong một số kiến ​​trúc không có ràng buộc căn chỉnh và hướng dẫn chiều rộng biến, và bạn có thể bắt đầu tháo rời tại một số điểm, chỉ để khám phá ra rằng bạn đã bắt đầu biên dịch lại với bù đắp.

Chúng ta hãy tập lệnh đơn giản hóa bao gồm hai hướng dẫn và đăng ký đơn A:

41 xx (size 2): Add xx to `A`. 
42 (size 1): Increment `A` by one. 

Hãy lấy chương trình nhị phân sau:

41 42 

Hãy nói rằng điểm bắt đầu là byte đầu tiên 41 . Bạn làm:

41 42 (size 2): Add 42 to `A`. 

Nhưng nếu 41 là một phần dữ liệu thì sao? Sau đó, chương trình của bạn trở thành:

42 (size 1): Increment `A` by one. 

Vấn đề này được phóng đại trong các trò chơi cũ, mà thường được tối ưu hóa trực tiếp trong lắp ráp, và nơi mà các lập trình viên sức mạnh cố ý expect some byte to be interpreted as both code and data, depending on the context!

Thậm chí tệ hơn, chương trình biên dịch có thể tạo ra mã chính nó! Hãy tưởng tượng biên dịch lại trình biên dịch JIT. Kết quả sẽ vẫn là mã đầu ra cho kiến ​​trúc nguồn và cố gắng nhảy vào nó, rất có thể khiến chương trình này chết rất sớm. Mã biên dịch lại tĩnh chỉ có sẵn trong thời gian chạy yêu cầu thủ thuật vô hạn!

Phân tích nhị phân tĩnh là một lĩnh vực nghiên cứu rất trực tiếp (chủ yếu trong lĩnh vực an ninh, để tìm lỗ hổng trong các hệ thống không có sẵn nguồn), và thực sự tôi biết nỗ lực sản xuất NES emulator that tries to statically recompile programs. Bài viết rất thú vị.

Sự thỏa hiệp giữa JIT và biên dịch lại tĩnh sẽ được biên dịch tĩnh càng nhiều mã càng tốt, chỉ giữ các bit không thể dịch được tĩnh.

+0

Giải thích tuyệt vời. Tôi muốn đề cập đến một thỏa hiệp có thể khác giữa biên dịch tĩnh/động - lưu vào bộ nhớ đệm (vào một tệp) của các đoạn mã được biên dịch động. Có lẽ cách tiếp cận này sẽ thu được nhiều lợi ích của việc biên dịch lại tĩnh, được tăng cường bởi khả năng 'phát hiện đường dẫn mã' của phân tích thời gian chạy. – Cauterite

Các vấn đề liên quan