하나의 Linux 버전에서 컴파일된 작고 매우 간단한 프로그램(아래 표시)의 실행 파일이 다른 버전의 Linux에서 실행될 수 있습니까? 아니면 다시 컴파일해야 합니까?
이 경우 머신 아키텍처가 중요합니까?
int main()
{
return (99);
}
답변1
간단히 말해서: 컴파일된 바이너리를 한 호스트에서 다른 호스트로 전송하기 위해 동일한(또는 호환 가능한) 호스트를 사용하는 경우건축학, 아마도 완전히 다른 곳으로 가져갈 수도 있을 것입니다분배하다. 그러나 코드가 복잡해지면 다른 위치에 설치되지 않았거나 다른 버전에 설치된 라이브러리에 연결할 가능성이 높아집니다. 예를 들어, 코드는 ldd
(Debian 파생) Ubuntu Linux 호스트에서 컴파일할 때 다음 종속성을 보고합니다.gcc -o exit-test exit-test.c
$ ldd exit-test
linux-gate.so.1 => (0xb7748000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757b000)
/lib/ld-linux.so.2 (0x8005a000)
이 바이너리를 Mac( )으로 전송하면 ./exit-test: cannot execute binary file: Exec format error
실행되지 않는 것 같습니다. RHEL 상자로 옮겨 보겠습니다.
$ ./exit-test
-bash: ./exit-test: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
이런. 왜 이런 일이 발생합니까?
$ ls /lib/ld-l* # reference the `ldd` output above
ls: cannot access /lib/ld-l*: No such file or directory
이 사용 사례에서도 공유 라이브러리 누락으로 인해 Forklift가 실패합니다.
gcc -static exit-test-static exit-test.c
그러나 라이브러리 없이 시스템으로 포팅 하면 . 물론, 디스크 공간을 희생하면서:
$ ls -l ./exit-test{,-static}
-rwxr-xr-x 1 username groupname 7312 Jan 29 14:18 ./exit-test
-rwxr-xr-x 1 username groupname 728228 Jan 29 14:27 ./exit-test-static
또 다른 가능한 해결 방법은 새 호스트에 필수 라이브러리를 설치하는 것입니다.
U&L 세계의 많은 것들과 마찬가지로 이 고양이는 여러 개의 스킨을 가지고 있으며 그 중 두 개가 위에 설명되어 있습니다.
답변2
때에 따라 다르지. IA-32(Intel 32비트)용으로 컴파일된 콘텐츠는 amd64에서 실행될 수 있습니다. Intel 기반 Linux는 32비트 응용 프로그램(적절한 소프트웨어가 설치된 경우)과의 이전 버전과의 호환성을 유지하기 때문입니다. 이는 code
RedHat 7.3 32비트 시스템(2002년경, gcc 버전 2.96)에서 컴파일된 다음 바이너리가 Centos 7.4 64비트 시스템(2017년경)에 복사되어 Centos 7.4 64비트 시스템에서 실행되었습니다.
-bash-4.2$ file code
code: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.2.5, not stripped
-bash-4.2$ ./code
-bash: ./code: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
-bash-4.2$ sudo yum -y install glibc.i686
...
-bash-4.2$ ./code ; echo $?
99
고대 RedHat 7.3부터 Centos 7.4(기본적으로 RedHat Enterprise Linux 7.4)는 동일한 "배포" 제품군의 일부이므로 2002년부터 임의의 "처음부터 Linux"를 설치하는 것보다 2018년 버전의 다른 임의 Linux 배포판에 비해 이식성이 더 좋습니다. .
amd64용으로 컴파일된 항목은 32비트 전용 Linux 버전에서는 실행되지 않습니다(오래된 하드웨어는 새 하드웨어에 대해 알지 못합니다). 라이브러리와 같은 고대 레거시 사물에서 실행되도록 설계된 최신 시스템에서 컴파일된 새로운 소프트웨어의 경우에도 마찬가지입니다.시스템콜이라도백포팅이 불가능할 수 있으므로 컴파일 트릭이 필요할 수도 있고, 오래된 컴파일러 등을 구하거나, 오래된 시스템에서 컴파일해야 할 수도 있습니다. (이것이 VM을 고대의 것에 유지하는 좋은 이유입니다.)
아키텍처는 중요합니다. amd64(또는 IA-32)는 ARM 또는 MIPS와 매우 다르기 때문에 한 쪽의 바이너리는 다른 쪽에서 실행될 것으로 예상되지 않습니다. 어셈블리 수준에서 main
IA-32의 코드 부분은 gcc -S code.c
다음과 같이 컴파일됩니다.
main:
pushl %ebp
movl %esp,%ebp
movl $99,%eax
popl %ebp
ret
amd64 시스템이 이를 처리할 수 있습니다(Linux 시스템 - OpenBSD 대 amd64).확실히32비트 바이너리 지원, 이전 Arch와의 하위 호환성은 공격자에게 여유 공간을 제공합니다.CVE-2014-8866그리고 친구들). 동시에 하나로빅엔디안 MIPS 시스템 main
대신 다음과 같이 컴파일됩니다.
main:
.frame $fp,8,$31
.mask 0x40000000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
addiu $sp,$sp,-8
sw $fp,4($sp)
move $fp,$sp
li $2,99
move $sp,$fp
lw $fp,4($sp)
addiu $sp,$sp,8
j $31
nop
Intel 프로세서는 이를 어떻게 처리해야 할지 알 수 없으며 MIPS의 Intel 구성 요소에도 마찬가지입니다.
QEMU나 다른 에뮬레이터를 사용하여 외부 코드를 실행할 수 있습니다(매우 느릴 수 있음).
하지만! 귀하의 코드는 매우 간단하므로 이식성 문제가 다른 프로그램보다 적습니다. 프로그램은 종종 다양한 라이브러리의 이전 버전을 설치해야 하는 사람들을 위해 시간이 지남에 따라 변경된 라이브러리(예: RedHat는 일반적으로 추가)를 사용합니다. 패키지 이름 어딘가에 "compat"가 있음)
compat-glibc.x86_64 1:2.12-4.el7.centos
또는 glibc를 사용하는 이전 항목에 대한 ABI(Application Binary Interface) 변경 사항이나 C++11 또는 기타 C++ 버전으로 인한 최근 변경 사항이 걱정될 수도 있습니다. 라이브러리 문제를 피하기 위해 정적으로 컴파일할 수도 있습니다(디스크의 바이너리 크기를 상당히 늘림). 그러나 일부 이전 바이너리에 대해 이 작업이 수행되는지 여부는 이전 Linux 배포판이 대부분 동적 항목을 컴파일하는지 여부에 따라 다릅니다(RedHat: 예). 반면에 비슷한 것은 다른 라이브러리를 사용하기 위해 patchelf
동적(ELF이지만 형식이 지정되지 않은) 바이너리의 용도를 변경할 수 있습니다 .a.out
하지만! 프로그램을 실행할 수 있는 것과 프로그램을 사용하여 실제로 유용한 작업을 수행하는 것은 별개의 문제입니다. 이전 32비트 Intel 바이너리가 일부 끔찍하고 백포트되지 않은 보안 문제가 있는 OpenSSL 버전을 사용하는 경우 보안 문제가 있을 수 있습니다. 또는 프로그램이 최신 웹 서버와 협상할 수 없을 수도 있습니다(최신 웹 서버에서처럼). 서버 거부) 이전 프로그램에 대한 이전 프로토콜 및 암호) 또는 SSH 프로토콜 버전 1이 더 이상 지원되지 않습니다.
답변3
@thrig 및 @DopeGhoti의 탁월한 답변에 추가하려면 Unix 또는 Unix와 유사한 운영 체제(Linux 포함)는 전통적으로 항상 바이너리보다 소스 코드 이식성을 위해 더 많이 설계되고 조정되었습니다.
특정 하드웨어가 없거나 예제와 같은 간단한 소스인 경우 거의 다른 하드웨어 간에 이동할 수 있습니다.어느소스 코드로서의 Linux 버전 또는 아키텍처대상 서버에 C 개발 패키지가 설치되어 있는 한, 필요한 라이브러리를 선택하고 해당 개발 라이브러리를 설치합니다.
이전 버전의 Linux 또는 보다 구체적인 프로그램(예: 다른 커널 버전의 커널 모듈)에서 고급 코드를 포팅하는 경우 더 이상 사용되지 않는 라이브러리/API/ABI를 수용하도록 소스 코드를 조정하고 수정해야 할 수도 있습니다.
답변4
아마도.
쉽게 깨뜨릴 수 있는 것들은 다음과 같습니다.
- 다양한 아키텍처. 분명히 완전히 다른 아키텍처는 작동하지 않습니다(사용자 모드 qemu 및 binfmt_misc와 같은 것이 없으면 일반적인 구성이 아닙니다). x86 바이너리는 amd64에서 실행될 수 있지만 필수 32비트 라이브러리를 사용할 수 있는 경우에만 가능합니다.
- 라이브러리 버전. 버전이 잘못된 경우 라이브러리를 전혀 찾을 수 없습니다. 버전은 동일하지만 바이너리가 실행 중인 버전보다 최신 라이브러리 버전에 대해 빌드된 경우 새 기호 또는 새 버전의 기호로 인해 로드되지 않을 수 있습니다. 특히 glibc는 기호 버전 관리를 많이 사용하므로 최신 glibc에 대해 빌드된 바이너리는 이전 glibc에서 실패할 가능성이 높습니다.
빠르게 변화하는 라이브러리를 사용하지 않고 아키텍처 변경을 피하고 대상으로 삼으려는 가장 오래된 배포판을 기반으로 구축한다면 여러 배포판에서 하나의 바이너리를 실행할 가능성이 높습니다.