0

I am trying to use the C function int sprintf(char *str, const char *format, ...) from NASM on 64-bit Linux. However, no matter how I try to make it work, I end up with a segmentation fault!

Here is my assembly function:

global sprintf_attempt

extern sprintf

section .text
sprintf_attempt:
    mov rdx, rdi
    xor rax, rax
    mov rdi, output_buff
    mov rsi, busStringFormat
    call sprintf
    ; should return a pointer to the beginning of the string output_buff
    mov rax, output_buff
    ret

section .data
    busStringFormat: db "%ll is a wonderful number.", 10, 0
section .bss
    ; make buffer really big so I know that's not the problem
    output_buff: resb 5000

To test this function out, I call it from C like so:

#include <inttypes.h>
#include <stdio.h>

char* sprintf_attempt(int64_t);

int main(void) {
    printf("%s", sprintf_attempt((int64_t)22));
    return 0;
}

All good so far; finally I attempt to assemble, link, compile and run the code, getting a segmentation fault:

alex@home:~/Code/asm_fun$ nasm -felf64 sprintf_attempt.asm \
>     && gcc sprintf_attempt.o sprintf_attempt.c -no-pie -std=c99 \
>     && ./a.out
Segmentation fault (core dumped)

:o Of course, I loaded the program into GDB to see what might be going wrong. Everything looks fine up until we call sprintf - all the registers have the values I'd expect, anyway. The problem also does not appear to be that output_buff is not writable. I can write to it manually via eg mov BYTE [output_buff], '*' (and so on), just fine.

I really am quite a newbie when it comes to assembly, so I'm sure I've misunderstood something obvious. Thanks for your time!

PS: I did see the question at Calling sprintf in x64 assembly, however there is no answer and the comments were not particularly helpful for me (maybe they are but I simply lack the knowledge to grok their wisdom)

Alex V
  • 3,416
  • 2
  • 33
  • 52
  • 1
    You're violating the ABI by making a `call` with the stack still 8 bytes away from a 16-byte boundary on function entry. [glibc scanf Segmentation faults when called from a function that doesn't align RSP](https://stackoverflow.com/q/51070716). For most functions that happens to work when you don't pass any FP args, but maybe not for sprintf. I didn't notice any mistakes in passing args. You didn't show where the segfault happened exactly; get GDB to show you which instruction. (`info fr` and `bt` after the crash) – Peter Cordes Dec 24 '20 at 08:03
  • 1
    And BTW, the problem in the question you linked is that `call memset` clobbers RDI. (memset returns the input pointer, but x86-64 calling conventions return in RAX, not RDI. Some ISAs have calling conventions that return in the first arg-passing register, but only a few x86 calling conventions do that, like GCC's 32-bit regparm passes in EAX). It's a totally separate problem from the one you're having. You're only making one call in your function so you don't need your incoming args to survive across a function call before sprintf. – Peter Cordes Dec 24 '20 at 08:10
  • @PeterCordes You really are a gift to people like me who are learning assembly. Thanks a lot, that was precisely the problem! – Alex V Dec 24 '20 at 08:22
  • 1
    Cheers. In future, it can help to look at *optimized* compiler output. You'll see a dummy push in clang's output, or a `sub rsp,8` in GCC's. Since it's optimized, it (probably) wouldn't be there if it didn't need to be. That can be a good way to discover things that are needed. – Peter Cordes Dec 24 '20 at 08:24

0 Answers0