I'm trying to learn how to use inline assembly in C code.
I have created a small program that should add two integers:
int main(){
int a=1;
int b=2;
asm( "movl %0, %%r8d;"
"movl %1, %%r9d;"
"addl %%r8d, %%r9d;"
"movl %%r9d, %1;"
: "=r" (a)
: "r" (b)
:"%r8","%r9" );
printf("a=%d\n",a);
return 0;
}
The aim was to load a and b into the registers %r8 and %r9, add them, and then put the output back in a.
However this program prints a=2 instead a=3. I'm not sure if the problem is in the inline technique or in the assembly itself.
There are two issues here:
First: The "=r" constraint you use for the output operand a indicates to the compiler that the operand is write-only — it is allowed to assume that the initial value is not needed. This is definitely not the case for your code! Change the qualifier to "+r" to let the compiler know that the initial value is important.
Second: You are moving the result to the wrong register! The target %1 of the last movl is the register corresponding to b, not a. You want %0.
Fixed:
asm(
"movl %0, %%r8d;"
"movl %1, %%r9d;"
"addl %%r8d, %%r9d;"
"movl %%r9d, %0;"
: "+r" (a)
: "r" (b)
: "%r8", "%r9"
);
Related
For entertainment, I am learning gnu extended assembly using AT&T syntax for x86 with a 32bit Linux target. I have just spent the last three hours coding two possible solutions to my challenge of swapping the values of two integer variables a and b, and neither of my solutions completely solved my problem. First, let's look at my TODO obstacle in some more detail:
int main()
{
int a = 2, b = 1;
printf("a is %d, b is %d\n", a, b);
// TODO: swap a and b using extended assembly, and do not modify the program in any other way
printf("a is %d, b is %d\n", a, b);
}
After reading this HOWTO, I wrote the following inline extended assembler code. Here is my first attempt at swapping the integers:
asm volatile("movl %0, %%eax;"
"movl %1, %%ecx;"
"movl %%ecx, %0;"
: "=r" (a)
: "r" (b)
: "%eax", "%ecx");
asm volatile("movl %%eax, %0;"
: "=r" (b)
: "r" (a)
: "%eax", "%ecx");
My reasoning was that to set a = b, I needed an extended assembly call that was separated from the assembly to set b = a. So I wrote the two extended assembly calls, compiled my code, i.e., gcc -m32 asmPractice.c, and ran a.out. The results were as follows:
a is 2, b is 1
a is 1, b is 1
Seeing how that did not work properly, I then decided to combine the two extended assembler calls, and wrote this:
asm volatile("movl %0, %%eax;"
"movl %1, %%ecx;"
"movl %%ecx, %0;"
"movl %%eax, %1;"
: "=r" (a)
: "r" (b));
After recompiling and linking, my code still does not correctly swap both values. See for yourself. Here are my results:
a is 2, b is 1
a is 1, b is 1
Here are some solutions from the comments:
Solution #0 (best option): https://gcc.gnu.org/wiki/DontUseInlineAsm
Even the zero-instruction solution defeats constant-propagation, and any other optimization that involves gcc knowing anything about the value. It also forces the compiler to have both variables in registers at the same time at that point. Always keep these downsides in mind when considering using inline-asm instead of builtins / intrinsics.
Solution #1: x86 xchg, no scratch regs, and works in both AT&T and Intel-syntax modes. Costs about the same as 3 mov instructions on most Intel CPUs, or only 2 uops on some AMD.
asm("xchg %0, %1;" : "+r" (a), "+r" (b));
Solution #2: purely using GNU C inline asm constraints. (Bonus: portable to all architectures)
asm("" : "=r" (a), "=r" (b) : "1" (a), "0" (b));
See all three solutions in action on the Godbolt compiler explorer, including examples of them defeating optimization:
int swap_constraints(int a, int b) {
asm("" : "=r" (a), "=r" (b) : "1" (a), "0" (b));
return a;
}
// Demonstrate the optimization-defeating behaviour:
int swap_constraints_constants(void) {
int a = 10, b = 20;
return swap_constraints(a, b) + 15;
}
swap_constraints_constants:
movl $10, %edx
movl $20, %eax
addl $15, %eax
ret
vs. with a pure C swap:
swap_noasm_constants:
movl $35, %eax # the add is done at compile-time, and `a` is optimized away as unused.
ret
So, I have this code that works as expected:
#include <stdio.h>
int main () {
unsigned int i;
int x=5;
__asm__ ("movl %1, %0;"
"subl %2, %0;"
: "=r" (i)
: "r" (4), "r" (x)
: "0");
__asm__ ("movl %1, %0;"
: "=r" (x)
: "r" (i)
: "0");
printf("%d\n", x);
return 0;
}
How do I merge the two calls into one?
The first __asm__ sets: i <- 4 - x. The second sets: x <- i. They can be combined as:
__asm__ ("subl %k2, %k0" : "=r" (x) : "0" ((unsigned)(4)), "g" (x));
No clobbers are needed (unless you want to include "cc", which is always assumed by gcc anyway), and there's no issue of inputs being overwritten before they are read. The "g" constraint indicates that a register, memory, or immediate operand is acceptable. The output should be: "-1" in this case: x <- 4 - 5
Note: the first __asm__ should have =&r for its output. Furthermore, neither should list %0 as clobbered - this is implicit, as %0 is an output in both cases.
Those are nonsensical asm blocks to start with. Rule of thumb is, if you ever use a mov you are probably doing it wrong. Also, even though you claimed it works as expected, the code is broken. Nothing stops the compiler from allocating %2 to the same register as %0 producing wrong code. You were just lucky it didn't happen for you.
Anyway, the second asm block simply copies i into x. As such a merged asm could look like:
__asm__ ("movl %2, %0;"
"subl %3, %0;"
"movl %0, %1;"
: "=&r" (i), "=r" (x)
: "r" (4), "r" (x));
This is of course still silly, but at least it isn't technically wrong. Notice the early-clobber & modifier for the first output. It isn't needed for the second, however.
//quick inline asm statements performing the swap_byte for key_scheduling
inline void swap_byte(unsigned char *x, unsigned char *y)
{
unsigned char t;
asm("movl %1, %%eax;"
"movl %%eax, %0;"
:"=r"(t)
:"r"(*x)
:"%eax");
asm("movl %1, %%eax;"
"movl %%eax, %0;"
:"=r"(*x)
:"r"(*y)
:"%eax");
asm("movl %1, %%eax;"
"movl %%eax, %0;"
:"=r"(*y)
:"r"(t)
:"%eax");
}
Here I am trying to swap the char from x and store in y, and the same for y to x.
I have compiled these instructions by changing movl to mov but with no success. Where is the problem in compiling/linking?
Here is the output from compiling in cygwin:
$ gcc rc4_main.c -o rc4ex
/tmp/ccy0wo6H.s: Assembler messages:
/tmp/ccy0wo6H.s:18: Error: operand type mismatch for `mov'
/tmp/ccy0wo6H.s:18: Error: operand type mismatch for `mov'
/tmp/ccy0wo6H.s:26: Error: operand type mismatch for `mov'
/tmp/ccy0wo6H.s:26: Error: operand type mismatch for `mov'
/tmp/ccy0wo6H.s:34: Error: operand type mismatch for `mov'
/tmp/ccy0wo6H.s:34: Error: operand type mismatch for `mov'
To simplify it even more (than user35443):
asm("" : "=r" (*x), "=r" (*y) : "1" (*x), "0" (*y));
Look ma! No code! And yes, this really works.
To explain how this works:
When the compiler is building the code, it keeps track of what value is in each register. So if had these for inputs to asm:
"r" (*x), "r" (*y)
The compiler will pick a register and put *x in it, then pick a register and put *y in it, then call your asm. But it also keeps track of what variable is in which register. If there were just some way to tell the compiler that all it had to do was start treating the two registers as the opposite variables, then we'd be set. And that's what this code does:
Saying "=r" (*x) means that we are going to be overwriting the value in *x, that that we will be putting the value into a register.
Saying "0" (*y) means that on input to the asm, the compiler must put the value of *y into the same register as is being used by output parameter #0.
So, without using any actually assembly instructions, we have told the compiler to swap these two values.
We don't get this quite "for free" since the compiler must load the values into registers before calling the asm. But since that has to happen anyway...
What about actually updating memory? The compiler will (if necessary) write these values from the registers back to memory. And since it knows what variable is in which register, all works as expected.
unsigned char t;
asm("movl %1, %%eax;"
"movl %%eax, %0;"
:"=r"(t) /* <--here */
:"r"(*x) /* <-- and here */
:"%eax");
You can not move a value from a 32-bit register to a single-byte memory location. t is on the stack and x is somewhere else, but both are accessed in the same way. Problems on the other lines are similar. You should move only a byte.
Try something like this, but there are more ways to do it (I haven't tried that, read below):
unsigned char t;
asm("movb %1, %%al\n"
"movb %%al, %0\n"
:"=r"(t)
:"r"(*x)
:"%al");
asm("movb %1, %%al\n"
"movb %%al, %0\n"
:"=r"(*x)
:"r"(*y)
:"%al");
asm("movb %1, %%al\n"
"movb %%al, %0\n"
:"=r"(*y)
:"r"(t)
:"%al");
The whole procedure can be simplified into this:
asm("movb (%1), %%al\n"
"movb (%2), %%ah\n"
"movb %%ah, (%1)\n"
"movb %%al, (%2)\n"
: /* no outputs for compiler to know about */
: "r" (x), "r" (y)
: "%ax", "memory");
line
movl %%eax, %0;
is full nonsence! So you try to change 0 constant by %eax register It's impossible. In fortran many years ago It was. After that all programs will behave quite unpredictable. Since to avoid that was introduce the rule that any identificatot can't begin with number. But You try do it. It's well to get error. You maybe mean another
movl %0, %%eax;
to set zerro to eax. So better do another code
xorl %%eax, %%eax;
is much better!
can somebody please explain what exactly this function is doing, I tried to google it, but found nothing:
long __res; //some variable
__asm__ volatile (
"movl $244, %%eax;"
"movl %1, %%ebx;"
"movl %2, %%ecx;"
"movl %3, %%edx;"
"int $0x80;"
"movl %%eax,%0"
: "=m" (__res) //from here can't understand
: "m" (a), "m" (b) , "m" (c)
: "%eax","%ebx","%ecx", "%edx",
);
thanks in advance for any explanation
Taking this step by step:
long __res; //some variable
__asm__ volatile (
"movl $244, %%eax;"
... system calls for 32-bit x86 Linux are selected by the value in %eax. This is the number of the get_thread_area syscall. See arch/x86/include/asm/unistd_32.h (in recent kernels at least) for the list of syscall numbers. (Note: syscall numbers are not the same between 32-bit and 64-bit.)
"movl %1, %%ebx;"
"movl %2, %%ecx;"
"movl %3, %%edx;"
... parameters to system calls are passed in registers. For 32-bit x86, up to five parameters are passed in registers, in the order %ebx, %ecx, %edx, %esi, %edi (the 6th argument, for the few syscalls that need it, is passed on the user stack). %1, %2, %3 refer to the 2nd, 3rd and 4th items mentioned in the "constraints" for the inline assembler (see below).
(This seems slightly odd, by the way: the get_thread_area syscall only needs one argument...)
"int $0x80;"
... invokes the syscall.
"movl %%eax,%0"
... syscalls return a result in %eax; the %0 refers to the first item mentioned in the constraints.
: "=m" (__res) //from here can't understand
... "constraints" tell gcc where it can put the input and output values that are used and produced by the inline assembler block. This first section (after the first :) is for outputs. "=m" here says that __res should be held in memory ("m") and that it is write-only, i.e. any previous value will be overwritten ("="). Operands in constraints can be referred to by numbers in the inline assembly block (e.g. %0), starting from 0 for the first to appear.
: "m" (a), "m" (b) , "m" (c)
... this next section is for inputs. This says that they will all be placed in memory.
: "%eax","%ebx","%ecx", "%edx",
... this final section indicates "clobbered" registers, i.e. those that will be overwritten as a result of code inside the inline assembler block. GCC will put the inline assembler block into the middle of other code that it generates, but it doesn't know what the instructions inside the block actually do - so you have to tell it that any values that may have been in those registers before the block will no longer be valid afterwards.
);
That function is performing a syscall (due to the x86 int 0x80).
The part you flagged is the GCC inline assembler helpers - which allows GCC to change the placeholders (%0-%3) to actual values as given by a C name, in this case, __res, a, b, c.
You can read about the exact inline assembler syntax here:
http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
You can find all the info you need about the gcc inline assembly syntax here:
http://www.ibm.com/developerworks/linux/library/l-ia.html
I am trying to write some inline assembly into C. I have two arrays as input, what I need is to copy one element in array1 into array2, and the following is what I have at the moment:
asm (
"movl %0,%%eax;"
"movl %1,%%ebx;"
"movl (%%eax),%%ecx;"
"movl %%ecx,(%ebx);"
"xor %%ecx,%%ecx;"
"movl 4(%%eax),%%ecx;"
//do something on %ecx
"movl %%ecx,4(%ebx);" //write second
:
:"a"(array1),"b"(array2)
);
Why do I get a segmentation fault?
Your inline assembler code is broken. You can't directly use EAX and EBX without adding them to the clobber list. Otherwise the compiler does not know which registers have been modified.
It is very likely that one of the registers that you've modified contained something damn important that later caused the segmentation fault.
This code will copy one element from array1 to array2:
asm (
"movl (%0), %%eax \n\t" /* read first dword from array1 into eax */
"movl %%eax, (%1) \n\t" /* write dword into array2
: /* outputs */
: /* inputs */ "r"(array1),"r"(array2)
: /* clobber */ "eax", "memory"
);
A better version with proper register constraints would drop the hard coded EAX like this:
int dummy;
asm (
"movl (%1), %0 \n\t"
"movl %0, (%2) \n\t"
: /* outputs, temps.. */ "=r" (dummy)
: /* inputs */ "r"(array1),"r"(array2)
: /* clobber */ "memory"
);
Btw - In general I have the feeling that you're not that familiar with assembler yet. Writing inline-assembler is a bit harder to get right due to all the compiler magic. I suggest that you start writing some simple functions in assembler and put them into a separate .S file first.. That's much easier..
Your best option is C code:
target_array[target_idx] = source_array[source_idx];
This avoids segmentation faults as long as the indexes are under control.
what about memcpy ?