Pull to refresh

Comments 29

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

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

Интерпретаторы С и С++ тоже существуют, например cling, однако носят скорее теоретическую ценность.

UFO just landed and posted this here

дополнение — ассемблер обычно вручную вызывать не надо
Сразу получаем объектник, а потом линкуем их в правльном порядке, с правильными библиотеками

Нужно заметить, что линковка в нужном порядке — особенность не всех линкеров.
Помнится, линкер студии справлялся с библиотеками без правильного порядка.
Но это мелочи.

Флажки --start-group/--end-group у ld(1) позволяют не думать о зависимостях между объектными файлами.

Ух ты, я был уверен, что компиляция с промежуточным компилированием в ассемблер осталась далеко в прошлом.
А что мешает компилировать сразу в исполняемый код? И, как я понимаю, другие языки такой подход не используют, или я не прав?

Компиляция с явной генерацией полноценного файла для ассемблера — это подход в первую очередь GCC. Даже во многом близкий к нему Clang не делает это основным методом — он вместо этого строит представление на LLVM IR в памяти и, если не сказано иное флагами, уже из него генерирует объектный файл без ассемблера. (Но его можно попросить выдать как IR в текстовом виде, так и код для ассемблера).
Компиляторы, созданные в других традициях — например, MSVC — такого не делают; их можно попросить сделать листинг в ассемблере, но этот листинг нельзя потом скормить ассемблеру, чтобы получить объектный файл.

Причины такой двухстадийности gcc, по-моему, в его истории: у Столлмана получилось нормально сделать и лицензионно, и по сути результата — конверсию в задокументированный ассемблер на целевых платформах первого поколения (таких, как SunOS, HP-UX, AIX), но уже объектный формат имел сложновыясняемые и/или лицензионно недоступные особенности. Сейчас не нагуглилось, так что это обоснование только по воспоминаниям о прочитанном и услышанном около 20 лет назад. Потом же решили не менять этот подход, потому что он оказался вполне недорогим (по сравнению как с парсингом C++, так и с ценой преобразований программы в процессе компиляции), даже с промежуточным файлом.

И ещё вкусность — наличие полноценного ассемблерного исходника, который можно посмотреть и, если нужно, подправить под себя, очень помогло в росте белой хакерской культуры. Помню это по себе — если что не понимаешь, смотришь ассемблер и экспериментируешь и в нём тоже :)

С другими языками — опять же, кто компилирует:) Все языки из комплекта GCC (C, C++, Fortran, Ada...) или LLVM-based проходят такую фазу, обязательно или опционально. Аналогично, например, Go, но у него существенно свой ассемблер (причём на всех платформах; это мини-кошмар для системщика на x86 — кроме AT&T и Intel syntax, есть ещё Go syntax). В Unix мире много компиляторов переводят код своего языка на C, и уже этот результат компилируют, чтобы не заморачиваться после этого проблемами оптимизации; тогда тоже есть ещё фаза ассемблера.
Компилирование в ассемблер вместо объектного кода — традиционная манера 60-х годов, призванная упростить и уменьшить компилятор за счет времени компиляции.

Компилятор С исходно был сделан точно так же.
В организацию компилятора в Bell Unix версиях я не успел заглянуть, но верю. Но факт, что в одном мире (Unix) этот подход сохранили или как обязательный, или по крайней мере как доступный по запросу, даже когда давление ресурсов ослабло — а в других (DOS/Windows/etc.) он просто не возник, или не закрепился. Вероятные причины этого, IMHO, совпадают с теми, что я уже описал.
«60-е годы» это или чрезмерное обобщение, или надо говорить уже про 70-е. Под OS/360, например, компиляторы не использовали ассемблер в явном виде, у них был прямой «выхлоп» объектным файлом — а это середина 60-х.
Я таки думаю тут автор намеренно это сделал, дабы показать последовательность.
Без знания ассемблера бесполезно читать подобные статьи.

Пока не знаешь, как процессор работает с памятью, как вызываются процедуры… все рассуждения так и останутся пассами рук, магией.

Для тех, кто хочет реально разобраться, лучший способ что-то понять — сгенерировать компилятором ассемблерный листинг и изучить его, для начала лучше с отключенными оптимизациями. Будет повод узнать про ассемблер, что и как. Потом разберётесь, как устроена реализация объектов ООП и пр. Это действительно красиво.

Потом станет понятнее, зачем нужны obj-файлы и связывание, а уж как работает загрузка исполняемых файлов…

… После этого вы станете презирать интерпретаторы ))

Ну, как бы, интерпретация и компиляция — разные вещи со своими плюсами и минусами.
И просто презирать интерпретаторы(кстати, какие именно) попахивает максимализмом юношеским.

попахивает максимализмом юношеским

Спасибо за комплимент ))

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

Ну мы обсуждаем C++, не так ли. При реверсе я не замечил принципиальной разницы реализации ООП между MSVC, GCC, а также их версиями x86 и ARM. Отличаются передачей параметров (через регистры и/или стек в разных комбинациях). Ну и естественно RTL, обработка исключений.
Просто когда видишь, как производители CPU по крупицам улучшают предсказатели ветвления и бьются за каждый такт, становится грустно, что всё это богатство впоследствии успешно про$ирается программистами через высокоуровневые фронтенды, явы, дотнеты и виртуальные машины.
PS: сижу на ноутбуке с Core 2 Duo 1.6-1.8GHz и чувствую это почти физически )))
Ну и естественно RTL
… пардон, конечно же RTTI…
Посмотрите исходник gcc и вы поймете, что вы полные дауны.

Смелое заявление, аргументируете?

Не раскрыт процесс получения bin и hex файлов :)
print «hello world» на ассемблере пишется в ~12+длина строки байт.

driver.s
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E
movl $0, %eax
popq %rbp

Зачем столько мусора в коде?
Это что бы перевести строчку передаётся указатель на функцию endl. Оптимизатор просто разводит руками и оставляет как есть.

CXX - это переменная окружения, значением которой подразумевается g++ или иной компилятор языка C++. Например: CXX=g++ ./configure ; make ; make install

Sign up to leave a comment.

Articles