Adding values in C using inline assembly - c

Im trying to get a grasp on the basics of inline assembly in C (ATT assembly), so Im practicing by adding 2 variables.
Ok so this works as intended; the src variable gets copied to the dst variable and then the dst variable gets added by 5. The values of src and dst are 1 and 6 respectively.
int src = 1;
int dst = 0;
asm ("mov %[SRC], %[DEST]\n\t"
"add $5, %0"
: [DEST] "=r" (dst));
: [SRC] "r" (src));
But when I try this out, the values of src and dst are still 1 and 6. I was expecting src to have the value 1 and dst to have the value 5 since adding 5 to dst (which has the value of 0 since the MOV operation has been removed) should have the output 5.
int src = 1;
int dst = 0;
asm ("add $5, %[DEST]"
: [DEST] "=r" (dst)
: [SRC] "r" (src));
So I then try removing the src as an input operand using the following code, but now dst gets the value 11.
int dst = 0;
asm (
"add $5, %[DEST]"
: [DEST] "=r" (dst));
Now I'm a bit confused how it works. What am I misunderstanding?

The first part of your code works as expected. There
mov %[SRC], %[DEST] ; copies %[SRC] into %[DEST], which is now 1
add $5, %0 ; adds 5 to %0 (which is %[DEST]), so that's 6
The second part does not work because you never use %[SRC], and because %[DEST] is not an input operand, so its value doesn't come into the calculation. You just get what happens to be in the register gcc decides to use. The third part fails for the same reason.
For it to work, you need to specify dst as both an input and output operand, since you're both using its value and changing it. However, this does not work:
asm("add $5, %0" // This does not work!
: "=r" (dst)
: "r" (dst));
because now you have an input operand %1 with value dst and a distinct output operand %0 whose value will be written to dst, and you never use %1. This notation would allow you to write
asm("mov %1, %0; add $5, %0" // needlessly inefficient!
: "=r" (dst)
: "r" (dst));
but that is, of course, needlessly inefficient. In order to do this with a single register, you need to use a matching constraint like this:
asm("add $5, %0"
: "=r" (dst)
: "0" (dst));
This tells gcc that %0 as an input operand is allowed, and that it has the value of dst. Here is the relevant part of the gcc manual.
With named operands, finally, it looks like this:
asm ("add $5, %[DEST]"
: [DEST] "=r" (dst)
: "[DEST]" (dst));

Related

Input parameter index when using "+" modifier in extended asm output parameter?

Extended asm gives the following description regarding the "+" modifier:
Operands using the ‘+’ constraint modifier count as two operands (that
is, both as input and output) towards the total maximum of 30 operands
per asm statement.
So I assume that it is not necessary to mention output operand with the "+" modifier in the input section again, but it is not specified how to determine their index. I wrote the following example Godbolt :
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
void asm_add(uint64_t o1, uint64_t o2, uint64_t o3){
__asm__ volatile (
"addq %2, %3\n\
addq %2, %4":
"+r" (o2), "+r" (o3):
"r" (o1):
"cc"
);
printf("o2 = %" PRIu64 "\n", o2);
printf("o3 = %" PRIu64 "\n", o3);
}
int main(void){
asm_add(20, 30, 40);
}
Which printed
o2 = 50
o3 = 60
Is the template using +
__asm__ volatile (
"addq %2, %3\n\
addq %2, %4":
"+r" (o2), "+r" (o3):
"r" (o1):
"cc"
);
exactly the same as
__asm__ volatile (
"addq %2, %3\n\
addq %2, %4":
"+r" (o2), "+r" (o3):
"r" (o1), "0" (o2), "1" (o3):
"cc"
);
where all inputs are specified explicitly? So in the first example the "implicit" inputs are appended.
By using "+r" (o2), you are saying that this parameter needs to contain o2 on entry to the asm block, and will contain an updated value on exit.
In other words, %0 describes both input and output. The fact that you can (apparently?) reference indices greater than the number of parameters is an undocumented quirk. Don't depend upon it.
You might also consider using symbolic names, which (I find) are easier to read, especially as the number of asm lines goes up. Names are particularly useful when you are first creating the asm and there's the potential for adding/removing parameters. Having to renumber everything is painful and error prone:
__asm__ volatile (
"addq %[o1], %[o2]\n\
addq %[o1], %[o3]":
[o2] "+r" (o2), [o3] "+r" (o3):
[o1] "r" (o1):
"cc"
);
Lastly, consider not using inline asm for anything beyond educational purposes. And even then, inline asm is the hardest possible way to learn asm.

how to access and change char array in c x86 AT&T inline assembly

I'm having trouble solving a school exercise , I'm supposed to change a char array in c using inline assembly. In this case change "ahoy" to "aXoy", but I'm getting segmentation fault. This is my code:
#include <stdio.h>
int main() {
char string[] = "ahoy";
__asm__ volatile (
"mov %0, %%eax;"
"movb $'X', 1(%%eax);"
: "=m"(string) : "0"(string) : "memory", "eax");
printf("%s\n", string);
return 0
}
with this: "mov %0, %%eax;" I'm trying to store address of the array in register
then with this: "movb $'X', 1(%%eax);" I want to store byte 'X' in location pointed to by (%%eax) offset by 1 byte(char),
I have string both as output and input, and "memory","eax" in clobber since I'm modifying both. What is wrong with my code?
Use gcc -S instead of -c to look at the compiler's assembly output and you should see what's wrong. The "m" constraint produces a memory reference expression for accessing the object associated with it, not a register containing its address. So it will expand to something like (%ecx), not %ecx, and the first mov will load 4 bytes from string, rather than the address of string, into eax.
One way to fix this would be to use a register constraint "r"(string) instead.
Alternatively you could replace the mov with lea: lea %0, %%eax.
There are other issues with your code too like the useless temporary/clobber register eax but they shouldn't keep it from working.

