Passing argument from C to Assembly - c

I'm trying to make a program in C that uses a function from Assembly. Below you can see the code:
sum_c.c
#include <stdio.h>
extern int _assemblySum(int x, int y);
int main(int argc, char *argv[]){
int total;
total = _assemblySum(4, 2);
printf("%d\n", total);
return 0;
}
assembly_Sum.asm
SECTION .DATA
SECTION .TEXT
GLOBAL _assemblySum
_assemblySum:
push rbp
mov rbp, rsp
mov rax, [rbp+16]
mov rbx, [rbp+24]
add rax, rbx
pop rbp
ret
COMPILE
nasm -f elf64 assembly_sum.asm -o assembly_sum.o
gcc c_sum.c assembly_sum.o -o sum
./sum
When I run the program I just get random numbers like -1214984584 or 2046906200. I know that I need to use the registers rdi, rsi, rdx and rcx because the 64bit GNU/Linux compiler uses them (Passing arguments from C to 64bit linux Assembly). But how can I do that?

You may have confused which calling convention is being used.
Linux uses the 64-bit System V calling convention. Under this convention, registers are strongly preferred over the stack for the passing of INTEGER type parameters. The registers for integer passing are used in the following order:
%rdi
%rsi
%rdx
%rcx
%r8
%r9
If additional Integer parameters are used, they will be passed on the stack.
You can find detailed information in the System V ABI specification.

Related

Can ASLR randomization be different per function?

