Skip to content

Instantly share code, notes, and snippets.

@rrampage
Last active June 4, 2025 14:43
Show Gist options
  • Save rrampage/d31e75647a77badb3586ebae1e414cb6 to your computer and use it in GitHub Desktop.
Save rrampage/d31e75647a77badb3586ebae1e414cb6 to your computer and use it in GitHub Desktop.
HTTP server in ARM assembly
.equ SYS_READ, 63
.equ SYS_WRITE, 64
.equ SYS_EXIT, 93
.equ SYS_SOCKET, 198
.equ SYS_BIND, 200
.equ SYS_LISTEN, 201
.equ SYS_ACCEPT, 202
.equ SYS_CLOSE, 57
.equ SYS_OPENAT, 56
.equ SYS_SENDFILE, 71
.equ SYS_SETSOCKOPT, 208
.equ SYS_NEWFSTATAT, 79
.equ SYS_FSTAT, 80
// File descriptors
.equ STDIN, 0
.equ STDOUT, 1
.equ SOCKLEN, 16
.equ SOMAXCONN, 128
.equ PORT, 8000
.equ BUF_SIZE, 4096
.equ FDCWD, -100
.equ SOL_SOCKET, 1
.equ SO_REUSEADDR, 2
.data
/* Data segment: define our message string and calculate its length. */
msg:
.ascii "HTTP/1.0 200 OK\r\nServer: armlet\r\nContent-type: text/html\r\n\r\n";
len = . - msg
file_name:
.asciz "./out/index.html";
sock_addr:
.string "\x02\x00\x1f\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
sock_len: .word SOCKLEN
so_reuse: .word 1
cork: .word 1
statbuf:
.space 128 // struct stat is 128 bytes on AArch64
offset:
.quad 0
.word 0 // padding to align to 64-bit (optional)
.bss
buffer: .space BUF_SIZE
.text
/* Our application's entry point. */
.globl _start
_start:
/*openat(dfd, path, flags, mode)*/
mov x0, #FDCWD
ldr x1, =file_name
mov x2, #0
mov x3, #400
mov x8, #SYS_OPENAT
svc #0
cmp x0, #0
blt exit_section
mov x13, x0
/*fstat(fd, &statbuf)*/
mov x0, x13
adrp x1, statbuf
add x1, x1, :lo12:statbuf
mov x8, #SYS_FSTAT
svc #0
cmp x0, #0
bne exit_section
ldr x14, [x1, #0x30] // Move size of file to x14
/* syscall (domain: u32, socket_type: u32, protocol: u32) */
mov x0, #2 /* domain := AF_INET */
mov x1, #1 /* socket_type := SOCK_STREAM */
mov x2, #6 /* protocol := TCP */
mov w8, #SYS_SOCKET
svc #0
/*sock in x0*/
mov x9, x0 /*mov socket_fd to x9*/
/*setsockopt: setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, [1], 16)*/
mov x0, x9
mov x1, #SOL_SOCKET
mov x2, #SO_REUSEADDR
adrp x3, so_reuse
add x3, x3, :lo12:so_reuse
mov x4, #SOCKLEN
mov w8, #SYS_SETSOCKOPT
svc #0
/*Check if return is 0 otherwise exit*/
cmp x0, #0
bne exit_section
/* bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize */
mov x0, x9
ldr x1, =sock_addr
mov x2, #SOCKLEN
mov w8, #SYS_BIND
svc #0
/*Check if return is 0 otherwise exit*/
cmp x0, #0
bne exit_section
/* listen(sock, linux.SOMAXCONN) */
mov x0, x9
mov x1, #SOMAXCONN
mov w8, #SYS_LISTEN
svc #0
/*Check if return is 0 otherwise exit*/
cmp x0, #0
bne exit_section
loop:
/* linux.accept(sock, @ptrCast(&address), &sock_len)*/
mov x0, x9
ldr x1, =sock_addr
adrp x2, sock_len
add x2, x2, :lo12:sock_len
mov x8, #SYS_ACCEPT
svc #0
cmp x0, #0
blt loop
mov x10, x0 /*mov new_sock_fd to x10 */
/*mov x0, x10 */ // new_sock_fd
ldr x1, =buffer // address of buffer to read into
mov x2, #BUF_SIZE // max bytes to read
mov x8, #SYS_READ
svc #0
cmp x0, #0
blt loop
mov x11, x0 /*mov nread to x11 */
/* syscall write(int fd, const void *buf, size_t count) */
mov x0, #1 /* fd := STDOUT_FILENO */
ldr x1, =buffer
mov x2, x11 /* count := len */
mov w8, #SYS_WRITE /* write is syscall #64 */
svc #0 /* invoke syscall */
/* syscall write(int fd, const void *buf, size_t count) */
mov x0, x10 /* fd := new_sock_fd */
/* Load into reg x1 address of msg */
ldr x1, =msg
ldr x2, =len /* count := len */
mov w8, #SYS_WRITE /* write is syscall #64 */
svc #0 /* invoke syscall */
cmp x0, #0
blt loop
/* nwrite = linux.sendfile(new_sock, fd, &offset, @intCast(stat.size));*/
mov x0, x10
mov x1, x13
adrp x2, offset
add x2, x2, :lo12:offset
mov x3, x14
mov w8, #SYS_SENDFILE
svc #0
cmp x0, #0
blt loop
/*linux.close(new_sock)*/
mov x0, x10
mov w8, #SYS_CLOSE
svc #0
b loop
exit_section:
/* syscall exit(int status) */
mov x0, #0 /* status := 0 */
mov w8, #93 /* exit is syscall #93 */
svc #0 /* invoke syscall */
const std = @import("std");
const linux = std.os.linux;
const port = 8000;
const BUF_SIZE = 4096;
var buffer = [_:0]u8{0} ** BUF_SIZE;
// const resp = "HTTP/1.0 200 OK\r\nServer: armlet\r\nContent-type: text/html\r\n\r\n<html>hello, world</html>\r\n";
const resp = "HTTP/1.0 200 OK\r\nServer: armlet\r\nContent-type: text/html\r\n\r\n";
var so_reuse = [_]u8{1};
pub fn main() !void {
var address : linux.sockaddr.in = .{
.port = std.mem.nativeToBig(u16, port),
.addr = 0x00000000
};
// var address = try std.net.Address.parseIp("127.0.0.1", port);
const sock : i32 = @intCast(linux.socket(linux.AF.INET, linux.SOCK.STREAM, linux.IPPROTO.TCP));
var sock_len : u32 = @sizeOf(linux.sockaddr.in);
const mode: linux.O = .{ .DIRECTORY = false, .ACCMODE = .RDONLY };
// const mode_int: u32 = @bitCast(mode);
// std.debug.print("SOCK_LEN: {} PORT: {}, mode: {d}, statSize: {}", .{sock_len, std.mem.nativeToBig(u16, port), mode_int, @sizeOf(linux.Stat)});
_ = linux.setsockopt(sock, linux.SOL.SOCKET, linux.SO.REUSEADDR, &so_reuse, 16);
const st: i32 = @intCast(linux.bind(sock, @ptrCast(&address), sock_len));
if (st != 0) {
_ = linux.write(1,"Bind Error\n", 11);
return;
}
// linux.sendfile(outfd: i32, infd: i32, offset: ?*i64, count: usize)
if (linux.listen(sock, linux.SOMAXCONN) != 0) {
_ = linux.write(1, "Listen Error\n", 13);
return;
}
var stat: linux.Stat = undefined;
const fd: i32 = @intCast(linux.openat(linux.AT.FDCWD, "./out/index.html", mode, 400));
_ = linux.fstat(fd, &stat);
while (true) {
const new_sock: i32 = @intCast(linux.accept(sock, @ptrCast(&address), &sock_len));
if (new_sock < 0) {
_ = linux.write(1, "Accept Error\n", 13);
continue;
}
const nread = linux.read(new_sock, &buffer, buffer.len);
if (nread < 0) {
_ = linux.write(1, "Read Error\n", 11);
continue;
}
_ = linux.write(1, &buffer, nread);
var offset: i64 = 0;
var cork = [_]u8{1};
_ = linux.setsockopt(new_sock, linux.IPPROTO.TCP, linux.TCP.CORK, &cork, 16);
var nwrite = linux.write(new_sock, resp, resp.len);
if (nwrite < 0) {
_ = linux.write(1, "Write Error\n", 12);
continue;
}
nwrite = linux.sendfile(new_sock, fd, &offset, @intCast(stat.size));
if (nwrite < 0) {
_ = linux.write(1, "Sendfile Error\n", 15);
continue;
}
_ = linux.close(new_sock);
}
}
<html>
<script>
alert("hello, world");
</script>
<body>
<b>hello, world</b>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment