Во что на самом деле компилируют C и Ассемблер? [закрытый]

Таким образом, я узнал, что C (++) программы на самом деле не компилируют в простой "двоичный файл" (я, возможно, понял некоторые вещи превратно здесь, в этом случае я сожалею: D), но к диапазону вещей (таблица символов, связанный с OS материал...), но...

  • Ассемблер "Компилирует" в чистый двоичный файл? Это не означает дополнительного материала помимо ресурсов как предопределенные строки и т.д.

  • Если C компилирует во что-то еще, чем простой двоичный файл, как тот маленький ассемблерный загрузчик может просто скопировать инструкции от жесткого диска до памяти и выполнить их? Я имею в виду, компилирует ли ядро ОС, которое, вероятно, записано в C, во что-то другое, чем простой двоичный файл - как загрузчик обрабатывает его?

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

47
задан starblue 26 January 2010 в 20:25
поделиться

11 ответов

C Как правило, компилируется к ассемблере, только потому, что делает жизнь легкой для писателя плохого компилятора.

Код сборки всегда собирается (не «компилирует») к RELOCATable объектному коду . Вы можете думать об этом как бинарный машинный код и двоичные данные, но с большим количеством украшений и метаданных. Ключевые части:

  • код и данные отображаются в названных «разделах».

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

  • Решемые объектные файлы могут включать «отверстия», которые должны быть заполнены значениями меток, определенных в другом месте. Официальное имя для такого отверстия представляет собой запись .

Например, если вы компилируете и собираете (но не ссылаетесь) эту программу

int main () { printf("Hello, world\n"); }

, вы, скорее всего, вытесните с помощью переезжаемого объекта с помощью

  • в разделе в разделе , содержащую машину Код для Главная

  • Определение метки для Основное , которые указывают на начало текстового раздела

  • A ROTATA (данные только для чтения), содержащие байты Струнной буквальной буквы «Hello, World \ N»

  • Ввод переселения, который зависит от Printf , и это указывает на «отверстие» в инструкции по вызову в середине текстового раздела.

Если вы находитесь на системе Unix, рельзательный объектный файл, как правило, называется файлом .o, как в Hello.o , и вы можете исследовать определения этикетки и используемые с помощью простого инструмента NM , и вы можете получить более подробную информацию от несколько сложнее всего более сложного инструмента objdump .

Я учу класс, который охватывает эти темы, и у меня есть студенты, пишут ассемблер и линкер, который занимает пару недель, но когда они сделали, что большинство из них имеют довольно хорошую ручку на ReloCatable объекта. Это не такая легкая вещь.

47
ответ дан 26 November 2019 в 19:31
поделиться

Есть две вещи, которые вы можете смешать здесь. Обычно есть две темы:

Последнее может составить первой в процессе сборки. Некоторые промежуточные форматы не собираются, но выполнены виртуальной машиной. В случае C ++ может быть составлен .

Но в целом C и C ++ обычно скомпилированы в двоичные или другие слова в исполняемый формат файла.

1
ответ дан 26 November 2019 в 19:31
поделиться

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

Оба проходят через то, что удаляется и почему, и вы используете сборки для создания файлов ELF без использования параметров, которые сделают это для вас.

Надеюсь, что поможет.

Отредактируйте - вы также можете взглянуть на сборку для загрузчика, например, в TrueCrypt http://www.truecrypt.org или "Stage1" Grub (то, что на самом деле написано к МЛУ).

0
ответ дан 26 November 2019 в 19:31
поделиться

Как я понимаю, чипсет (CPU и т. Д.) будет иметь набор регистров для хранения данных и понять набор инструкций для манипулирования этими регионами. Инструкции будут такие вещи, как «хранить это значение для этого реестра», «переместите это значение», или «сравнить эти два значения». Эти инструкции часто экспрессируются в коротких людских алфавитных кодах (язык сборки или ассемблер), которые сопоставлены с числами, которые чипсет понимают - эти цифры представлены в чип в двоичном (машинный код.)

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

0
ответ дан 26 November 2019 в 19:31
поделиться

