Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/backup-asm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: backup-asm

on:
push:
paths:
- 'coolify-backup-asm/**'
- '.github/workflows/backup-asm.yml'
pull_request:
paths:
- 'coolify-backup-asm/**'
- '.github/workflows/backup-asm.yml'

jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install NASM
run: sudo apt-get update && sudo apt-get install -y nasm
- name: Build and test backup ASM
run: make -C coolify-backup-asm clean test
2 changes: 2 additions & 0 deletions coolify-backup-asm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.o
/coolify-backup-asm
32 changes: 32 additions & 0 deletions coolify-backup-asm/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
NASM ?= nasm
NASMFLAGS ?= -f elf64 -g -F dwarf -I.
LD ?= ld
LDFLAGS ?= -nostdlib -static --no-dynamic-linker -z noexecstack
TARGET := coolify-backup-asm
SRCS := $(wildcard src/*.asm)
OBJS := $(SRCS:.asm=.o)
SIZE_LIMIT := 65536

$(TARGET): $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^
@size=$$(stat -c%s $@); \
if [ $$size -gt $(SIZE_LIMIT) ]; then \
echo "ERROR: Binary size $$size exceeds 64KB limit ($(SIZE_LIMIT) bytes)"; \
rm -f $@; \
exit 1; \
fi; \
echo "Binary size: $$size bytes (limit: $(SIZE_LIMIT))"

%.o: %.asm include/*.inc
$(NASM) $(NASMFLAGS) -o $@ $<

test: $(TARGET)
@bash tests/test_runner.sh

objdump: $(TARGET)
@objdump -d $(TARGET)

clean:
rm -f $(OBJS) $(TARGET)

.PHONY: test clean objdump
21 changes: 21 additions & 0 deletions coolify-backup-asm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# coolify-backup-asm

Initial NASM/x86_64 foundation for issue #2, the bare-metal Coolify backup daemon.

This subtree intentionally has no libc, Rust, PHP, or OpenSSL dependency. It builds a static ELF via `nasm` + `ld`, performs direct Linux syscalls, and enforces the requested 64KB binary limit at link time.

Implemented in this PR:

- `main.asm` entry point with `--help`, `--version`, and `--self-test`
- `syscall.asm` wrappers for core Linux syscalls needed by upcoming pipeline modules
- `string.asm` primitives: `strlen`, `strcmp`, `memcpy`, `memset`, `atoi`, `itoa`
- `memory.asm` 64KB ring buffer and control block primitives
- Makefile size guard and shell test harness
- Documentation for calling convention and ring buffer layout

Build and test:

```bash
cd coolify-backup-asm
make clean test
```
18 changes: 18 additions & 0 deletions coolify-backup-asm/docs/CALLING_CONVENTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# coolify-backup-asm calling convention

Internal routines use the issue-specified register contract rather than libc or the System V function ABI where practical:

- `rax`: return value or syscall number
- `rbx`: ring buffer base pointer, preserved
- `rcx`: ring write position
- `rdx`: ring read position
- `rsi`: source pointer
- `rdi`: destination pointer / first argument
- `r8`: byte count / length
- `r9`-`r11`: scratch/options
- `r12`: stage context, preserved
- `r13`: error accumulator (`0` success, negative errno)
- `r14`: current file descriptor, preserved
- `r15`: timestamp/counter, preserved

All exported routines are 16-byte aligned and document inputs/outputs in comments. Linux syscalls are made directly with the kernel syscall ABI.
15 changes: 15 additions & 0 deletions coolify-backup-asm/docs/RING_BUFFER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Ring buffer protocol

The initial implementation reserves the full 64KB data buffer plus a 64-byte control block in `.bss`:

| Offset | Field |
| --- | --- |
| `0x00000..0x0ffff` | data buffer |
| `0x10000` | write position |
| `0x10008` | read position |
| `0x10010` | watermark |
| `0x10018` | flags |
| `0x10020` | total bytes written |
| `0x10028` | stage id |

This PR provides single-producer/single-consumer `ring_write` and `ring_read` primitives with 16-bit masking for wraparound. Later pipeline PRs can replace the simple position stores with the issue's `lock xadd` synchronization when multiple concurrent stages are introduced.
18 changes: 18 additions & 0 deletions coolify-backup-asm/include/constants.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
%define STDIN 0
%define STDOUT 1
%define STDERR 2
%define EXIT_OK 0
%define EXIT_USAGE 64
%define EXIT_SELFTEST 70
%define RING_SIZE 65536
%define RING_MASK 0xffff
%define CTRL_WRITE_POS 0x00
%define CTRL_READ_POS 0x08
%define CTRL_WATERMARK 0x10
%define CTRL_FLAGS 0x18
%define CTRL_TOTAL 0x20
%define CTRL_STAGE 0x28
%define CTRL_BYTES 64
%define FLAG_EOF 1
%define FLAG_ERROR 2
%define FLAG_FLUSH 4
10 changes: 10 additions & 0 deletions coolify-backup-asm/include/macros.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
%macro FUNC 1
global %1
align 16
%1:
%endmacro

%macro SYSCALL 1
mov rax, %1
syscall
%endmacro
35 changes: 35 additions & 0 deletions coolify-backup-asm/include/syscall_numbers.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
%define SYS_read 0
%define SYS_write 1
%define SYS_open 2
%define SYS_close 3
%define SYS_stat 4
%define SYS_fstat 5
%define SYS_lseek 8
%define SYS_mmap 9
%define SYS_munmap 11
%define SYS_pipe 22
%define SYS_dup2 33
%define SYS_clone 56
%define SYS_fork 57
%define SYS_execve 59
%define SYS_exit 60
%define SYS_wait4 61
%define SYS_kill 62
%define SYS_uname 63
%define SYS_fcntl 72
%define SYS_getcwd 79
%define SYS_chdir 80
%define SYS_rename 82
%define SYS_mkdir 83
%define SYS_unlink 87
%define SYS_gettimeofday 96
%define SYS_getpid 39
%define SYS_socket 41
%define SYS_connect 42
%define SYS_sendto 44
%define SYS_recvfrom 45
%define SYS_shutdown 48
%define SYS_exit_group 231
%define SYS_clock_gettime 228
%define SYS_pipe2 293
%define SYS_getrandom 318
138 changes: 138 additions & 0 deletions coolify-backup-asm/src/main.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
%include "include/constants.inc"
%include "include/syscall_numbers.inc"
%include "include/macros.inc"

extern sys_write, sys_exit
extern asm_strlen, asm_strcmp, asm_atoi, asm_itoa, asm_memcpy, asm_memset
extern ring_init, ring_write, ring_read

global _start

section .rodata
msg_help: db "coolify-backup-asm - zero-runtime backup daemon prototype",10,10,"Usage: coolify-backup-asm [--help|--version|--self-test]",10,10,"Initial PR implements direct syscall wrappers, string primitives, and the 64KB ring buffer foundation for the backup pipeline.",10,0
msg_version: db "coolify-backup-asm 0.1.0-asm",10,0
msg_ok: db "self-test: ok",10,0
msg_fail: db "self-test: failed",10,0
arg_help: db "--help",0
arg_version: db "--version",0
arg_selftest: db "--self-test",0
sample: db "ring-buffer-check",0
num12345: db "12345",0

section .bss
scratch: resb 128

section .text
_start:
mov rbp, rsp
mov rax, [rbp] ; argc
cmp rax, 2
jb .help
mov rdi, [rbp + 16] ; argv[1]
lea rsi, [rel arg_help]
call asm_strcmp
test rax, rax
je .help
mov rdi, [rbp + 16]
lea rsi, [rel arg_version]
call asm_strcmp
test rax, rax
je .version
mov rdi, [rbp + 16]
lea rsi, [rel arg_selftest]
call asm_strcmp
test rax, rax
je .selftest
jmp .help_usage
.help:
lea rdi, [rel msg_help]
call print_cstr
xor rdi, rdi
call sys_exit
.version:
lea rdi, [rel msg_version]
call print_cstr
xor rdi, rdi
call sys_exit
.help_usage:
lea rdi, [rel msg_help]
call print_cstr
mov rdi, EXIT_USAGE
call sys_exit
.selftest:
call run_selftest
test rax, rax
jne .bad
lea rdi, [rel msg_ok]
call print_cstr
xor rdi, rdi
call sys_exit
.bad:
lea rdi, [rel msg_fail]
call print_cstr
mov rdi, EXIT_SELFTEST
call sys_exit

print_cstr:
; in rdi=c string
push rdi
call asm_strlen
mov rdx, rax
pop rsi
mov rdi, STDOUT
call sys_write
ret

run_selftest:
; returns rax=0 success else 1
lea rdi, [rel sample]
call asm_strlen
cmp rax, 17
jne .fail
lea rdi, [rel sample]
lea rsi, [rel sample]
call asm_strcmp
test rax, rax
jne .fail
lea rdi, [rel num12345]
call asm_atoi
cmp rax, 12345
jne .fail
mov rdi, 67890
lea rsi, [rel scratch]
call asm_itoa
cmp rax, 5
jne .fail
lea rdi, [rel scratch]
lea rsi, [rel expect_67890]
call asm_strcmp
test rax, rax
jne .fail
call ring_init
lea rsi, [rel sample]
mov r8, 17
call ring_write
cmp rax, 17
jne .fail
lea rdi, [rel scratch]
xor esi, esi
mov r8, 32
call asm_memset
lea rdi, [rel scratch]
mov r8, 17
call ring_read
cmp rax, 17
jne .fail
lea rdi, [rel scratch]
lea rsi, [rel sample]
call asm_strcmp
test rax, rax
jne .fail
xor rax, rax
ret
.fail:
mov eax, 1
ret

section .rodata
expect_67890: db "67890",0
Loading