Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would I use NASM over GNU Assembler (GAS)? [closed]

Why would I use NASM over GNU Assembler (GAS) when GAS supports multiple architectures whereas NASM supports the x86 architecture exclusively? I know that there are syntactic differences, but that doesn't bother me too much.

like image 806
Serket Avatar asked Dec 23 '22 16:12

Serket


1 Answers

ASM is inherently non-portable. Only the GAS directives are the same for other ISAs (not instructions or even comment character), and any given asm source file will be for a single ISA. GAS itself supporting multiple targets is not really relevant, you still have to learn other machines. (Which is not hard to pick up at least the basics once you know one, but directive syntax differences are generally even easier). Some other assemblers for other ISAs use NASM-style db and dd data pseudo-instructions, others use Unix-style .byte and .long like GAS (or are GAS). But that's very easy to adapt to.

GAS was first designed to assemble compiler output; as such its error messages aren't always wonderful for human usage. (In terms of clarity about what exactly it doesn't like about a line). NASM tends to be better (but by no means perfect), and modern GAS is generally not bad either; some major projects (like glibc and Linux) have hand-written GAS source files so there certainly is human use of it.

NASM has a macro language (https://www.nasm.us/doc/nasmdoc4.html) that in some ways is easier to use for complex stuff than GAS's .macro macros. (Although it's common to use CPP macros with GAS).

For microbenchmarking (one of the few reasons to play around with asm by hand these days), NASM has a features that I don't know how to do as easily in GAS: times to repeat one line. %rep to repeat a block is also useful. GAS's .rept can do that, but if you want to vary the block (e.g. to unroll and do [rdi+0] / [rdi+4] / ...), NASM is nicer. In GAS you can use a recursive macro that calls itself with a smaller arg, but NASM allows %assign to implement a counter inside a %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 has very nice syntax for multi-character constants as numbers, e.g. mov rax, 'abcdefgh' / push rax results in those 8 ASCII characters on the stack in source order. GAS can't do anything equivalent; you need to use raw numbers or stuff like 'h'<<56 | 'g'<<48 | ...

NASM can assemble a flat binary without needing the complication of a linker + linker script. Mostly only relevant for 16-bit bootloaders, but possibly shellcode.

Until recently, GAS had some bad stuff like add [rdi], 1234 silently defaulting to dword operand size, instead of warning you that it's ambiguous for instructions other than mov. Recent GAS has fixed that; now it's an error just like in NASM. (In AT&T mode, GAS just warns, but in Intel-syntax mode it's an error.)


NASM's syntax is 100% consistent about [] - brackets present always means a memory operand, not present means never a memory operand. (Just to be clear about what I mean, LEA takes a memory operand but doesn't load from it.) AT&T syntax is also easy to distinguish ($ or not is the difference between immediate or memory), but GAS .intel_syntax noprefix depends on how a symbol was declared. add eax, foobar could be add eax, imm32 or add eax, [disp32]. Even worse, it depends whether foobar was declared with an equ or = before or after the instruction that uses it, unless you use OFFSET even if you're later doing foobar = 42!!! Distinguishing memory from constant in GNU as .intel_syntax.

Intel syntax is NASM's primary and only mode, so you can easily look up instructions in Intel's or AMD's manuals. .intel_syntax noprefix is a secondary feature for GAS, not as well documented as its AT&T. It does work decently except for the above ambiguity.

And sometimes GNU tools even in Intel-syntax mode (like objdump -d -Mintel) still follow the AT&T syntax bug swapping of some forms of x87 instructions like fdiv vs. fdivr. If you ever want to use x87, avoid GAS if you can. See 9.16.17 AT&T Syntax bugs in the GAS manual. I think modern objdump might "get this right" now, but if I was only using GAS + binutils I wouldn't have something I trusted not to be corrupted by stupid AT&T implementation bugs that GAS had to be bug-compatible with. Totally irrelevant outside of x87 instructions, so fortunately irrelevant most of the time now.

like image 157
Peter Cordes Avatar answered Dec 28 '22 08:12

Peter Cordes