3

I'm trying to learn how to use the x86 DIV instruction. Here is my external function located in a .S file:

.intel_syntax noprefix
.text
.global divExtern

divExtern:
        mov edx, 0
        mov eax, [esp+4]
        mov ebx, [esp+8]
        div ebx
        ret

Here is my C code:

#include <stdio.h>

int extern divExtern(int n, int m);

int main() {
        printf("%d", divExtern(10, 5));
        return 0;
}

As you can see I am testing to see if 10/5 = 2. Using GDB I verified that 2 is indeed in EAX right before executing RET.

0x565555eb <divExtern>          mov    $0x0,%edx                                                                   3
   30x565555f0 <divExtern+5>        mov    0x4(%esp),%eax                                                              3
   30x565555f4 <divExtern+9>        mov    0x8(%esp),%ebx                                                              3
   30x565555f8 <divExtern+13>       div    %ebx                                                                        3
  >30x565555fa <divExtern+15>       ret

(gdb) p $eax
$2 = 2

However I am still getting a seg fault.

GDB has shown me that EAX changed in between executing the function and printf. Why is this the case and what can I do to prevent this?

0x565555af <main+15>    call   0x56555470 <__x86.get_pc_thunk.bx>                                                  3
   30x565555b4 <main+20>    add    $0x1a4c,%ebx                                                                        3
   30x565555ba <main+26>    sub    $0x8,%esp                                                                           3
   30x565555bd <main+29>    push   $0x5                                                                                3
   30x565555bf <main+31>    push   $0xa                                                                                3
   30x565555c1 <main+33>    call   0x565555eb <divExtern>                                                              3
   30x565555c6 <main+38>    add    $0x10,%esp                                                                          3
   30x565555c9 <main+41>    sub    $0x8,%esp                                                                           3
   30x565555cc <main+44>    push   %eax                                                                                3
   30x565555cd <main+45>    lea    -0x1980(%ebx),%eax                                                                  3
   30x565555d3 <main+51>    push   %eax                                                                                3
  >30x565555d4 <main+52>    call   0x56555400 <printf@plt>


(gdb) p $eax
$3 = -6523

In case you need to know I compiled this by running gcc test.c divExtern.S -m32 -o test

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Sam K9
  • 99
  • 9
  • 4
    The [32-bit CDECL calling convention](https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl) makes _EBX_ a callee saved register (If you change it, you must save and restore it). Just use one of the other volatile (caller saved) registers like _ECX_ or _EDX_ instead of _EBX_ – Michael Petch Dec 06 '17 at 23:31
  • 3
    You could also write it as `xor edx, edx` `mov eax, [esp+4]` `div dword ptr [esp+8]` `ret` – Michael Petch Dec 07 '17 at 01:05
  • 3
    I should point out though that your assembly routine is doing unsigned division. If you make one of the parameters negative the results won't be as you expect. Your function is actually `unsigned int extern divExtern(unsigned int n, unsigned int m);` . If you want to do signed division you'd could do something like `mov eax, [esp+4]` `cdq` `idiv dword ptr [esp+8]` `ret` . The CDQ takes the value in _EAX_ and sign extends it into _EDX_. We use `idiv` to do signed division rather than `div` for the unsigned division. – Michael Petch Dec 07 '17 at 01:14
  • 1
    Your compiler is actually using `ebx` because it was configured with `--enable-default-pie` to make Position-Independent Executables (which applies even in 32-bit mode). See https://stackoverflow.com/questions/43367427/32-bit-absolute-addresses-no-longer-allowed-in-x86-64-linux. (Your code is still wrong with `-no-pie -fno-pie`; the calling convention doesn't change but your code might have happened to work.) Mostly I mention this because `-fpie` costs more performance in 32-bit mode than in 64-bit mode. – Peter Cordes Dec 07 '17 at 04:30

0 Answers0