flat assembler
Message board for the users of flat assembler.

Index > Linux > Exercise with getdents64

Author
Thread Post new topic Reply to topic
Hans-Joachim Rudolph



Joined: 29 Jan 2023
Posts: 1
Hans-Joachim Rudolph 30 Jan 2023, 01:16
Hello everybody,

did you like Volkov Commander? I still have it on some of my (32-bit XP) PCs, but of course it has aged. And apparently, the only all-assembly orthodox file manager after that was KFAR from KolibriOS, maybe? So the idea was to throw something together that at least tries to resemble a decent OFM (for Linux x86_64, because I parted with Windows since their No. 8).

The first step would be a simple file browser, where one can navigate through folders by selecting and hitting enter. Then the first step to such browser is a program to simply list files inside a folder. Starting with the example from `man 2 getdents` I took my first steps into ELF64 land:

Code:
        format ELF64 executable

; cf. /usr/include/x86_64-linux-gnu/asm/unistd_64.h
sys_write       equ 1
sys_open        equ 2
sys_exit        equ 60
sys_getdents64  equ 217
; cf. /usr/include/x86_64-linux-gnu/asm/bits/fcntl-linux.h
O_DIRECTORY     equ 200000o                     ; may be not actally necessary
; common knowledge
stdout          equ 1
stderr          equ 2
; string constants
err_open        db 'Directory unopenable!',10   ; my take at English++ ;) 
err_open_len    db $ - err_open
err_getdents64  db 'Entries ungettable!',10     ; sorry, native speakers
err_getdents64_len db $ - err_getdents64
nl              db 10
; parametric constants
buf_len         equ 512
cur_dir         db '.',0

entry $
        mov     eax, sys_open
        mov     rdi, cur_dir
        mov     rsi, O_DIRECTORY        ; fail if rdi doesn't point to directory
        syscall
        test    rax, rax                ; error handling
        jg      @f
        mov     edi, 1                  ; return value
        mov     rsi, err_open           ; error message
        movzx   edx, byte[err_open_len]
        jmp     abort
    @@: mov     r14, rax                ; keep for when outer loop jumps back
    next_getdents:
        mov     edi, r14d
        mov     eax, sys_getdents64
        mov     esi, buf
        mov     rdx, buf_len
        syscall
        cmp     rax, -1
        jne     @f
        mov     edi, 2
        mov     rsi, err_getdents64
        movzx   edx, byte[err_getdents64_len]
        jmp     abort
    @@: test    rax, rax
        je      succ
        add     rax, rsi
        mov     r13, rax                ; keep as finish line for inner loop
    next_dirent:
        mov     rbx, rsi                ; base of current entry
        add     rsi, 16                 ; cf. `man getdents` for 64-bit struct
        add     bx, word[rsi]           ; now points to next entry
        mov     r12, rbx                ; save before syscall
        add     rsi, 3                  ; now points to the entry's file name
        mov     rdi, rsi                ; save for count comparison
    @@: lodsb                           ; find zero terminator
        test    al, al                  ; may not work with wide characters
        jnz     @b                      ; *should* always terminate in Linux
        mov     eax, sys_write
        sub     esi, edi                ; calculate count
        mov     edx, esi                ; count
        mov     esi, edi                ; buf
        mov     edi, stdout             ; fd
        syscall
        mov     eax, sys_write
        mov     esi, nl
        mov     edx, 1
        mov     edi, 1
        syscall
        cmp     r12, r13
        jge     next_getdents
        mov     rsi, r12                ; now points to next dirent
        jmp     next_dirent
  succ: mov     eax, sys_exit
        xor     edi, edi                ; successful return
        syscall
abort:
        mov     r12, rdi
        mov     edi, stderr
        mov     eax, sys_write
        syscall
        mov     rdi, r12
        mov     eax, sys_exit
        syscall
buf     rb       buf_len
    


It is actually crazy to see, how much time I needed for implementing this, but so nice to see it working in the end with just 393 bytes executable size. This may need string formatting (see the single-byte sys_write) and maybe sorting before I go on drawing little rectangles with console codes. I yet have to think about how to make scrolling work.

If there is anything to make the code simpler or better readable, please let me know.
Post 30 Jan 2023, 01:16
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 19076
Location: In your JS exploiting you and your system
revolution 30 Jan 2023, 07:59
Hans-Joachim Rudolph wrote:
If there is anything to make the code simpler or better readable, please let me know.
The code is a good example.

You can define structures with struc to remove some of the magic values.
Code:
        add     rsi, 16  ; cf. `man getdents` for 64-bit struct  <---  can use a structure member    
Code:
;...
        mov     edi, stdout             ; fd
        syscall
        mov     eax, sys_write
        mov     esi, nl
        mov     edx, 1
        mov     edi, 1 ; <--- isn't this also stdout?
        syscall
;...    
Post 30 Jan 2023, 07:59
View user's profile Send private message Visit poster's website Reply with quote
FlierMate11



Joined: 13 Oct 2022
Posts: 44
FlierMate11 30 Jan 2023, 08:01
This is a cool program, it works nicely in Linux.

But as you said, a list sorting would be better. Hope to see you progress further!


Description: Output
Filesize: 128.58 KB
Viewed: 52 Time(s)

Screenshot 2023-01-30 155843.png


Post 30 Jan 2023, 08:01
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8099
Location: Kraków, Poland
Tomasz Grysztar 30 Jan 2023, 12:59
Hans-Joachim Rudolph wrote:
Code:
        movzx   edx, byte[err_open_len]    
The "byte" override is not really necessary here, "err_open_len" is already defined as DB label, so fasm infers the size from that.

You would need to use size override if the declared size of labeled data was different from the size you wanted to use in the instruction.
Code:
        add     bx, word[rsi]           ; now points to next entry    
Similarly, there is no need to explicitly name the size here, because it is implied by the first argument. The size would be required only when there was no way to guess the correct size from the other operand or from the label meta-information, as in:
Code:
        add     word[rsi], 1    

Of course you may want to keep size annotations for reasons like readability - it might be important to stress out that a given instruction must operate on a byte. But this also has its drawbacks - if for any reason you change the data definition, for example from DB to DW, the instruction without size override would automatically adapt, while the instruction that forces size is going to keep using the original one and you may then end up with a hidden bug. It all depends on the circumstances, though.

Also, for numeric equates I would generally recommend = instead of EQU - the latter is a plain substitution of text at the preprocessing stage, and it may lead to unexpected results if you start using it with expressions. Although for simple numbers, like used here, it is completely safe.
Post 30 Jan 2023, 12:59
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2023, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.