I tried to search about intel x64 assembly tutorials with examples or a good book but I didn't find even in the intel site.
so, Could you suggest me a good tutorials or book for that ?? I'm using nasm on linux.
Thanks
ARM is much, much easier to learn, and is much, much more likely to be useful in the future. ARM processors currently outsell x86 processors by an enormous margin, and are used in many more ways. ARM assembly has all the features of a good assembly language. x86 has all the features of a horrible assembly language.
What is the difference between x86 and x64? As you guys can already tell, the obvious difference will be the amount of bit of each operating system. x86 refers to a 32-bit CPU and operating system while x64 refers to a 64-bit CPU and operating system.
x86-64 (also known as x64, x86_64, AMD64, and Intel 64) is a 64-bit version of the x86 instruction set, first released in 1999. It introduced two new modes of operation, 64-bit mode and compatibility mode, along with a new 4-level paging mode.
x86 instructions can be anywhere between 1 and 15 bytes long. The length is defined separately for each instruction, depending on the available modes of operation of the instruction, the number of required operands and more.
Admittedly it's personal bias how you prefer to learn about programming.
But with respect to assembly languages in particular, I found an approach which to me has been more useful than reading instruction set reference manuals and/or books on assembly language (where they exist).
What I normally do to figure out how assembly works for a new CPU / a CPU unknown to me on an OS platform I've not worked with yet is to leverage the developer toolchain. Like that:
install yourself a (cross-)compiler and disassembler for the target CPU. These days, GNU gcc's / binutils ubiquity often mean that's gcc
and objdump -d
.
create a bunch of small programs / small pieces of sourcecode, like:
extern int funcA(int arg);
extern int funcB(int arg1, int arg2);
extern int funcC(int arg1, int arg2, int arg3);
extern int funcD(int arg1, int arg2, int arg3, int arg4);
extern int funcE(int arg1, int arg2, int arg3, int arg4);
extern int funcF(int arg1, int arg2, int arg3, int arg4, int arg5);
extern int funcG(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6);
extern int funcH(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6,
int arg7);
int main(int argc, char **argv)
{
printf("sum of all funcs: %d\n",
funcA(1) + funcB(2, 3) + funcC(4, 5, 6) + funcD(7, 8, 9, 10) +
funcE(11, 12, 13, 14, 15) + funcF(16, 17, 18, 19, 20, 21) +
funcG(22, 23, 24, 25, 26, 27, 28) + funcH(29, 30, 31, 32, 33, 34, 35));
return 12345;
}
compile these with compiler optimization and disassemble the generated object code.
The structure of the code is simple enough to demonstrate how the ABI behaves wrt. to function calling, passing arguments and returning values, managing the register space wrt. to which registers are preserved / volatile when making function calls. It'll also show you some basic assembly code for initializing constant data, and "glue" like stack access and management.
extend this for simple C language constructs like loops and if
/else
or switch
statements. Always keep some calls to external undefined functions in because doing so will prevent the compiler optimizer from throwing all your "test code" out, and when you use if()
tests of switch()
, predicate on argc
(or other function arguments) because the compiler cannot predict that either (and hence optimize the building blocks of the code "weirdly").
extend this for using struct {}
and class {}
definitions containing sequences of different primitive data types, in order to find out how the compiler arranges these in memory, which assembly instructions are used to access bytes/words/ints/longs/floats etc.
All these pieces of test code you can deliberately vary (like, use different operations than +
), and/or make more complex in order to learn more about specific pieces of the instruction set and ABI.
After you've done that, and looked at the output, locate a copy (electronic or not) of the Platform ABI. That contains the rulebook for how the above is done / why it is done that way, and it'll help you get a feeling for why these rules apply to the specific platform. It's essential to get an idea about the above because when you write your own assembly code, you'll have to interface that with other non-assembly (unless for pure demos). That's where you need to play by the rules, so even if you don't know them by heart, at least know where the rulebook is.
Only after that would I suggest you actually track down the instruction set reference for the specific platform.
That's because when you've gone through the above first, then you already got enough experience / you've already seen enough to start with a small C program, compile it down to assembly source, modify that a little, assemble and link it and see if your modification does what it's supposed to do.
Attempting to, at that stage, to use some more uncommon / specialized instructions will be much easier because you've already seen how function calling works, what sort of glue code is necessary to interface your assembly with other parts of the program, you've already used the toolchain, so you don't need to start completely from scratch anymore.
I.e., to sum this all up, my suggestion is to learn assembly from the top down instead of from the bottom up.
Side note:
Why am I suggesting to use compiler optimization when analyzing compiler-generated assembly code for such simple examples ?
Well, the answer to that is because, counterintuitively to some, the generated assembly code is much simpler if you let the compiler optimize the hell out of things. Without optimization, compilers often create "stupid" code that e.g. puts all variables into the stack, saves and restores them from there for no reason you can see, does register saves/restores/initializations just to overwrite that reg the very next instruction and many more such things. The amount of code emitted is much much larger because of this. It's peppered with cruft and much harder to understand. The compiler optimization forces trimming this cruft down to the essential, which is what you want to see in order to understand the platform ABI, and the assemnbly. Therefore, use compiler optimization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With