홈으로 돌아가기

어셈블러 x86-64 GUI X11 fasmg

이 기사는 fasmg를 사용한 x86-64 어셈블러에서 X11 프로토콜로 최소 GUI를 생성하는 방법을 설명합니다. vararg printf, 시스템 호출 및 mmap을 위한 구조체 최적화 매크로가 논의됩니다. VSCode에서의 디버그/릴리스 빌드와 Cutter에서의 디버깅 세부 사항.

어셈블러에서 최소 GUI: X11 + fasmg 최적화
Advertisement 728x90

x86-64 어셈블리로 X11을 활용한 최소 GUI 구현: fasmg 최적화

저수준 코드를 작성하는 개발자들은 종종 실행 파일 크기를 최소화하면서도 기능을 유지하는 과제에 직면합니다. 이 글에서는 X11 프로토콜을 사용하여 x86-64 어셈블리로 최소 GUI를 만드는 방법을 살펴봅니다. 링커 없이 fasmg를 사용하여 컴팩트한 바이너리를 생성하고, 동적 메모리 할당을 위해 mmap을 활용하며, 시스템 호출을 단순화하기 위해 매크로를 사용합니다.

목표는 텍스트가 포함된 GUI 창을 원래 구현보다 더 작게 만드는 것입니다. 중급 이상의 개발자를 위한 코드 가독성에 초점을 맞춥니다: X11 요청 구조, ABI 호환성, 데이터 복사 최적화.

개발 환경 설정

VSCode에서 릴리스 및 디버그 모드 작업으로 빌드합니다. 릴리스: DEBUG=0, 디버그: DEBUG=1 및 DWARF 심볼 생성.

Google AdInline article slot
{
    "label": "릴리스 실행 파일 생성",
    "type": "shell",
    "command": "chmod",
    "args": ["+x", "${fileBasenameNoExtension}"],
    "dependsOn": "fasmg로 릴리스 빌드"
}

Cutter에서 디버깅을 위한 launch.json:

{
    "name": "Cutter 실행",
    "type": "node",
    "request": "launch",
    "preLaunchTask": "fasmg로 디버그 빌드",
    "runtimeExecutable": "Cutter.AppImage",
    "runtimeArgs": [
        "${fileDirname}/${fileBasenameNoExtension}",
        "--analysis", "2",
        "--arch", "x86",
        "--bits", "64"
    ]
}

디버그 빌드(.asm 파일)는 Ctrl+Shift+T를 누르세요.

디버깅 및 로깅을 위한 매크로

디버그 모드에서만 libc를 동적으로 링크:

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

%d, %f, %e, %g, %a를 지원하는 가변 인수 printf 매크로. COUNT_ARGS_TO는 인수를 세고, GET_VARARG_TO는 인덱스로 인수를 검색합니다.

fasm 가상 메모리를 통해 float/double을 IEEE-754로 변환:

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

주요 VARG_FUNC 매크로는 형식 문자열을 파싱하고, System V ABI(xmm0-xmm7는 부동소수점, rsi/rdx/...는 정수)에 따라 레지스터에 인수를 분배합니다. DEBUG_MSG는 로그를 위한 래퍼입니다.

Google AdInline article slot

X11 요청 구조

통합된 FULL_LAYOUT 구조는 모든 필요한 요청을 포함합니다:

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

필드는 복사 코드를 최소화하도록 정렬됩니다. 레이아웃은 r12, 소켓은 r13+에 위치합니다.

범용 시스템 호출 매크로

MAKE_SYSCALL은 ptr [c], [c], 0 지원과 함께 레지스터(rdi, rsi, rdx, r10, r8, r9) 설정을 자동화합니다:

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
        ; rsi, rdx, r10...에 대해 유사하게 처리
    end iterate
    ; syscall scall
end macro

크기 및 성능 최적화

  • 정적 데이터 대신 mmap 사용: 모든 X11 구조를 위한 4KB 페이지.
  • 복사를 위한 movsb: 필드 그룹화로 호출 최소화.
  • 상수를 위한 레지스터: r12-r15는 변경되지 않음.
  • 바이너리의 기본값: 매크로는 사용된 필드만 생성.
  • 고정 폰트: X11이 자동으로 "fixed" 선택.

fasmg의 장점: 컴팩트한 코드, 매크로, 링커 불필요.

핵심 요약

  • X11 요청 구조는 최적의 movsb 복사를 위해 정렬됩니다.
  • 가상 메모리를 통한 IEEE-754 변환을 포함한 가변 인수 printf 매크로.
  • 범용 시스템 호출 매크로로 상용구 코드 70% 감소.
  • mmap 4KB로 바이너리의 정적 데이터 없이 모든 구조 처리.
  • VSCode + Cutter에서 디버그/릴리스 빌드로 리버스 엔지니어링.

— Editorial Team

Advertisement 728x90

다음 읽기