Mã máy có thể được dịch sang một kiến ​​trúc khác không?


11

Vì vậy, đây là loại liên quan đến một câu hỏi về việc chạy một máy chủ Windows trên ARM . Vì vậy, tiền đề của câu hỏi của tôi là, liệu mã máy có thể được dịch từ kiến ​​trúc này sang kiến ​​trúc khác để thực thi nhị phân trên một kiến ​​trúc khác với kiến ​​trúc được biên dịch để chạy không.

QEMU và các trình giả lập khác có thể dịch các hướng dẫn một cách nhanh chóng và do đó chạy một tệp thực thi trên máy tính mà nó không được biên dịch. Tại sao không thực hiện bản dịch này trước thời hạn, thay vì trên đường bay để tăng tốc quá trình? Từ kiến ​​thức có phần hạn chế của tôi về lắp ráp, hầu hết các hướng dẫn như MOV, ADDvà những hướng dẫn khác nên có thể di chuyển trên các kiến ​​trúc.

Bất cứ điều gì không có ánh xạ trực tiếp đều có thể được ánh xạ tới một số hướng dẫn khác, vì tất cả các máy đều là Turing Complete. Làm điều này sẽ quá phức tạp? Nó sẽ không hoạt động vì một số lý do tôi không quen thuộc? Nó sẽ hoạt động, nhưng không mang lại kết quả tốt hơn so với sử dụng trình giả lập?


Kỹ thuật này có thể đã rơi vào tình trạng không hài lòng bởi vì (ngoài tính dễ vỡ của nó) nó không cần thiết nhiều. Khả năng di động / tiêu chuẩn hóa (tốt hơn) một chút trong những ngày này (nếu chỉ vì Wintel đã chiếm lĩnh thế giới) và, trong đó việc mô phỏng máy chéo thực sự cần thiết (ví dụ, đối với trình giả lập điện thoại trong môi trường phát triển ứng dụng), mô phỏng trực tiếp cung cấp kết quả chính xác và đáng tin cậy hơn. Thêm vào đó, bộ xử lý đủ nhanh để chi phí mô phỏng không phải là vấn đề nghiêm trọng như trong quá khứ.
Daniel R Hicks

Câu trả lời:


6

Câu trả lời ngắn gọn : Bạn không thể dịch một tập tin thực thi được liên kết. Mặc dù về mặt kỹ thuật là có thể, nhưng rất khó để thực hiện (xem bên dưới). Tuy nhiên , nếu bạn có tệp nguồn lắp ráp (chứa các hướng dẫn và nhãn), thì rất có thể làm được (mặc dù nếu bạn bằng cách nào đó có được nguồn lắp ráp, trừ khi chương trình được viết bằng cách lắp ráp, bạn nên có mã nguồn chương trình gốc là tốt, vì vậy bạn nên bắt đầu biên dịch nó cho các kiến ​​trúc khác nhau để bắt đầu).


Câu trả lời dài :

QEMU và các trình giả lập khác có thể dịch các hướng dẫn một cách nhanh chóng và do đó chạy một tệp thực thi trên máy tính mà nó không được biên dịch. Tại sao không thực hiện bản dịch này trước thời hạn, thay vì trên đường bay để tăng tốc quá trình?

Tôi biết về nguyên tắc có vẻ dễ dàng, nhưng trong thực tế, gần như không thể vì một vài lý do chính. Để bắt đầu, các bộ hướng dẫn khác nhau sử dụng các chế độ địa chỉ phần lớn khác nhau, cấu trúc opcode khác nhau, kích thước từ khác nhau và một số thậm chí không có hướng dẫn bạn cần.

Giả sử bạn cần thay thế hướng dẫn XYZbằng hai hướng dẫn khác ABCDEF. Bây giờ bạn đã chuyển tất cả các địa chỉ tương đối / bù trong toàn bộ chương trình từ thời điểm đó trở đi một cách hiệu quả, do đó bạn sẽ cần phân tích và duyệt qua toàn bộ chương trình và cập nhật các điểm bù (cả trước và sau khi thay đổi). Bây giờ, giả sử một trong những điểm bù thay đổi đáng kể - bây giờ bạn cần thay đổi chế độ địa chỉ, có thể thay đổi kích thước của địa chỉ. Điều này một lần nữa sẽ buộc bạn quét lại toàn bộ tệp và tính lại tất cả các địa chỉ, v.v.

