0

I am fairly new to ASM. I am calling sys_open and am trying to print the return value of that syscall (I want to see the fd or error). However, my code isn't working. Any help in the right direction is greatly appreciated. Thanks!

Makefile

asm: test.o
    ld -o asm test.o

test.o: test.asm
    nasm -f elf64 -g test.asm

test.asm

SECTION .text
    GLOBAL _start

    _start: mov rax, 2  ; sys_open
        mov rdi, file   ; get file name address
        mov rsi, 0  ; read only
        syscall
        
        sub al, '0'     ; load fd
        mov byte [buf], al  ; move fd to buf
        mov rsi, buf        ; set address of buf
        mov rdx, 64     ; set length of buf QWORD
        call print

    .exit:  mov rax, 60 ; sys_exit
        mov rdi, 0  ; exit success
        syscall



    ; rsi address of buffer
    ; rdx length of buffer
    print:  push rax
        push rdi
        mov rax, 1  ; sys_write
        mov rdi, 1  ; stdout
        syscall
        pop rdi
        pop rax
        ret

SECTION .data
    file DB `test.asm\0`

SECTION .bss
    buf RESQ 1

Strace output

execve("./asm", ["./asm"], [/* 26 vars */]) = 0
open("test.asm", O_RDONLY)              = 3
write(1, "\323\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 64Ó) = 64
exit(0)                                 = ?
+++ exited with 0 +++
Community
  • 1
  • 1
Rafael
  • 7,605
  • 13
  • 31
  • 46
  • Run your program under `strace` to see what args it's passing to system calls, and what the results are. e.g. `strace ./asm` will decode everything into symbolic constants like `O_RDONLY`. See also the [x86 tag wiki](http://stackoverflow.com/tags/x86/info). – Peter Cordes May 31 '16 at 18:22
  • Also, why does your `print` function save/restore rax and rdi? Those regs are call-clobbered in the standard SysV calling convention, and this program has no need to preserve them either. – Peter Cordes May 31 '16 at 18:25
  • awesome, I did not know about strace. I am not sure what you mean the standard SysV convention. I preserve the registers because it's a function. I read that it is good practice to preserve your registers when calling a function. Please explain or show me what I need to read to understand. I am research the x86 calling conventions and the link that you shared. Thanks! – Rafael May 31 '16 at 18:38
  • In most calling conventions, some registers are "scratch" registers that functions are allowed to use without saving/restoring, but some aren't. See the ABI/Calling convention links in the x86 tag wiki, esp [this one](http://stackoverflow.com/questions/8335582/why-does-ia-32-have-a-non-intuitive-caller-and-callee-register-saving-convention). This reduces the total instruction count in programs, especially the dynamic instruction count (total executed), but there's a code size (and static instruction count) benefit, too. – Peter Cordes May 31 '16 at 18:47
  • @PeterCordes I definitely see your point two years later. From your advice, I ended up researching a lot more about calling conventions and now I understand the bigger picture. At the time, I was just trying to get this to work. I guess a proper implementation is due. I think I was trying to print a file out...who knows... Thanks a ton! – Rafael Sep 30 '18 at 07:01

2 Answers2

2

You need the -g -F stabs for debugging symbols

nasm -f elf64 -g -F stabs test.asm

Dwaddle
  • 21
  • 1
  • it says it is default :( with `-g -y`. I will try explicitly stating it – Rafael May 31 '16 at 18:07
  • I added the -g option to the linker on accident. I put it on nasm and it worked. BTW stabs is the default for elf64. At least now I can debug it, thanks – Rafael May 31 '16 at 18:11
0

This block is incorrect:

sub al, '0'     ; load fd
mov byte [buf], al  ; move fd to buf
mov rsi, buf        ; set address of buf
mov rdx, 64     ; set length of buf QWORD
call print

Since sys_open returns an int, eax should be used instead of al. In addition, sub should be add when converting int to the ASCII character equivalent (in this ex. it's okay to assume fd < 10). Lastly, mov rdx, 64 should be mov rdx, 8, moving 8 bytes, 64-bits.

Here's the modified block:

add eax, '0'     ; load fd
mov dword [buf], eax  ; move fd to buf
mov rsi, buf        ; set address of buf
mov rdx, 8     ; set length of buf QWORD
call print

This prints the fd returned by sys_open:

$ ./a.out
3$ 
Rafael
  • 7,605
  • 13
  • 31
  • 46
  • If it was more than 1 digit long, adding `'0'` wouldn't work for converting a binary integer to a multi-digit decimal ASCII string. So you might as well `add eax, \`0\n\`` and print 2 bytes. Note that your current code does an 8-byte `write` to stdout, including 7 bytes of trailing zeros, as you could see with `strace ./a.out`. – Peter Cordes Sep 30 '18 at 07:09
  • @PeterCordes Yes, I meant to disclaim that in my post but I misspoke :) --- fixed. – Rafael Sep 30 '18 at 17:06
  • If you're not intending to handle multi-byte strings, then why a dword store into a qword buffer? There's no reason to use `eax` instead of `al`, unless you're also going to add a newline, like `add eax, '0' | (0x10 << 8)`. `add al, '0'` / `mov [buf], al` is perfectly fine, and `mov edi, 1` to only print that one byte. – Peter Cordes Sep 30 '18 at 17:11
  • Yeah, I thought the same thing, but I figured the code was screwed anyway if fd > 9. I just wanted to get it working. – Rafael Sep 30 '18 at 17:17
  • But your answer claims that it's actually *wrong* to use `al`, and that you *should* use `eax`. That is misleading at best. Your code is only useful if the upper bytes of the `int` are all zero anyway. – Peter Cordes Sep 30 '18 at 17:21
  • Well technically it is since open returns an `int`. Definitely misleading if one was looking for a proper implementation. I'll pay the due sometime, hopefully sometime tonight – Rafael Sep 30 '18 at 17:45
  • In C you'd want to do `(char)(open() + '0')` or something, and truncating to `char` before or after the addition is exactly equivalent. This is assembly language where everything is just bytes. It be wrong if you were using repeated division to make a decimal ASCII string, but you're not. There's no requirement to use an operand-size that matches the type width if you're truncating or ignoring high bits anyway. There are multiple valid ways to implement this correct-in-limited-circumstances version, and `add al, '0'` is definitely one of them. (But printing trailing 0 bytes isn't really.) – Peter Cordes Sep 30 '18 at 18:05