I have a C program linked into my NASM program to call printf. The C progam has two functions, one to print integer values and the other to print float values:
int Test_Print_2(int64_t value_passed_in)
{
printf ("value: %lu\n", value_passed_in);
return 0;
}
int Test_Print_2F(double value_passed_in)
{
printf ("Inside Test_Print - Float ");
printf ("q[0]: %f\n", value_passed_in);
//printf ("q[0]: %4.4lf\n", value_passed_in);
return 0;
}
The first program (Test_Print_2) works with no problem, but the second program (Test_Print_2F) throws a segfault. It doesn’t even print the line "Inside Test_Print - Float " If I comment out the print line then it does print the line "Inside Test_Print - Float " – so something is wrong with my format for printing doubles.
To print as integer, I call it from NASM like this:
movsd xmm0,[rdi] ; the values in the array pointed to by rdi are doubles
cvttsd2si rax,xmm0 ; convert to integer
%include "/opt/P01_SH/_Include_Utilities/ABI_Stack_Push.asm"
mov rdi,rax
call [rel Test_Print_2 wrt ..got]
%include "/opt/P01_SH/_Include_Utilities/ABI_Stack_Pop.asm"
That works and prints just the integer part of the floating point number. However, when I call it from NASM to print as a double like this:
movsd xmm0,[rdi]
%include "/opt/P01_SH/_Include_Utilities/ABI_Stack_Push.asm"
mov rax,1
call [rel Test_Print_2F wrt ..got]
%include "/opt/P01_SH/_Include_Utilities/ABI_Stack_Pop.asm"
it segfaults without printing anything. Both programs are linked in from the object file (I often link C object files into NASM).
Thanks for any help on this.
This question already has answers here:
Why does it return a random value other than the value I give to the function?
(2 answers)
Nasm segmentation fault on RET in _start
(1 answer)
What is the default register state when program launches (asm, linux)?
(3 answers)
Closed 1 year ago.
hybrid.s
.section .text # specifices the beginning of the 'text' or code section
.global collatz # makes the name 'collatz' visible to the linker
collatz: # int collatz(long long n) {
ret # return
hybrid.c
#include <stdio.h>
#include <stdlib.h>
extern int collatz(long long n);
int main(int argc, char *argv[]){
if (argc < 2) {
printf("Parameter \"n\" is missing. \n");
return -1;
}
int output=0;
long long n = atoll(argv[1]);
for (long long i=1 ; i<n ; i++) {
output = collatz(i);
printf("collatz(%lld) is %d\n", i,output);
}
}
I was testing what %rax is initialized to by doing
>> gcc -o hybrid hybrid.c hybrid.s
>> ./hybrid 5
Yielding:
collatz(1) is 1
collatz(2) is 2
collatz(3) is 3
collatz(4) is 4
I expected hybrid.s to always return 0, since my guess was that %rax is initialized to 0; but as you can see, this is not the case. By observing the output, my hyptothesis is that %rax is equal to %rdi by default?
This prompts the question: What is %rax initialized to?
EDIT:
In the following whenever I say "run hybrid.s isolated", I mean "run hybrid.s isolated with all occurrences of collatz replaced by '_start'", of course.
Based on #ErikEidt's comment, Shouldn't running hybrid.s isolated as follows result in just returning whatever %rax was before (last set to), since it wasn't initialized? Why do I then get a Segmentation fault (core dumped)?
>> as hybrid.s -o hybrid.o
>> ld hybrid.o hybrid
>> ./hybrid 5
Yields
Segmentation fault (core dumped)
I mean the %rax has to contain some value at any given moment .. right? So why a segmentation fault instead of just returning that value?
Compile with debug info and open run it in gdb. Gdb can set a breakpoint and you can list the contents of your registers :) funny man's industry am I right 8))))))
In a C program, there is a swap function and this function takes a parameter called x.I expect it to return it by changing the x value in the swap function inside the main function.
When I value the parameter as a variable, I want it, but when I set an integer value directly for the parameter, the program produces random outputs.
#include <stdio.h>
int swap (int x) {
x = 20;
}
int main(void){
int y = 100;
int a = swap(y);
printf ("Value: %d", a);
return 0;
}
Output of this code: 100 (As I wanted)
But this code:
#include <stdio.h>
int swap (int x) {
x = 20;
}
int main(void){
int a = swap(100);
printf ("Value: %d", a);
return 0;
}
Return randomly values such as Value: 779964766 or Value:1727975774.
Actually, in two codes, I give an integer type value into the function, even the same values, but why are the outputs different?
First of all, C functions are call-by-value: the int x arg in the function is a copy. Modifying it doesn't modify the caller's copy of whatever they passed, so your swap makes zero sense.
Second, you're using the return value of the function, but you don't have a return statement. In C (unlike C++), it's not undefined behaviour for execution to fall off the end of a non-void function (for historical reasons, before void existed, and function returns types defaulted to int). But it is still undefined behaviour for the caller to use a return value when the function didn't return one.
In this case, returning 100 was the effect of the undefined behaviour (of using the return value of a function where execution falls off the end without a return statement). This is a coincidence of how GCC compiles in debug mode (-O0):
GCC -O0 likes to evaluate non-constant expressions in the return-value register, e.g. EAX/RAX on x86-64. (This is actually true for GCC across architectures, not just x86-64). This actually gets abused on codegolf.SE answers; apparently some people would rather golf in gcc -O0 as a language than ANSI C. See this "C golfing tips" answer and the comments on it, and this SO Q&A about why i=j inside a function putting a value in RAX. Note that it only works when GCC has to load a value into registers, not just do a memory-destination increment like add dword ptr [rbp-4], 1 for x++ or whatever.
In your case (with your code compiled by GCC10.2 on the Godbolt compiler explorer)
int y=100; stores 100 directly to stack memory (the way GCC compiles your code).
int a = swap(y); loads y into EAX (for no apparent reason), then copies to EDI to pass as an arg to swap. Since GCC's asm for swap doesn't touch EAX, after the call, EAX=y, so effectively the function returns y.
But if you call it with swap(100), GCC doesn't end up putting 100 into EAX while setting up the args.
The way GCC compiles your swap, the asm doesn't touch EAX, so whatever main left there is treated as the return value.
main:
...
mov DWORD PTR [rbp-4], 100 # y=100
mov eax, DWORD PTR [rbp-4] # load y into EAX
mov edi, eax # copy it to EDI (first arg-passing reg)
call swap # swap(y)
mov DWORD PTR [rbp-8], eax # a = EAX as the retval = y
...
But with your other main:
main:
... # nothing that touches EAX
mov edi, 100
call swap
mov DWORD PTR [rbp-4], eax # a = whatever garbage was there on entry to main
...
(The later ... reloads a as an arg for printf, matching the ISO C semantics because GCC -O0 compiles each C statement to a separate block of asm; thus the later ones aren't affected by the earlier UB (unlike in the general case with optimization enabled), so do just print whatever's in a's memory location.)
The swap function compiles like this (again, GCC10.2 -O0):
swap:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-4], 20
nop
pop rbp
ret
Keep in mind none of this has anything to do with valid portable C. This (using garbage left in memory or registers) one of the kinds of things you see in practice from C that invokes undefined behaviour, but certainly not the only thing. See also What Every C Programmer Should Know About Undefined Behavior from the LLVM blog.
This answer is just answering the literal question of what exactly happened in asm. (I'm assuming un-optimized GCC because that easily explains the result, and x86-64 because that's a common ISA, especially when people forget to mention any ISA.)
Other compilers are different, and GCC will be different if you enable optimization.
You need to use return or use pointer.
Using return function.
#include <stdio.h>
int swap () {
return 20;
}
int main(void){
int a = swap(100);
printf ("Value: %d", a);
return 0;
}
Using pointer function.
#include <stdio.h>
int swap (int* x) {
(*x) = 20;
}
int main(void){
int a;
swap(&a);
printf ("Value: %d", a);
return 0;
}
This question already has answers here:
What if there is no return statement in a CALLed block of code in assembly programs
(2 answers)
Why is no value returned if a function does not explicity use 'ret'
(2 answers)
Closed 2 years ago.
I've got an assignment in one of my lectures and here I'm stuck right at the beginning.
My ASM file:
.intel_syntax noprefix
.text
.global stuff
stuff:
mov eax, 1
My C file:
#include <stdio.h>
extern int stuff();
int main()
{
int result = stuff();
printf("%d\n", result);
return 0;
}
and I compile with:
gcc -m32 -o runme main.c a.S
Edit:
./runme
prints 0 instead of expected 1. Why is that and how should I fix it?
I'm trying to do the following for the sake of practice in NASM:
1)Read a string from command-line in C
2)Pass that string to a NASM function which takes the string as its first parameter
3)Return that exact string from NASM function
prefix.asm:
;nasm -f elf32 prefix.asm -o prefix.o
segment .bss
pre resb 256
segment .text
global prefix
prefix:
push ebp ;save the old base pointer value
mov ebp,esp ;base pointer <- stack pointer
mov eax,[ebp+8] ;function argument
add esp, 4
pop ebp
ret
prefix c:
//nasm -f elf32 prefix.asm -o prefix.o
//gcc prefix.c prefix.o -o prefix -m32
#include <stdio.h>
#include <string.h>
char* prefix(char *str);
int main(void)
{
char str[256];
char* pre;
int a;
printf("Enter string: ");
scanf("%s" , str) ;
pre = prefix(str);
printf("Prefix array: %s\n", pre);
return 0;
}
After I run(it compiles w/o any problem) and supply my string to the program I get a Segmentation fault (core dumped) error.
First try to write a C program to implement char* prefix(char *str), disassemble it and understand it.
Problem 1: the add esp, 4 should be deleted. A function should preserve the stack pointer. I.e. the esp should be the same before the first instruction and before the return instruction. Your assembly code increases esp by 4.
Problem 2: Don't name your .asm and .c to be the same. Use different names.