Khi bạn viết chương trình lắp ráp, bạn có thể sử dụng nhãn, nhưng CPU thì không - khi tệp được lắp ráp, tất cả các nhãn được tính là vị trí tương đối, tuyệt đối hoặc bù. Bạn có thể thấy tại sao điều này nhanh chóng trở thành một nhiệm vụ không hề nhỏ, và bên cạnh không thể. Thay thế một đơn hướng dẫn có thể yêu cầu bạn phải đi qua các chương trình hàng trăm toàn bộ thời gian trước khi chuyển.

Từ kiến ​​thức lắp ráp có phần hạn chế của tôi, hầu hết các hướng dẫn như MOV, ADD và các hướng dẫn khác nên có thể di chuyển trên các kiến ​​trúc.

Có, nhưng nhìn vào các vấn đề tôi đã nêu ở trên. Kích thước từ của máy thì sao? Địa chỉ dài? Nó thậm chí có cùng chế độ địa chỉ? Một lần nữa, bạn không thể chỉ "tìm và thay thế" hướng dẫn. Mỗi phân đoạn của một chương trình có một địa chỉ được xác định cụ thể. Nhảy tới các nhãn khác được thay thế bằng các địa chỉ bộ nhớ bằng chữ hoặc bù khi chương trình được lắp ráp.

Bất cứ điều gì không có ánh xạ trực tiếp đều có thể được ánh xạ tới một số hướng dẫn khác, vì tất cả các máy đều là Turing Complete. Làm điều này sẽ quá phức tạp? Nó sẽ không hoạt động vì một số lý do tôi không quen thuộc? Nó sẽ hoạt động, nhưng không mang lại kết quả tốt hơn so với sử dụng trình giả lập?

Bạn đúng 100% rằng cả hai đều có thể , và sẽ nhanh hơn rất nhiều . Tuy nhiên, viết một chương trình để thực hiện điều này là vô cùng khó khăn và rất khó khả thi, nếu không phải vì bất cứ điều gì ngoại trừ các vấn đề tôi đã nêu ở trên.

Nếu bạn có mã nguồn lắp ráp thực tế, việc dịch mã máy sang kiến ​​trúc tập lệnh khác sẽ là chuyện nhỏ. Tuy nhiên, bản thân mã máy được lắp ráp , do đó không có nguồn lắp ráp (chứa các nhãn khác nhau được sử dụng để tính toán địa chỉ bộ nhớ), điều đó trở nên vô cùng khó khăn. Một lần nữa, thay đổi một lệnh đơn có thể thay đổi bù đắp bộ nhớ trong toàn bộ chương trình và yêu cầu hàng trăm lượt đi để tính lại địa chỉ.

Làm điều này cho một chương trình với một vài ngàn hướng dẫn sẽ cần hàng chục nếu không phải là hàng trăm ngàn lượt. Đối với các chương trình tương đối nhỏ, điều này có thể khả thi, nhưng hãy nhớ rằng số lần vượt qua sẽ tăng theo cấp số nhân với số lượng hướng dẫn máy trong chương trình. Đối với bất kỳ chương trình có kích thước vừa đủ, gần như không thể.


Về cơ bản, những gì người ta phải làm là "dịch ngược" hoặc "tháo rời" mã đối tượng nguồn. Đối với mã tương đối đơn giản (đặc biệt là mã được tạo bởi một số trình biên dịch hoặc gói tạo mã nhất định có "kiểu" đã biết), việc chèn lại nhãn và tương tự là khá đơn giản. Tuy nhiên, chắc chắn, các trình biên dịch tối ưu hóa cao hơn mới hơn sẽ tạo ra mã khó hơn nhiều để "grock" theo cách này.
Daniel R Hicks

@DanH nếu bạn có mã đối tượng nguồn, bạn có khá nhiều nguồn lắp ráp ( không phải mã máy). Tệp đối tượng chứa các chuỗi được đặt tên (đọc: có nhãn) của mã máy được liên kết với nhau. Vấn đề xảy ra khi bạn liên kết các tệp mã đối tượng thành một tệp thực thi. Các phân đoạn nhỏ hơn này có thể được xử lý (hoặc thiết kế ngược) dễ dàng hơn nhiều sau đó toàn bộ thực thi được liên kết.
Đột phá