I have the following code snippet:
#include <inttypes.h>
#include <stdio.h>
uint64_t
esp_func(void)
{
__asm__("movl %esp, %eax");
}
int
main()
{
uint32_t esp = 0;
__asm__("\t movl %%esp,%0" : "=r"(esp));
printf("esp: 0x%08x\n", esp);
printf("esp: 0x%08lx\n", esp_func());
return 0;
}
Which prints the following upon multiple executions:
❯ clang -g esp.c && ./a.out
esp: 0xbd3b7670
esp: 0x7f8c1c2c5140
❯ clang -g esp.c && ./a.out
esp: 0x403c9040
esp: 0x7f9ee8bd8140
❯ clang -g esp.c && ./a.out
esp: 0xb59b70f0
esp: 0x7fe301f8c140
❯ clang -g esp.c && ./a.out
esp: 0x6efa4110
esp: 0x7fd95941f140
❯ clang -g esp.c && ./a.out
esp: 0x144e72b0
esp: 0x7f246d4ef140
esp_func shows that ASLR is active with 28 bits of entropy, which makes sense on my modern Linux kernel.
What doesn't make sense is the first value: why is it drastically different?
I took a look at the assembly and it looks weird...
// From main
0x00001150 55 push rbp
0x00001151 4889e5 mov rbp, rsp
0x00001154 4883ec10 sub rsp, 0x10
0x00001158 c745fc000000. mov dword [rbp-0x4], 0
0x0000115f c745f8000000. mov dword [rbp-0x8], 0
0x00001166 89e0 mov eax, esp ; Move esp to eax
0x00001168 8945f8 mov dword [rbp-0x8], eax ; Assign eax to my variable `esp`
0x0000116b 8b75f8 mov esi, dword [rbp-0x8]
0x0000116e 488d3d8f0e00. lea rdi, [0x00002004]
0x00001175 b000 mov al, 0
0x00001177 e8b4feffff call sym.imp.printf ; For whatever reason, the value in [rbp-0x8]
; is assigned here. Why?
// From esp_func
0x00001140 55 push rbp
0x00001141 4889e5 mov rbp, rsp
0x00001144 89e0 mov eax, esp ; Move esp to eax (same instruction as above)
0x00001146 488b45f8 mov rax, qword [rbp-0x8] ; This changes everything. What is this?
0x0000114a 5d pop rbp
0x0000114b c3 ret
0x0000114c 0f1f4000 nop dword [rax]
So my question is, what is in [rbp-0x8], how did it get there, and why are the two values different?
No, stack ASLR happens once at program startup. Relative adjustments to RSP between functions are fixed at compile time, and are just the small constants to make space for a function's local vars. (C99 variable-length arrays and alloca do runtime-variable adjustments to RSP, but not random.)
Your program contains Undefined Behaviour and isn't actually printing RSP; instead some stack address left in a register by the previous printf call (which appears to be a stack address, so its high bits do vary with ASLR). It tells you nothing about stack-pointer differences between functions, just how not to use GNU C inline asm.
The first value is printing the current ESP correctly, but that's only the low 32 bits of the 64-bit RSP.
Falling off the end of a non-void function is not safe, and using the return value is Undefined Behaviour. Any caller that uses the return value of esp_func() necessarily would trigger UB, so the compiler is free to leave whatever it wants in RAX.
If you want to write mov %rsp, %rax / ret, then write that function in pure asm, or mov to an "=r"(tmp) local variable. Using GNU C inline asm to modify RAX without telling the compiler about it doesn't change anything; the compiler still sees this as a function with no return value.
MSVC inline asm is different: it is apparently supported to use _asm{ mov eax, 123 } or something and then fall off the end of a non-void function, and MSVC will respect that as the function return value even when inlining. GNU C inline asm doesn't need silly hacks like that: if you want your asm to interact with C values, use Extended asm with an output constraint like you're doing in main. Remember that GNU C inline asm is not parsed by the compiler, just emit the template string as part of the compiler's asm output to be assembled.
I don't know exactly why clang is reloading a return value from the stack, but that's just an artifact of clang internals and how it does code-gen with optimization disabled. But it's allowed to do this because of the undefined behaviour. It is a non-void function, so it needs to have a return value. The simplest thing would be to just emit a ret, and is what some compilers happen to do with optimization enabled, but even that doesn't fix the problem because of inter-procedural optimization.
It's actually Undefined Behaviour in C to use the return value of a function that didn't return one. This applies at the C level; using inline asm that modifies a register without telling the compiler about it doesn't change anything as far as the compiler is concerned. Therefore your program as a whole contains UB, because it passes the result to printf. That's why the compiler is allowed to compile this way: your code was already broken. In practice it's just returning some garbage from stack memory.
TL:DR: this is not a valid way to emit mov %rsp, %rax / ret as the asm definition for a function.
(C++ strengthens this to it being UB to fall off the end in the first place, but in C it's legal as long as the caller doesn't use the return value. If you compile the same source as C++ with optimization, g++ doesn't even emit a ret instruction after your inline asm template. Probably this is to support C's default-int return type if you declare a function without a return type.)
This UB is also why your modified version from comments (with the printf format strings fixed), compiled with optimization enabled (https://godbolt.org/z/sE7e84) prints "surprisingly" different "RSP" values: the 2nd one isn't using RSP at all.
#include <inttypes.h>
#include <stdio.h>
uint64_t __attribute__((noinline)) rsp_func(void)
{
__asm__("movq %rsp, %rax");
} // UB if return value used
int main()
{
uint64_t rsp = 0;
__asm__("\t movq %%rsp,%0" : "=r"(rsp));
printf("rsp: 0x%08lx\n", rsp);
printf("rsp: 0x%08lx\n", rsp_func()); // UB here
return 0;
}
Output example:
Compiler stderr
<source>:7:1: warning: non-void function does not return a value [-Wreturn-type]
}
^
1 warning generated.
Program returned: 0
Program stdout
rsp: 0x7fff5c472f30
rsp: 0x7f4b811b7170
clang -O3 asm output shows that the compiler-visible UB was a problem. Even though you used noinline, the compiler can still see the function body and try to do inter-procedural optimization. In this case, the UB led it to just give up and not emit a mov %rsp, %rsi between call rsp_func and call printf, so it's printing whatever value the previous printf happened to leave in RSI
# from the Godbolt link
rsp_func: # #rsp_func
mov rax, rsp
ret
main: # #main
push rax
mov rsi, rsp
mov edi, offset .L.str
xor eax, eax
call printf
call rsp_func # return value ignored because of UB.
mov edi, offset .L.str
xor eax, eax
call printf # printf("0x%08lx\n", garbage in RSI left from last printf)
xor eax, eax
pop rcx
ret
.L.str:
.asciz "rsp: 0x%08lx\n"
GNU C Basic asm (without constraints) is not useful for anything (except the body of a __attribute__((naked)) function).
Don't assume the compiler will do what you expect when there is UB visible to it at compile time. (When UB isn't visible at compile time, the compiler has to make code that would work for some callers or callees, and you get the asm you expected. But compile-time-visible UB means all bets are off.)

C - external assembly function returning different results with the same input

