Ответ 1
(Конечно, все перечисленные ниже применимы только к ассемблеру x86 и x86-64 для процессоров и операционных систем IA-32 и AMD64.)
Остальные ответы, видимые на данный момент, все правильные, но, на мой взгляд, пропустите точку. Синтаксис AT & T по сравнению с синтаксисом Intel является полным без проблем; любой достойный инструмент будет работать с обоими синтаксисами или иметь аналог или замену. И все равно они собираются. (Protip: вы действительно хотите использовать синтаксис Intel. Вся официальная документация по процессору делает это. Синтаксис AT & T - это всего лишь одна гигантская головная боль.) Да, найти правильные флаги для передачи ассемблеру и компоновщику может быть сложным, но вы будете узнайте, когда у вас есть это, и вам нужно сделать это только один раз для ОС (если вы забыли записать его где-нибудь!).
Сам сборщик, конечно же, полностью ОС-агностик. ЦПУ не заботится о том, какая операционная система работает. Если вы не делаете крайне хакерский хаос (то есть разработку ОС), гайки и болты о том, как взаимодействуют ОС и ЦП, практически не имеют значения.
Внешний мир
Проблема с языком ассемблера возникает, когда вы взаимодействуете с внешним миром: ядром ОС и другим кодом пользовательского пространства. Пользовательское пространство является самым сложным: вы должны получить право ABI, или ваша программа сборки практически бесполезна. Эта часть обычно не переносима между ОС, если вы не используете батуты /thunks (в основном другой слой абстракции, который должен быть перезаписан для каждой ОС, которую вы собираетесь поддерживать).
Самая важная часть ABI - это то, что конвенция вызова используется для функций стиля C. Они чаще всего поддерживаются, и то, с чем вы, вероятно, будете взаимодействовать, если вы пишете сборку. Agner Fog поддерживает несколько хороших ресурсов на его сайте; особенно полезно подробное описание соглашений о вызовах. В своем ответе Норман Рэмси упоминает ПИК и динамические библиотеки; по моему опыту, вам, как правило, не нужно беспокоиться об этом, если вы этого не хотите. Статическое связывание отлично подходит для типичного использования языка ассемблера (например, для перезаписи основных функций внутреннего контура или другой точки доступа).
Вызывающее соглашение работает в двух направлениях: вы можете вызывать C из сборки или сборки из C. Последний имеет тенденцию быть немного легче, но нет большой разницы. Вызов C из сборки позволяет использовать такие вещи, как функции вывода стандартной библиотеки C, а вызов сборки из C - это, как правило, доступ к реализации сборки одной критически важной функции.
Системные вызовы
Другое, что ваша программа будет делать, это сделать системные вызовы. Вы можете написать полную и полезную программу сборки, которая никогда не вызовет внешние функции C, но если вы хотите написать чистую ассемблерную программу, которая не передает аутсорсинг Fun Stuff кому-то другому, вам понадобятся системные вызовы. И, к сожалению, системные вызовы полностью и совершенно разные для каждой ОС. Системные вызовы в стиле Unix, которые вам потребуются (но, безусловно, не ограничены!) open
, creat
, read
, write
и все важные exit
, а также mmap
если вам нравится динамически распределять память.
В то время как каждая ОС отличается, большинство современных ОС следуют общей схеме: вы загружаете номер системного вызова в регистр, обычно EAX
в 32-битном коде, затем загружаете параметры (как вы это делаете сильно варьируется) и, наконец, выдает запрос прерывания: он INT 2E
для ядер Windows NT или INT 80h
для Linux 2.x и FreeBSD (и, я считаю, OSX). Затем ядро запускает, выполняет системный вызов и возвращает выполнение в вашу программу. В зависимости от ОС, он может обрезать регистры или стек как часть системного вызова; вам нужно будет убедиться, что вы прочитали документацию по системному вызову для своей платформы.
SYSENTER
Ядро Linux 2.6 (и, я считаю, Windows XP и новее, хотя я никогда не пытался его использовать в Windows) также поддерживает более быстрый и быстрый способ сделать системный вызов: инструкция SYSENTER
, введенная Intel в новой версии Чипы Pentium. Чипы AMD имеют SYSCALL
, но мало 32-разрядных ОС используют его (хотя это стандартный для 64-битных, я думаю, мне не приходилось делать прямые системные вызовы из 64-битной программы, поэтому я не уверен на этом). SYSENTER
значительно сложнее настроить и использовать (см., например, Линус Торвальдс по внедрению поддержки SYSENTER
для Linux 2.6: "Я отвратительная свинья, и горжусь этим, чтобы загрузиться".) Я лично могу подтвердить ее особенность; Я как-то написал функцию сборки, которая выпустила SYSENTER
прямо в ядро Linux 2.6, и я до сих пор не понимаю различные стеки и регистрирую трюки, которые заставили его работать... но работайте!
SYSENTER
несколько быстрее, чем выдача INT 80h
, и поэтому его использование желательно, когда доступно. Для упрощения написания как быстрого, так и портативного кода Linux отображает VDSO под названием linux-gate
в адресное пространство каждой программы; вызов специальной функции в этом VDSO вызовет системный вызов самым быстрым доступным механизмом. К сожалению, использование этого, как правило, больше проблем, чем стоит: INT 80h
гораздо проще делать в малой процедуре сборки, которая стоит небольшого штрафа за скорость. Если вам не нужна максимальная производительность... и если вам это нужно, вы, вероятно, не захотите звонить в VDSO, и вы знаете свое оборудование, поэтому вы можете просто сделать ужасно опасную вещь и выпустить SYSENTER
самостоятельно.
Все остальное
Помимо требований, предъявляемых взаимодействием с ядром и другими программами, между операционными системами существует очень мало различий. Ассамблея раскрывает душу машины: вы можете работать по своему усмотрению, и внутри вашего собственного кода вы не связаны каким-либо конкретным соглашением о вызове. У вас есть свободный доступ к блокам FPU и SSE; вы можете PREFETCH
напрямую передавать данные из памяти в кеш L1 и следить за тем, чтобы он был горячим, когда вам это нужно; вы можете побить стек по своему желанию; вы можете выпустить INT 3
, если вы хотите взаимодействовать с внешним отладчиком (правильно настроенным, удачным!). Ни одна из этих функций не зависит от вашей ОС. Единственное реальное ограничение, которое у вас есть, это то, что вы работаете в Ring 3, а не Ring 0, и поэтому некоторые регистры управления процессором будут недоступны для вас. (Но если вам это нужно, вы пишете код ОС, а не код приложения.) Кроме этого, машина заложена вам: идите и вычислите!