Будет ли исполняемый файл Linux, скомпилированный на одном «варианте» Linux, работать на другом?

59

Будет ли исполняемый файл небольшой, чрезвычайно простой программы, такой как показанная ниже, скомпилированной на одном варианте Linux, работать на другом варианте? Или это нужно будет перекомпилировать?

Имеет ли значение архитектура машины в таком случае, как этот?

int main()
{
  return (99);
}
JCDeen
источник
2
Спасибо всем за выдающиеся ответы! Я узнал намного больше, чем ожидал. Я специально сделал код искусственно простым, чтобы он зависел от как можно меньшего количества библиотек; но я действительно должен был заявить об этом заранее. Большая часть моего C ++ кодирования на разных платформах заключалась в разработке с использованием инструмента Microsoft, такого как Visual Studio, а затем переносе кода в систему * nix и перекомпиляции.
JCDeen
4
Множество аспектов и соображений, высказанных здесь, поразили меня! Я искренне желаю, чтобы я мог выбрать несколько в качестве ответа. Еще раз спасибо всем! С уважением.
JCDeen
2
Android также является операционной системой на основе Linux. Удачи, однако, запуск любого кода, скомпилированного glibcтам, или наоборот. Конечно, это не совсем невозможно .
Undercat
2
Для максимальной совместимости инструмента командной строки вы можете использовать uClibc, musl или dietlibc вместо glibc и статически связать свой 32-битный исполняемый файл ( gcc -m32 -static). При этом любой i386 или amd64 Linux сможет запустить исполняемый файл.
оч
10
Вы должны вернуться 42 ! :)
Гомункул Ретулли

Ответы:

49

По-разному. Что-то скомпилированное для IA-32 (32-разрядная версия Intel) может работать на amd64, поскольку Linux на Intel сохраняет обратную совместимость с 32-разрядными приложениями (с установленным подходящим программным обеспечением). Вот ваш codeскомпилированный в 32-битной системе RedHat 7.3 (около 2002 года, gcc версии 2.96), а затем скопированный двоичный файл в 64-битную систему Centos 7.4 и его запуск (около 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) остаются в том же семействе «дистрибутивов», поэтому, вероятно, будут иметь лучшую переносимость, чем переход от какой-то случайной установки «Linux с нуля» с 2002 года к другому случайному дистрибутиву Linux в 2018 году ,

То, что скомпилировано для 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 - OpenBSD, в отличие от amd64 , не поддерживает 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

с которым процессор Intel понятия не имеет, что делать, и аналогично для сборки Intel на MIPS.

Вы можете использовать QEMU или другой эмулятор для запуска стороннего кода (возможно, очень, очень медленно).

Тем не мение! Ваш код очень простой, поэтому у него будет меньше проблем с переносимостью, чем с чем-либо еще; программы обычно используют библиотеки, которые со временем менялись (glibc, openssl, ...); для них также может потребоваться установка более старых версий различных библиотек (например, RedHat обычно помещает «compat» где-то в имени пакета)

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

или, возможно, беспокоиться об изменениях ABI (Application Binary Interface) для старых вещей, которые используют glibc, или о более недавних изменениях из-за C ++ 11 или других выпусков C ++. Можно также скомпилировать static (значительно увеличив бинарный размер на диске), чтобы попытаться избежать проблем с библиотеками, хотя от того, делал ли какой-нибудь старый двоичный файл это, зависит, компилирует ли старый дистрибутив большинство всего динамического (RedHat: да) или нет. С другой стороны, такие вещи patchelfмогут перенастраивать динамические (ELF, но, вероятно, не a.outотформатированные) двоичные файлы для использования других библиотек.

Тем не мение! Возможность запустить программу - это одно, а на самом деле делать что-то полезное с этим - другое. Старые 32-разрядные двоичные файлы Intel могут иметь проблемы с безопасностью, если они зависят от версии OpenSSL, в которой есть какая-то ужасная проблема безопасности, о которой не сообщалось в бэкпорте, или программа может вообще не иметь возможности вести переговоры с современными веб-серверами (как современные серверы отклоняют старые протоколы и шифры старой программы), или протокол SSH версии 1 больше не поддерживается, или ...

thrig
источник
14
По поводу первого абзаца: нет, Intel называет его «Intel 64» (в наши дни, после того, как пройдем некоторые другие имена ранее). IA-64 относится к Itanium, а не к x86-совместимым.
Хоббс
1
@hobbs спасибо, я заменил эти ссылки на amd64; Я оставлю наименование вещей отделу маркетинга Intel.
августа
3
почему бы не упомянуть статические ссылки?
dcorking
2
Меняются не только библиотечные ABI - интерфейс системного вызова ядра также расширяется со временем. Обратите внимание на for GNU/Linux 2.6.32(или такие) в выводе file /usr/bin/ls.
Чарльз Даффи
1
@ Wilbert Возможно, вам не хватает того, что упоминалось о Red Hat Linux, который отличается от Red Hat Enterprise Linux.
Боб
67

Короче говоря: если вы переносите скомпилированный двоичный файл с одного хоста на другой, используя ту же (или совместимую) архитектуру , вы, возможно, прекрасно подойдете к другому дистрибутиву . Однако по мере увеличения сложности кода увеличивается вероятность того, что он будет связан с библиотекой, которая не установлена; установлен в другом месте; или установлен на другой версии, увеличивается. Возьмем, к примеру, ваш код, для которого lddсообщается о следующих зависимостях при компиляции gcc -o exit-test exit-test.cна хосте Ubuntu Linux (производном от 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)

Очевидно, что этот бинарный файл не запустится, если я перекину его, скажем, на 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, это кошка с множеством скинов, две из которых описаны выше.

