1

I'm new to coding with intel asm 64bits and I'm trying to recode strdup from libc and I got a segfault when try to test it in my main in c but I don't understand why I got this error

here is my main.c:

#include "libasm.h"

int main(void)
{

    printf("  _____________\n");
    printf("//             \\\\\n");
    printf("||  ft_strdup  ||\n");
    printf("||_____________||\n");
    char *str;
    char *dup;

    str = "test";
    printf("segfault1\n");
    dup = ft_strdup(str);
    printf("segfault2\n");
    printf("str: %s\n", dup);
    free(dup);
    return 0;
}

WHen I launch my program with the flags -g -fsanitize I got this segfault error on the line where I call ft_strdup:

ASAN:DEADLYSIGNAL

=================================================================
==21414==ERROR: AddressSanitizer: SEGV on unknown address 0x55789815fa80 (pc 0x55789815fa80 bp 0x7ffe98c5e8a0 sp 0x7ffe98c5e3d8 T0)
==21414==The signal is caused by a READ memory access.
ASAN:DEADLYSIGNAL
AddressSanitizer: nested bug in the same thread, aborting.

gdb backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x00005554f7a79140 in ?? ()
(gdb) backtrace
#0  0x00005554f7a79140 in ?? ()
#1  0x0000555555554fb2 in ft_strdup ()
#2  0x000055555555508a in ?? ()
#3  0x0000555555554e69 in main () at main.c:90

here is the instruction 0x0000555555554fb2:

(gdb) x/10i 0x0000555555554fb2
   0x555555554fb2 <ft_strdup+18>:       pop    %rsi
   0x555555554fb3 <ft_strdup+19>:       mov    %rax,%rdi
   0x555555554fb6 <ft_strdup+22>:       jmpq   0x555555554ef0 <ft_strcpy>
   0x555555554fbb <ft_strdup+27>:       nopl   0x0(%rax,%rax,1)
   0x555555554fc0 <__libc_csu_init>:    push   %r15
   0x555555554fc2 <__libc_csu_init+2>:  push   %r14
   0x555555554fc4 <__libc_csu_init+4>:  mov    %rdx,%r15
   0x555555554fc7 <__libc_csu_init+7>:  push   %r13
   0x555555554fc9 <__libc_csu_init+9>:  push   %r12
   0x555555554fcb <__libc_csu_init+11>: lea    0x200d9e(%rip),%r12        # 0x555555755d70

here is my ft_strdup code:

global    ft_strdup
extern    malloc
extern    ft_strlen
extern    ft_strcpy

ft_strdup:
    push rdi
    call ft_strlen
    add rax, 1
    mov rdi, rax
    call malloc
    pop rsi
    mov rdi, rax
    jmp ft_strcpy

here is my ft_strcpy code:


global ft_strcpy

ft_strcpy:
    xor     rax, rax            

loop:
    cmp     byte [rsi + rax], 0
    jz      return
    mov     dl, [rsi + rax]
    mov     [rdi + rax], dl
    inc     rax
    jmp     loop

return:
    mov     byte [rdi + rax], 0
    mov     rax, rdi
    ret

and my ft_strlen code:

global ft_strlen

ft_strlen:
    xor rcx, rcx

count:
    cmp byte [rdi], 0
    je ret;
    inc rdi
    inc rcx
    jmp count

ret :
    mov rax, rcx
    ret

If someone could give me some indications on what could cause this segfault it would really help me!