I have a program in C which uses a NASM function. Here is the code of the C program:
#include <stdio.h>
#include <string.h>
#include <math.h>
extern float hyp(float a); // supposed to calculate 1/(2 - a) + 6
void test(float (*f)(float)){
printf("%f %f %f\n", f(2.1), f(2.1), f(2.1));
}
void main(int argc, char** argv){
for(int i = 1; i < argc; i++){
if(!strcmp(argv[i], "calculate")){
test(hyp);
}
}
}
And here is the NASM function:
section .data
a dd 1.0
b dd 2.0
c dd 6.0
section .text
global hyp
hyp:
push ebp
mov ebp, esp
finit
fld dword[b]
fsub dword[ebp + 8]
fstp dword[b]
fld dword[a]
fdiv dword[b]
fadd dword[c]
mov esp, ebp
pop ebp
ret
These programs were linked in Linux with gcc and nasm. Here is the Makefile:
all: project clean
main.o: main.c
gcc -c main.c -o main.o -m32 -std=c99
hyp.o: hyp.asm
nasm -f elf32 -o hyp.o hyp.asm -D UNIX
project: main.o hyp.o
gcc -o project main.o hyp.o -m32 -lm
clean:
rm -rf *.o
When the program is run, it outputs this:
5.767442 5.545455 -4.000010
The last number is correct. My question is: why do these results differ even though the input is the same?
The bug is that you do this:
fstp dword[b]
That overwrites the value of b, so the next time you call the function, the constant is wrong. In the overall program's output, this shows up as the rightmost evaluation being the only correct one, because the compiler evaluated the arguments to printf from right to left. (It is allowed to evaluate the arguments to a multi-argument function in any order it wants.)
You should have used the .rodata section for your constants; then the program would crash rather than overwrite a constant.
You can avoid needing to store and reload an intermediate value by using fdivr instead of fdiv.
hyp:
fld DWORD PTR [b]
fsub DWORD PTR [esp+4]
fdivr DWORD PTR [a]
fadd DWORD PTR [c]
ret
Alternatively, do what a Forth programmer would do, and load the constant 1 before everything else, so it's in ST(1) when it needs to be. This allows you to use fld1 instead of putting 1.0 in memory.
hyp:
fld1
fld DWORD PTR [b]
fsub DWORD PTR [esp+4]
fdivp
fadd DWORD PTR [c]
ret
You do not need to issue a finit, because the ABI guarantees that this was already done during process startup. You do not need to set up EBP for this function, as it does not make any function calls itself (the jargon term for this is "leaf procedure"), nor does it need any scratch space on the stack.
Another alternative, if you have a modern CPU, is to use the newer SSE2 instructions. That gives you normal registers instead of an operand stack, and also means the calculations are all actually done in float instead of 80-bit extended, which can be very important — some complex numerical algorithms will malfunction if they have more floating-point precision than the designers expected to have. Because you're using the 32-bit ELF ABI, though, the return value still needs to wind up in ST(0), and there's no direct move instructions between SSE and x87 registers, you have to go through memory. I don't know how to write SSE2 instructions in Intel syntax, sorry.
hyp:
subl $4, %esp
movss b, %xmm1
subss 8(%esp), %xmm1
movss a, %xmm0
divss %xmm1, %xmm0
addss c, %xmm0
movss %xmm0, (%esp)
flds (%esp)
addl $4, %esp
ret
In the 64-bit ELF ABI, with floating-point return values in XMM0 (and argument passing in registers by default as well), that would just be
hyp:
movss b(%rip), %xmm1
subss %xmm0, %xmm1
movss a(%rip), %xmm0
divss %xmm1, %xmm0
addss c(%rip), %xmm0
ret

Can't pass parameter from C to Assembly code

From what I understand, when a parameter is passed in a function call in C, the callee can find the first parameter at [ebp+8].
Returning a value through eax works for me, reading the right parameter value from the stack doesn't.
Right now I'm just trying to write an assembly function, that can be called from C and returns the same value, that it is being passed.
When I run the following program, it prints number: 1 to the console, no matter what value is passed into myFunc. What am I doing wrong?
assembly.s
section .text
global _myFunc
_myFunc:
mov eax, [ebp+8]
ret
main.c
#include <stdio.h>
extern unsigned int myFunc(unsigned int somedata);
int main() {
unsigned int i = myFunc(6);
printf("number: %i\n",i);
return 0;
}
I'm using a Mac, nasm to assemble the code and gcc for C compilation.
Makefile
macho32:
nasm -f macho32 assembly.s
gcc -m32 -o macho32 assembly.o main.c
You need to setup to access the the parameters first by saving esp. This is explained in:
http://www.nasm.us/doc/nasmdoc9.html
in section "9.1.2 Function Definitions and Function Calls"
The following works for me
assembly.s
section .text
global myFunc:function
myFunc:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov esp, ebp
pop ebp
ret
main.c
#include <stdio.h>
extern unsigned int myFunc(unsigned int somedata);
int main() {
unsigned int i = myFunc(6);
printf("number: %i\n",i);
return 0;
}
Assembly & Compilation
ericu#eric-phenom-linux:~$ nasm -f elf32 assembly.s
ericu#eric-phenom-linux:~$ gcc -m32 assembly.o main.c
ericu#eric-phenom-linux:~$ ./a.out
number: 6
I am on a linux machine so I use elf32. The use of macho32 is correct on your Mac.
you refer to argument on stack, by reading from [EBP+offset] - has EBP been set up to actually point at stack? If no, you may have to do that first, conventionally done by:
push ebp
mov ebp,esp
Only then points EBP to its stacked previous contents, below stacked return address, and below passed arguments.
Your function should look like this,
_myFunc:
push ebp ; setup ebp as frame pointer
mov ebp, esp
mov eax, [ebp + 8]
leave ; mov esp,ebp / pop ebp
ret
The convention is to use ebp to access the parameters, to do so you need to save ebp on the stack and make it to point to the new top of the stack. At function exit you should restore ebp and esp, as the leave instruction does.
In nasm there is a macro package, c32.mak, that can help in supporting the C calling convention, these macros are arg, proc and endproc.
Using these macros your code should look like,
proc _myfunc
%$i arg
mov eax, [ebp + %$i]
endproc

