바이너리는 서로 다른 CPU 아키텍처 간에 이식 가능합니까?

바이너리는 서로 다른 CPU 아키텍처 간에 이식 가능합니까?

제 목표는 임베디드 리눅스용으로 개발하는 것입니다. 저는 ARM을 사용하는 베어메탈 임베디드 시스템에 대한 경험이 있습니다.

다양한 CPU 대상 개발에 관한 몇 가지 일반적인 질문이 있습니다. 내 질문은 다음과 같습니다.

  1. "와 같이 컴파일된 애플리케이션이 있는 경우x86 대상, Linux 운영 체제 버전 xyz', 다른 시스템에서 동일한 컴파일된 바이너리를 실행할 수 있습니다'ARM 타겟, Linux 운영 체제 버전 xyz'?

  2. 위의 내용이 사실이 아닌 경우 유일한 방법은 관련 툴체인 "예: arm-linux-gnueabi"를 사용하여 애플리케이션 소스 코드를 다시 빌드/재컴파일하는 것입니다.

  3. 마찬가지로, 나에게x86 대상, Linux 운영 체제 버전 xyz', 다른 시스템에서 동일한 컴파일된 .ko를 로드/사용할 수 있습니다.'ARM 타겟, Linux 운영 체제 버전 xyz'?

  4. 위의 내용이 사실이 아닌 경우 유일한 방법은 관련 툴체인 "예: arm-linux-gnueabi"를 사용하여 드라이버 소스 코드를 다시 빌드/재컴파일하는 것입니다.

답변1

캔트. 바이너리는 대상 아키텍처에 대해 (재)컴파일되어야 하며 Linux는 유사한 기능을 제공하지 않습니다.뚱뚱한 바이너리상자 밖에. 그 이유는 코드가 특정 아키텍처에 대한 기계어 코드로 컴파일되고 기계어 코드가 대부분의 프로세서 제품군에서 매우 다르기 때문입니다(예: ARM과 x86은 매우 다릅니다).

편집: 일부 아키텍처는 64비트 CPU에서 이전 버전과의 호환성 수준(더 드물게는 다른 아키텍처와의 호환성)을 제공한다는 점에 주목할 가치가 있습니다. 32비트 버전과 이전 버전과 호환되는 것이 일반적입니다(그러나 기억하세요: 종속 라이브러리) C 표준 라이브러리를 포함하여 32비트여야 합니다.정적 링크). 또한 언급할 가치가 있습니다.아이테니엄, x86 코드(32비트만 해당)를 실행할 수 있지만 x86 코드 실행 속도가 느리다는 것이 적어도 시장에서 성공하지 못한 이유 중 하나입니다.

호환 모드에서도 이전 CPU에서는 최신 명령어로 컴파일된 바이너리를 사용할 수 없습니다(예: 32비트 바이너리에서는 AVX를 사용할 수 없음).네할렘 x86 프로세서; CPU가 이를 지원하지 않습니다.

커널 모듈은 관련 아키텍처에 맞게 컴파일되어야 합니다. 또한 32비트 커널 모듈은 64비트 커널에서 실행될 수 없으며 그 반대의 경우도 마찬가지입니다.

크로스 컴파일 바이너리에 대한 정보(대상 ARM 장치에 툴체인이 필요하지 않음)는 아래 grochmal의 포괄적인 답변을 참조하세요.

답변2

Elizabeth Myers의 말이 맞습니다. 각 아키텍처에는 해당 아키텍처에 맞게 컴파일된 바이너리가 필요합니다. 시스템이 실행 중인 것과 다른 아키텍처용 바이너리를 빌드하려면 cross-compiler.


대부분의 경우 크로스 컴파일러로 컴파일해야 합니다. 나는 그것에 대해 경험이 있을 뿐입니다 gcc(그러나 , 및 다른 컴파일러에도 비슷한 매개변수가 있다고 생각합니다 llvm). gcc크로스 컴파일러는 구성에 다음을 추가하여 구현할 수 있습니다.--target

./configure --build=i686-arch-linux-gnu --target=arm-none-linux-gnueabi

gcc이러한 매개변수 및 glibcbinutils(그리고 대상 머신의 커널에 대한 커널 헤더를 제공)을 사용하여 컴파일해야 합니다 .