DopeGhoti
источник
4
Действительно, я забыл о статических двоичных файлах. Некоторые поставщики используют статические двоичные файлы, а некоторые авторы вредоносных программ - для обеспечения максимальной двоичной совместимости между версиями Linux с одинаковой архитектурой
Руи Ф. Рибейро,
8
Forklifting ...?
user253751
2
@immibis Я думаю, что это означает копирование данных (исполняемого файла) из одной среды (дистрибутива) в другую, где данные не предназначены для целевой среды.
wjandrea
13
Ваш пример Linux, к сожалению, довольно искусственный и иллюстрирует вашу точку зрения об архитектуре, а не о дистрибутивах: вы создали 32-битный двоичный файл в Debian и попытались запустить его на 64-битном RHEL; это разные архитектуры ... Двоичные файлы с одинаковой архитектурой с таким небольшим количеством библиотечных зависимостей могут быть скопированы просто отлично.
Стивен Китт
7
@MSalters Я не говорю, что это неразумно, я говорю, что это плохой пример, учитывая то, что пытается сделать DopeGhoti (вы не можете копировать двоичные файлы из одного дистрибутива в другой - что неправильно). Конечно, 64-битный Linux на Intel также поддерживает запуск 32-битных исполняемых файлов с соответствующей инфраструктурой. Допустимым примером в этом случае будет IMO: сборка amd64двоичного файла и запуск его в другом amd64дистрибутиве или сборка i386двоичного файла и запуск в другом i386дистрибутиве.
Стивен Китт
25

Добавим к отличным ответам @thrig и @DopeGhoti: Unix или Unix-подобные ОС, включая Linux, традиционно всегда разрабатывались и выстраивались больше для переносимости исходного кода, чем двоичные файлы.

Если у вас нет ничего аппаратного или простой источник, как в вашем примере, вы можете переместить его без каких-либо проблем из практически любой версии Linux или архитектуры в исходный код, если на целевых серверах установлены пакеты разработки C , необходимые библиотеки и соответствующие библиотеки разработки установлены.

Поскольку при переносе более продвинутого кода из более старых версий Linux, удаленных во времени, или более специфических программ, таких как модули ядра, для разных версий ядра, вам, возможно, придется адаптировать и модифицировать исходный код для учета устаревших библиотек / API / ABI.

Руи Ф Рибейро
источник
19

По умолчанию вы почти наверняка столкнетесь с проблемами с внешними библиотеками. В некоторых других ответах более подробно рассматриваются эти проблемы, поэтому я не буду дублировать их работу.

Однако вы можете скомпилировать множество программ - даже нетривиальных - для переносимости между системами Linux. Ключом является инструментарий, называемый Linux Standard Base . LSB предназначен для создания только этих типов портативных приложений. Скомпилируйте приложение для LSB v5.0, и оно будет работать в любой другой среде Linux (той же архитектуры), которая реализует LSB v5.0. Некоторые дистрибутивы Linux совместимы с LSB, а другие включают наборы / библиотеки LSB в виде устанавливаемого пакета. Если вы создаете свое приложение с использованием инструментов LSB (например, lsbccоболочки gcc) и ссылаетесь на версию библиотек LSB, вы создадите переносимое приложение.

ВТА
источник
и с qemuвами можно даже запускать программы, скомпилированные для другой архитектуры (не с высокой производительностью, но вы можете запускать их)
Jasen
1
Я даже не знал о наборе инструментов Linux Standard Base, так что спасибо! Я начал работать с C / C ++ очень давно, поэтому большая часть информации в этих ответах является новой для меня. И очень полезно.
JCDeen
1
В статье в Википедии говорится, что Debian и Ubuntu не поддерживают LSB (и не намерены это делать).
Блэкджек,
2
@ BlackJack - Сам дистрибутив не реализует 100% его как часть базовой ОС, но вы можете установить библиотеки и инструментарии для совместимости с LSB в качестве дополнительных пакетов. Я использовал Ubuntu (например) для создания LSB-совместимых программ, которые затем запускались на Suse и Centos, вам просто нужно apt-get installпару пакетов.
ВТА
10

Может быть.

Вещи, которые имеют тенденцию ломать это включают.

  1. Разные архитектуры. Очевидно, что совершенно разные архитектуры не будут работать (если у вас нет чего-то вроде пользовательского режима qemu с binfmt_misc, но это вряд ли нормальная конфигурация). Двоичные файлы x86 могут работать на amd64, но только при наличии необходимых 32-разрядных библиотек.
  2. Библиотечные версии. Если версия неверна, тогда она вообще не найдет библиотеку. Если soversion такой же, но бинарный создан на основе более новой версии библиотеки, чем та, с которой он работает, он может не загрузиться из-за новых символов или новых версий символов. В частности, glibc является активным пользователем версионирования символов, поэтому двоичные файлы, созданные на основе более нового glibc, с большой вероятностью могут потерпеть неудачу с более старым glibc.

Если вы избегаете использования каких-либо быстро меняющихся библиотек, избегаете изменений в архитектуре и опираетесь на самый старый дистрибутив, на который вы хотите ориентироваться, у вас есть хороший шанс заставить один двоичный файл работать на многих дистрибутивах.

plugwash
источник
4

В дополнение к некоторым вещам, упомянутым ранее, произошли некоторые изменения в формате исполняемого файла. В большинстве случаев linux использует ELF, но в более старых версиях использовались a.out или COFF.

Начало wikihole:

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

Возможно, есть способ заставить старые версии работать с новыми форматами, но я лично никогда не изучал это.

Бен
источник
"старше" в данный момент очень стар, хотя.
штепсельная розетка