summaryrefslogtreecommitdiff
path: root/lib/thread_finish.asm
diff options
context:
space:
mode:
authorPrefetch2023-07-24 16:23:27 +0200
committerPrefetch2023-07-24 16:23:27 +0200
commita211da8cfe9b0565881537cc81b09ae55c722111 (patch)
tree90c05adac663125fc4f0604edb01431c2dcbc9af /lib/thread_finish.asm
parent7231a21b00028a52d7938131bfeca4d663d09071 (diff)
Rename lib/ to src/ (better for Tab-completion)
Diffstat (limited to 'lib/thread_finish.asm')
-rw-r--r--lib/thread_finish.asm147
1 files changed, 0 insertions, 147 deletions
diff --git a/lib/thread_finish.asm b/lib/thread_finish.asm
deleted file mode 100644
index 860b0a4..0000000
--- a/lib/thread_finish.asm
+++ /dev/null
@@ -1,147 +0,0 @@
-; Under MIT license, see /LICENSE.txt
-
-
-; Cheat sheet for Linux' x86_64 calling convention:
-;
-; - free to overwrite (caller should save them):
-; rax, rcx, rdx, rsi, rdi, r8-r11, xmm0-xmm15
-; - caller expects be kept (callee should save them):
-; rbx, rbp, r12-r15
-;
-; - for passing paramters to functions:
-; rdi, rsi, rdx, rcx, r8, r9, xmm0-xmm7
-; - for getting return values from functions:
-; rax, rdx, xmm0
-;
-; - for passing parameters to syscalls:
-; rax, rdi, rsi, rdx, r10, r8, r9
-; - for getting return values from syscalls:
-; rax, rdx
-; - overwritten by syscalls (all others preserved):
-; rcx, r11
-
-
-section .text
-
-
-; Relevant system call IDs
-%define SYS_MUNMAP 11
-%define SYS_FUTEX 202
-
-; Relevant operations for futex
-%define FUTEX_WAIT 0
-%define FUTEX_PRIVATE_FLAG 0x80
-
-
-%define STACK_SIZE 2097152 ; 2 MiB stack
-%define GUARD_PAGE 4096 ; 4 KiB guard page
-
-
-; Wait for thread to exit, save its return value, and clean up. Arguments:
-; rdi: struct{u32,u32}* = handle of the thread to wait for
-; rsi: void** = where to put void* returned by thread
-; Returns zero on success, or a standard error code.
-global linen_thread_finish
-linen_thread_finish:
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;; Check validity of arguments ;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ; Return EINVAL if rdi is NULL or otherwise invalid
- mov eax, -22 ; (EINVAL = -22)
-
- test rdi, rdi
- jz finish_return ; rdi is NULL
-
- ; rdi is nonzero, so let's just assume it's a valid pointer;
- ; if that assumption is wrong we'll get a segmentation fault.
- ; But we don't yet trust that [rdi] is a valid thread handle!
- ; To verify this we check the canary value stored at [rdi + 4].
- mov ecx, [rdi + 4]
- cmp ecx, 0xDEADBEEF
- jnz finish_return
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;; Wait until thread is finished ;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ; We'll clobber rsi if we need to set up a futex call
- mov r8, rsi
-
- finish_retry:
- ; When spawning, we set CLONE_CHILD_SETTID and CLONE_CHILD_CLEARTID:
- ; [rdi] contains the child thread's TID, and will get automatically
- ; cleared (to 0) when the child exits; this is what we'll watch for.
-
- ; Atomically check whether the target thread is still running.
- ; if ([rdi] == 0) { goto finish_success; } else { eax = [rdi]; }
- xor eax, eax
- lock cmpxchg [rdi], eax
- jz finish_success
-
- ; The thread is still busy, so block until it's done.
- ; The futex system call waits until the dword at an
- ; address (rdi) deviates from an expected value (eax).
- ; See: man 2 futex
-
- ; futex: rdi = uaddr: address of the dword to watch
- ; futex: rsi = futex_op: which futex operation we want:
- ; - FUTEX_WAIT: block until the value at [rdi] changes
- ; - FUTEX_PRIVATE_FLAG: FIXME waits forever, I don't understand why
- mov esi, FUTEX_WAIT
- ; futex: rdx = val: the expected value at [rdi] before it changes
- mov edx, eax
- ; futex: r10 = timeout: in case we had a deadline (we don't)
- xor r10, r10
- ; futex: r8 = uaddr2: ignored when FUTEX_WAIT is used
- ; futex: r9 = val3: ignored when FUTEX_WAIT is used
- ; futex: rax = system call ID
- mov eax, SYS_FUTEX
- ; futex: rax = futex(rdi, rsi, rdx, r10, (r8), (r9))
- syscall
-
- ; Sometimes the thread exits after the "lock cmpxchg" instruction
- ; but before the futex call. In that case, futex returns EAGAIN.
- cmp rax, -11 ; (EAGAIN = -11)
- je finish_retry
-
- ; Any other nonzero return value means failure
- test rax, rax
- jnz finish_return
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;; Clean up after thread's exit ;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- finish_success:
- ; The thread left its function return value on the stack, read it
- mov rdx, [rdi - 8]
-
- ; The munmap system call destroys mappings created by mmap.
- ; In this case that means deallocating the stack buffer.
- ; See: man 2 munmap
-
- ; munmap: rdi = addr: lowest address of region to unmap
- ; Our rdi is near the buffer's top, so we must subtract
- sub rdi, (STACK_SIZE + GUARD_PAGE - 8)
- ; munmap: rsi = length: size of region starting from rdi
- mov esi, (STACK_SIZE + GUARD_PAGE)
- ; munmap: rax = system call ID
- mov eax, SYS_MUNMAP
- ; munmap: rax = munmap(rdi, rsi)
- syscall
-
- ; Check result of munmap: nonzero means failure
- test rax, rax
- jnz finish_return
-
- ; Check if caller gave a location (r8) to save the return value (rdx)
- test r8, r8
- jz finish_return ; caller doesn't care: gave NULL pointer
- mov [r8], rdx
- ; Note: if munmap failed, the buffer is still there, so we
- ; can safely return an error without losing the return value.
-
- finish_return:
- ret
-