하나의 “맛”Linux에서 컴파일 된 Linux 실행 파일이 다른 Linux 버전에서 실행됩니까? 한 버전에서 컴파일되어 다른 버전으로 실행됩니까? 아니면

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

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

int main()
{
  return (99);
}


답변

따라 다릅니다. 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이 더 이상 지원되지 않거나 …


답변

간단히 말해 : 동일한 (또는 호환 가능한) 아키텍처를 사용하여 한 호스트에서 다른 호스트로 컴파일 된 바이너리를 가져 오는 경우 다른 배포판으로 가져가는 것이 좋습니다. 그러나 코드의 복잡성이 증가함에 따라 설치되지 않은 라이브러리에 링크 될 가능성이 있습니다. 다른 위치에 설치; 또는 다른 버전으로 설치하면 증가합니다. 예를 들어 (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 세계의 많은 것들과 마찬가지로, 이것은 많은 스킨을 가진 고양이이며, 그 중 두 개는 위에서 설명되어 있습니다.


답변

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

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

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


답변

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

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


답변

아마도.

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

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

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


답변

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

위키 홀의 시작 :

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

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