0

I'm creating a file and then opening it using system calls.

Creating:

mov rax, 85
mov rdi, path
mov rsi, 400
syscall

Opening:

mov rax, 2
mov rdi, path
mov rsi, 2 ; I found somewhere that I should use 400 to append
syscall
mov r8, rax ; save the file handle

Writing:

mov rax, 1
mov rdi, r8
mov rsi, output
mov rdx, length
syscall

After I complete these steps I close the file naturally, however I am unable to append to it and each time I perform these actions it just rewrites the content of the file, but for example I calculated the first n prime numbers and stored them away in some chunk of memory and now I'd like to write those n prime numbers down in the file, but I'm not able to do so without appending more lines.

Also is there a way to not specify the length of the string for write in rdx, but instead have the string be terminated by a /0?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • You are asking multiple questions in one post. Please limit each post to a single question. – fuz Jan 31 '18 at 12:38

1 Answers1

6

O_APPEND by itself would imply O_RDONLY (because it's 0), not O_WRONLY or O_RDWR. Opening read-only for append isn't usually useful. As in C, you need to OR together multiple flags to get a file descriptor you can use to write at the end of the file, even if its length is changed by something other than than your writes.


For appending to a file use the O_APPEND = 0x400 flag as int flags parameter in rsi. So you were close: it's a value of hexadecimal 400 and not decimal 400 as you attempted.

Here is a list of the raw int flags values in hexadecimal:

O_ACCMODE                        = 0x3
O_APPEND                         = 0x400
O_ASYNC                          = 0x2000
O_CLOEXEC                        = 0x80000
O_CREAT                          = 0x40
O_DIRECT                         = 0x4000
O_DIRECTORY                      = 0x10000
O_DSYNC                          = 0x1000
O_EXCL                           = 0x80
O_FSYNC                          = 0x101000
O_LARGEFILE                      = 0x0
O_NDELAY                         = 0x800
O_NOATIME                        = 0x40000
O_NOCTTY                         = 0x100
O_NOFOLLOW                       = 0x20000
O_NONBLOCK                       = 0x800
O_RDONLY                         = 0x0
O_RDWR                           = 0x2
O_RSYNC                          = 0x101000
O_SYNC                           = 0x101000
O_TRUNC                          = 0x200
O_WRONLY                         = 0x1

So this should work:

mov   rax, 2
lea   rdi, [rel path]
mov   rsi, 0x441        ; O_CREAT| O_WRONLY | O_APPEND
mov   edx, 0q666        ; octal permissions in case O_CREAT has to create it
syscall
mov   r8, rax      ; save the file descriptor

I added the three values to create the file if it doesn't exist or open it in write_only mode if it does exist. You could define them as assemble-time equ constants so you could actually write mov esi, O_CREAT| O_WRONLY | O_APPEND in your source.

Also, make sure to supply a value for the 3rd arg (permissions) any time you include O_CREAT. Otherwise the file could get created with random garbage for its permissions, including possibly setuid + exec. Normally you pass octal 666 (NASM 0q666) and let the user's umask knock off write permission for other and optionally group.

If you don't actually want open to create it if it doesn't already exist, omit O_CREAT. Then it will return -ENOENT.


Also is there a way to not specify the length of the string in rdx, but instead have the string be terminated by a /0?

In short: No.
Create a strlen function and pass the result in rdx. Kernel system calls take data to be read/written on file descriptors as buffer + length, only pathnames as C implicit-length strings.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
zx485
  • 28,498
  • 28
  • 50
  • 59
  • According to [Linux sys\_open in 64-bit NASM returns negative value](https://stackoverflow.com/q/59202746), these constants are defined in `/usr/include/asm-generic/fcntl.h`. (With the call number for `open` coming from `asm/unistd_64.h`) – Peter Cordes Sep 11 '20 at 20:21
  • 2
    For the record: `mov rdi, path` is a crappy way to put a label address in a register. Use `lea rdi, [rel path]` for the standard position-independent way, or `mov edi, path` for the optimized Linux non-PIE executable way. You basically never want to use a 64-bit absolute address; it's inefficient. [How to load address of function or label into register in GNU Assembler](https://stackoverflow.com/q/57212012) – Peter Cordes Sep 11 '20 at 20:25