luweglarz
  • 103
  • 2
  • 9
  • 1
    Have you used a debugger to see where the exception happens? – Thomas Jager Apr 16 '21 at 13:20
  • 1
    Also, although I don't know if it'd cause issues here, you don't seem to be keeping 16-byte stack alignment on function call. – Thomas Jager Apr 16 '21 at 13:22
  • 1
    Are all your `ft` functions written in assembly? Do the others work as expected? – Some programmer dude Apr 16 '21 at 13:23
  • If it's a mix between C and asm then it's almost certainly related to calling convention. Disassemble the C code. – Lundin Apr 16 '21 at 13:26
  • Thank you for your answers I'm using the flags -g -fsanitize=address to debug my code but it doesn't show me more informations, what do you mean by keeping 16-byte stack alignment on function call? – luweglarz Apr 16 '21 at 13:32
  • all my other ft functions work as expected yes. – luweglarz Apr 16 '21 at 13:32
  • 1
    Provide [mcve] along with commands used. If the error is really on the line with the call, that means you somehow got the address of `ft_strdup` wrong. – Jester Apr 16 '21 at 13:44
  • BTW, your stack is not 16-byte aligned when you do `pop rsi` / `call ft_strcpy`. It would not violate the ABI if you use jmp as tail call instead of call. So it should be `pop rsi` / `jmp ft_strcpy`. And the `ret` can be ommited. – Ammar Faizi Apr 16 '21 at 14:09
  • 1
    I recommend you to run your program under the GDB, and then see the `backtrace` when segfault happens, so you can see exactly which part of your code causes segfault. – Ammar Faizi Apr 16 '21 at 14:13
  • I'm trying to understand what does it mean to align my stack at 16bytes, how come using jmp and call doesn't violate the ABI. When I use the backtrace of GDB I got this: `Program received signal SIGSEGV, Segmentation fault. 0x00005554f6ef6a80 in ?? () (gdb) backtrace #0 0x00005554f6ef6a80 in ?? () #1 0x0000555555556000 in ?? () #2 0x0000555555555c46 in main () at main.c:90 ` the line 90 is where I call my ft_strdup – luweglarz Apr 16 '21 at 14:30
  • System V ABI requires you align your stack at 16-byte before you call a function. In order to make it easy, the ABI guarantees that "On function entry, if you `sub` your stack pointer with `8 * n` (n is an odd number), your stack will be 16-byte aligned". – Ammar Faizi Apr 16 '21 at 14:40
  • Note that your `push rdi` subtracts the `rsp` with 8, or you can say `8 * 1`, `1` is an odd number, so the call to `ft_strlen` and `malloc` don't violate the ABI. But before you call `ft_strcpy`, you `pop rsi`, it adds the `rsp` with 8, so your `rsp` is not 16-byte aligned anymore. – Ammar Faizi Apr 16 '21 at 14:41
  • Please edit your question, put your GDB debugging output and show the `ft_strdup` code. – Ammar Faizi Apr 16 '21 at 14:42
  • I already show my ft_strdup code, do you mean the code of my other functions ? – luweglarz Apr 16 '21 at 14:47
  • Yeah, I still can't see the problem, honestly, the code of `ft_strcpy` may also be helpful. – Ammar Faizi Apr 16 '21 at 14:48
  • 1
    The backtrace is likely to be confused because your code does not set up a stack frame. You'll probably have more success single-stepping the code to the point of the crash. Your code for `ft_strdup` looks okay at first glance (except for the stack misalignment for `ft_strcpy`, which is probably not a problem if `ft_strcpy` is written in pure asm), so the bug is probably in one of the other functions. A minimal reproducible example means *all* the code that would allow someone to compile and run your program. – Nate Eldredge Apr 16 '21 at 14:49
  • Something may also be helpful if you can post the instruction at `#1 0x0000555555554fb2 in ft_strdup ()`. You can dump it from GDB by using `x/10i 0x0000555555554fb2` – Ammar Faizi Apr 16 '21 at 14:52
  • I updated my post with my other functions – luweglarz Apr 16 '21 at 14:52
  • I see now, after you dumped the instructions at that address, something is wrong with `malloc()`, you are in the `malloc()` when segfault happens. – Ammar Faizi Apr 16 '21 at 14:56
  • I suspect you write beyond the allocated capacity, in your `main.c`, you may do something wrong with `malloc(n)`, something like you write more than `n` bytes to it. So it makes mess to the internal book allocator. Use after free may lead to this problem too. – Ammar Faizi Apr 16 '21 at 14:59
  • Another way to debug it is by using `valgrind`. Execute it like this `valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./your_program` – Ammar Faizi Apr 16 '21 at 15:01
  • I already tried to debug with valgrind and it didn't behave like my code have any issues, I just retried with your flags and it displays everything without segfault – luweglarz Apr 16 '21 at 15:06
  • Well, I don't have further clue for this problem. You may post your `main.c`. So can I compile it at home. – Ammar Faizi Apr 16 '21 at 15:11
  • I just edited with all of my main.c – luweglarz Apr 16 '21 at 15:17
  • I compiled your code and annotated the `ft_strdup` declaration with `char *ft_strdup(const char *s);`, and it works perfectly without memory error. I believe in your error case the `main.c` is not the code what you post here. The `backtrace` even said `main.c:90` (means your `main.c` has at least 90 lines of code). – Ammar Faizi Apr 16 '21 at 15:24
  • I just noticed that when I lauch my program I have this error : ./a.out: Symbol `malloc' causes overflow in R_X86_64_PC32 relocation and when I compile with the flags -no-pie with gcc the segfault disappears – luweglarz Apr 16 '21 at 15:25
  • I trimmed my code because in my main I had all the test of my other functions that worked well – luweglarz Apr 16 '21 at 15:26
  • Great, you may post your solution as an answer to your own question. – Ammar Faizi Apr 16 '21 at 15:28
  • Yes thank you very much for taking your time to help me ! – luweglarz Apr 16 '21 at 15:28
  • 2
    @AmmarFaizi: The code at `555555554fb2` is the return address from `call malloc`. That's not super helpful; seeing the machine code for the `call` instruction itself is what would have made diagnosing the segfault more possible (i.e. the rel32 got garbage because of the relocation overflow), so `dis ft_strdup` would have been much more useful. Of course, including the `Symbol 'malloc' causes overflow in R_X86_64_PC32 relocation` error message as part of the [mcve] in the question would have saved everyone a lot of time. – Peter Cordes Apr 17 '21 at 00:21

1 Answers1

0

I found this post: Nasm - Symbol `printf' causes overflow in R_X86_64_PC32 relocation

that explains how to fix this error that was causing my segfault by using:

./a.out: Symbol `malloc' causes overflow in R_X86_64_PC32 relocation

by using the flags gcc -no-pie main.c mylibrary.a:

luweglarz
  • 103
  • 2
  • 9
  • 1
    While this works, it does so by disabling an important security feature and so shouldn't be used in production code. – Joseph Sible-Reinstate Monica Apr 16 '21 at 21:00
  • 3
    @JosephSible-ReinstateMonica: Indeed, see [Can't call C standard library function on 64-bit Linux from assembly (yasm) code](https://stackoverflow.com/a/52131094) for 3 ways of calling libc functions from NASM code, 2 of them not requiring `-fno-pie -no-pie`. – Peter Cordes Apr 17 '21 at 00:18