Last active
June 4, 2025 14:43
-
-
Save rrampage/d31e75647a77badb3586ebae1e414cb6 to your computer and use it in GitHub Desktop.
HTTP server in ARM assembly
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.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 */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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