Chắc chắn, các định dạng tệp đối tượng nhất định làm cho công việc dễ dàng hơn một chút. Một số thậm chí có thể chứa thông tin gỡ lỗi, cho phép bạn khôi phục hầu hết các nhãn. Những người khác là ít hữu ích. Trong một số trường hợp, phần lớn thông tin này được lưu giữ ngay cả ở định dạng tệp được liên kết, trong các trường hợp khác thì không. Có một số lượng lớn các định dạng tập tin khác nhau.
Daniel R Hicks

2

Vâng, những gì bạn đề nghị có thể và đã được thực hiện. Nó không quá phổ biến và tôi không biết bất kỳ hệ thống hiện tại nào sử dụng kỹ thuật này, nhưng nó chắc chắn cũng nằm trong lĩnh vực khả thi kỹ thuật.

Nó đã từng được thực hiện rất nhiều để cho phép chuyển mã từ hệ thống này sang hệ thống khác, trước khi bất kỳ ai đạt được "tính di động" thô thiển mà chúng ta có bây giờ. Nó đòi hỏi phân tích phức tạp về "nguồn" và có thể bị cản trở bởi sửa đổi mã và các thực hành kỳ quặc khác, nhưng nó vẫn được thực hiện.

Gần đây, các hệ thống như Hệ thống IBM / 38 - iSeries - Hệ thống i đã tận dụng tính di động của mã trung gian (tương tự mã byte Java) được lưu trữ với các chương trình được biên dịch để cho phép tính di động giữa các kiến ​​trúc tập lệnh không tương thích.


Đồng ý rằng điều này đã được thực hiện, thường là với các bộ hướng dẫn cũ hơn (đơn giản hơn). Có một dự án của IBM vào những năm 1970 để chuyển đổi các chương trình nhị phân 7xx cũ sang System / 360.
mùn cưa

1

Mã máy là kiến ​​trúc cụ thể.

Các ngôn ngữ cho phép dễ dàng di chuyển trên nhiều kiến ​​trúc (Java có lẽ là phổ biến nhất) có xu hướng rất cao, yêu cầu trình thông dịch hoặc khung phải được cài đặt trên máy để chúng hoạt động.

Các khung hoặc trình thông dịch này được viết cho từng kiến ​​trúc hệ thống cụ thể mà chúng sẽ chạy và do đó, bản thân chúng không di động hơn chương trình "bình thường".


2
Các ngôn ngữ được biên dịch cũng có khả năng di động, không chỉ các ngôn ngữ được giải thích, nó là trình biên dịch có cấu trúc cụ thể vì nó là thứ cuối cùng dịch mã thành nền tảng mà nó có thể nhận ra. Sự khác biệt duy nhất là các ngôn ngữ được biên dịch được dịch vào thời gian biên dịch và các ngôn ngữ được dịch được dịch theo từng dòng khi cần thiết.
MaQleod

1

Hoàn toàn có thể. Mã máy là gì? Nó chỉ là ngôn ngữmà một máy tính cụ thể hiểu được. Hãy nghĩ về bản thân bạn như một chiếc máy tính và bạn đang cố gắng hiểu một cuốn sách viết bằng tiếng Đức. Bạn không thể làm điều đó, bởi vì bạn không hiểu ngôn ngữ. Bây giờ nếu bạn lấy một từ điển tiếng Đức và tra từ "Kopf", bạn sẽ thấy nó dịch sang từ tiếng Anh "head". Từ điển bạn đã sử dụng là cái được gọi là lớp mô phỏng trong thế giới máy tính. Dễ dàng phải không? Vâng, nó trở nên khó khăn hơn. Lấy từ tiếng Đức "Schadenfruede" và dịch nó sang tiếng Anh. Bạn sẽ thấy không có từ nào trong ngôn ngữ tiếng Anh, nhưng có một định nghĩa. Vấn đề tương tự tồn tại trong thế giới máy tính, dịch những thứ không có từ tương đương. Điều này làm cho các cổng trực tiếp trở nên khó khăn vì các nhà phát triển của lớp mô phỏng phải thực hiện việc giải thích ý nghĩa của từ đó và làm cho máy tính chủ hiểu nó. Đôi khi nó không hoạt động theo cách người ta mong đợi. Chúng ta đều đã thấy những bản dịch hài hước của sách, cụm từ, vv trên internet phải không?