Inline asm in GCC, specifying offset by expression

Not exactly sure how to title this, but
I want to emulate this code:
asm("movl %%fs:0x30, %0" : "=r" (peb) : : );
but I want to specify the offset variably in C
trying:
int mee = 48;
asm("movl %%fs:%1, %0"
: "=r" (peb)
: "r" (mee)
:
);
Error is bad memory operand '%eax'
For what you have written, the compiler translates the first operand to %fs:%eax because it chooses %eax to be the register holding the value of mee. The addressing mode I think you're trying to use is base:offset and the offset must be a value rather than a register. This is the reason for the error "bad memory operand". It worked in the %fs:0x30 case because 0x30 is an immediate value.
To use the register %eax as an offset, try a dereference of it %%fs:(%1) to get the value in the register:
int mee = 48;
asm("movl %%fs:(%1), %0" : "=r" (peb) : "r" (mee) :);
See also this guide, which contains some possibly useful examples of memory access (and more) in inline assembly.

Switching between Intel and ATT mode in GCC

So I have this inline assembly code along with my C code, and I want to use intel syntax for this particular call to asm(), however I need to switch back to ATT syntax or else it will give a long list of errors.
asm(".intel_syntax prefix");
asm volatile (
"add %0, $1 \n\t"
: "=r" (dst)
: "r" (src));
asm(".att_syntax prefix");
Now it gives the following error
/tmp/ccDNa2Wk.s: Assembler messages:
/tmp/ccDNa2Wk.s:180: Error: no such instruction: `movl -16(%ebp),%eax'
/tmp/ccDNa2Wk.s:187: Error: no such instruction: `movl %eax,-12(%ebp)'
I dont understand how to fix the error, i have no call to movl in any part of my code.
Since you haven't yet accepted an answer (<hint><hint>), let me add a third thought:
1) Instead of having 3 asm statements, do it in 1:
asm(".intel_syntax prefix\n\t"
"add %0, 1 \n\t"
".att_syntax prefix"
: "=r" (dst)
: "r" (src));
2) Change your compile options to include -masm=intel and omit the 2 syntax statements.
3) It is possible to support both intel and att at the same time. This way your code works whatever value is passed for -masm:
asm("{addl $1, %0 | add %0, 1}"
: "=r" (dst)
: "r" (src));
I should also mention that your asm may not work as expected. Since you are updating the contents of dst (instead of overwriting it), you probably want to use "+r" instead of "=r". And you do realize that this code doesn't actually use src, right?
Oh, and your original asm is NOT intel format (the $1 is the give-away).
I would try to do the following tests:
In some C code not containing inline assembler insert the line
asm(".att_syntax prefix");
in multiple different locations. Then compile the C code to object files and disassemble these object files (compiling to assembler won't work for this test).
Then compare the disassembly of the original code with the disassembly of the code containing the ".att_syntax" lines.
If the line ".att_syntax prefix" indeed is the correct line for switching back to AT&T mode the disassemblies must be equal AND compiling must work without any errors.
In the next step take your code and compile to assembler instead of object code ("-S" option of GCC). Then you can look at the assembler code.
My idea is the following one:
If you use data exchange in inline assembler ("=r" and "r" for example) GCC needs to insert code that is doing the data exchange:
asm(".intel_syntax prefix");
// GCC inserts code moving "src" to "%0" here
asm volatile (
"add %0, $1 \n\t"
: "=r" (dst)
: "r" (src));
// GCC inserts code moving "%0" to "dst" here
asm(".att_syntax prefix");
This code inserted by GCC is of course in AT&T syntax.
If you want to use Intel syntax in inline assembly you have to use the ".att_syntax" and ".intel_syntax" instructions in the same inline assembly block, just like this:
// GCC inserts code moving "src" to "%0" here
asm volatile (
".intel_syntax prefix \n\t"
"add %0, $1 \n\t"
".att_syntax prefix \n\t"
: "=r" (dst)
: "r" (src));
// GCC inserts code moving "%0" to "dst" here

Inserting the address of a constant in inline assembly code

I want to translate this function:
iowrite32(mem1, value1);
into assembly code.
mem1 is defined as:
int * mem1;
in order to use ioremap.
I've written this code:
asm volatile(
"mov %[whr],%[wht]"
: [whr] "=r" (mem1)
: [wht] "r" (value)
);
Then I've realized I don't want to move value to mem1, but to the ADDRESS stored in mem1.
How do I write it in assembly?
You might want to take a look at the m constraint
asm volatile(
"mov %[wht], %[whr];"
: [whr] "=m" (*mem1)
: [wht] "r" (value)
);

Resources