0

What is the proper NASM equivalent of the following two functions?

get_user_input:  ; (char*, unsigned long)
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     qword [rbp-8], rdi
        mov     qword [rbp-16], rsi
        mov     rdx, qword stdin[rip]  ; previously: mov   rdx, QWORD PTR stdin[rip]
        mov     rax, qword [rbp-16]
        mov     ecx, eax
        mov     rax, qword [rbp-8]
        mov     esi, ecx
        mov     rdi, rax
        call    fgets
        nop
        leave
        ret

print_string:  ; (char const*)
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     qword [rbp-8], rdi
        mov     rdx, qword stdout[rip]  ; previously: mov   rdx, QWORD PTR stdout[rip]
        mov     rax, qword [rbp-8]
        mov     rsi, rdx
        mov     rdi, rax
        call    fputs
        nop
        leave
        ret

The above snippet was originally in GAS syntax and I manually converted it to NASM. It was generated using GCC (-O0) from the following C++ code:

void
get_user_input( char* const buffer, const std::size_t buffer_size ) noexcept
{
    std::fgets( buffer, static_cast<int>( buffer_size ), stdin );
}

void
print_string( const char* const str ) noexcept
{
    std::fputs( str, stdout );
}

But my conversion was not without errors. Apparently, the external symbols must be declared at the top of the file:

extern fgets
extern fputs
extern stdin
extern stdout

This solves some issues. But NASM says there are still issues in these two lines:

        mov     rdx, qword stdin[rip]

and

        mov     rdx, qword stdout[rip]

The error message:

nasm -g -F dwarf -f elf64 Main.asm -l Main.lst -o Main.o
Main.asm:96: error: symbol `rip' not defined
Main.asm:111: error: symbol `rip' not defined

How do we tell NASM to move the address of stdout or stdin to the rdx register similar to the above GAS assembly code?

digito_evo
  • 3,216
  • 2
  • 14
  • 42
  • 2
    nasm uses the `rel` keyword for rip relative addressing. Try `mov rdx, [rel stdin]`. – Jester Jun 06 '23 at 13:12
  • @Jester This worked. Thanks. I still don't quite understand what it loads to the register though. – digito_evo Jun 06 '23 at 13:46
  • 1
    Not an exact duplicate of [32-bit absolute addresses no longer allowed in x86-64 Linux?](https://stackoverflow.com/q/43367427) but my answer there does mention NASM's `default rel` , and links [How do RIP-relative variable references like "\[RIP + \_a\]" in x86-64 GAS Intel-syntax work?](https://stackoverflow.com/q/54745872) which shows the NASM alternative `[rel + stdout]`. That's maybe closer to a duplicate since the problem is trying to use GAS's syntax in NASM. – Peter Cordes Jun 06 '23 at 13:56
  • [Why use RIP-relative addressing in NASM?](https://stackoverflow.com/a/36952302) shows examples of NASM RIP-relative addressing, mostly in the context of LEA but also one MOV load. IIRC there's also a Q&A somewhere about translating GAS to NASM; assembling with GAS and disassembling with Agner Fog's `objconv` into NASM syntax should work to get the instruction syntax right. (Also, compile with at least `-Og` for trivial functions like this where the debug-mode stack frame stuff is not useful.) – Peter Cordes Jun 06 '23 at 14:01
  • 2
    It loads the content of `stdin`. It's functionally equivalent to `mov rdx, [stdin]` but it uses a position independent addressing mode. – Jester Jun 06 '23 at 14:20
  • [Referencing the contents of a memory location. (x86 addressing modes)](https://stackoverflow.com/q/34058101) shows NASM `[rel foo]` and mentions `default rel` at once point, but not a great duplicate. [How to generate assembly code with gcc that can be compiled with nasm](https://stackoverflow.com/q/35102193) basically just suggests `objconv` and does some wonky and ill-advised processing like changing `main` to `_start`, but `_start` isn't a function. – Peter Cordes Jun 06 '23 at 14:37
  • @Jester So does that interfere with `-no-pie` linker option? – digito_evo Jun 06 '23 at 16:55
  • 1
    Oops, typo on my earlier comment. `[rel stdin]` not `[rel + stdin]`. Re: `-no-pie`: You can always use RIP-relative addressing, even if it's not required. It's more efficient for loads/stores, only useful to use absolute addressing like `mov edi, format_string` or something in a non-PIE, or `mov eax, [global_array + rdx*4]`, which rely on 32-bit absolute addressing. See [Why does this MOVSS instruction use RIP-relative addressing?](https://stackoverflow.com/q/44967075) and [32-bit absolute addresses no longer allowed in x86-64 Linux?](https://stackoverflow.com/q/43367427) – Peter Cordes Jun 06 '23 at 18:49
  • 1
    The `-no-pie` relaxes the requirements so you don't have to use rip relative but you may still do so. – Jester Jun 06 '23 at 18:49
  • @Jester Makes sense. I guess I'll keep it that way. – digito_evo Jun 06 '23 at 18:58
  • @PeterCordes I think you could as well turn your comment into an answer. Would be better for others viewing this question later. – digito_evo Jun 06 '23 at 19:00
  • The main reason I didn't want to answer this question is that the title isn't the real issue, it's how to load a global variable in x86-64 NASM syntax. Or how to write a RIP-relative addressing mode in NASM. Those are useful canonical questions to have for future readers to find. The fputs stuff is just the context (or a separate question about why `FILE *stdin` needs a load?). A title about translating from GAS `.intel_syntax noprefix` to NASM for x86-64 could also work, since the previous questions about that topic didn't mention the RIP-relative addressing mode syntax difference. – Peter Cordes Jun 06 '23 at 19:08
  • 1
    I notice you said the asm was output by GCC, but nowhere did you mention changing `qword ptr` to `qword`. That's necessary (or just remove the size override when implied by the other operand), but it would be better to show the actual original GCC output of `mov rdx, qword ptr stdin[rip]` as well as your attempt at porting it to NASM. – Peter Cordes Jun 06 '23 at 19:12
  • @PeterCordes The new title should make more sense now. – digito_evo Jun 06 '23 at 19:16
  • @PeterCordes I only changed `QWORD PTR` to `qword`. The rest of it was left intact. – digito_evo Jun 06 '23 at 19:19
  • I know, since I know what GCC output looks like. I meant to make it a better question for future readers to find by searching, like if they searched on "qword ptr" "rip" or something. I realized after posting the comment that the variable name comes in between the `qword ptr` and the `rip`, so an exact string match from a search engine isn't going to easily find it. So at least to make the question make sense to other total beginners who wouldn't realize you took out a `PTR` if you don't say so, and they wonder why their GCC output doesn't look like that. – Peter Cordes Jun 06 '23 at 19:32
  • I guess one way to formulate this into a useful question would be what else do you have to do to translate GAS Intel-syntax addressing modes to NASM, beyond taking out `ptr` everywhere. (Your current title is good, too, focusing on RIP-relative.) I'll probably post an answer sometime if you or someone else doesn't answer. – Peter Cordes Jun 06 '23 at 19:34
  • @PeterCordes I have added the previous GAS code where needed. You might want to write an answer. – digito_evo Jun 07 '23 at 20:11

0 Answers0