Чтобы ответить на ваши вопросы, обратите внимание, что это субъективно, так как в этом случае это различные процессоры, разные платформы, разные сборки и компиляторы C, в этом случае я расскажу о платформе Intel X86.

  1. Уборки не составляют чистых двоичных данных, они являются необработанным машинным кодом, определенным с сегментами, такими как данные, текст и BSS, но и несколько, это называется объектным кодом. Линкер входит в действие и регулирует сегменты, чтобы сделать его исполняемым, то есть готовым к запуску. Между прочим, вывод по умолчанию при компиляции с использованием GCC является «aTOUT», то есть сокращение для выхода ассемблера.
  2. Загрузчики имеют определенную специальную директиву, обратно в дни DOS, было бы обычным образом найти такую ​​директиву, такую ​​как .org 100H , который определяет код ассемблера для старого .com Разнообразие до .exe захватила популярность. Кроме того, вам не нужно было иметь ассемблер для создания файла .com, используя старый Debug.exe, который пришел с MSDOS, сделал трюк для небольших простых программ, файлы .com не нуждался в линкера и были подсказки Для запуска двоичного формата. Вот простая сессия с использованием отладки.
1:*a 0100
2:* mov AH,07
3:* int 21
4:* cmp AL,00
5:* jnz 010c
6:* mov AH,07
7:* int 21
8:* mov AH,4C
9:* int 21
10:*
11:*r CX
12:*10
13:*n respond.com
14:*w
15:*q

Это производит готовую программу .Com .com под названием «indept.com», которая ждет нажатия клавиши и не повторяет его на экран. Уведомление, начало, использование «A 100H», которое показывает, что указатель инструкции начинается на 100 часов, что является особенностью .COM. Этот старый скрипт в основном использовался в пакетных файлах, ожидающих ответа, а не эхо его. Оригинальный скрипт можно найти здесь .

Опять же, в случае загрузочных погрузчиков они преобразуются в двоичный формат, произошла программа, которая использовалась для приема DOS, называемых EXE2Bin . Это была задача преобразования необработанного объекта в формат, который можно скопировать на загрузочный диск для загрузки. Помните, что Linker не работает против собранного кода, так как линкер предназначен для среды выполнения и устанавливает код, чтобы сделать его Runnable и исполняемым.

