mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
528 lines
12 KiB
C
528 lines
12 KiB
C
#ifndef COSMOPOLITAN_LIBC_MACROS_H_
|
|
#define COSMOPOLITAN_LIBC_MACROS_H_
|
|
#if 0
|
|
/*───────────────────────────────────────────────────────────────────────────│─╗
|
|
│ cosmopolitan § macros ─╬─│┼
|
|
╚────────────────────────────────────────────────────────────────────────────│*/
|
|
#endif
|
|
|
|
/**
|
|
* @fileoverview Common C preprocessor, assembler, and linker macros.
|
|
*/
|
|
|
|
#ifdef MAX
|
|
#undef MAX
|
|
#endif
|
|
|
|
#ifdef MIN
|
|
#undef MIN
|
|
#endif
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#define IS2POW(X) (!((X) & ((X)-1)))
|
|
#define ROUNDUP(X, K) (((X) + (K)-1) & -(K))
|
|
#define ROUNDDOWN(X, K) ((X) & -(K))
|
|
#ifndef __ASSEMBLER__
|
|
#define ABS(X) ((X) >= 0 ? (X) : -(X))
|
|
#define MIN(X, Y) ((Y) > (X) ? (X) : (Y))
|
|
#define MAX(X, Y) ((Y) < (X) ? (X) : (Y))
|
|
#else
|
|
// The GNU assembler does not grok the ?: ternary operator; furthermore,
|
|
// boolean expressions yield -1 and 0 for "true" and "false", not 1 and 0.
|
|
#define __MAPBOOL(P) (!!(P) / (!!(P) + !(P)))
|
|
#define __IFELSE(P, X, Y) (__MAPBOOL(P) * (X) + __MAPBOOL(!(P)) * (Y))
|
|
#define MIN(X, Y) (__IFELSE((Y) > (X), (X), (Y)))
|
|
#define MAX(X, Y) (__IFELSE((Y) < (X), (X), (Y)))
|
|
#endif
|
|
#define PASTE(A, B) __PASTE(A, B)
|
|
#define STRINGIFY(A) __STRINGIFY(A)
|
|
#define EQUIVALENT(X, Y) (__builtin_constant_p((X) == (Y)) && ((X) == (Y)))
|
|
#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT)
|
|
#define TYPE_SIGNED(type) (((type)-1) < 0)
|
|
#define TYPE_INTEGRAL(type) (((type)0.5) != 0.5)
|
|
|
|
#define ARRAYLEN(A) \
|
|
((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A)))))
|
|
|
|
#define __STRINGIFY(A) #A
|
|
#define __PASTE(A, B) A##B
|
|
#ifdef __ASSEMBLER__
|
|
// clang-format off
|
|
|
|
// Ends function definition.
|
|
// @cost saves 1-3 lines of code
|
|
.macro .endfn name:req bnd vis
|
|
.size "\name",.-"\name"
|
|
.type "\name",@function
|
|
.ifnb \bnd
|
|
.\bnd "\name"
|
|
.endif
|
|
.ifnb \vis
|
|
.\vis "\name"
|
|
.endif
|
|
.endm
|
|
|
|
// Ends variable definition.
|
|
// @cost saves 1-3 lines of code
|
|
.macro .endobj name:req bnd vis
|
|
.size "\name",.-"\name"
|
|
.type "\name",@object
|
|
.ifnb \bnd
|
|
.\bnd "\name"
|
|
.endif
|
|
.ifnb \vis
|
|
.\vis "\name"
|
|
.endif
|
|
.endm
|
|
|
|
// Shorthand notation for widely-acknowledged sections.
|
|
.macro .rodata
|
|
.section .rodata,"a",@progbits
|
|
.endm
|
|
.macro .init
|
|
.section .init,"ax",@progbits
|
|
.endm
|
|
.macro .real
|
|
.section .text.real,"ax",@progbits
|
|
.endm
|
|
.macro .head
|
|
.section .text.head,"ax",@progbits
|
|
.endm
|
|
.macro .text.startup
|
|
.section .text.startup,"ax",@progbits
|
|
.endm
|
|
.macro .text.exit
|
|
.section .text.exit,"ax",@progbits
|
|
.endm
|
|
.macro .firstclass
|
|
.section .text.hot,"ax",@progbits
|
|
.endm
|
|
.macro .text.unlikely
|
|
.section .text.unlikely,"ax",@progbits
|
|
.endm
|
|
.macro .text.likely
|
|
.section .text.hot,"ax",@progbits
|
|
.endm
|
|
.macro .text.modernity
|
|
.section .text.modernity,"ax",@progbits
|
|
.balign 16
|
|
.endm
|
|
.macro .text.antiquity
|
|
.section .text.antiquity,"ax",@progbits
|
|
.endm
|
|
.macro .text.hot
|
|
.section .text.hot,"ax",@progbits
|
|
.endm
|
|
.macro .preinit_array
|
|
.section .preinit_array,"a",@init_array
|
|
.endm
|
|
.macro .init_array
|
|
.section .init_array,"a",@init_array
|
|
.endm
|
|
.macro .text.windows
|
|
.section .text.windows,"ax",@progbits
|
|
.endm
|
|
|
|
// Mergeable NUL-terminated UTF-8 string constant section.
|
|
//
|
|
// @note linker de-dupes C strings here across whole compile
|
|
// @note therefore item/values are reordered w.r.t. link order
|
|
// @note therefore no section relative addressing
|
|
.macro .rodata.str1.1
|
|
.section .rodata.str1.1,"aMS",@progbits,1
|
|
.balign 1
|
|
.endm
|
|
|
|
// Locates unreferenced code invulnerable to --gc-sections.
|
|
.macro .keep.text
|
|
.section .keep.text,"ax",@progbits
|
|
.endm
|
|
|
|
// Flags code as only allowed for testing purposes.
|
|
.macro .testonly
|
|
.section .test,"ax",@progbits
|
|
.endm
|
|
|
|
// Makes code runnable while code morphing.
|
|
.macro .privileged
|
|
.section .privileged,"ax",@progbits
|
|
.endm
|
|
|
|
// Declares alternative implementation of function.
|
|
// @param implement e.g. tinymath_pow
|
|
// @param canonical e.g. pow
|
|
.macro .alias implement:req canonical:req
|
|
.equ \canonical,\implement
|
|
.weak \canonical
|
|
.endm
|
|
|
|
#ifdef __aarch64__
|
|
.macro jmp dest:req
|
|
b \dest
|
|
.endm
|
|
#endif
|
|
|
|
// Pulls unrelated module into linkage.
|
|
//
|
|
// In order for this technique to work with --gc-sections, another
|
|
// module somewhere might want to weakly reference whats yoinked.
|
|
.macro .yoink symbol:req
|
|
.section .yoink
|
|
#ifdef __x86_64__
|
|
nopl "\symbol"(%rip)
|
|
#elif defined(__aarch64__)
|
|
b "\symbol"
|
|
#endif
|
|
.previous
|
|
.endm
|
|
|
|
// Begins definition of frameless function that calls no functions.
|
|
.macro .leafprologue
|
|
#if !(defined(TINY) && !defined(__PG__))
|
|
#ifdef __x86_64__
|
|
push %rbp
|
|
mov %rsp,%rbp
|
|
#elif defined(__aarch64__)
|
|
stp x29,x30,[sp,#-16]!
|
|
mov x29,sp
|
|
#endif
|
|
#endif
|
|
.endm
|
|
|
|
// Ends definition of frameless function that calls no functions.
|
|
.macro .leafepilogue
|
|
#if !(defined(TINY) && !defined(__PG__))
|
|
#ifdef __x86_64__
|
|
pop %rbp
|
|
#elif defined(__aarch64__)
|
|
ldp x29,x30,[sp],#16
|
|
#endif
|
|
#endif
|
|
ret
|
|
.endm
|
|
|
|
// Documents unreachable assembly code.
|
|
.macro .unreachable
|
|
#if !defined(NDEBUG) && defined(__x86_64__)
|
|
ud2 // crash if contract is broken
|
|
#elif !defined(NDEBUG) && defined(__aarch64__)
|
|
brk #1000
|
|
#elif defined(__FNO_OMIT_FRAME_POINTER__) && defined(__x86_64__)
|
|
nop // avoid noreturn tail call backtrace ambiguity
|
|
#endif
|
|
.endm
|
|
|
|
// Embeds Fixed-Width Zero-Padded String.
|
|
// @note .fxstr is better
|
|
.macro .ascin str:req fieldsize:req
|
|
1347: .ascii "\str"
|
|
.org 1347b+\fieldsize,0x00
|
|
.endm
|
|
|
|
// Inserts --ftrace pre-prologue.
|
|
// This goes immediately before the function symbol.
|
|
// @see .ftrace2
|
|
.macro .ftrace1
|
|
#ifdef FTRACE
|
|
#ifdef __x86_64__
|
|
.rept 9
|
|
nop
|
|
.endr
|
|
#elif defined(__aarch64__)
|
|
.rept 6
|
|
nop
|
|
.endr
|
|
#endif /* __x86_64__ */
|
|
#endif /* FTRACE */
|
|
.endm
|
|
|
|
// Inserts --ftrace prologue.
|
|
// This goes immediately after the function symbol.
|
|
// @see .ftrace1
|
|
.macro .ftrace2
|
|
#ifdef FTRACE
|
|
#ifdef __x86_64__
|
|
xchg %ax,%ax
|
|
#elif defined(__aarch64__)
|
|
nop
|
|
#endif /* __x86_64__ */
|
|
#endif /* FTRACE */
|
|
.endm
|
|
|
|
#ifdef __x86_64__
|
|
|
|
#if __MNO_VZEROUPPER__ + 0
|
|
#define vzeroupper
|
|
#endif
|
|
|
|
// Mergeable numeric constant sections.
|
|
//
|
|
// @note linker de-dupes item/values across whole compile
|
|
// @note therefore item/values are reordered w.r.t. link order
|
|
// @note therefore no section relative addressing
|
|
.macro .rodata.cst4
|
|
.section .rodata.cst4,"aM",@progbits,4
|
|
.balign 4
|
|
.endm
|
|
.macro .rodata.cst8
|
|
.section .rodata.cst8,"aM",@progbits,8
|
|
.balign 8
|
|
.endm
|
|
.macro .rodata.cst16
|
|
.section .rodata.cst16,"aM",@progbits,16
|
|
.balign 16
|
|
.endm
|
|
.macro .rodata.cst32
|
|
.section .rodata.cst32,"aM",@progbits,32
|
|
.balign 32
|
|
.endm
|
|
.macro .rodata.cst64
|
|
.section .rodata.cst64,"aM",@progbits,64
|
|
.balign 64
|
|
.endm
|
|
.macro .tdata
|
|
.section .tdata,"awT",@progbits
|
|
.balign 4
|
|
.endm
|
|
.macro .tbss
|
|
.section .tdata,"awT",@nobits
|
|
.balign 4
|
|
.endm
|
|
|
|
// Loads address of errno into %rcx
|
|
.macro .errno
|
|
call __errno_location
|
|
.endm
|
|
|
|
// Post-Initialization Read-Only (PIRO) BSS section.
|
|
// @param ss is an optional string, for control image locality
|
|
.macro .piro ss
|
|
.ifnb \ss
|
|
.section .piro.sort.bss.\ss,"aw",@nobits
|
|
.else
|
|
.section .piro.bss,"aw",@nobits
|
|
.endif
|
|
.endm
|
|
|
|
// Helpers for Cosmopolitan _init() amalgamation magic.
|
|
// @param name should be consistent across macros for a module
|
|
// @see libc/runtime/_init.S
|
|
.macro .initro number:req name:req
|
|
.section ".initro.\number\().\name","a",@progbits
|
|
.balign 8
|
|
.endm
|
|
.macro .initbss number:req name:req
|
|
.section ".piro.bss.init.2.\number\().\name","aw",@nobits
|
|
.balign 8
|
|
.endm
|
|
.macro .init.start number:req name:req
|
|
.section ".init.\number\().\name","ax",@progbits
|
|
"\name":
|
|
.endm
|
|
.macro .init.end number:req name:req bnd=globl vis
|
|
.endfn "\name",\bnd,\vis
|
|
.previous
|
|
.endm
|
|
|
|
// LOOP Instruction Replacement.
|
|
.macro .loop label:req
|
|
.byte 0x83
|
|
.byte 0xe9
|
|
.byte 0x01
|
|
jnz \label
|
|
.endm
|
|
|
|
// Pushes CONSTEXPR ∈ [-128,127].
|
|
// @note assembler is wrong for non-literal constexprs
|
|
.macro pushb x:req
|
|
.byte 0x6a
|
|
.byte \x
|
|
.endm
|
|
|
|
// Sign-extends CONSTEXPR ∈ [-128,127] to REGISTER.
|
|
// @cost ≥1 cycles, -2 bytes
|
|
.macro pushpop constexpr:req register:req
|
|
pushb \constexpr
|
|
pop \register
|
|
.endm
|
|
|
|
// Moves REGISTER to REGISTER.
|
|
// @cost ≥1 cycles, -1 REX byte
|
|
.macro movpp src:req dest:req
|
|
push \src
|
|
pop \dest
|
|
.endm
|
|
|
|
// Embeds fixed-width zero-filled string table.
|
|
// @note zero-padded ≠ nul-terminated
|
|
.macro .fxstr width head rest:vararg
|
|
.ifnb \head
|
|
0: .ascii "\head"
|
|
.org 0b+\width
|
|
.fxstr \width,\rest
|
|
.endif
|
|
.endm
|
|
|
|
// Marks symbols as object en-masse.
|
|
// @note zero-padded ≠ nul-terminated
|
|
.macro .object symbol rest:vararg
|
|
.ifnb \symbol
|
|
.type \symbol,@object
|
|
.object \rest
|
|
.endif
|
|
.endm
|
|
|
|
// Pads function prologue unconditionally for runtime hooking.
|
|
// @cost ≥0.3 cycles, 5 bytes
|
|
// @see .ftrace1
|
|
.macro .hookable
|
|
.byte 0x0f
|
|
.byte 0x1f
|
|
.byte 0x44
|
|
.byte 0x00
|
|
.byte 0x00
|
|
.endm
|
|
|
|
// Puts initialized data in uninitialized data section.
|
|
.macro .bsdata name:req expr:req bnd vis
|
|
.section ".initbss.300._init_\name","aw",@nobits
|
|
"\name":
|
|
.quad 0
|
|
.endobj "\name",\bnd,\vis
|
|
.previous
|
|
.section ".initro.300._init_\name","a",@progbits
|
|
.quad \expr
|
|
.previous
|
|
.section ".init.300._init_\name","ax",@progbits
|
|
"_init_\name":
|
|
movsq
|
|
.endfn "_init_\name"
|
|
.previous
|
|
.endm
|
|
|
|
// ICE Breakpoint.
|
|
// Modern gas forgot this but objdump knows
|
|
// @mode long,legacy,real
|
|
.macro icebp
|
|
.byte 0xF1
|
|
.endm
|
|
.macro int1
|
|
icebp
|
|
.endm
|
|
|
|
// Sets breakpoint for software debugger.
|
|
// @mode long,legacy,real
|
|
.macro .softicebp
|
|
.byte 0x53 # push bx
|
|
.byte 0x87 # xchg bx,bx (bochs breakpoint)
|
|
.byte 0xdb
|
|
.byte 0x5b # pop bx
|
|
.byte 0x66 # xchg ax,ax (microsoft breakpoint)
|
|
.byte 0x90
|
|
int3 # gdb breakpoint
|
|
.endm
|
|
|
|
// Assembles Intel Official 4-Byte NOP.
|
|
.macro fatnop4
|
|
.byte 0x0f,0x1f,0x40,0x00
|
|
.endm
|
|
|
|
// Calls Windows function.
|
|
//
|
|
// @param cx,dx,r8,r9,stack
|
|
// @return ax
|
|
// @clob ax,cx,dx,r8-r11
|
|
.macro ntcall symbol:req
|
|
sub $32,%rsp
|
|
call *\symbol(%rip)
|
|
add $32,%rsp
|
|
.endm
|
|
|
|
// Custom emulator instruction for bottom stack frame.
|
|
.macro bofram endfunc:req
|
|
.byte 0x0f,0x1f,0105,\endfunc-. # nopl disp8(%rbp)
|
|
.endm
|
|
|
|
// Good alignment for functions where alignment actually helps.
|
|
// @note 16-byte
|
|
.macro .alignfunc
|
|
#ifndef __OPTIMIZE_SIZE__
|
|
.p2align 4
|
|
#endif
|
|
.endm
|
|
|
|
// TODO(jart): delete
|
|
// Loads Effective Address
|
|
// Supporting security blankets
|
|
.macro ezlea symbol:req reg:req
|
|
#if __pic__ + __pie__ + __code_model_medium__ + __code_model_large__ + 0 > 1
|
|
// lea \symbol(%rip),%r\reg
|
|
mov $\symbol,%e\reg
|
|
#else
|
|
mov $\symbol,%e\reg
|
|
#endif
|
|
.endm
|
|
|
|
// Loads address of linktime mergeable string literal into register.
|
|
.macro loadstr text:req reg:req regsz bias=0
|
|
.section .rodata.str1.1,"aSM",@progbits,1
|
|
.type .Lstr\@,@object
|
|
.Lstr\@: .asciz "\text"
|
|
.Lstr\@.size = .-.Lstr\@ - 1
|
|
.size .Lstr\@,.-.Lstr\@
|
|
.previous
|
|
ezlea .Lstr\@,\reg
|
|
.ifnb \regsz
|
|
#ifdef __OPTIMIZE_SIZE__
|
|
.if .Lstr\@.size + \bias < 128
|
|
pushpop .Lstr\@.size,%r\regsz
|
|
.else
|
|
mov $.Lstr\@.size,%e\regsz
|
|
.endif
|
|
#else
|
|
mov $.Lstr\@.size,%e\regsz
|
|
#endif
|
|
.endif
|
|
.endm
|
|
|
|
.macro .poison name:req kind:req
|
|
#ifdef __SANITIZE_ADDRESS__
|
|
2323: .quad 0
|
|
.init.start 304,"_init_\name\()_poison_\@"
|
|
push %rdi
|
|
push %rsi
|
|
ezlea 2323b,di
|
|
mov $8,%esi
|
|
mov $\kind,%edx
|
|
call __asan_poison
|
|
pop %rsi
|
|
pop %rdi
|
|
.init.end 304,"_init_\name\()_poison_\@"
|
|
#endif
|
|
.endm
|
|
|
|
.macro .underrun
|
|
#ifdef __SANITIZE_ADDRESS__
|
|
.poison __BASE_FILE__, -20 # kAsanGlobalUnderrun
|
|
#endif
|
|
.endm
|
|
|
|
.macro .overrun
|
|
#ifdef __SANITIZE_ADDRESS__
|
|
.poison __BASE_FILE__, -21 # kAsanGlobalOverrun
|
|
#endif
|
|
.endm
|
|
|
|
#else
|
|
|
|
.macro .underrun
|
|
.endm
|
|
.macro .overrun
|
|
.endm
|
|
|
|
// clang-format on
|
|
#endif /* __x86_64__ */
|
|
#endif /* __ASSEMBLER__ */
|
|
#endif /* COSMOPOLITAN_LIBC_MACROS_H_ */
|