실제로 이는 훨씬 더 복잡하며 다양한 시스템에서 다양한 빌드 오류가 발생합니다.

GNU 툴체인을 컴파일하는 방법에 대한 여러 가이드가 있지만 저는 다음을 권장합니다.처음부터 리눅스, 이는 지속적으로 유지 관리되며 제공된 명령의 기능에 대한 좋은 설명을 제공합니다.

또 다른 옵션은 크로스 컴파일러를 사용한 부트스트랩 컴파일입니다. 크로스 컴파일러를 다른 아키텍처로 컴파일하는 데 어려움을 겪어 주셔서 감사합니다crosstool-ng생성됩니다. 크로스 컴파일러를 구축하는 데 필요한 툴체인에 대한 부트스트랩을 제공합니다.

crosstool-ng여러 지원목표 삼중항다른 아키텍처에서는 기본적으로 사람들이 크로스 컴파일러 툴체인을 컴파일하는 동안 발생하는 문제를 해결하는 데 시간을 보내는 부트스트랩입니다.


일부 배포판은 크로스 컴파일러를 패키지로 제공합니다.

즉, 크로스 컴파일러 측면에서 배포판이 무엇을 사용할 수 있는지 확인하십시오. 배포판에 요구 사항을 충족하는 크로스 컴파일러가 없는 경우 언제든지 직접 컴파일할 수 있습니다.

인용하다:


커널 모듈 주석

크로스 컴파일러를 수동으로 컴파일하면 커널 모듈을 컴파일하는 데 필요한 모든 것이 갖추어져 있습니다. 를 컴파일하려면 커널 헤더가 필요하기 때문입니다 glibc.

그러나 배포판에서 제공하는 크로스 컴파일러를 사용하는 경우 대상 컴퓨터에서 실행되는 커널에 대한 커널 헤더 파일이 필요합니다.

답변3

최후 의 수단으로(즉 , 소스 코드가 없는 경우) qemu. 일부 에뮬레이터는 Linux 이외의 시스템을 에뮬레이트하도록 설계되었습니다(예를 들어 MS-DOS 프로그램을 실행하도록 설계되었으며 널리 사용되는 게임 콘솔용 에뮬레이터가 많이 있습니다). 에뮬레이션에는 상당한 성능 오버헤드가 있습니다. 에뮬레이션 프로그램은 기본 프로그램보다 2~10배 느리게 실행됩니다.dosboxexageardosbox

기본이 아닌 CPU에서 커널 모듈을 실행해야 하는 경우 동일한 아키텍처의 커널을 포함하여 전체 운영 체제를 에뮬레이트해야 합니다. AFAIK Linux 커널 내에서는 외부 코드를 실행할 수 없습니다.

답변4

항상 목표를 세워야 해플랫폼. 가장 간단한 경우, 대상 CPU는 바이너리로 컴파일된 코드를 직접 실행합니다(이는 대략 MS DOS의 COM 실행 파일에 해당합니다). 제가 방금 발명한 두 가지 플랫폼인 Armistice와 Intellio를 고려해 보겠습니다. 두 경우 모두 화면에 42를 인쇄하는 간단한 hello world 프로그램을 갖게 됩니다. 또한 플랫폼 독립적인 방식으로 다중 플랫폼 언어를 사용한다고 가정하므로 두 언어의 소스 코드는 동일합니다.

Print(42)

Armistice에는 숫자 인쇄를 담당하는 간단한 장치 드라이버가 있으므로 포트로 출력하기만 하면 됩니다. 이식 가능한 어셈블리 언어에서는 다음과 같습니다.

out 1234h, 42

그러나 Intellio 시스템에는 그러한 기능이 없으므로 다른 계층을 거쳐야 합니다.

mov a, 10h
mov c, 42
int 13h

도대체 기계 코드에 도달하기도 전에 둘 사이에는 이미 상당한 차이가 있습니다! 이는 대략 Linux와 MS DOS, IBM PC와 X-Box(둘 다 동일한 CPU를 사용하더라도)의 차이에 해당합니다.