Calling Nasm function from C but return -nan

I'm learning assembly and how to call nasm function from C.
So i wrote a simple code just to try it:
This is my test.c file:
#include <stdio.h>
extern int test(int c);
int main(int argc, char ** argv){
int x = 10;
printf("Original value: %d\nUpdated value: %d\n",x,test(x));
return 0;
}
This is my test.nasm file:
; 32 bit
SECTION .text
global test ;unix
global _test ;windows
test:
_test:
push ebp
mov ebp, esp
push ebx
mov ebx , [ebp+8]
mov eax, ebx
sub eax, 2
pop ebx
mov esp, ebp
pop ebp
ret
As expected i get:
Original value: 10
Updated value: 8
But if i change test.c file as:
#include <stdio.h>
extern float test(float c);
int main(int argc, char ** argv){
float x = 10.0;
printf("Original value: %f\nUpdated value: %f\n",x,test(x));
return 0;
}
The output is:
Original value: 10.000000
Updated value: -nan
Could, please, someone explain why i get this and how should i correct it?
Thanks in advance.
Edit:
Thanks all for answers. I've edited the nasm file for the floating variables:
; 32 bit
SECTION .data
align 16
y: dd 2.0 , 2.0, 2.0, 2.0
SECTION .bss
alignb 16
A: resd 4
SECTION .text
global test ;unix
global _test ;windows
test:
_test:
push ebp
mov ebp,esp
push ebx
movss xmm0 , [ebp+8]
subps xmm0, [y]
movaps [A], xmm0
fld dword[A]
pop ebx
mov esp, ebp
pop ebp
ret
Now it works as expected. Is that code correct?
machine code generated by C compilers generally follows a specific calling protocol, depending on the function signature. For instance, on the X86-64 platform, functions will see their parameters placed in certain registers, depending on their types and numbers: integral types (int, long, char, etc.) will be placed in generic registers (RAX, RDX, RCX, etc), while floating point values will be passed in XMM registers. Likewise, values may be returned differently depending on the function return type.
In your case, you are targeting the IA-32 platform: the convention there is to return integral values through the x86 EAX register, and floating point values through the x87 ST0 register (parameters are still passed through the stack).
So, by changing the function signature, you are telling the compiler that test will no longer return its value the same way, and since your assembly code doesn't touch the floating point registers, you get as a result whatever was stored in there. Furthermore, subtracting the integer encoded value 2 to the 32bit floating point encoded value 10.0 (which turns out to be $41200000) will probably not yield what you would expect.

different assembly instructions from gcc2.9 to 5.3

I have the following C code:
#include <stdio.h>
void function(int a, int b, int c) {
int buff_1[5];
int buff_2[10];
buff_1[0] = 6;
buff_2[0] = 'A';
buff_2[1] = 'B';
}
int main(void) {
int i = 1;
function(1,2,3);
return 0;
}
now I want to analyze the associated assembly code:
The assembly instructions before the function call, according to the book I'm reading are:
pushl $3
pushl $2
pushl $1
call function
The underlying object file was created using gcc-5.3 -O0 -c functions.c.
However, if I create the assembly code using objdump I get the following instructions:
movl $3, %edx
movl $2, %esi
movl $1, %edi
As far as I understand assembly (I'm pretty new to it) the first one makes more sense to me.
Is the book simply wrong? Or is the books output just outdated because of using gcc 2.9
The book is out of date with respect to 64-bit x86. The x86-64 calling conventions per Wikipedia are:
System V AMD64 ABI
The calling convention of the System V AMD64 ABI is followed on Solaris, Linux, FreeBSD, OS X, and other UNIX-like or POSIX-compliant operating systems. The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX (R10 in the Linux kernel interface), R8, and R9, while XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are used for certain floating point arguments. As in the Microsoft x64 calling convention, additional arguments are passed on the stack and the return value is stored in RAX.
Since you're passing 32-bit values, gcc is using the lower half of each register, hence %edi, %esi, and %edx.

Resources