BIOS при загрузке, ожидает, что код должен быть в сегменте: смещение, 0x7C00, если моя память обслуживает меня правильным, код (после того, как exe2bin'd) начнет выполнять, то загрузчик переносит себя ниже вниз в память и Продолжайте загрузку, выпустив int 0x13, чтобы прочитать с диска, включите ворота A20, включите DMA, включите защищенный режим, когда BIOS находится в режиме 16bit, то данные, прочитанные с диска, загружаются в память, то проблемы загрузчика Дальнее прыжка в код данных (скорее всего, будет написан в C). Это по сути, как система сапоги.

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

Надеюсь, это поможет, С уважением, Том.

3
ответ дан 26 November 2019 в 19:31
поделиться

Чтобы ответить на сборку часть вопроса, сборка не составляется бинарным, насколько я понимаю. Сборка === двоичный. Это напрямую переводит. Каждая операция сборки имеет двоичную строку, которая непосредственно соответствует им. Каждая операция имеет двоичный код, и каждая переменная регистра имеет двоичный адрес.

То есть, если не ассемблер! = Сборка, и я неправильно понимаю ваш вопрос.

-121--1099926-

с (++) (неуправляемый) действительно компилирует до простых двоичных. Некоторые, связанные с ОС - это вызовы функций BIOS и OS, они отличаются для каждой ОС, но все еще двоичные.
1. Ассемблер компилируется в чистый двоичный двоичный, но, как это становится странным, оно менее оптимизировано, чем C (++)
2. Ядро ОС, а также загрузчик, также написано в C, поэтому нет проблем здесь.

Java, управлял C ++, а другие .NET материал, компилирует в какой-то псевдокод (MSIL в .NET), что делает его поперечной ОС и кроссплатформенной платформой, но требует локального интерпретатора или переводчика.

-4
ответ дан 26 November 2019 в 19:31
поделиться

Они компилируют файл в определенном формате (COFF для Windows и т. Д.), Состоит из заголовков и сегментов, некоторые из которых имеют «простые двоичные» коды OP. Сборщики и компиляторы (такие как C) создают один и тот же выход. Некоторые форматы, такие как старые файлы * .com, не имели заголовков, но все же имели определенные предположения (например, где в памяти он будет загружен или насколько это может быть).

На машинах Windows Boostrapper OS находится в дисковом секторе, загруженном BIOS, где оба из них являются «простой». Как только ОС загрузила свой погрузчик, он может читать файлы, которые имеют заголовки и сегменты.

Это помогает?

1
ответ дан 26 November 2019 в 19:31
поделиться

Есть разные фазы в переводе C ++ в двоичный исполняемый. Спецификация языка не ясно указывает на фазы перевода. Тем не менее, я опишу общие фазы перевода.

Источник C ++ на сборку или ивермедийный язык

Некоторые компиляторы фактически переводят код C ++ на язык сборки или промежуточный язык. Это не требуемая фаза, а полезная в отладке и оптимизации.

Сборка к объектному коду

Следующим общим шагом является перевод языка сборки в объектный код. Объектный код содержит код сборки с относительными адресами и открытыми ссылками на внешние подпрограммы (методы или функции). В целом, переводчик помещает как можно больше информации в объектный файл, поскольку он может, все остальное - нерешено .

Связывание объекта объекта (ы)

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

Выполнение файлов двоичных

Операционная система загружает исполняемый файл, обычно с жесткого диска, и помещает его в память. ОС может преобразовать относительные адреса в физические места. ОС также может подготовить ресурсы (такие как DLL и виджеты GUI), которые требуются исполняемыми (которые могут быть указаны в исполняемом файле).

Компиляция непосредственно в двоичном Некоторые компиляторы, такие как те, которые используются в встроенных системах, имеют возможность компилировать от C ++ непосредственно на исполняемый двоичный код. Этот код будет иметь физические адреса вместо относительного адреса и не требует загрузки ОС.

Преимущества

Одним из преимуществ этих этапов заключается в том, что программы C ++ могут быть разбиты на кусочки, скомпилированные индивидуально и связаны с более поздним временем. Они могут даже связаны с частями других разработчиков (A.k.a. Библиотеки). Это позволяет разработчикам только в частях компилятора в разработке и ссылку на частях, которые уже подтверждены. В целом, перевод с C ++ на объект - это трудовая часть процесса. Кроме того, человек не хочет ждать всех фаз, чтобы завершить, когда в исходном коде есть ошибка.

Держите разум и всегда ожидайте, что третья альтернатива .

18
ответ дан 26 November 2019 в 19:31
поделиться

У вас есть много ответов, но я думаю, что я могу сохранить этот краткий.

«Двоичный код» относится к битам, которые питаются через микропроцессорные цепи. Микропроцессор загружает каждую инструкцию из памяти в последовательности, делая все, что они говорят. Различные семейства процессоров имеют разные форматы для получения инструкций: x86, ARM, PowerPC и т. Д. Вы указываете процессору на инструкции, которую вы хотите, предоставив ему адрес инструкции в памяти, а затем он наглядят в память через остальную часть программы.

Когда вы хотите загрузить программу в процессор, сначала вы должны сделать двоичный код доступен в память, поэтому он имеет адрес в первую очередь. Компилятор C выводит файл в файловой системе, который должен быть загружен в новое виртуальное адресное пространство. Следовательно, в дополнение к двоичному коду, этот файл должен включать информацию о том, что она имеет двоичный код, а какое его адресное пространство должно выглядеть.

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

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

1
ответ дан 26 November 2019 в 19:31
поделиться

Чтобы ответить на сборочную часть вопроса, сборка не компилируется в двоичный файл, как я понимаю. Сборка = = = двоичная. Это прямо переводится. Каждая операция сборки имеет двоичную последовательность, которая непосредственно соответствует ей. Каждая операция имеет двоичный код, а каждая переменная регистра имеет двоичный адрес.

То есть, если только Assembler! = Assembly и я неправильно понимаю ваш вопрос.

-121--1099926-

Я знаю, что это может звучать как умный ответ * *, но... ЛУЧШИЙ способ разработки классов threadsafe состоит в том, чтобы на самом деле знать о многопоточности, о ее последствиях, ее тонкостях и о том, что она подразумевает. Там нет серебряной пули.

Серьезно... не пытайтесь использовать многопоточность (в производственных сценариях, я имею в виду), пока не поймете, во что вы ввязываетесь... Это может быть огромной ошибкой.

Изменить: Вы, конечно, должны знать примитивы синхронизации как операционной системы, так и языка выбора (в данном случае C # под Windows, я полагаю).

Мне жаль, что я не даю просто код, чтобы просто сделать класс threadsafe. Это потому, что он не существует . Совершенно threadsafe класс, вероятно, будет просто медленнее, чем просто избежать потоков и, вероятно, будет действовать как узкое место для того, что вы делаете... эффективно отменяя все, чего вы добиваетесь, используя потоки.

-121--2814196-

Давайте возьмем программу Си.

При запуске gcc , clang или «cl» в программе c она будет проходить следующие этапы:

  1. Препроцессор (# include, # ifdef, анализ триграфа, кодирование трансляций, управление комментариями, макросы...), включая лексирование в маркеры препроцессора и, в конечном итоге, получение плоского текста для ввода в собственно компилятор.
  2. Лексический анализ (создание маркеров и лексических ошибок).
  3. Синтаксический анализ (создание дерева синтаксического анализа и синтаксических ошибок).
  4. Семантический анализ (создание таблицы символов, информация об объеме и ошибки определения объема/ввода) Также поток данных, преобразующий логику программы в «промежуточное представление», с которым может работать оптимизатор. (Часто SSA ). clang/LLVM использует LLVM-IR, gcc использует GIMPLE, а затем RTL.
  5. Оптимизация логики программы, включая постоянное распространение, встраивание, подъем инвариантов из петель, автовекторизацию и многие другие вещи. (Большая часть кода для широко используемого современного компилятора - passes оптимизации.)Преобразование через промежуточные представления является лишь частью работы некоторых компиляторов, что делает невозможным/бессмысленным «отключить все оптимизации»
  6. Вывод в источник сборки (или другой промежуточный формат, такой как .NET IL bytecode )
  7. Сборка сборки в какой-либо двоичный формат объекта.
  8. Связывание сборки в любые статические библиотеки, а также при необходимости ее перемещение.
  9. Вывод конечного исполняемого файла в elf, PE/coff, MachO64 или любом другом формате

На практике некоторые из этих шагов могут выполняться одновременно, но это логический порядок. Большинство компиляторов имеют опции остановки после любого заданного шага (например, препроцесса или asm), включая дамп внутреннего представления между проходами оптимизации для компиляторов с открытым исходным кодом, таких как GCC. ( -ftree-dump-... )

Обратите внимание, что вокруг фактического исполняемого двоичного файла имеется «контейнер» формата elf или coff, если это не DOS .com executable

Вы обнаружите, что книга на компиляторах (я рекомендую Dragon book, стандартная вводная книга в поле) будет иметь всю информацию, которую вы

Как прокомментировал Марко, связывание и загрузка является большой областью, и книга Dragon более или менее останавливается на выходе исполняемого двоичного файла. На самом деле перейти оттуда к работе на операционной системе - это прилично сложный процесс, который Ливайн в Linkers and Loaders охватывает.

Я вики-я получил этот ответ, чтобы позволить людям исправить любые ошибки/добавить информацию.

37
ответ дан 26 November 2019 в 19:31
поделиться

Чтобы ответить на сборочную часть вопроса, сборка не компилируется в двоичный, как я понимаю. Сборка === бинарная. Это прямо переводит. Каждая операция сборки имеет двоичную строку, которая ей напрямую соответствует. Каждая операция имеет двоичный код, и каждая регистровая переменная имеет двоичный адрес.

То есть, если только Assembler! = Assembly, и я неправильно понимаю ваш вопрос.

1
ответ дан 26 November 2019 в 19:31
поделиться