Powrót do strony głównej

Asembler x86-64 GUI X11 fasmg

Artykuł opisuje tworzenie minimalnego GUI w asemblerze x86-64 z protokołem X11 przy użyciu fasmg. Omówiono makra dla vararg printf, wywołań systemowych i optymalizację struktur dla mmap. Szczegółowo o budowie debug/release w VSCode i debugowaniu w Cutter.

Minimalny GUI w asemblerze: X11 + optymalizacja fasmg
Advertisement 728x90

Minimalny GUI w asemblerze x86-64 z X11: optymalizacja dla fasmg

Programiści piszący kod niskiego poziomu często mierzą się z zadaniem minimalizacji rozmiaru plików wykonywalnych przy zachowaniu funkcjonalności. W tym artykule omawiamy tworzenie minimalnego GUI w asemblerze x86-64 z wykorzystaniem protokołu X11. Używamy fasmg do generowania kompaktowego pliku binarnego bez linkera, mmap do dynamicznej alokacji pamięci oraz makr do uproszczenia wywołań systemowych.

Celem jest okno GUI z tekstem o rozmiarze mniejszym niż w oryginalnych implementacjach. Skupiamy się na czytelności kodu dla programistów średniego/zaawansowanego poziomu: struktury żądań X11, zgodność ABI, optymalizacja kopiowania danych.

Przygotowanie środowiska programistycznego

Kompilacja w VSCode z zadaniami dla trybów release i debug. Release: DEBUG=0, debug: DEBUG=1 z generowaniem symboli DWARF.

Google AdInline article slot
{
    "label": "Utwórz plik wykonywalny release",
    "type": "shell",
    "command": "chmod",
    "args": ["+x", "${fileBasenameNoExtension}"],
    "dependsOn": "Kompilacja release z fasmg"
}

Debugowanie w Cutter z launch.json:

{
    "name": "Uruchom Cutter",
    "type": "node",
    "request": "launch",
    "preLaunchTask": "Kompilacja debug z fasmg",
    "runtimeExecutable": "Cutter.AppImage",
    "runtimeArgs": [
        "${fileDirname}/${fileBasenameNoExtension}",
        "--analysis", "2",
        "--arch", "x86",
        "--bits", "64"
    ]
}

Skrót klawiszowy Ctrl+Shift+T dla kompilacji debug (pliki .asm).

Makra do debugowania i logowania

Dołączamy libc dynamicznie tylko w trybie debug:

Google AdInline article slot
include 'dynamic/import64.inc'
if DEBUG
    interpreter '/lib64/ld-linux-x86-64.so.2'
    needed 'libc.so.6'
    import printf
end if

Makra dla vararg printf z obsługą %d, %f, %e, %g, %a. COUNT_ARGS_TO zlicza argumenty, GET_VARARG_TO pobiera według indeksu.

Konwersja float/double do IEEE-754 przez wirtualną pamięć fasm:

macro MOVQ_IMM reg, val
    local bits
    virtual at 0
        dq val
        load bits qword from 0
    end virtual
    mov rax, bits
    movq reg, rax
end macro

Główne makro VARG_FUNC parsuje ciąg formatujący, rozdziela argumenty do rejestrów zgodnie z System V ABI (xmm0-xmm7 dla float, rsi/rdx/... dla integer). DEBUG_MSG — nakładka na logi.

Google AdInline article slot

Struktury żądań X11

Jednolita struktura FULL_LAYOUT zawiera wszystkie potrzebne żądania:

struct FULL_LAYOUT
    ridBase             dd  0
    ridMask             dd  0
    windowRootId        dd  0
    rootVisualId        dd  0
    gcId                dd  0
    exposed             db  0
    create_window_req   X11_CREATE_WINDOW
    draw_string_req     STRING_TO_DRAW
    handshake           X11_HANDSHAKE
    gc_request          X11_CREATE_GC
    addr_un             SOCKADDR_UN
    map_window_req      X11_MAP_WINDOW
    union
        free    rb     HEAP_SIZE - ($ - create_window_req)
        setup   X11_SETUP
        pollfd  POLLFD
    endu
ends

Pola uporządkowane dla minimalizacji kodu kopiowania. Układ w r12, gniazda w r13+.

Uniwersalne makro wywołań systemowych

MAKE_SYSCALL automatyzuje ustawianie rejestrów (rdi, rsi, rdx, r10, r8, r9) z obsługą ptr [c], [c], 0:

macro MAKE_SYSCALL scall, args&
    iterate arg, args
        if % = 1
            match =ptr [c], arg
                lea    rdi, [c]
            else match [c], arg
                mov     rdi, [c]
            else match =0, arg
                xor    rdi, rdi
            else
                mov    rdi, arg
            end match
        ; analogicznie dla rsi, rdx, r10...
    end iterate
    ; syscall scall
end macro

Optymalizacje rozmiaru i wydajności

  • mmap zamiast danych statycznych: strona 4KB dla wszystkich struktur X11.
  • movsb do kopiowania: grupowanie pól minimalizuje wywołania.
  • Rejestry dla stałych: r12-r15 niezmienne.
  • Wartości domyślne w pliku binarnym: makra generują tylko używane pola.
  • Stała czcionka: X11 automatycznie wybiera "fixed".

Zalety fasmg: kompaktowy kod, makra, brak linkera.

Co jest ważne

  • Struktury żądań X11 uporządkowane dla optymalnego kopiowania movsb.
  • Makro vararg printf z konwersją IEEE-754 przez wirtualną pamięć.
  • Uniwersalne makro syscall redukuje boilerplate o 70%.
  • mmap 4KB obejmuje wszystkie struktury bez danych statycznych w pliku binarnym.
  • Kompilacje debug/release w VSCode + Cutter do inżynierii wstecznej.

— Editorial Team

Advertisement 728x90

Czytaj dalej