기계 코드를 다른 아키텍처로 번역 할 수 있습니까?


11

따라서 이것은 ARM에서 Windows 서버를 실행하는 것에 대한 질문과 관련이 있습니다. 내 질문의 전제는 기계 코드를 한 아키텍처에서 다른 아키텍처로 변환 하여 실행되도록 컴파일 된 아키텍처와 다른 아키텍처 에서 이진 파일 을 실행할 수 있다는 것입니다.

QEMU 및 기타 에뮬레이터는 명령을 즉석에서 번역 할 수 있으므로 컴파일되지 않은 컴퓨터에서 실행 파일을 실행할 수 있습니다. 프로세스 속도를 높이기 위해이 번역을 즉석 대신 미리 수행하지 않겠습니까? 조립에 대한 다소 제한된 지식 MOV에서 ADD와 같은 대부분의 명령 과 다른 명령은 아키텍처 전체에서 이식 가능해야합니다.

모든 기계가 Turing Complete이므로 직접 매핑이없는 것은 다른 명령어 세트에 매핑 될 수 있습니다. 이 작업이 너무 복잡합니까? 내가 익숙하지 않은 이유로 전혀 작동하지 않습니까? 작동하지만 에뮬레이터를 사용하는 것보다 더 나은 결과를 얻지 못합니까?


이 기술은 그 단점과 더불어 많이 필요하지 않기 때문에 바람직하지 않을 수 있습니다. 이식성 / 표준화는 요즘 (Wintel이 전 세계를 인수했기 때문에) 약간 개선되었으며, 교차 머신 에뮬레이션이 실제로 필요한 경우 (예 : 앱 개발 환경의 전화 에뮬레이터) 직접 에뮬레이션은 보다 안정적이고 정확한 결과. 또한 프로세서 속도는 에뮬레이션 비용이 과거처럼 심각한 문제가되지 않을 정도로 빠릅니다.
다니엘 R

답변:


6

짧은 대답 : 컴파일 된 링크 된 실행 파일을 번역 할 수 없습니다. 기술적으로는 가능하지만 달성하기는 매우 어렵습니다 (아래 참조). 그러나 어셈블리 소스 파일 이있는 경우 (명령 및 레이블 포함) 수행 할 수 있습니다 (어떻게 어셈블리 소스를 얻더라도 프로그램을 어셈블리로 작성하지 않는 한 원래 프로그램 소스 코드는 다음과 같습니다). 글쎄, 그래서 당신은 다른 아키텍처가 시작되도록 컴파일하는 것이 좋습니다.)


긴 대답 :

QEMU 및 기타 에뮬레이터는 명령을 즉석에서 번역 할 수 있으므로 컴파일되지 않은 컴퓨터에서 실행 파일을 실행할 수 있습니다. 프로세스 속도를 높이기 위해이 번역을 즉석 대신 미리 수행하지 않겠습니까?

원칙적으로 쉽지만 실제로는 몇 가지 주요 이유로 거의 불가능합니다. 시작하기 위해 다른 명령어 세트는 크게 다른 주소 지정 모드, 다른 opcode 구조, 다른 단어 크기를 사용하며 일부 명령어에는 필요한 명령어가 없습니다.

하자 당신이 명령을 대체하는 데 필요한 말을 XYZ두 개 더 지침, ABC그리고 DEF. 이제 전체 프로그램의 모든 상대 / 오프셋 주소를 그 시점부터 효과적으로 이동 했으므로 전체 프로그램을 분석하고 전체 프로그램을 살펴보고 오프셋 (변경 전후)을 업데이트해야합니다. 이제 오프셋 중 하나가 크게 변경되었다고 가정하겠습니다. 이제 주소 지정 모드를 변경해야하므로 주소 크기가 변경 될 수 있습니다. 이렇게하면 전체 파일을 다시 스캔하고 모든 주소 등을 다시 계산해야합니다.

