0

C code

#include <stdio.h>

int fibonacci(int);

int main()

{

    int x = fibonacci(3);

    printf("Fibonacci is : %d",x);

    return 0;
}

Assembly

section .text

global fibonacci

fibonacci:

    push ebp;
    mov ebp, esp;
    ; initialize
    mov dword [prev], 0x00000000;
    mov dword [cur], 0x00000001;
    mov byte [it], 0x01; 
    mov eax, dword [ebp + 8]; // n = 3
    mov byte [n], al;

getfib:

    xor edx,edx;
    mov dl, byte [n];
    cmp byte [it] , dl;
    jg loopend; 
    mov eax,dword [prev];
    add eax, dword [cur];
    mov ebx, dword [cur];
    mov dword [prev], ebx;
    mov dword [cur] , eax;
    inc byte [it];
    jmp getfib;

loopend:

    mov eax, dword [cur];

    pop ebp;

    ret;

section .bss

    it resb 1

    prev resd 1

    cur resd 1

    n resb 1

I was trying to run this assembly function in C code and on debugging , i saw that value in variable x in C code is right but there is some error coming when i use the printf function

Need Help on it

Command to compile:

nasm -f elf32 asmcode.asm -o a.o

gcc -ggdb -no-pie -m32 a.o ccode.c -o a.out

Click Below Pictures if they seem blurred

Below is debug before printf execute

Below is after printf execute

1 Answers1

2

Your code does not preserve the ebx register which is a callee-preserved register. The main function apparently tries to do some rip-relative addressing to obtain the address of the format string for printf using ebx as a base register. This fails because your code overwrote ebx.

To fix this issue, make sure to save all callee-saved registers before you use them and then restore their value on return. For example, you can do

fibonacci:

    push ebp
    mov ebp, esp
    push ebx ; <---

    ...

    pop ebx ; <---
    pop ebp
    ret
fuz
  • 88,405
  • 25
  • 200
  • 352
  • Thank you so much sir, i understand the mistake now and program is working now fine – Bhavya Kapoor Jul 19 '21 at 19:43
  • is there any way we can know before that this is a callee register and we should not change that – Bhavya Kapoor Jul 19 '21 at 19:56
  • @BhavyaKapoor Refer to the ABI document for the platform you are programming for. For Linux, a good overview is found [on Wikipedia](https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl). `cdecl` is the convention used on i386 Linux. – fuz Jul 19 '21 at 20:04
  • *apparently tries to do some rip-relative addressing* - That's normal these days for `gcc -m32`, remember that most distros build GCC with `-fPIE -pie` on by default. This unfortunately still applies to `-m32` code where it's much less efficient. – Peter Cordes Jul 19 '21 at 20:07
  • @fuz: `cdecl` is not the actual name of the i386 System V calling convention. It happens to work the same as MSVC `cdecl` for basic arg passing and call-preserved registers, but current Linux i386 System V requires maintaining 16-byte stack alignment, unlike `cdecl`. They also don't 100% agree on how to pass/return structs, e.g. i386 SysV returns 64-bit structs of two int members in memory, but MSVC cdecl returns in EDX:EAX. https://godbolt.org/z/seaG75svo. – Peter Cordes Jul 19 '21 at 20:11
  • @BhavyaKapoor: See [What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64](https://stackoverflow.com/q/2535989) for descriptions of the calling conventions and links to the docs. For x86-64, see [What registers are preserved through a linux x86-64 function call](https://stackoverflow.com/q/18024672). But really, go read https://www.agner.org/optimize/calling_conventions.pdf for nice details on how they work, which will hopefully cover any other stuff you didn't realize you need to know. (And other stuff on https://www.agner.org/optimize – Peter Cordes Jul 19 '21 at 20:15
  • @PeterCordes I wanted to link the first one at first, but I think it didn't talk about callee saved registers. – fuz Jul 19 '21 at 21:07
  • 1
    @fuz: Oh right, it only mentions that for system calls. That Q&A is really over-crowded and messy and should be split up. (With a link to a separate Q&A about function calls). – Peter Cordes Jul 19 '21 at 21:42
  • 1
    Ok Thank You fuz and Peter Cordes , i will go through these references and would read about cdecl too – Bhavya Kapoor Jul 21 '21 at 08:52
  • @BhavyaKapoor Have fun! Do not hesitate to come back with further questions. You are good at asking questions in comparison with many other beginners. – fuz Jul 21 '21 at 09:00
  • @fuz Thank you sir and yep i will mark as resolved – Bhavya Kapoor Jul 23 '21 at 14:54
  • @fuz can you please also send a link for understanding rip relative addressing which could beginner understandable – Bhavya Kapoor Aug 07 '21 at 11:46
  • @BhavyaKapoor Sorry, I don't know any good text on the subject. The idea is that as the load address of the program is not known, it has to compute the addresses of functions by adding the distance from the current address to the contents of the `eip` register. It's not something you really have to wrangle with as a beginner. Just compile with `-fno-pie` to get rid of this. – fuz Aug 09 '21 at 08:27
  • Note that you will still need to preserve the `ebx` register. It might be used for other purposes as well. – fuz Aug 09 '21 at 08:27
  • 1
    @fuz okey , i got things cleared about callee and caller based register and now got an abstract idea of the relative addressing too – Bhavya Kapoor Aug 09 '21 at 11:12