Зачем мне использовать NASM вместо GNU Assembler (GAS), если GAS поддерживает несколько архитектур, тогда как NASM поддерживает исключительно архитектуру x86? Я знаю, что есть синтаксические различия, но меня это не слишком беспокоит.
@BasileStarynkevitch Я изучаю ассемблер
Мне любопытно: зачем ты изучаешь ассемблер? См. также Руководство по сборке Linux и, возможно, отправьте мне письмо по адресу [email protected]
@BasileStarynkevitch Итак, вы хотите, чтобы я отправил вам по электронной почте причины, по которым я заинтересован в ассемблере / ly?
Да, потому что в 2020 году это 95% времени потери времени вашего разработчика. Более эффективно использовать операторы asm с GCC, например. чтобы смешать несколько операторов asm в вашем коде C
@BasileStarynkevitch Я могу рассказать вам причины прямо здесь: я хочу понять, как работает процессор, более подробно. Я хочу понять, как работает память. Я хотел бы прочитать исходный код ядра Linux и заинтересован в взломе памяти.
Но ядро Linux (или musl-libc , или GNU libc...) написано в основном на C
@BasileStarynkevitch, в нем все еще есть код на ассемблере. Я также заинтересован в написании собственной ОС и программировании встроенных систем.
@BasileStarynkevitch плюс, это интересно
Тогда смотрите также OSDEV .... Для встраиваемых систем рассмотрите Arduino
@BasileStarynkevitch: понимание языка ассемблера по-прежнему полезно для понимания производительности и архитектуры ЦП (проектные решения, которые принимают разработчики ЦП). На самом деле я не трачу время на написание производственного кода на ассемблере, только микробенчмарки, но я довольно хорошо знаю ассемблер и процессоры. (Ради развлечения и прибыли.) Я не вижу смысла в написании 16-битного загрузчика или программы для DOS, если они будут просто работать с эмулируемым устаревшим оборудованием, но я получил свой опыт работы с Atari ST еще в конце прошлого века. 90-х, прежде чем получить ПК для запуска Linux.
@BasileStarynkevitch блин... большое спасибо!!! я искал что-то подобное целую вечность.
@PeterCordes: Я тоже. Но в последний раз я писал код на ассемблере в прошлом веке.
@BasileStarynkevitch: обычно я пишу asm в голове, а затем набираю встроенные функции C или C++ SIMD, чтобы заставить компилятор выдавать что-то близкое к тому, о чем я думал. Иногда компиляторы усложняют задачу заставить их генерировать asm настолько эффективно, насколько мне хотелось бы, тогда я либо отправляю отчёт о пропущенной ошибке оптимизации, либо добавляю его в список TODO, к которому нужно вернуться...
@Serket: добавлено еще несколько пунктов в пользу NASM, особенно %rep / times важны для удобного экспериментирования / микробенчмаркинга.
Писать ассемблер очень весело! Я действительно могу порекомендовать это.
Я думаю, что лучше изолировать сборку от кода C и использовать встроенную сборку только там, где это необходимо, поскольку компиляторы иногда могут поиграть с вашей сборкой и испортить ее.