하지만 그것이 바로 운영체제의 존재 이유입니다. 모든 다양한 하드웨어 구성이 애플리케이션 계층에서 동일한 방식으로 처리되도록 보장하는 HAL이 있다고 가정해 보겠습니다. 기본적으로 우리는 Armistice에서도 Intellio 접근 방식을 사용할 것이며 "이식 가능한 어셈블리" 코드는 결국 동일하게 됩니다. 이는 최신 Unix 계열 시스템 및 Windows에서 사용되며 임베디드 시나리오에서도 자주 사용됩니다. 훌륭합니다. 이제 Armistice와 Intellio에서 동일한 이식 가능한 어셈블리 코드를 가질 수 있습니다. 하지만 바이너리는 어떻습니까?

가정했듯이 CPU는 바이너리를 직접 실행해야 합니다. mov a, 10hIntellio의 첫 번째 코드 줄을 살펴보겠습니다 .

20 10

오. mov a, constant자체 지침과 opcode가 있을 정도로 인기가 높은 것으로 입증되었습니다 . 휴전협정은 이 문제를 어떻게 다루었나요?

36 01 00 10

잘. opcode mov.reg.imm이므로 할당할 레지스터를 선택하려면 또 다른 매개변수가 필요합니다. 상수는 빅엔디안 표기법에서 항상 2바이트 단어입니다. 이것이 휴전이 설계된 방식이며 실제로 휴전의 모든 명령은 예외 없이 4바이트 길이입니다.

이제 Armistice에서 Intellio의 바이너리를 실행한다고 상상해 보십시오. CPU가 명령 디코딩을 시작하고 opcode를 찾습니다 20h. 휴전 협정에서 이것은 and.imm.reg지시에 해당합니다. 2바이트 워드 상수(읽기 10XX, 이미 문제가 있음)를 읽은 다음 레지스터 번호(다른 하나 XX)를 읽으려고 시도합니다. 우리는 잘못된 매개변수로 잘못된 명령을 실행합니다. 더 나쁜 것은 우리가 실제로 다른 명령어를 데이터라고 생각하여 먹었기 때문에 다음 명령어는 완전히 가짜가 된다는 것입니다.

응용 프로그램이 실행될 기회가 없으며 즉시 충돌하거나 중단될 가능성이 높습니다.

이는 실행 파일이 항상 Intellio 또는 Armistice에서 실행 중이라고 표시해야 한다는 의미는 아닙니다. CPU 독립적인 플랫폼(예 bash: Unix) 또는 CPU와 운영 체제 독립적인 플랫폼(예: Java 또는 .NET, 이제는 JavaScript 등) 을 정의하기만 하면 됩니다 . 이 경우 애플리케이션은 모든 다른 CPU 및 운영 체제에 대해 하나의 실행 파일을 사용할 수 있지만, 코드는 플랫폼 독립적인 대상 시스템(올바른 CPU 및/또는 운영 체제를 직접 대상으로 함)에 일부 애플리케이션 또는 서비스가 있습니다. CPU가 실제로 실행할 수 있도록 변환됩니다. 이는 성능, 비용 또는 기능에 영향을 미칠 수도 있고 그렇지 않을 수도 있습니다.

CPU는 일반적으로 직렬로 제공됩니다. 예를 들어, x86 제품군의 모든 CPU에는 정확히 동일한 방식으로 인코딩된 공통 명령 세트가 있으므로 모든 x86 CPU는 확장(예: 부동 소수점 연산)을 사용하지 않는 한 모든 x86 프로그램을 실행할 수 있습니다. 또는 벡터 산술). x86에서 오늘날 가장 일반적인 예는 물론 Intel과 AMD입니다. Atmel은 임베디드 장치에서 매우 널리 사용되는 ARM 시리즈 CPU를 설계하는 잘 알려진 회사입니다. 예를 들어 Apple에는 자체 ARM CPU도 있습니다.

그러나 ARM은 x86과 완전히 호환되지 않습니다. 설계 요구 사항이 매우 다르며 공통점도 거의 없습니다. 이러한 명령어는 완전히 다른 opcode를 가지며, 다르게 디코딩되고, 메모리 주소가 다르게 처리됩니다... 몇 가지 안전한 연산을 사용하여 x86 CPU와 ARM CPU 모두에서 실행되는 바이너리를 만드는 것이 가능합니다. 완전히 다른 명령어 세트이지만 이는 여전히 두 버전 모두 별도의 명령어를 갖고 있고 오직 하나의 부트스트랩만이 런타임에 올바른 명령어 세트를 선택한다는 것을 의미합니다.

관련 정보