어셈블리 프로그램을 작성할 때 레이블을 사용할 수 있지만 CPU는 그렇지 않습니다. 파일이 어셈블 될 때 모든 레이블은 상대, 절대 또는 오프셋 위치로 계산됩니다. 이것이 왜 사소한 일이 아닌 불가능한 일이되는 이유를 알 수 있습니다. 교체 단일 명령은에 이동하기 전에 전체 프로그램의 수백 시간을 통과하도록 요구할 수 있습니다.

조립에 대한 다소 제한된 지식으로 인해 MOV, ADD 및 기타 명령과 같은 대부분의 명령은 아키텍처에서 이식 가능해야합니다.

예, 그러나 위에서 설명한 문제를 살펴보십시오. 기계의 단어 크기는 어떻습니까? 주소 길이? 동일한 주소 지정 모드가 있습니까? 다시 한 번, "찾기 및 바꾸기"지침 만 수행 할 수 없습니다. 프로그램의 모든 세그먼트에는 구체적으로 정의 된 주소가 있습니다. 프로그램이 조립 될 때 다른 레이블로의 점프는 리터럴 또는 오프셋 메모리 주소로 대체됩니다.

모든 기계가 Turing Complete이므로 직접 매핑이없는 것은 다른 명령어 세트에 매핑 될 수 있습니다. 이 작업이 너무 복잡합니까? 내가 익숙하지 않은 이유로 전혀 작동하지 않습니까? 작동하지만 에뮬레이터를 사용하는 것보다 더 나은 결과를 얻지 못합니까?

둘 다 가능 하고 훨씬 빠르다 는 것을 100 % 정확합니다 . 그러나 위에서 설명한 문제를 제외하고는 그렇지 않은 경우이 작업을 수행하기 위해 프로그램을 작성하는 것은 매우 어렵고 불가능합니다.

실제 어셈블리 소스 코드를 가지고 있다면 머신 코드를 다른 명령어 세트 아키텍처로 변환하는 것이 쉽지 않습니다. 그러나 머신 코드 자체는 어셈블 되므로 메모리 소스를 계산하는 데 사용되는 다양한 레이블이 포함 된 어셈블리 소스가 없으면 매우 어려워집니다. 다시 한 번, 단일 명령을 변경하면 전체 프로그램의 메모리 오프셋이 변경 될 수 있으며 주소를 다시 계산하려면 수백 번의 패스가 필요합니다.

몇 천 개의 명령어가있는 프로그램에 이것을 수행하려면 수십만 패스가 아니라면 수십 개가 필요합니다. 비교적 작은 프로그램의 경우 이것이 가능할 수 있지만 프로그램의 기계 명령 수에 따라 패스 수가 기하 급수적으로 증가한다는 점을 기억하십시오. 충분한 크기의 프로그램은 거의 불가능합니다.


기본적으로 소스 객체 코드를 "디 컴파일"또는 "분해"해야합니다. 비교적 간단한 코드 (특히 알려진 "스타일"이있는 특정 컴파일러 또는 코드 생성 패키지에 의해 생성 된 코드)의 경우 레이블 등의 재 삽입은 매우 간단합니다. 그러나 새로 최적화 된 최신 컴파일러는 이러한 방식으로 "강화"하기가 훨씬 어려운 코드를 생성 할 것입니다.
Daniel R

당신이 소스 오브젝트 코드가있는 경우 @DanH, 당신은 거의 어셈블리 소스 (이 되지 기계 코드). 오브젝트 파일에는 함께 링크 될 명명 된 (읽기 : 레이블이 지정된) 기계 코드 시퀀스가 ​​있습니다. 개체 코드 파일을 실행 파일에 연결할 때 문제가 발생합니다. 이러한 작은 세그먼트는 전체 링크 된 실행 파일보다 훨씬 쉽게 처리 (또는 리버스 엔지니어링) 될 수 있습니다.
획기적인

