Liệu một chương trình thực thi Linux có được biên dịch trên một hương vị khác của tinh hoa của Linux chạy trên một bản khác không?


59

Liệu chương trình thực thi của một chương trình nhỏ, cực kỳ đơn giản, như chương trình dưới đây, được biên dịch trên một hương vị của Linux sẽ chạy trên một hương vị khác? Hoặc nó sẽ cần phải được biên dịch lại?

Liệu kiến ​​trúc máy có vấn đề trong một trường hợp như thế này?

int main()
{
  return (99);
}

2
Cảm ơn tất cả mọi người cho câu trả lời xuất sắc! Tôi đã học được nhiều hơn tôi dự đoán. Tôi đã làm cho mã đơn giản một cách giả tạo về mục đích để nó phụ thuộc vào càng ít thư viện càng tốt; nhưng tôi thực sự nên nói rằng lên phía trước Hầu hết mã hóa C ++ của tôi trên các nền tảng liên quan đến việc phát triển bằng một công cụ của Microsoft như Visual Studio và sau đó chuyển mã sang hệ thống * nix và biên dịch lại.
JCDeen

4
Nhiều khía cạnh và sự cân nhắc thể hiện ở đây đã làm tôi ngạc nhiên! Tôi thành thật mong muốn tôi có thể chọn một số câu trả lời. Cảm ơn một lần nữa cho tất cả! Trân trọng.
JCDeen

2
Android cũng là một hệ điều hành dựa trên Linux. Tuy nhiên, điều may mắn nhất là chạy bất kỳ mã nào được biên dịch dựa vào glibcđó hoặc ngược lại. Cấp, nó không hoàn toàn không thể .
undercat

2
Để tương thích tối đa với công cụ dòng lệnh, bạn có thể muốn sử dụng uClibc, musl hoặc dietlibc thay vì glibc và liên kết tĩnh 32-bit thực thi của bạn ( gcc -m32 -static). Bằng cách đó, mọi i386 hoặc amd64 Linux sẽ có thể chạy được tệp thực thi.
pts

10
Bạn nên trở về 42 ! :)
Homunculus Reticulli

Câu trả lời:


49

