하나의 "맛"Linux에서 컴파일 된 Linux 실행 파일이 다른 Linux 버전에서 실행됩니까?


59

아래에 표시된 것과 같이 작고 매우 간단한 프로그램의 실행 파일이 Linux의 한 버전에서 컴파일되어 다른 버전으로 실행됩니까? 아니면 다시 컴파일해야합니까?

이와 같은 경우 기계 아키텍처가 중요합니까?

int main()
{
  return (99);
}

2
뛰어난 답변을 주신 모든 분들께 감사드립니다! 나는 예상했던 것보다 훨씬 더 많은 것을 배웠다. 가능한 한 적은 수의 라이브러리에 의존하도록 코드를 의도적으로 인위적으로 간단하게 만들었습니다. 그러나 나는 그것을 먼저 선포 했어야했다. 플랫폼 전반에 걸친 대부분의 C ++ 코딩은 Visual Studio와 같은 Microsoft 도구로 개발 한 다음 코드를 * nix 시스템으로 이식하고 다시 컴파일하는 것과 관련이있었습니다.
JCDeen

4
여기에 표현 된 많은면 및 고려 사항이 나를 놀라게했습니다! 솔직히 답변으로 몇 가지를 선택할 수 있기를 바랍니다. 모두에게 다시 감사합니다! 진정으로.
JCDeen

2
Android는 Linux 기반 운영 체제이기도합니다. 그러나 운이 좋으면 컴파일 된 코드를 실행 glibc하거나 그 반대의 경우도 마찬가지입니다. 물론, 완전히 불가능하지는 않습니다 .
undercat

2
명령 줄 도구의 호환성을 극대화하기 위해 glibc 대신 uClibc, musl 또는 dietlibc를 사용하고 32 비트 실행 파일을 정적으로 링크 할 수 있습니다 ( gcc -m32 -static). 이렇게하면 i386 또는 amd64 Linux가 실행 파일을 실행할 수 있습니다.
pts

10
당신은 42를 반환해야합니다 ! :)
Homunculus Reticulli

답변:


49

따라 다릅니다. Intel의 Linux가 32 비트 응용 프로그램 (적절한 소프트웨어가 설치된 상태)과의 하위 호환성을 유지하므로 IA-32 (Intel 32 비트) 용으로 컴파일 된 것이 amd64에서 실행될 수 있습니다. 다음 code은 RedHat 7.3 32 비트 시스템 (2002 년경, gcc 버전 2.96)에서 컴파일 된 다음 바이너리가 Centos 7.4 64 비트 시스템으로 복사되어 실행됩니다 (2017 년경).

-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 릴리스에서는 실행되지 않습니다 (이전 하드웨어는 새 하드웨어에 대해 알지 못함). 라이브러리와 심지어 시스템 호출 도 거꾸로 이식 할 수 없으므로 컴파일 트릭이 필요하거나 이전 컴파일러 등을 구해야 할 수도 있기 때문에 고대 시스템에서 실행되도록 현대 시스템에서 컴파일 된 새 소프트웨어의 경우에도 마찬가지입니다. 이전 시스템에서 컴파일 중입니다. (이것은 고대의 오래된 가상 머신을 유지하는 좋은 이유입니다.)

건축은 중요하다. 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 시스템의 경우 amd64와 대조적으로 OpenBSD는 32 비트 바이너리를 지원 하지 않습니다 . 오래된 아치 와의 하위 호환성으로 인해 공격자는 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

인텔 프로세서는 MIPS의 인텔 어셈블리와 관련하여 무엇을 해야할지 모릅니다.

QEMU 또는 다른 에뮬레이터를 사용하여 외부 코드를 실행할 수 있습니다 (아마도 매우 느림).

하나! 코드는 매우 간단한 코드이므로 다른 것보다 이식성 문제가 적습니다. 프로그램은 일반적으로 시간이 지남에 따라 변경된 라이브러리 (glibc, openssl 등)를 사용합니다. 이러한 라이브러리의 경우 다양한 라이브러리의 이전 버전을 설치해야 할 수도 있습니다 (예 : RedHat은 일반적으로 패키지 이름의 어딘가에 "compat"를 넣습니다)

compat-glibc.x86_64                     1:2.12-4.el7.centos

또는 glibc를 사용하는 오래된 방식에 대한 ABI 변경 (Application Binary Interface) 또는 C ++ 11 또는 기타 C ++ 릴리스로 인한 최근 변경에 대해 걱정할 수도 있습니다. 라이브러리 문제를 피하기 위해 정적 (디스크에서 이진 크기를 크게 증가)을 컴파일 할 수도 있지만 이전 바이너리 배포가 이전 Linux 배포판에서 동적 (RedHat : yes)의 모든 것을 컴파일하는지 여부에 달려 있습니다. 반면에 다른 라이브러리를 사용하기 위해 patchelf동적 (ELF, 아마도 a.out형식화 되지 않음 ) 바이너리를 다시 트리거 할 수 있습니다 .

