404 not found

Actually, there was just some arbitrary error. Maybe

" .ascii "your request was bad.

" err404_end: .text sys3 $__NR_write, sock_reg, $err404, $(err404_end - err404) sys1 $__NR_close, sock_reg .endm .macro exit_successfully ## For use in child processes. sys1 $__NR_exit, $0 .endm ### Low-level system interface: # We can’t #include because it tries to define # a bunch of C structures. So here are the things we use from # it. .equiv SOCK_STREAM, 1 # /usr/include/i386-linux-gnu/bits/socket.h:42 .equiv PF_INET, 2 # /usr/include/i386-linux-gnu/bits/socket.h:78 .equiv AF_INET, PF_INET # /usr/include/i386-linux-gnu/bits/socket.h:121 I think .equiv SOL_SOCKET, 1 # /usr/include/asm-generic/socket.h:7 .equiv SO_REUSEADDR, 2 # /usr/include/asm-generic/socket.h:10 .equiv O_RDONLY, 0 # /usr/include/asm-generic/fcntl.h:19 .equiv sockaddr_in_size, 16 # ??? I got it from fucking strace. ## System call numbers, which I originally mostly found in dietlibc .equiv __NR_exit, 1 # linux/arch/x86/include/asm/unistd_32.h:9 .equiv __NR_fork, 2 .equiv __NR_read, 3 .equiv __NR_write, 4 .equiv __NR_open, 5 .equiv __NR_close, 6 .equiv __NR_waitpid, 7 .equiv __NR_alarm, 27 .equiv __NR_socketcall, 102 ## socketcall takes these identifiers for which call you want: .equiv SYS_SOCKET, 1 # linux/include/linux/net.h:26 .equiv SYS_BIND, 2 .equiv SYS_LISTEN, 4 .equiv SYS_ACCEPT, 5 .equiv SYS_SETSOCKOPT, 14 ## waitpid takes this option to tell it to reap zombie ## children without waiting, which makes you think that maybe ## they could have found a better fucking name for the system ## call, but it's too late now. .equiv WNOHANG, 1 # diet/sys/wait.h:9 ## Generally we don't care about specific errno values (we ## leave that up to the user), except when waitpid gives us ## ECHILD. .equiv ECHILD, 10 # I got this from strace. # Another arbitrary parameter: .equiv bufsiz, 1024 # plenty big enough for an HTTP GET request ## There are also a lot of variables. Because we don’t have ## any recursion (or for that matter any functions), we can ## simply allocate them all statically, which simplifies ## things a lot. But we still have to allocate them, which is ## simpler to do with a macro like this: .macro my var, contents:vararg .data \var: \contents .previous .endm ## But some variables don't need to be in the .data segment, ## because they're initially zero. We can put them in .bss ## and get a smaller executable. .macro var var .bss \var: .int 0 .previous .endm # The other thing we spend a lot of code doing is handling # errors. When we bail out with an error, we first print the # string pointed to by the variable `errs`: var errs # So here’s a macro to set that. .macro errloc_is description my errloc_\@, .asciz "\description" movl $errloc_\@, (errs) .endm ### System calls with different numbers of arguments. ### `be x, y` is a macro that does `mov x, y` or equivalent. .macro sys3 call_no, a, b, c be \c, %edx sys2 \call_no, \a, \b .endm .macro sys2 call_no, a, b be \b, %ecx sys1 \call_no, \a .endm .macro sys1 call_no, a be \a, %ebx sys0 \call_no .endm .macro sys0 call_no be \call_no, %eax ## There's a new, faster instruction for system calls, but I ## don't know how to use it yet. int $0x80 .endm ### Set dest = src. Usually just `mov src, dest`, but sometimes ### there's a shorter way. .macro be src, dest .ifnc \src,\dest .ifc \src,$0 xor \dest,\dest .else .ifc \src,$1 xor \dest,\dest inc \dest .else .ifc \src,$2 xor \dest,\dest inc \dest inc \dest .else mov \src, \dest .endif .endif .endif .endif .endm ### This macro leaves the number of nonzero bytes in the asciz string at ### addr in %ecx, clobbering %eax and %edi in the process. .macro do_strlen addr mov \addr, %edi xor %eax, %eax lea -1(%eax), %ecx # save a byte over xor/dec ## cld # not necessary; see comment on other cld repne scasb not %ecx dec %ecx .endm ### At long last, here is the macro invocation that is our program: http_server ### A note about dietlibc: ### I was building this with dietlibc for a while, which was easier, ### but I eventually opted out of that to reduce overhead even ### further. The Debian default dietlibc configuration adds around a ### kilobyte of overhead, which is totally reasonable. But Fefe ### points out that you can configure dietlibc to eliminate most of ### its overhead! He built a binary is 2080 bytes when the default ### one was 3140 bytes; note that this is only 112 bytes more than I ### got it down to by removing dietlibc. You just have to comment out ### these things in dietfeatures.h: ### - WANT_TLS ### - WANT_THREADSAFE ### - WANT_SYSENTER (I think I'm already not using sysenter) ### - WANT_GNU_STARTUP_BLOAT ### - WANT_VALGRIND_SUPPORT ### - WANT_SSP ### Problems with `be` macro: ### * .equiv defines a symbol, not a textual replacement like #define. ### Consequently sys1 $__NR_exit is not the same as sys1 $1, even ### though __NR_exit is 1, and so we end up with an unreasonably ### long instruction sequence: ## 080484bc : ## 80484bc: 31 db xor %ebx,%ebx ## 80484be: b8 01 00 00 00 mov $0x1,%eax ## 80484c3: cd 80 int $0x80 ### * In many cases we know what the value in some other register is, ### but we don't use it. For example: ## 804845b: ba 04 00 00 00 mov $0x4,%edx ## 8048460: b9 b0 96 04 08 mov $0x80496b0,%ecx ## 8048465: 89 eb mov %ebp,%ebx ## 8048467: b8 04 00 00 00 mov $0x4,%eax ## 804846c: cd 80 int $0x80 ### The second 04 00 00 00 is totally gratuitous; moving %edx to ### %eax would have worked fine, and that would be two bytes. ### Hmm, can I maybe shrink things a bit more by using writev() or the ### manual equivalent? In report_sys_error and send_header there are ### things that use them.