Nó phụ thuộc. Một cái gì đó được biên dịch cho IA-32 (Intel 32 bit) có thể chạy trên amd64 vì Linux trên Intel vẫn giữ được khả năng tương thích ngược với các ứng dụng 32 bit (đã cài đặt phần mềm phù hợp). Đây là bản codetổng hợp của bạn trên hệ thống RedHat 7.3 32 bit (khoảng năm 2002, gcc phiên bản 2.96) và sau đó nhị phân được sao chép sang và chạy trên hệ thống Centos 7.4 64 bit (khoảng năm 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

Ancient RedHat 7.3 đến Centos 7.4 (về cơ bản là RedHat Enterprise Linux 7.4) đang ở trong cùng một gia đình "phân phối", do đó có thể sẽ có tính di động tốt hơn so với việc cài đặt "Linux từ đầu" ngẫu nhiên từ năm 2002 sang một bản phân phối Linux ngẫu nhiên khác vào năm 2018 .

Một cái gì đó được biên dịch cho amd64 sẽ không chạy trên các bản phát hành Linux 32 bit (phần cứng cũ không biết về phần cứng mới). Điều này cũng đúng với phần mềm mới được biên dịch trên các hệ thống hiện đại dự định chạy trên những thứ cũ kỹ, vì các thư viện và thậm chí các cuộc gọi hệ thống có thể không di động được, do đó có thể yêu cầu các thủ thuật biên dịch, hoặc có được trình biên dịch cũ, v.v. biên dịch trên hệ thống cũ. (Đây là một lý do tốt để giữ các máy ảo của những thứ cổ xưa xung quanh.)

Kiến trúc có vấn đề; amd64 (hoặc IA-32) khác rất nhiều so với ARM hoặc MIPS, do đó, nhị phân từ một trong số đó sẽ không được mong đợi chạy trên một cái khác. Ở cấp lắp ráp các mainphần của mã của bạn trên IA-32 biên dịch thông qua gcc -S code.cđể

main:
    pushl %ebp
    movl %esp,%ebp
    movl $99,%eax
    popl %ebp
    ret

mà một hệ thống amd64 có thể xử lý (trên hệ thống Linux - OpenBSD ngược lại với amd64 không hỗ trợ nhị phân 32 bit; khả năng tương thích ngược với các vòm cũ sẽ tạo cho kẻ tấn công ngọ nguậy, ví dụ CVE-2014-8866 và bạn bè). Trong khi đó, trên một hệ thống MIPS cuối lớn main thay vì biên dịch thành:

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

mà bộ xử lý Intel sẽ không biết phải làm gì và tương tự như vậy đối với lắp ráp Intel trên MIPS.

Bạn có thể có thể sử dụng QEMU hoặc một số trình giả lập khác để chạy mã nước ngoài (có lẽ rất, rất chậm).

Tuy nhiên! Mã của bạn là mã rất đơn giản, do đó sẽ có ít vấn đề về tính di động hơn bất kỳ thứ gì khác; các chương trình thường sử dụng các thư viện đã thay đổi theo thời gian (glibc, openssl, ...); đối với những người cũng có thể cần cài đặt các phiên bản cũ hơn của các thư viện khác nhau (ví dụ RedHat thường đặt "compat" ở đâu đó trong tên gói như vậy)

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

hoặc có thể lo lắng về các thay đổi ABI (Giao diện nhị phân ứng dụng) để biết những thứ cũ sử dụng glibc hoặc những thay đổi gần đây do C ++ 11 hoặc các bản phát hành C ++ khác. Người ta cũng có thể biên dịch tĩnh (tăng đáng kể kích thước nhị phân trên đĩa) để cố gắng tránh các sự cố thư viện, mặc dù một số nhị phân cũ có làm điều này hay không phụ thuộc vào việc bản phân phối Linux cũ có biên dịch hầu hết mọi thứ động hay không (RedHat: yes) hay không. Mặt khác, những thứ như patchelfcó thể kích hoạt lại các a.outnhị phân động (ELF, nhưng có thể không định dạng) để sử dụng các thư viện khác.

Tuy nhiên! Có thể chạy một chương trình là một chuyện, và thực sự làm một cái gì đó hữu ích với nó khác. Các tệp nhị phân Intel 32 bit cũ có thể có vấn đề về bảo mật nếu chúng phụ thuộc vào phiên bản OpenSSL có vấn đề bảo mật khủng khiếp và không được hỗ trợ trong đó, hoặc chương trình có thể không thể đàm phán với tất cả các máy chủ web hiện đại (như hiện đại máy chủ từ chối các giao thức và mật mã cũ của chương trình cũ) hoặc giao thức SSH phiên bản 1 không còn được hỗ trợ hoặc ...


14
đoạn đầu tiên: không, Intel gọi nó là "Intel 64" (ngày nay, sau khi trải qua một số tên khác trước đó). IA-64 đề cập đến Itanium, không phải bất cứ thứ gì tương thích x86.
hobbs

1
@hobbs cảm ơn bạn, tôi đã thay thế các tài liệu tham khảo bằng amd64; Tôi sẽ để lại việc đặt tên cho bộ phận tiếp thị của Intel.
thrig

3
Tại sao không đề cập đến liên kết tĩnh?
vào

2
Không chỉ thư viện ABI thay đổi - giao diện tòa nhà của hạt nhân cũng được mở rộng theo thời gian. Lưu ý for GNU/Linux 2.6.32(hoặc như vậy) trong đầu ra của file /usr/bin/ls.
Charles Duffy

1
@Wilbert Có lẽ bạn đang thiếu điều đó khi nhắc đến Red Hat Linux khác với Red Hat Enterprise Linux.
Bob

68

Tóm lại: Nếu bạn đang lấy một tệp nhị phân được biên dịch từ máy chủ này sang máy chủ khác bằng cách sử dụng cùng một kiến trúc (hoặc tương thích) , bạn có thể hoàn toàn ổn khi đưa nó đến một bản phân phối khác . Tuy nhiên, khi độ phức tạp của mã tăng lên, khả năng được liên kết với thư viện không được cài đặt; lắp đặt ở vị trí khác; hoặc cài đặt ở một phiên bản khác, tăng. Lấy ví dụ mã của bạn, để lddbáo cáo các phụ thuộc sau khi được biên dịch gcc -o exit-test exit-test.ctrên máy chủ Ubuntu Linux (có nguồn gốc từ Debian):

$ 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)