확실히, 특정 객체 파일 형식은 작업을 조금 더 쉽게 만듭니다. 일부는 디버깅 정보를 포함하여 대부분의 레이블을 복원 할 수 있습니다. 다른 사람들은 덜 도움이됩니다. 어떤 경우에는이 정보의 대부분이 링크 된 파일 형식으로 유지되지만 다른 경우에는 그렇지 않습니다. 엄청나게 많은 파일 형식이 있습니다.
Daniel R은

2

그렇습니다, 당신이 제안한 것은 할 수 있고 끝났습니다. 너무 흔하지는 않지만이 기술을 사용하는 현재 시스템을 모르지만 기술적으로 실현 가능한 범위 내에 있습니다.

우리가 지금까지 가지고있는 혹독한 "이동성"을 달성하기 전에 한 시스템에서 다른 시스템으로 코드를 포팅 할 수 있도록하기 위해 많은 노력을 기울였습니다. "소스"에 대한 복잡한 분석이 필요했으며 코드 수정 및 기타 홀수 관행에 의해 방해받을 수 있지만 여전히 수행되었습니다.

보다 최근에 IBM System / 38-iSeries-System i와 같은 시스템은 컴파일 된 프로그램과 함께 저장된 중간 코드 (Java 바이트 코드와 유사)의 이식성을 활용하여 호환되지 않는 명령어 세트 아키텍처 간의 이식성을 가능하게했습니다.


이 작업은 일반적으로 훨씬 더 오래된 (더 간단한) 명령 세트로 수행되었다는 데 동의하십시오. 1970 년대에는 7xx 바이너리 프로그램을 System / 360으로 변환하는 IBM 프로젝트가있었습니다.
톱밥

1

기계 코드 자체는 아키텍처에 따라 다릅니다.

여러 아키텍처에서 쉽게 이식 할 수있는 언어 (Java가 가장 잘 알려져 있음)는 매우 높은 수준 인 경향이 있으므로 통역사 또는 프레임 워크를 컴퓨터에 설치해야 작동합니다.

이 프레임 워크 나 인터프리터는 각각의 특정 시스템 아키텍처를 위해 작성되었으므로 "정상"프로그램보다 이식성이 뛰어납니다.


2
컴파일 된 언어는 해석 가능한 언어뿐만 아니라 이식성도 뛰어납니다. 궁극적으로 코드를 플랫폼에서 인식 할 수있는 코드로 변환하는 것이기 때문에 아키텍처 고유의 컴파일러입니다. 유일한 차이점은 컴파일 된 언어가 컴파일 타임에 번역되고 해석 된 언어는 필요에 따라 한 줄씩 번역된다는 것입니다.
MaQleod

1

물론 가능합니다. 머신 코드 란 무엇입니까? 그냥 언어특정 컴퓨터가 이해합니다. 자신을 컴퓨터라고 생각하면 독일어로 작성된 책을 이해하려고합니다. 당신은 언어를 이해하지 못하기 때문에 그것을 할 수 없습니다. 이제 독일어 사전을 가져 와서 "Kopf"라는 단어를 찾아 보면 영어 단어 "head"로 번역되는 것을 볼 수 있습니다. 사용한 사전은 컴퓨터 세계에서 에뮬레이션 계층이라고합니다. 쉬운가요? 글쎄, 그것은 더 어려워진다. 독일어 "Schadenfruede"를 가져와 영어로 번역하십시오. 영어에는 단어가 없지만 정의가 있습니다. 컴퓨터 세계에서도 동일한 문제가없는 단어를 번역하면서 동일한 문제가 발생합니다. 에뮬레이션 계층의 개발자가 해당 단어의 의미를 해석하고 호스트 컴퓨터가이를 이해하도록해야하므로 직접 포트가 어려워집니다. 때로는 예상대로 작동하지 않을 수도 있습니다. 우리는 모두 인터넷에서 책, 문구 등의 재미있는 번역본을 보았습니까?


