2016-12-14 6 views
2

Предположим, у меня есть программа, написанная на C, и у меня есть два одинаковых компьютера, один из которых работает под управлением Windows, а другой - Linux. Поскольку компьютеры идентичны, их процессоры имеют один и тот же набор команд, поэтому машинный код после компиляции должен быть одинаковым. Так почему мне нужно скомпилировать мою программу дважды? Предположим, я не звоню в любую связанную с ОС функцию или что-то, что зависит от реальной ОС.Почему машинный код зависит от типа ОС?

+6

Исполняемые форматы отличаются. –

+7

Вы не можете избежать вызова функции ОС или того, что вызывает функцию ОС. Нет, если вы хотите, чтобы ваша программа имела видимый эффект, например, например, печать номера. –

+0

Почему они разные? Связано ли это с режимом защищенного режима/ядра? Если процессоры идентичны, не должны ли они работать с одним и тем же машинным кодом? –

ответ

6

Машинный код не зависит от ОС, он одинаковый для одного и того же ЦП.

Если вы не указали какой-либо OS-код машинного кода в целевом режиме процессора (скажем, x86 32b) и загрузите его в некоторую ROM-память, то он будет доступен, вы можете сопоставить эту часть ROM как в Windows, так и в в linux (совершенно другим OS API для сопоставления физической памяти и предоставления ему прав на выполнение), и прыгайте туда .. и машинный код в ROM будет работать одинаково.

Зачем мне нужно скомпилировать мою программу дважды? Предположим, я не звоню в любую связанную с ОС функцию или что-то, что зависит от реальной ОС.

Вам не обязательно. Но обычно вам нужна какая-то точка входа в ваш код, и, как правило, самый простой способ предоставления универсальной точки входа - следовать установленному OS ABI (Application Binary Interface), например, в 32-битных окнах вы читаете аргументы из стека, а в 64b linux вы получаете аргументы в регистрах (когда это возможно). Если вы не будете корректировать код пролога процедуры, чтобы правильно выбирать аргументы, он будет работать с неправильными входами в «другой» ОС, чем это написано.

Но сам машинный код, инструкции CPU, такие же.

При этом на x86 ситуация немного более волосатая из-за исторической обратной совместимости, поэтому процессор может находиться в режиме 16b, в режиме 32b [защищенный] (несколько из них плюс по-разному настроен) или режиме 64b. Инструкция CPU 80386 mov eax,1 имеет различную кодировку машинного кода для режима 16b и для режима 32b.

Но пока вы нацеливаете один и тот же режим процессора, машинный код той же команды скомпилирован таким же образом. Вы просто пишете источник по-разному, чтобы следовать различным ABI.

И исполняемые файлы ... каждый формат отличается от других, это даже не «по ОС», из-за исторических причин почти все ОС x86 поддерживают несколько исполняемых форматов файлов, поэтому метаданные вокруг машинного кода, хранящиеся в файле (для использования ОС во время загрузки машинного кода в память и установки его для запуска) совершенно разные.

Практический пример: приложение linux wine, которое может выполнять исполняемые файлы Windows, предоставляя поддельные точки привязки ОС для имитации ОС Windows и понимая исполняемые двоичные файлы Windows, поэтому правильно загружаем их в память. Машинный код такого приложения Windows запускается без каких-либо дополнительных исправлений.

+1

Еще одно примечание. Ассемблеры обычно производят «объектные файлы», которые снова в формате, специфичном для привязки к программе, поэтому Microsoft Visual Studio использует разные файлы «.obj» для хранения одного и того же собранного машинного кода, чем gcc для linux, производящего «.o». Часть машинного кода объектного файла такая же, но метаданные, позволяющие связать такой файл, могут быть совершенно разными (плюс другой формат метаданных отладки и т. Д.). Так вот, это еще одна причина, почему вы должны скомпилировать один и тот же источник несколько раз, но это не для ОС, а для каждой инструментальной цепочки. – Ped7g

+2

Кроме того, различия ABI в размерах типоразмеров: 'long' - 32 бита на x86-64 Windows, но 64-битные в System V x86-64 ABI. Таким образом, одна и та же структура C может означать разные вещи при компиляции для разных ABI, не говоря уже о том, что отдельные переменные различаются по размерам (и, следовательно, они нуждаются в различном размере операнда в машинный код и разной компоновке стека для местных жителей и т. Д.) –

+2

Это хуже чем «per-toolchain». Это для версии-toolchain. Двоичная совместимость промежуточных файлов не гарантируется неограниченно для всех инструментов от конкретного поставщика. Я не уверен, что люди GCC пытаются обеспечить обратную совместимость здесь, но Microsoft явно не делает этого. (На самом деле это довольно иронично). Единственная вещь, которая работает с cross-toolchain, - это отладочная информация, поскольку она имеет стандартизованный формат. Но, конечно, самое лучшее в стандартах есть так много на выбор: COFF, ELF, CodeView, PDB, ... :-) –