Ich bin auf einen minimalen HTTP-Server gestoßen, der ohne libc geschrieben ist: https://github.com/Francesco149/nolibc-httpd
Ich kann sehen, dass grundlegende String-Handling-Funktionen definiert sind, was zu dem führt write
Systemaufruf:
#define fprint(fd, s) write(fd, s, strlen(s))
#define fprintn(fd, s, n) write(fd, s, n)
#define fprintl(fd, s) fprintn(fd, s, sizeof(s) - 1)
#define fprintln(fd, s) fprintl(fd, s "\n")
#define print(s) fprint(1, s)
#define printn(s, n) fprintn(1, s, n)
#define printl(s) fprintl(1, s)
#define println(s) fprintln(1, s)
Und die grundlegenden Systemaufrufe sind in der C-Datei deklariert:
size_t read(int fd, void *buf, size_t nbyte);
ssize_t write(int fd, const void *buf, size_t nbyte);
int open(const char *path, int flags);
int close(int fd);
int socket(int domain, int type, int protocol);
int accept(int socket, sockaddr_in_t *restrict address,
socklen_t *restrict address_len);
int shutdown(int socket, int how);
int bind(int socket, const sockaddr_in_t *address, socklen_t address_len);
int listen(int socket, int backlog);
int setsockopt(int socket, int level, int option_name, const void *option_value,
socklen_t option_len);
int fork();
void exit(int status);
Ich schätze also, die Magie passiert in start.S
was beinhaltet _start
und eine spezielle Methode zum Codieren von Systemaufrufen durch Erstellen globaler Labels, die durchfallen, und Ansammeln von Werten in r9, um Bytes zu sparen:
.intel_syntax noprefix
/* functions: rdi, rsi, rdx, rcx, r8, r9 */
/* syscalls: rdi, rsi, rdx, r10, r8, r9 */
/* ^^^ */
/* stack grows from a high address to a low address */
#define c(x, n) \
.global x; \
x:; \
add r9,n
c(exit, 3) /* 60 */
c(fork, 3) /* 57 */
c(setsockopt, 4) /* 54 */
c(listen, 1) /* 50 */
c(bind, 1) /* 49 */
c(shutdown, 5) /* 48 */
c(accept, 2) /* 43 */
c(socket, 38) /* 41 */
c(close, 1) /* 03 */
c(open, 1) /* 02 */
c(write, 1) /* 01 */
.global read /* 00 */
read:
mov r10,rcx
mov rax,r9
xor r9,r9
syscall
ret
.global _start
_start:
xor rbp,rbp
xor r9,r9
pop rdi /* argc */
mov rsi,rsp /* argv */
call main
call exit
Ist dieses Verständnis richtig? GCC verwendet die in definierten Symbole start.S
für die Systemaufrufe, dann startet das Programm in _start
und ruft main
aus der C-Datei?
Auch wie funktioniert das trennen httpd.asm
benutzerdefinierte binäre Arbeit? Nur eine handoptimierte Bestückung, die C-Quelle kombiniert und die Bestückung startet?
Übrigens, mit
clang -Oz
, habe ich die .c + .S-Version auf 992 Bytes runterbekommen. Siehe oben in meiner Antwort.– Peter Cordes
30. März 2021 um 4:59 Uhr