1

설명하는 프로세스를 정적 ​​재 컴파일 (Static Recompilation)이라고하며, 일반적으로 적용 가능한 방식이 아니라 완료되었습니다. 그것은 불가능하다는 것을 의미하며 여러 번 수행되었지만 수동 작업이 필요했습니다.

조사 할만한 역사적 사례가 많이 있지만, 현대의 관심사를 잘 보여줄 수는 없습니다. 나는 모든 회의론자들이 본질적으로 모든 것을 어렵게 주장하는 사람들에게 의문을 제기해야하는 두 가지 예를 발견했다.

먼저이 녀석은 NES ROM을위한 완전한 정적 아키텍처 및 플랫폼을 수행했습니다. http://andrewkelley.me/post/jamulator.html

그는 몇 가지 좋은 점을 지적하지만 JIT가 여전히 실용적이라고 결론을 내립니다. 나는 왜 그가이 상황에 대해 이미 알지 못했는지 잘 모르겠습니다. 이것이 대부분의 사람들이 고려하는 상황 유형일 수 있습니다. 바로 가기를 사용하지 않고 전체 사이클 정확도를 요구하며 본질적으로 ABI를 전혀 사용하지 않습니다. 그것이 전부라면, 우리는 쓰레기통에 개념을 던져서 그것을 하루라고 부를 수 있었지만, 전부는 아니고 결코 그런 적이 없었습니다 ... 어떻게 알 수 있습니까? 모든 성공적인 프로젝트가이 방법을 사용하지 않았기 때문입니다.

이제 가능성이 덜 명확 해지려면 이미 가지고있는 플랫폼을 활용하십시오 ... Linux ARM 핸드 헬드의 스타 크래프트? 예, 작업을 동적으로 수행하는 작업으로 제한하지 않으면 접근 방식이 작동합니다. Winlib를 사용하면 Windows 플랫폼 호출이 모두 기본이므로 아키텍처 만 걱정하면됩니다.

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

ARM 핸드 헬드 판도라는 파이보다 약간 강하다는 점을 감안할 때 둔화가 거의 무시할 수 있다고 도넛에 달러를 던졌습니다. 그가 사용한 도구는이 저장소에 있습니다.

https://github.com/notaz/ia32rtools

그 사람은 매우 수동으로 디 컴파일했습니다. 프로세스가 적은 작업으로 크게 자동화 될 수 있다고 생각합니다 ...하지만 여전히 사랑의 노동입니다. 누군가 불가능하다고 말해주지 말고, 그것이 실용적이지 않다고 말해 주지도 말아라. 그렇게하는 새로운 방법을 혁신하자마자 실용적 일 수있다.


0

이론적으로는 가능합니다. 가장 큰 문제는 운영 체제 (또는 커널) 용 응용 프로그램을 다른 운영 체제 (또는 커널)로 변환하는 것입니다. Windows, Linux, OSX 및 iOS 커널 저수준 작업 간에는 해당 장치의 모든 응용 프로그램에서 사용해야하는 중요한 차이점이 있습니다.

다시 한 번 이론 상으로는 응용 프로그램 및 응용 프로그램을 실행하도록 컴파일 된 운영 체제와 관련된 모든 기계 코드뿐만 아니라 다른 장치에 대한 모든 기계 코드를 다시 컴파일 할 수있는 응용 프로그램을 작성할 수 있습니다. 그러나 이는 거의 모든 경우에 매우 불법이며 작성하기가 매우 어려울 것입니다. 사실, 내 머릿속의 기어는 단지 그것에 대해 생각하기 시작했습니다.

최신 정보

