Initial import
This commit is contained in:
181
libc/runtime/cxaatexit.S
Normal file
181
libc/runtime/cxaatexit.S
Normal file
@@ -0,0 +1,181 @@
|
||||
/*-*- mode:asm; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ This program is free software; you can redistribute it and/or modify │
|
||||
│ it under the terms of the GNU General Public License as published by │
|
||||
│ the Free Software Foundation; version 2 of the License. │
|
||||
│ │
|
||||
│ This program is distributed in the hope that it will be useful, but │
|
||||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||||
│ General Public License for more details. │
|
||||
│ │
|
||||
│ You should have received a copy of the GNU General Public License │
|
||||
│ along with this program; if not, write to the Free Software │
|
||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/relocations.h"
|
||||
#include "libc/macros.h"
|
||||
.section .text.exit,"ax",@progbits
|
||||
.yoink __FILE__
|
||||
|
||||
/ Delegates to __cxa_atexit().
|
||||
/
|
||||
/ @param rdi callback typed void(*)(void) (nullable)
|
||||
/ @return 0 on success or nonzero if out of space
|
||||
atexit: xor %esi,%esi
|
||||
xor %edx,%edx
|
||||
/ 𝑠𝑙𝑖𝑑𝑒
|
||||
|
||||
/ Registers destructor to be called upon exit().
|
||||
/
|
||||
/ Destructors are called in reverse order. They won't be called
|
||||
/ if the program aborts or _exit() is called. Invocations of this
|
||||
/ function are usually generated by the C++ compiler.
|
||||
/
|
||||
/ @param rdi callback typed void(*)(T) (nullable)
|
||||
/ @param rsi callback arg typed T (nullable)
|
||||
/ @param rdx dso handle (nullable)
|
||||
/ @return 0 on success or nonzero if out of space
|
||||
/ @note folks have forked libc in past just to unbloat atexit()
|
||||
/ @see news.ycombinator.com/item?id=20228082
|
||||
/ @preinitsafe
|
||||
__cxa_atexit:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable
|
||||
push %r15
|
||||
ezlea g_cxa,cx
|
||||
mov %rcx,%r15
|
||||
mov (%r15),%rax # get state->block
|
||||
mov 8(%r15),%rcx # get state->offset
|
||||
test %rcx,%rcx
|
||||
jz 2f
|
||||
0: sub $24,%rcx
|
||||
mov %rdx,(%rax,%rcx) # set cb->dso
|
||||
mov %rsi,8(%rax,%rcx) # set cb->arg
|
||||
mov %rdi,16(%rax,%rcx) # set cb->fn
|
||||
mov %rcx,8(%r15) # set state->offset
|
||||
xor %eax,%eax # success
|
||||
1: pop %r15
|
||||
pop %rbp
|
||||
ret
|
||||
2: .weak calloc
|
||||
ezlea calloc,cx
|
||||
test %rcx,%rcx
|
||||
jz 1b # fail (no malloc)
|
||||
push %rax
|
||||
push %rdi
|
||||
push %rsi
|
||||
pushpop ATEXIT_MAX+1,%rdi
|
||||
pushpop 16,%rsi
|
||||
call *%rcx
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
pop %rcx # rax=new rcx=old
|
||||
test %rax,%rax
|
||||
jz 1b # fail (no memory)
|
||||
mov $ATEXIT_MAX*8*3,%r8
|
||||
mov %rax,(%r15) # set state->block
|
||||
mov %rcx,(%rax,%r8) # set block->next
|
||||
mov %r8,%rcx
|
||||
jmp 0b
|
||||
.endfn __cxa_atexit,globl
|
||||
.endfn atexit,globl
|
||||
|
||||
/ Triggers destructors.
|
||||
/
|
||||
/ This implementation supports DSO handles, but is optimized for
|
||||
/ cases when it's called only once by exit().
|
||||
/
|
||||
/ @param rdi is dso predicate or null to destroy all
|
||||
/ @see libc/exit.c
|
||||
__cxa_finalize:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable
|
||||
push %r14
|
||||
push %r13
|
||||
push %r12
|
||||
mov g_cxa(%rip),%rsi
|
||||
mov %rdi,%r14
|
||||
0: mov %rsi,%r12 # loop through blocks
|
||||
pushpop ATEXIT_MAX,%rcx
|
||||
1: lodsq #→ dso # loop through callbacks
|
||||
xchg %rax,%rdx
|
||||
lodsq #→ arg
|
||||
xchg %rax,%rdi
|
||||
lodsq #→ fn
|
||||
test %rax,%rax
|
||||
jz 2f # ignore empty slots
|
||||
test %r14,%r14
|
||||
jmp 5f # null predicate match all
|
||||
cmp %r14,%rdx
|
||||
jne 2f # predicate mismatch
|
||||
5: push %rsi
|
||||
push %rcx
|
||||
push %rcx
|
||||
call *%rax
|
||||
pop %rcx
|
||||
pop %rcx
|
||||
pop %rsi
|
||||
xor %eax,%eax # clear slot (never reused)
|
||||
mov %rax,-8(%rsi)
|
||||
2: loop 1b
|
||||
lodsq # get next block ptr
|
||||
test %rax,%rax # don't free static block no. 1
|
||||
jz 3f # which always has next == NULL
|
||||
test %r14,%r14
|
||||
jz 1f # don't free anything if just one dso
|
||||
push %rax
|
||||
mov %r12,%rdi
|
||||
.weak free
|
||||
call free # can't panic due to earlier test
|
||||
1: pop %rsi
|
||||
jmp 0b
|
||||
3: pop %r12 # align stack for next call
|
||||
test %r14,%r14 # no static dtor for dso exit
|
||||
jnz 9f
|
||||
ezlea __fini_array_end,ax # static dtors in reverse order
|
||||
.weak __fini_array_end # could be called multiple times
|
||||
ezlea __fini_array_start,cx # idempotency recommended
|
||||
.weak __fini_array_start # or consider atexit()
|
||||
8: sub $8,%rax # @see ape/ape.lds
|
||||
cmp %rcx,%rax
|
||||
jl 9f
|
||||
push %rax
|
||||
push %rcx
|
||||
call *(%rax)
|
||||
pop %rcx
|
||||
pop %rax
|
||||
jmp 8b
|
||||
9: pop %r13
|
||||
pop %r14
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn __cxa_finalize,globl,hidden
|
||||
|
||||
.bss
|
||||
.align 16 # static/dynamic hybrid linked list
|
||||
g_cxa: .quad 0 # last block ptr: (long (*)[32][3])
|
||||
.quad 0 # block byte offset moves backwards
|
||||
.endobj g_cxa
|
||||
g_cxa_static:
|
||||
.rept ATEXIT_MAX
|
||||
.quad 0 # dso
|
||||
.quad 0 # arg
|
||||
.quad 0 # fn (or NULL for empty)
|
||||
.endr
|
||||
.quad 0 # next (always NULL in static block)
|
||||
.endobj g_cxa_static
|
||||
.previous
|
||||
|
||||
.init.start 300,_init_g_cxa
|
||||
ezlea g_cxa,cx
|
||||
lea g_cxa_static-g_cxa(%rcx),%rax
|
||||
mov %rax,(%rcx)
|
||||
movl $ATEXIT_MAX*8*3,8(%rcx)
|
||||
.init.end 300,_init_g_cxa
|
||||
Reference in New Issue
Block a user