gcc는``아키텍처 ''라는 용어를 사용하여 특정 CPU의``명령 세트 ''를 의미하고 "target"은 CPU와 아키텍처의 조합 및 ABI, libc, 엔디안 등의 다른 변수를 포함합니다. ( "베어 메탈"포함). 일반적인 컴파일러에는 제한된 대상 조합 세트 (아마도 하나의 ABI, 하나의 CPU 제품군, 그러나 32 비트 및 64 비트 둘 다)가 있습니다. 크로스 컴파일러는 일반적으로 실행되는 시스템 이외의 대상이있는 컴파일러 또는 여러 개의 대상 또는 ABI가있는 컴파일러를 의미합니다 ( this 참조 ).
바이너리는 다른 CPU 아키텍처에서 이식 가능합니까?
일반적으로 아닙니다. 일반적인 용어로 바이너리 는 특정 CPU 또는 CPU 제품군에 대한 기본 객체 코드 입니다. 그러나 적당히 휴대 성이 높을 수있는 몇 가지 경우가 있습니다.
- 한 아키텍처는 다른 아키텍처의 상위 집합입니다 (일반적으로 x86 바이너리는 최신 및 가장 큰 x86 대신 i386 또는 i686을 대상으로합니다
-march=core2
)
- 한 아키텍처는 다른 아키텍처의 네이티브 에뮬레이션 또는 번역을 제공하거나 ( Crusoe에 대해 들어봤을 수도 있음 ) 호환 가능한 보조 프로세서 (예 : PS2 )를 제공합니다.
- OS 및 런타임은 다중 아키텍처 (예 : x86_64에서 32 비트 x86 바이너리를 실행하는 기능)를 지원 하거나 VM / JIT를 원활하게 만듭니다 ( Dalvik 또는 ART를 사용하는 Android )
- 지원되는 각 아키텍처에 대해 본질적으로 중복 코드를 포함하는 "지방"바이너리에 대한 지원이 있습니다.
어떻게 든이 문제를 해결하면 무수한 라이브러리 버전 의 다른 휴대용 바이너리 문제 (당신을보고있는 glibc)가 나타납니다. (대부분의 임베디드 시스템은 적어도 특정 문제로부터 당신을 구할 수 있습니다.)
당신이 이미하지 않은 경우, 지금 실행하는 것이 시간 gcc -dumpspecs
과 gcc --target-help
당신에 대해 뭘하는지 볼 수는.
팻 바이너리는 여러 가지 단점 이 있지만 여전히 잠재적 인 용도 ( EFI )를 가지고 있습니다.
그러나 ELF와 ELF 인터프리터 및 임의의 이진 형식에 대한 Linux 커널 지원이라는 두 가지 추가 고려 사항이 다른 답변에서 누락되었습니다 . 여기서는 실제 프로세서가 아닌 프로세서의 바이너리 또는 바이트 코드에 대해서는 자세하게 설명하지 않겠지 만,이를 "네이티브"로 취급하고 Java 또는 컴파일 된 파이썬 바이트 코드 바이너리를 실행할 수 있지만 이러한 바이너리는 하드웨어 아키텍처와 무관합니다. 관련 VM 버전에서 궁극적으로 기본 바이너리를 실행합니다).
현대의 모든 Linux 시스템은 ELF 바이너리 ( 이 PDF의 기술적 인 세부 사항 )를 사용합니다. 동적 ELF 바이너리의 경우 커널이 이미지를 메모리에로드하는 것을 담당하지만 ELF에 설정된``통역사 ''의 역할입니다 헤비 리프팅을위한 헤더. 일반적으로 여기에는 모든 종속 동적 라이브러리를 사용할 수 있도록하는 것이 필요합니다 (라이브러리 및 필요한 기호를 나열하는 일부 다른 구조를 나열하는 ''동적 ''섹션의 도움으로). 이것은 거의 일반적인 목적의 간접 계층입니다.
$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses \
shared libs), stripped
$ readelf -p .interp /bin/ls
String dump of section '.interp':
[ 0] /lib/ld-linux.so.2
( /lib/ld-linux.so.2
또한 ELF 바이너리이며 인터프리터가 없으며 기본 바이너리 코드입니다.)
ELF의 문제점은 바이너리 ( readelf -h /bin/ls
) 의 헤더 가 특정 아키텍처, 클래스 (32 비트 또는 64 비트), 엔디안 및 ABI (Apple의 "범용"지방 바이너리는 대체 바이너리 형식 Mach-O를 사용함)로 표시한다는 것입니다 대신이 문제를 해결하는 것은 NextSTEP에서 시작되었습니다). 이는 ELF 실행 파일이 실행될 시스템과 일치해야한다는 것을 의미합니다. 하나의 이스케이프 해치는 인터프리터입니다. 이것은 모든 실행 파일 (원래 이진의 아키텍처 특정 하위 섹션을 추출 또는 매핑하고 호출하는 것을 포함) 일 수 있지만 여전히 시스템에서 실행할 수있는 ELF의 유형에 의해 제약을받습니다. . (FreeBSD는 흥미로운 방법이 리눅스 ELF 파일 처리 , 그 brandelf
수정합니다 ELF ABI 필드를.)
Linux 에서 Mach-O에 대한 (사용 binfmt_misc
) 지원이 있으며, 지방 (32 및 64 비트) 바이너리를 생성하고 실행하는 방법을 보여주는 예가 있습니다. 원래 Mac에서 수행 된 리소스 포크 / ADS 는 해결 방법이 될 수 있지만 기본 Linux 파일 시스템에서는이를 지원하지 않습니다.
커널 모듈에도 거의 같은 내용이 적용되며 .ko
파일도 ELF (인터프리터 세트는 없지만)입니다. 이 경우 uname -r
검색 경로에 커널 버전 ( ) 을 사용하는 추가 계층이 있습니다. 이는 ELF에서 버전 관리를 통해 대신 이론적으로 수행 할 수 있지만 다소 복잡하고 약간의 이득이 있습니다.
다른 곳에서 언급했듯이 Linux는 기본적으로 지방 바이너리를 지원하지 않지만 FatELF 라는 활성 지방 바이너리 프로젝트가 있습니다. 그것은 수년 전부터 있었고 (현재 만료 된) 특허 문제로 인해 표준 커널에 부분적으로 통합되지 않았습니다. 현재는 커널 및 툴체인 지원이 모두 필요합니다. binfmt_misc
접근 방식을 사용하지 않으므로 ELF 헤더 문제를 회피하고 뚱뚱한 커널 모듈도 허용합니다.
- 'x86 대상, Linux OS 버전 xyz'에서 실행되도록 컴파일 된 응용 프로그램이있는 경우 다른 시스템 'ARM target, linux OS version xyz'에서 동일한 컴파일 된 바이너리를 실행할 수 있습니까?
ELF가 아니라면 그렇게 할 수 없습니다.
- 위의 내용이 사실이 아닌 경우, 유일한 방법은 관련 툴체인 '예 : arm-linux-gnueabi'를 사용하여 애플리케이션 소스 코드를 재 빌드 / 재 컴파일하는 것입니까?
간단한 대답은 그렇습니다. 복잡한 답변에는 에뮬레이션, 중간 표현, 번역기 및 JIT가 포함됩니다. i686 바이너리 만 "다운 그레이드"하여 i386 opcode 만 사용하는 경우를 제외하고는 여기에서는 흥미롭지 않을 수 있으며 ABI 픽스 업은 잠재적으로 네이티브 코드를 번역하는 것만 큼 어렵습니다. )
- 마찬가지로 'x86 대상, Linux OS 버전 xyz'에서 작동하는로드 가능한 커널 모듈 (장치 드라이버)이있는 경우 다른 시스템 'ARM 대상, Linux OS 버전 xyz'에서 동일한 컴파일 된 .ko를로드 / 사용할 수 있습니까 ?
아니요, ELF는이를 허용하지 않습니다.
- 위의 내용이 사실이 아닌 경우 유일한 방법은 관련 툴체인 '예 : arm-linux-gnueabi'를 사용하여 드라이버 소스 코드를 다시 빌드 / 재 컴파일하는 것입니까?
간단한 대답은 그렇습니다. FatELF를 사용하면 .ko
다중 아키텍처 인 빌드를 만들 수 있지만 어느 시점에서 지원되는 모든 아키텍처에 대한 이진 버전을 만들어야합니다. 커널 모듈이 필요한 것은 종종 소스와 함께 제공되며 필요에 따라 빌드됩니다. 예를 들어 VirtualBox가이를 수행합니다.
이것은 이미 오래 걸리는 답입니다. 우회는 하나 더 있습니다. 커널 에는 이미 가상 머신이 내장되어 있지만 패킷을 일치시키는 데 사용되는 BPF VM 이 있습니다. 사람이 읽을 수있는 필터 ( "host foo and not port 22")는 바이트 코드로 컴파일되고 커널 패킷 필터가이를 실행합니다 . 새로운 eBPF 는 VM 코드가 모든 현대 리눅스에서 이식 가능하다는 이론에서 패킷만을위한 것이 아니라 llvm이이를 지원 하지만 보안상의 이유로 관리 규칙 이외의 다른 용도로는 적합하지 않을 것입니다.
이제 바이너리 실행 파일의 정의가 얼마나 관대했는지에 따라 binfmt_misc
쉘 스크립트와 ZIP 파일을 컨테이너 형식으로 사용 하여 지방 바이너리 지원을 구현 하는 데 사용할 수 있습니다 .
#!/bin/bash
name=$1
prog=${1/*\//} # basename
prog=${prog/.woz/} # remove extension
root=/mnt/tmpfs
root=$(TMPDIR= mktemp -d -p ${root} woz.XXXXXX)
shift # drop argv[0], keep other args
arch=$(uname -m) # i686
uname_s=$(uname -s) # Linux
glibc=$(getconf GNU_LIBC_VERSION) # glibc 2.17
glibc=${glibc// /-} # s/ /-/g
# test that "foo.woz" can unzip, and test "foo" is executable
unzip -tqq "$1" && {
unzip -q -o -j -d ${root} "$1" "${arch}/${uname_s}/${glibc}/*"
test -x ${root}/$prog && (
export LD_LIBRARY_PATH="${root}:${LD_LIBRARY_PATH}"
#readlink -f "${root}/${prog}" # for the curious
exec -a "${name}" "${root}/${prog}" "$@"
)
rc=$?
#rm -rf -- "${root}/${prog}" # for the brave
exit $rc
}
이것을 "wozbin"이라고 부르고 다음과 같이 설정하십시오.
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
printf ":%s:%s:%s:%s:%s:%s:%s" \
"woz" "E" "" "woz" "" "/path/to/wozbin" "" > /proc/sys/fs/binfmt_misc/register
이것은 .woz
커널에 파일을 등록 wozbin
하고, 첫 번째 인수가 호출 된 .woz
파일 의 경로로 설정된 대신 스크립트가 호출됩니다 .
이식 가능한 (뚱뚱한) .woz
파일 을 얻으 려면 test.woz
디렉토리 계층 구조 로 ZIP 파일을 작성하십시오 .
i686/
\- Linux/
\- glibc-2.12/
armv6l/
\- Linux/
\- glibc-2.17/
각 arch / OS / libc 디렉토리 내에 (임의의 선택) 아키텍처 별 test
바이너리 및 .so
파일 과 같은 구성 요소를 배치 합니다. 이를 호출 할 때 필요한 서브 디렉토리가 tmpfs 인 메모리 파일 시스템 ( /mnt/tmpfs
여기)으로 추출되어 호출됩니다.