Rõ ràng là nhị phân này sẽ không chạy nếu tôi đá nó tới, giả sử, một máy Mac ( ./exit-test: cannot execute binary file: Exec format error). Hãy thử chuyển nó sang hộp RHEL:

$ ./exit-test
-bash: ./exit-test: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory

Trời ơi. Tại sao điều này có thể được?

$ ls /lib/ld-l* # reference the `ldd` output above
ls: cannot access /lib/ld-l*: No such file or directory

Ngay cả đối với trường hợp sử dụng này, việc nâng cấp nó đã thất bại do thiếu các thư viện chia sẻ.

Tuy nhiên, nếu tôi biên dịch nó gcc -static exit-test-static exit-test.c, chuyển nó vào hệ thống mà không có các thư viện hoạt động tốt. Tất nhiên, với chi phí của không gian đĩa:

$ 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

Một giải pháp khả thi khác là cài đặt các thư viện cần thiết trên máy chủ mới.

Cũng như nhiều thứ trong vũ trụ U & L, đây là một con mèo có nhiều da, hai trong số đó được phác thảo ở trên.


4
Thật vậy, tôi quên về nhị phân tĩnh. Một số nhà cung cấp chấp nhận nhị phân tĩnh và một số tác giả phần mềm độc hại cũng để tối đa hóa khả năng tương thích nhị phân giữa các phiên bản Linux có cùng kiến ​​trúc
Rui F Ribeiro

8
Xe nâng ...?
dùng253751

2
@immibis Tôi nghĩ rằng nó có nghĩa là sao chép dữ liệu (có thể thực thi) từ một môi trường (distro) sang một môi trường khác, nơi dữ liệu không được thiết kế cho môi trường đích.
wjandrea

13
Thật không may, ví dụ Linux của bạn khá giả tạo và minh họa quan điểm của bạn về kiến ​​trúc chứ không phải phân phối: bạn đã xây dựng tệp nhị phân 32 bit trên Debian và cố gắng chạy nó trên RHEL 64 bit; đó là những kiến ​​trúc khác nhau ... Các nhị phân có cùng kiến ​​trúc với rất ít phụ thuộc thư viện có thể được sao chép tốt.
Stephen Kitt

7
@MSalters Tôi không nói điều đó là vô lý, tôi đang nói đó là một ví dụ tồi cho quan điểm mà DopeGhoti đang cố gắng thực hiện (bạn không thể sao chép nhị phân từ phân phối này sang phân phối khác - điều này là sai). Tất nhiên, Linux 64 bit trên Intel cũng hỗ trợ chạy các tệp thực thi 32 bit, với cơ sở hạ tầng phù hợp. Một ví dụ hợp lệ trong trường hợp này IMO sẽ xây dựng một amd64nhị phân và chạy nó trên một bản amd64phân phối khác , hoặc xây dựng một i386nhị phân và chạy nó trên một bản i386phân phối khác .
Stephen Kitt

25

Thêm vào các câu trả lời tuyệt vời @thrig và @DopeGhoti: Các hệ điều hành giống Unix hoặc Unix, bao gồm cả Linux, theo truyền thống luôn được thiết kế và căn chỉnh nhiều hơn cho tính di động của mã nguồn so với nhị phân.

Nếu không có gì cụ thể về phần cứng hoặc là một nguồn đơn giản như trong ví dụ của bạn, bạn có thể di chuyển nó mà không gặp vấn đề gì giữa bất kỳ phiên bản Linux hoặc kiến ​​trúc nào dưới dạng mã nguồn miễn là các máy chủ đích đã cài đặt các gói phát triển C , các thư viện cần thiết và các thư viện phát triển tương ứng được cài đặt.

Khi chuyển mã nâng cao hơn từ các phiên bản Linux cũ hơn theo thời gian hoặc các chương trình cụ thể hơn như mô-đun hạt nhân cho các phiên bản hạt nhân khác nhau, bạn có thể phải điều chỉnh và sửa đổi mã nguồn để giải thích cho các thư viện / API / ABI không dùng nữa.


19

Theo mặc định , gần như chắc chắn bạn sẽ gặp vấn đề với các thư viện bên ngoài. Một số câu trả lời khác đi sâu vào chi tiết hơn về những vấn đề đó, vì vậy tôi sẽ không sao chép công việc của họ.