1

Quá trình bạn mô tả được gọi là Biên dịch tĩnh và nó đã được thực hiện, không phải theo cách áp dụng chung. Có nghĩa là vượt quá khả năng, nó đã được thực hiện nhiều lần, nhưng nó đòi hỏi công việc thủ công.

Có nhiều ví dụ lịch sử đáng để nghiên cứu, nhưng chúng ít có khả năng chứng minh các mối quan tâm hiện đại. Tôi đã tìm thấy hai ví dụ về cơ bản khiến mọi người hoài nghi hoàn toàn nghi ngờ những người tuyên bố mọi thứ khó khăn là không thể.

Đầu tiên anh chàng này đã thực hiện đầy đủ Kiến trúc tĩnh và Nền tảng cho ROM NES. http://andrewkelley.me/post/jamulator.html

Ông đưa ra một số điểm rất tốt, nhưng kết luận rằng JIT vẫn thực tế hơn. Tôi thực sự không chắc tại sao anh ta không biết rằng trong tình huống này, đây có thể là loại tình huống mà hầu hết mọi người xem xét. Không sử dụng phím tắt, yêu cầu độ chính xác của chu kỳ đầy đủ và về cơ bản không sử dụng ABI. Nếu đó là tất cả, chúng ta có thể ném khái niệm vào thùng rác và gọi nó là một ngày, nhưng nó không phải là tất cả và không bao giờ là .... Làm sao chúng ta biết điều này? Bởi vì tất cả các dự án thành công đã không sử dụng phương pháp này.

Bây giờ đối với các khả năng ít rõ ràng hơn, Tận dụng nền tảng mà bạn đã có ... Starcraft trên thiết bị cầm tay Linux ARM? Phải, cách tiếp cận hoạt động khi bạn không giới hạn nhiệm vụ chính xác những gì bạn làm một cách linh hoạt. Bằng cách sử dụng Winlib, các cuộc gọi nền tảng Windows đều là bản địa, tất cả những gì chúng ta phải lo lắng là Kiến trúc.

http://www.geek.com/games/starcraft-has-been-reverse-engineered-to-run-on-arm-1587277/

Tôi sẽ ném đô la cho bánh rán rằng sự chậm lại gần như không đáng kể, vì cho rằng pandora cầm tay ARM chỉ mạnh hơn một chút so với Pi. Các công cụ anh sử dụng là trong kho lưu trữ này.

https://github.com/notaz/ia32rtools

Anh chàng đó đã dịch ngược rất thủ công, tôi tin rằng quá trình đó có thể được tự động hóa đáng kể với ít công việc hơn ... nhưng vẫn là một công việc của tình yêu vào lúc này. Đừng để ai nói với bạn điều gì đó không thể, thậm chí đừng để tôi nói với bạn điều đó không thực tế ... Nó có thể thực tế, ngay khi bạn đổi mới một cách mới để biến nó thành như vậy.


0

Về mặt lý thuyết, có điều này có thể được thực hiện. Vấn đề lớn hơn xảy ra là dịch một ứng dụng cho một hệ điều hành (hoặc kernel) sang một hệ điều hành khác. Có sự khác biệt đáng kể giữa các hoạt động cấp thấp của Windows, Linux, OSX và iOS, mà tất cả các ứng dụng cho các thiết bị đó đều phải sử dụng.

Về mặt lý thuyết, một lần nữa, người ta có thể viết một ứng dụng có thể phân tách ứng dụng cũng như tất cả mã máy được liên kết với hệ điều hành mà nó được biên dịch để chạy và sau đó biên dịch lại tất cả mã máy đó cho một thiết bị khác. Tuy nhiên, điều đó sẽ rất bất hợp pháp trong mọi trường hợp và sẽ cực kỳ khó viết. Thực tế, các bánh răng trong đầu tôi đang bắt đầu tăng lên khi chỉ nghĩ về nó.

CẬP NHẬT

