Archive

Archive for the ‘Assembly’ Category

Inline assembly the gcc way

September 15th, 2009 3 comments

This is the second time I write a post about inline assembly. The last one was at my old blog which I had to remove. However, since that was the most viewed article I think I should write about this again. Although this is just a small introduction, you must know C and assembly in order to continue. For the moment I will only describe x86 assembly, however the differences for other architectures are minimal. I must also point you to the GCC Inline Assembly HOWTO. I wrote the first post before reading it (well, the post was ready but before publishing it I googled a little bit and found it) and I think it is equally useful.

We are going to start with a little introduction to the AT&T syntax for the assembly for all of you who are familiar only with Intel syntax. Here are some basic rules:

  • Registers: All register names begin with the character %. So, if we want to use the eax register, then we will use the name %eax.
  • Immediate operands: The immediate operands begin with the character $. For example, if we want to use the value 0xdeadbeef as an immediate operand, then we will use $0xdeadbeef.
  • Indirect memory references: Indirect memory references are done using ( ). The byte pointed by register %esi is (%esi). If you want to use an offset, you put it in front of the (register). Warning! Since it is not an immediate operand, you don’t have to use the $. For example, you should use 0x4(%esi) and not $0x4(%esi).
  • Ordering of the operands: The ordering is:
    instruction source, destination
    This is the reverse of the intel syntax where the destination comes first.
  • Size of operand: All instructions taking operands that can have variable sizes must be suffixed with the appropriate letter:
    • b – Byte (1 byte)
    • w – Word (2 bytes)
    • l – Long (4 bytes)
    • q – Quad word (8 bytes) – Only when you use 64 bit programs.
  • Immediate long jumps and calls use the form
    ljmp/lcall $section, $offset
    instead of
    jmp/call far section:offset

Here are some examples in intel and at&t syntax.

Intel syntax:

This gets translated to:

If you want to see more examples, you can write your own code and disassemble it using the objdump command. By using the -d option you can disassemble any file you want. The default syntax is at&t but if you want to switch to intel you can add -M intel to the command line.

We can now continue to putting inline assembly to our programs. In order to insert assembly code we must use the “asm” construct.

This will execute a simple nop instruction. In case asm conflicts with something else in your program you can also use __asm__. Apart from this there is also a more advanced format.

The assembly template is your actual assembly code with some special operands that will be replaced by registers or memory locations. This code will be send directly to the assembler, so if you want to run more than one command you must use one of the following constructs:

OR

OR

The last one uses ; which is the line separator at most architectures. Since we are going to use only x86 we are free to use it, however, at other architectures, like H8/300, ; is the comment character. You can check the gas info page for more information about the line separator character.

The output and input operands are two lists the have the form

"constraint"(variable), "constraint"(variable), ...

At the output operands a constraint denotes what will be placed at variable after the execution of the assembly code and at the input operands a constraint denotes where a variable will be placed. The various constraints for the x86 architecture are:

  • r: any register, one will be chosen by the compiler
  • a: eax register
  • b: ebx register
  • c: ecx register
  • d: edx register
  • S: esi register
  • D: edi register
  • A: eax and edx combined as a 64-bit integer
  • f: floating point register
  • t: first floating point register
  • u: second floating point register
  • m: memory operand
  • 0-9: matching operand

The memory operand will not use a register, it will only pass the location of variable in memory. Furthermore, the matching operand is used when a register is used both as input and as output, e.g. by specifying 0 as an input operand, the first output register will be used for input. You must prefix the output constraints with the = character. If you use the r constraint more than once, the same register may be assigned. If you want to choose a different register, you should use the & character, i.e. the constraint &r.

Finally, the list of clobbered registers is a list of all registers modified by your program. These registers must have the format %eax, %ebx etc.

If your code must execute exact;y at the place you put it, you must put the volatile keyword after asm or __volatile__ after __asm__. Otherwise, the compiler may change its position if optimization takes place.

I will present some simple examples.

At the first example I will read the timestamp counter. This is done using the rdtsc command which returns the result at eax and edx.

The generated code will be:

-16(%ebp) and -12(%ebp) are the two words that hold the unsigned long long tsc that will get the timestamp counter.

The second example will read and print some cpuid information.

I will only present the second part of the program which prints the processor brand.

Location -32(%ebp) stores the op variable. At the beginning it is assigned the value 0x80000002. I will not comment the code generated for the loop, it is pretty simple. After the .L3 label the inline assembly begins. At the beginning, the op value is moved to the register %eax since it is in the input operands list with the constraint a. The cpuid instruction is then executed since it is in the assembly template. Finally, all registers at the output operands list are saved to the appropriate location, i.e. -28(%ebp) for %eax, -24(%ebp) for %ebx, -20(%ebp) for %ecx and -16(%ebp) for %edx.

You can find more information by writing your own programs and disassembling them using the objdump utility.

If you like this post I will continue with something more advanced.

Categories: Assembly, Programming Tags:
SEO Powered by Platinum SEO from Techblissonline

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close