Tuy nhiên, bạn có thể biên dịch nhiều chương trình - ngay cả những chương trình không tầm thường - để có thể di động giữa các hệ thống Linux. Chìa khóa là bộ công cụ gọi là Cơ sở Tiêu chuẩn Linux . Các LSB được thiết kế để tạo chỉ những loại ứng dụng di động. Biên dịch một ứng dụng cho LSB v5.0 và nó sẽ chạy trên bất kỳ môi trường Linux nào khác (có cùng kiến ​​trúc) thực hiện LSB v5.0. Một vài bản phân phối Linux tuân thủ LSB và các bản phân phối khác bao gồm các bộ công cụ / thư viện LSB như một gói có thể cài đặt. Nếu bạn xây dựng ứng dụng của mình bằng các công cụ LSB (như lsbcctrình bao bọc cho gcc) và liên kết với phiên bản thư viện LSB, bạn sẽ tạo một ứng dụng di động.


và với qemubạn thậm chí có thể chạy các chương trình được biên dịch cho các kiến ​​trúc khác nhau, (không phải hiệu suất cao, nhưng bạn có thể chạy chúng)
Jasen

1
Tôi thậm chí còn không biết về bộ công cụ Linux Standard Base, cảm ơn bạn rất nhiều! Tôi đã bắt đầu làm việc với C / C ++ từ rất lâu rồi, vì vậy rất nhiều thông tin trong những câu trả lời này là mới đối với tôi. Và rất hữu ích.
JCDeen

1
Bài viết trên Wikipedia nói rằng Debian và Ubuntu không triển khai LSB (và không có ý định làm như vậy.)
BlackJack

2
@ BlackJack- Bản phân phối không thực hiện 100% trong số đó như là một phần của HĐH lõi, nhưng bạn có thể cài đặt các thư viện và bộ công cụ tương thích LSB làm các gói tùy chọn. Tôi đã sử dụng Ubuntu (ví dụ) để xây dựng các chương trình tương thích LSB chạy trên Suse và Centos, bạn chỉ cần apt-get installmột vài gói.
bta

10

Có lẽ.

Những thứ có xu hướng phá vỡ nó bao gồm.

  1. Kiến trúc khác nhau. Rõ ràng các kiến ​​trúc hoàn toàn khác nhau sẽ không hoạt động (trừ khi bạn có một cái gì đó giống như chế độ người dùng qemu với binfmt_misc nhưng đó hầu như không phải là một cấu hình bình thường). Các nhị phân x86 có thể hoạt động trên amd64 nhưng chỉ khi các thư viện 32 bit cần thiết có sẵn.
  2. Phiên bản thư viện. Nếu việc chuyển đổi là sai thì nó sẽ không tìm thấy thư viện nào cả. Nếu sự chuyển đổi là như nhau nhưng nhị phân được xây dựng dựa trên phiên bản mới hơn của thư viện so với phiên bản mới thì nó có thể không tải được vì các ký hiệu mới hoặc các phiên bản ký hiệu mới. Cụ thể, glibc là một người sử dụng nhiều phiên bản biểu tượng, vì vậy các nhị phân được xây dựng dựa trên glibc mới hơn rất có khả năng thất bại với một glibc cũ hơn.

Nếu bạn tránh sử dụng bất kỳ thư viện thay đổi nhanh nào, hãy tránh thay đổi kiến ​​trúc và xây dựng trên bản phân phối cũ nhất mà bạn muốn nhắm mục tiêu, bạn có cơ hội tốt để thực hiện một công việc nhị phân trên nhiều bản phân phối.


4

Ngoài một số điều được đề cập trước đây, đã có một số thay đổi trong định dạng tệp thực thi. Đối với hầu hết các phần, linux sử dụng ELF, nhưng các phiên bản cũ hơn đã sử dụng a.out hoặc COFF.

Sự khởi đầu của một lỗ hổng wiki:

https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats

Có thể có một cách để khiến các phiên bản cũ hơn chạy các định dạng mới hơn, nhưng cá nhân tôi chưa bao giờ nhìn vào nó.


"cũ" là ở thời điểm này rất cũ bây giờ mặc dù.
cắm vào
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.