Một vài ý kiến ​​dưới đây dường như không đồng ý với phản hồi của tôi, tuy nhiên, tôi nghĩ rằng họ đang thiếu quan điểm của tôi. Theo hiểu biết của tôi, không có ứng dụng nào có thể lấy một chuỗi byte thực thi cho một kiến ​​trúc, phân tách nó ở cấp mã byte, bao gồm tất cả các lệnh gọi cần thiết đến các thư viện bên ngoài, bao gồm các lệnh gọi đến kernel hệ điều hành bên dưới và lắp lại nó cho hệ thống khác và lưu lại kết quả thực thi mã byte . Nói cách khác, không có ứng dụng nào có thể đơn giản như Notepad.exe, phân tách tệp 190k nhỏ như vậy và 100% lắp lại nó vào một ứng dụng có thể chạy trên Linux hoặc OSX.

Theo hiểu biết của tôi, người hỏi câu hỏi muốn biết rằng nếu chúng ta có thể ảo hóa phần mềm hoặc chạy các ứng dụng thông qua các chương trình như Wine hoặc Parallels, tại sao chúng ta không thể dịch lại mã byte cho các hệ thống khác nhau. Lý do là nếu bạn muốn lắp ráp lại hoàn toàn một ứng dụng cho một kiến ​​trúc khác, bạn phải phân tách tất cả các mã byte cần thiết để chạy nó trước khi lắp lại nó. Có nhiều thứ cho mọi ứng dụng hơn là tệp exe, giả sử, đối với máy Windows. Tất cả các ứng dụng Windows đều sử dụng các đối tượng và chức năng nhân Windows cấp thấp để tạo menu, vùng văn bản, phương thức thay đổi kích thước cửa sổ, vẽ lên màn hình, gửi / nhận tin nhắn OS, v.v.

Tất cả mã byte đó phải được phân tách nếu bạn muốn lắp lại vào ứng dụng và để nó chạy trên một kiến ​​trúc khác.

Các ứng dụng như Wine diễn giải các nhị phân của Windows ở mức byte. Họ nhận ra các cuộc gọi đến kernel và dịch các cuộc gọi đó sang các chức năng Linux có liên quan hoặc họ mô phỏng môi trường Windows. Nhưng, đó không phải là truyền lại byte theo byte (hoặc opcode cho opcode). Nó là một bản dịch theo chức năng và nó khá khác biệt.


Nó không phải là lý thuyết ở tất cả. Và có rất nhiều ứng dụng chạy các nhị phân khác trên các hệ điều hành khác nhau. Bạn đã nghe nói về Rượu vang? Nó chạy các nhị phân Windows trên các HĐH khác nhau, chẳng hạn như Linux, Solaris, Mac OSX, BSD và các hệ điều hành khác.
Keltari

Sự khác biệt trong các hệ điều hành có thể dễ dàng được giải quyết trên hầu hết các hệ thống bằng cách sử dụng một trình ảo hóa để chạy nhiều hệ điều hành (hoặc để chạy một "lớp" như Wine trên một hệ thống mô phỏng hệ thống khác). AFAIK, tất cả các bộ xử lý không nhúng "hiện đại" đều "ảo hóa", do đó, điều này không yêu cầu mô phỏng / dịch mã.
Daniel R Hicks

0

Có vẻ như tất cả các chuyên gia đều thiếu điểm này: 'Bản dịch' phức tạp nhưng rất phù hợp với máy tính (không thông minh, chỉ cần lao động). Nhưng sau khi dịch, các chương trình cần hỗ trợ HĐH, ví dụ: GetWindowVersion không tồn tại trong Linux. Điều này thường được cung cấp bởi trình giả lập (rất lớn). Vì vậy, bạn có thể 'dịch trước' một chương trình đơn giản nhưng bạn phải liên kết với một libary lớn để chạy độc lập. Hình ảnh chương trình của mọi cửa sổ đi kèm với kernel.dll + user.dll + shell.dll ...


Nó không chỉ tốn công, nó đòi hỏi trí thông minh. Ví dụ: giả sử bạn thấy một số tính toán có kết quả xác định địa chỉ bạn nhảy tới, có thể ở giữa một thứ dường như là một lệnh đơn.
David Schwartz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.