아래의 몇 가지 의견은 내 답변에 동의하지 않는 것 같지만 내 의견이 누락되었다고 생각합니다. 내 지식으로는, 하나의 아키텍처에 대해 일련의 실행 가능한 바이트를 취할 수있는 응용 프로그램이 없으며 기본 OS 커널에 대한 호출을 포함하여 외부 라이브러리에 대한 모든 필요한 호출을 포함하여 바이트 코드 수준에서 분해하고 다른 시스템에 대한 재 조립 및 저장 결과 실행 가능한 바이트 코드 . 즉, Notepad.exe와 같은 간단한 작업을 수행 할 수있는 응용 프로그램이없고 작은 190k 파일을 분해 한 다음 Linux 또는 OSX에서 실행할 수있는 응용 프로그램으로 100 % 다시 어셈블합니다.

질문을 한 사람이 Wine 또는 Parallels와 같은 프로그램을 통해 소프트웨어를 가상화하거나 응용 프로그램을 실행할 수 있다면 단순히 다른 시스템의 바이트 코드를 단순히 다시 변환 할 수 없다는 것을 알고 싶었습니다. 다른 아키텍처의 응용 프로그램을 완전히 재 조립하려는 경우 재 조립하기 전에 실행하는 데 필요한 모든 바이트 코드를 분해해야하기 때문입니다. 예를 들어 Windows 시스템의 경우 exe 파일보다 모든 응용 프로그램에 더 많은 것이 있습니다. 모든 Windows 응용 프로그램은 하위 수준의 Windows 커널 개체 및 기능을 사용하여 메뉴, 텍스트 영역, 창 크기 조정 방법, 디스플레이 그리기, OS 메시지 보내기 / 받기 등을 만듭니다.

애플리케이션으로 재 조립하고 다른 아키텍처에서 실행하려면 해당 바이트 코드를 모두 분해해야합니다.

Wine과 같은 응용 프로그램은 바이트 수준에서 Windows 바이너리를 해석합니다. 커널 호출을 인식하고 해당 호출을 관련 Linux 기능으로 변환하거나 Windows 환경을 에뮬레이션합니다. 그러나 이는 바이트 단위 (또는 opcode의 경우 opcode) 재번역이 아닙니다. 그것은 함수를위한 함수의 번역이고, 그것은 조금 다릅니다.


전혀 이론이 아닙니다. 그리고 다른 운영 체제에서 다른 바이너리를 실행하는 많은 응용 프로그램이 있습니다. 와인에 대해 들어 보셨습니까? Linux, Solaris, Mac OSX, BSD 등과 같은 다른 OS에서 Windows 바이너리를 실행합니다.
Keltari

하이퍼 바이저를 사용하여 여러 운영 체제를 실행하거나 한 시스템에서 다른 시스템을 에뮬레이트하는 와인과 같은 "계층"을 실행하여 대부분의 시스템에서 운영 체제의 차이를 쉽게 조정할 수 있습니다. AFAIK, 모든 "현대"비 임베디드 프로세서는 "가상화 가능"하므로 명령어 세트 에뮬레이션 / 번역이 필요하지 않습니다.
Daniel R은

0

모든 전문가들이이 점을 놓치고있는 것 같습니다. '번역'은 복잡하지만 컴퓨터에 매우 적합합니다 (지능형이 아니고 혐오스러운 것). 그러나 번역 후 프로그램은 OS 지원이 필요합니다. 예 : Linux에는 GetWindowVersion이 없습니다. 이것은 일반적으로 에뮬레이터 (매우 큰)에 의해 제공됩니다. 따라서 간단한 프로그램을 '사전 번역'할 수 있지만 독립적으로 실행하려면 거대한 라이브러리에 연결해야합니다. 모든 Windows 프로그램 이미징에는 자체 kernel.dll + user.dll + shell.dll이 함께 제공됩니다 ...


그것은 힘들고 지능이 필요합니다. 예를 들어, 결과가 점프하는 주소를 결정하는 계산이 있는데, 이는 단일 명령으로 보이는 것의 중간에있을 수 있습니다.
David Schwartz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.