ASM по своей сути не является переносимым. Только директивы GAS одинаковы для других ISA (не инструкции и даже не символы комментария), и любой данный исходный файл asm будет для одного ISA. Сама по себе ГАС с поддержкой множественных целей не особо актуальна, надо еще другие машины учить. (Что несложно понять хотя бы основы, когда вы их знаете, но различия в синтаксисе директив, как правило, еще проще). Некоторые другие ассемблеры для других ISA используют псевдоинструкции данных в стиле NASM db и dd, другие используют .byte и .long в стиле Unix, например GAS (или являются GAS). Но к этому очень легко приспособиться.
GAS был впервые разработан для сборки выходных данных компилятора; поэтому его сообщения об ошибках не всегда удобны для человека. (С точки зрения ясности о том, что именно ему не нравится в строке). NASM имеет тенденцию быть лучше (но ни в коем случае не идеальным), и современный GAS в целом тоже неплох; некоторые крупные проекты (такие как glibc и Linux) имеют исходные файлы GAS, написанные от руки, поэтому они, безусловно, используются людьми.
В NASM есть язык макросов (https://www.nasm.us/doc/nasmdoc4.html ), который в некоторых отношениях проще использовать для сложных вещей, чем макросы GAS .macro. (Хотя макросы CPP часто используются с GAS).
Для микробенчмаркинга (одна из немногих причин играть с asm вручную в наши дни) в NASM есть функции, которые я не знаю, как легко сделать в GAS: times повторять одну строку. %rep повторять блок тоже полезно. GAS .rept может это сделать, но если вы хотите изменить блок (например, развернуть и сделать [rdi+0] / [rdi+4] / ...), NASM лучше. В GAS вы можете использовать рекурсивный макрос, который вызывает сам себя с меньшим аргументом, но NASM позволяет %assign реализовать счетчик внутри %rep.
times 10 imul eax,eax ; repeat this line 10 times
%rep 10 ; repeat this block 10 times
add eax, ecx
add ecx, eax
%endrep
NASM имеет очень удобный синтаксис для многосимвольных констант в виде чисел, например. mov rax, 'abcdefgh' / push rax приводит к этим 8 символам ASCII в стеке в исходном порядке. GAS не может сделать ничего подобного; вам нужно использовать необработанные числа или что-то вроде 'h'<<56 | 'g'<<48 | ...
NASM может собрать плоский двоичный файл, не усложняя компоновщик + скрипт компоновщика. В основном актуально только для 16-битных загрузчиков, но возможно и для шеллкода.
До недавнего времени в GAS были некоторые плохие вещи, такие как add [rdi], 1234 молчаливое значение по умолчанию для размера операнда двойного слова, вместо того, чтобы предупреждать вас, что это неоднозначно для инструкций, отличных от mov. Недавний GAS исправил это; теперь это ошибка, как и в NASM. (В режиме AT&T GAS просто предупреждает, но в режиме синтаксиса Intel это ошибка.)
Синтаксис NASM на 100% согласован с [] - наличие квадратных скобок всегда означает операнд памяти, а отсутствие означает, что никогда не будет операнда памяти. (Чтобы было ясно, что я имею в виду, LEA берет операнд памяти, но не загружается из него.) Синтаксис AT&T также легко отличить ($ или нет — это разница между непосредственным или памятью), но GAS .intel_syntax noprefix зависит от того, как был объявлен символ. add eax, foobar может быть add eax, imm32 или add eax, [disp32]. Хуже того, это зависит от того, было ли foobar объявлено с equ или = до или после инструкции, которая его использует, если только вы не используете OFFSET, даже если вы позже делаете foobar = 42!!! Отличие памяти от константы в GNU как .intel_syntax.
Синтаксис Intel является основным и единственным режимом NASM, поэтому вы можете легко найти инструкции в руководствах Intel или AMD. .intel_syntax noprefix — это второстепенная функция GAS, не так хорошо задокументированная, как AT&T. Он работает прилично, за исключением вышеуказанной двусмысленности.
И иногда инструменты GNU даже в режиме синтаксиса Intel (например, objdump -d -Mintel) по-прежнему следуют замене синтаксической ошибки AT&T некоторых форм инструкций x87, таких как fdiv против fdivr. Если вы когда-нибудь захотите использовать x87, по возможности избегайте GAS. См. 9.16.17 Ошибки синтаксиса AT&T в руководстве GAS. Я думаю, что современный objdump может «сделать это правильно» сейчас, но если бы я использовал только GAS + binutils, у меня не было бы чего-то, что, как я доверял, не было бы повреждено глупыми ошибками реализации AT&T, с которыми GAS должен был быть совместим с ошибками. Совершенно не имеет значения за пределами инструкций x87, поэтому, к счастью, сейчас не имеет значения большую часть времени.
Это охватывает все это с точки зрения обзора. Я рад, что вы затронули функцию макросов NASM. Это часто упускается из виду, но очень удобно для небольших задач. Всегда поучительные ответы.
Это всего лишь дело вкуса. Но реальный вопрос заключается в том, зачем использовать какой-либо ассемблер, если компиляторы (например, GCC...) могут встраивать оператор
asm? В 2020 году обычно генерируется код на ассемблере. Вы можете скомпилировать некоторыеfoo.cсgcc -fverbose-asm -O2 -S foo.cи посмотреть на сгенерированныйfoo.s