하나! 프로그램을 실행할 수 있다는 것은 한 가지 일이며 실제로 다른 유용한 일을합니다. 오래된 32 비트 인텔 바이너리는 끔찍하고 지원되지 않는 보안 문제가있는 OpenSSL 버전에 의존하거나 보안 문제가 있거나 최신 웹 서버와 전혀 협상 할 수없는 경우가 있습니다. 서버는 이전 프로토콜 및 이전 프로그램의 암호를 거부하거나 SSH 프로토콜 버전 1이 더 이상 지원되지 않거나 ...


14
첫 번째 단락 : 아니오, 인텔은 "Intel 64"(이전에는 다른 이름을 사용한 후)라고 부릅니다. IA-64는 x86과 호환되는 것이 아니라 Itanium을 나타냅니다.
hobbs

1
@ hobbs는 고맙습니다. 나는 그 참조를 amd64로 대체했습니다. 나는 물건의 이름을 인텔 마케팅 부서에 맡길 것입니다.
thrig

3
정적 링크는 왜 언급하지 않습니까?
dcorking

2
변경되는 것은 라이브러리 ABI만이 아니라 커널의 syscall 인터페이스도 시간이 지남에 따라 확장됩니다. for GNU/Linux 2.6.32의 출력에서 ​​(또는 이와 같은) 참고하십시오 file /usr/bin/ls.
Charles Duffy

1
@Wilbert 당신은 아마 thrig가 Red Hat Enterprise Linux 와는 다른 Red Hat Linux 를 언급하고 있다는 것을 놓쳤을 것입니다 .

68

간단히 말해 : 동일한 (또는 호환 가능한) 아키텍처를 사용하여 한 호스트에서 다른 호스트로 컴파일 된 바이너리를 가져 오는 경우 다른 배포판으로 가져가는 것이 좋습니다. 그러나 코드의 복잡성이 증가함에 따라 설치되지 않은 라이브러리에 링크 될 가능성이 있습니다. 다른 위치에 설치; 또는 다른 버전으로 설치하면 증가합니다. 예를 들어 (Debian 파생) Ubuntu Linux 호스트에서 ldd컴파일 할 때 다음 종속성 을 보고하는 코드를 예로 들어 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

이 사용 사례에서도 공유 라이브러리가 누락되어 지게차가 실패했습니다.

그러나로 컴파일 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 세계의 많은 것들과 마찬가지로, 이것은 많은 스킨을 가진 고양이이며, 그 중 두 개는 위에서 설명되어 있습니다.


4
실제로 정적 바이너리를 잊어 버렸습니다. 일부 공급 업체는 정적 바이너리를 채택, 일부 악성 코드 제작자가 너무 리눅스 버전 간 바이너리 호환성을 극대화하기 위해 동일한 아키텍처의
루이 F 리베

8
지게차 ...?
user253751

2
@immibis 필자는 하나의 환경 (디스트로)에서 다른 환경으로 데이터 (실행 파일)를 복사하는 것을 의미한다고 생각합니다. 데이터는 대상 환경을 위해 설계되지 않았습니다.
wjandrea

13
리눅스 예제는 불행히도 인위적인 것이며 배포보다는 아키텍처에 대한 요점을 보여줍니다. Debian에서 32 비트 바이너리를 빌드하고 64 비트 RHEL에서 실행하려고했습니다. 그것들은 다른 아키텍처입니다 ... 라이브러리 의존성이 거의없는 동일한 아키텍처 바이너리는 잘 복사 될 수 있습니다.
Stephen Kitt

7
@MSalters 나는 그것이 합리적이지 않다고 말하는 것이 아닙니다. DopeGhoti가 만들고자하는 점을 감안할 때 그것이 나쁜 예라고 말하고 있습니다. 물론 Intel의 64 비트 Linux는 적절한 인프라와 함께 32 비트 실행 파일도 지원합니다. 이 경우 IMO의 올바른 예는 amd64바이너리를 빌드하고 다른 amd64배포에서 i386실행 하거나 바이너리를 빌드하고 다른 배포에서 실행하는 것입니다 i386.
Stephen Kitt

25

탁월한 @thrig 및 @DopeGhoti 답변에 추가 : Linux를 포함한 Unix 또는 Unix와 유사한 OS는 전통적으로 바이너리보다 소스 코드의 이식성을 위해 항상 설계되고 정렬되었습니다.

예제와 같이 하드웨어에 고유 한 것이 없거나 간단한 소스가 아닌 경우 대상 서버에 C 개발 패키지가 설치되어있는 한 거의 모든 Linux 버전 또는 아키텍처를 소스 코드 거의 문제없이 이동할 수 있습니다 . 필요한 라이브러리 및 해당 개발 라이브러리가 설치되었습니다.

이전 버전의 Linux 또는 다른 커널 버전의 커널 모듈과 같은 특정 프로그램에서 고급 코드를 이식하는 경우, 더 이상 사용되지 않는 라이브러리 / API / ABI를 설명하기 위해 소스 코드를 수정하고 수정해야 할 수도 있습니다.


19

하여 기본 , 당신은 거의 확실하게 외부 라이브러리 문제로 실행됩니다. 다른 답변 중 일부는 해당 문제에 대한 자세한 내용을 다루므로 해당 작업을 복제하지 않습니다.

그러나 Linux 시스템간에 이식 할 수 있도록 많은 프로그램 (사소하지 않은 프로그램)을 컴파일 있습니다 . 핵심은 Linux Standard Base 라는 툴킷 입니다. 는 LSB 휴대용 애플리케이션의 바로 이러한 유형의 생성을 위해 설계되었습니다. LSB v5.0 용 응용 프로그램을 컴파일하면 LSB v5.0을 구현하는 다른 모든 Linux 환경 (같은 아키텍처)에서 실행됩니다. 일부 Linux 배포판은 LSB와 호환되며 다른 Linux 배포판은 설치 가능한 패키지로 LSB 툴킷 / 라이브러리를 포함합니다. LSB 도구 (예 : lsbcc래퍼)를 사용하여 응용 프로그램을 빌드 gcc하고 LSB 버전의 라이브러리에 연결하면 이식 가능한 응용 프로그램이 만들어집니다.


로와 qemu당신도 프로그램을 실행할 수있는 다른 archtecture, 컴파일 (높지의 performace,하지만 당신은 실행할 수 있습니다)
Jasen

1
나는 Linux Standard Base 툴킷을 알지 못 했으므로 감사합니다! 아주 오래 전에 C / C ++로 작업을 시작했기 때문에이 답변의 많은 정보가 나에게 새롭습니다. 매우 도움이됩니다.
JCDeen

1
Wikipedia 기사에 따르면 데비안과 우분투는 LSB를 구현하지 않으며 그렇게 할 의사도 없습니다.
BlackJack

2
@ BlackJack- 배포판 자체는 핵심 OS의 일부로 100 %를 구현하지 않지만 LSB 호환 라이브러리 및 툴킷을 선택적 패키지로 설치할 수 있습니다. 우분투 (예 : Ubuntu)를 사용하여 Suse 및 Centos에서 실행되는 LSB 호환 프로그램을 작성 apt-get install했습니다. 몇 가지 패키지 만 있으면 됩니다.
bta

10

아마도.

그것을 깨뜨리는 경향이있는 것들이 포함됩니다.

  1. 다른 아키텍처. 분명히 완전히 다른 아키텍처는 작동하지 않습니다 (binfmt_misc를 사용하는 사용자 모드 qemu와 같은 것이 없지만 일반적인 구성은 아닙니다). x86 바이너리는 amd64에서 작동하지만 필요한 32 비트 라이브러리를 사용할 수있는 경우에만 가능합니다.
  2. 라이브러리 버전. 뒤집기가 잘못되면 라이브러리를 전혀 찾지 못합니다. soversion은 동일하지만 바이너리가 실행중인 라이브러리보다 최신 버전의 라이브러리에 대해 빌드 된 경우 새 심볼 또는 새 버전의 심볼로 인해로드되지 않을 수 있습니다. 특히 glibc는 심벌 버전 관리를 많이 사용하므로 새로운 glibc에 대해 빌드 된 바이너리는 이전 glibc와 함께 실패 할 가능성이 높습니다.

빠르게 변화하는 라이브러리를 사용하지 않으면 아키텍처의 변경을 피하고 가장 오래된 배포판을 빌드하려는 경우 여러 배포판에서 하나의 이진 작업을 수행 할 수 있습니다.


4

앞에서 언급 한 것 외에도 실행 파일 형식이 약간 변경되었습니다. 대부분의 경우 Linux는 ELF를 사용하지만 이전 버전은 a.out 또는 COFF를 사용했습니다.

위키 홀의 시작 :

https://en.wikipedia.org/wiki/Comparison_of_ executable_file_formats

이전 버전을 사용하여 더 새로운 형식을 실행하는 방법이있을 수 있지만 개인적으로는 본 적이 없습니다.


"이전"은이 시점에서 지금 매우 오래되었습니다.
plugwash
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.