Skip to content

Instantly share code, notes, and snippets.

@rygorous
Created May 2, 2016 22:11
Show Gist options
  • Save rygorous/f729919ff64526a46e591d8f8b52058e to your computer and use it in GitHub Desktop.
Save rygorous/f729919ff64526a46e591d8f8b52058e to your computer and use it in GitHub Desktop.
NASM glue to deal with ABI diffs (in particular, Win64 UNWIND_INFO)
; This is intended for long-running leaf funcs that don't use XMM registers,
; and just saves all callee-save registers regardless of whether they're used
; or not.
; detect some parameters from output format
%ifidn __OUTPUT_FORMAT__,win32
%define resp resd
%define LEADING_UNDERSCORES
%define CALLEE_SAVE_GPRS ebp,ebx,esi,edi
%define BYTES_PER_ARG 4
%define CDECL32_ABI
%elifidn __OUTPUT_FORMAT__,elf32
%define resp resd
%define LEADING_UNDERSCORES
%define CALLEE_SAVE_GPRS ebp,ebx,esi,edi
%define BYTES_PER_ARG 4
%elifidn __OUTPUT_FORMAT__,win64
%define resp resq
%define WIN64_ABI
%define BYTES_PER_ARG 8
%define CALLEE_SAVE_GPRS rbx,rbp,rsi,rdi,r12,r13,r14,r15
%elifidn __OUTPUT_FORMAT__,elf64
%define resp resq
%define SYSV64_ABI
%define BYTES_PER_ARG 8
%define CALLEE_SAVE_GPRS rbx,rbp,r12,r13,r14,r15
%else
%error "Unrecognized output format!"
%endif
; ---------------------------------------------------------------------------
; cglobal name: Defines a decorated name for a C function given the platform ABI
%macro cglobal 1
%ifdef LEADING_UNDERSCORES
global _%1
%define %1 _%1
%else
global %1
%endif
%assign prologue_pushcount 0
%endmacro
; prologue_push: save multiple regs (passed as arguments) and keep track of how many
; so we know how to find stack args later
%macro prologue_push 1-*
%rep %0
push %1
%rotate 1
%endrep
%assign prologue_pushcount prologue_pushcount+%0
%endmacro
; epilogue_pop: restore multiple regs (list passed as arguments, same order as in prologue_push)
%macro epilogue_pop 1-*
%rep %0
%rotate -1
pop %1
%endrep
%assign prologue_pushcount prologue_pushcount-%0
%endmacro
; stackarg_offs: offset for stack-passed argument i (which is not necessarily argument i)
%define stackarg_offs(i) ((prologue_pushcount + 1 + (i))*BYTES_PER_ARG)
; load_first_arg: loads first arg to given destination reg
%macro load_first_arg 1
%ifdef CDECL32_ABI
mov %1, [esp+stackarg_offs(0)]
%elifdef SYSV64_ABI
mov %1, rdi ; first arg in rdi
%elifdef WIN64_ABI
mov %1, rcx ; first arg in rcx
%else
%error "Unknown ABI!"
%endif
%endmacro
; win64_get_regnum: assigns preprocessor "regnum" to be the x64 register number
; corresponding to the given register name. Used to generate Win64 unwind
; tables.
%macro win64_get_regnum 1.nolist
%ifidni %1,rax
%assign regnum 0
%elifidni %1,rcx
%assign regnum 1
%elifidni %1,rdx
%assign regnum 2
%elifidni %1,rbx
%assign regnum 3
%elifidni %1,rsp
%assign regnum 4
%elifidni %1,rbp
%assign regnum 5
%elifidni %1,rsi
%assign regnum 6
%elifidni %1,rdi
%assign regnum 7
%elifidni %1,r8
%assign regnum 8
%elifidni %1,r9
%assign regnum 9
%elifidni %1,r10
%assign regnum 10
%elifidni %1,r11
%assign regnum 11
%elifidni %1,r12
%assign regnum 12
%elifidni %1,r13
%assign regnum 13
%elifidni %1,r14
%assign regnum 14
%elifidni %1,r15
%assign regnum 15
%else
%fatal %1 is not a valid 64-bit register.
%endif
%endmacro
; win64_setup_unwind_code: used to generate Win64 exception handling tables
; sets up prologue size in offs_in_prologue, unwind code in unwindcode
%macro win64_setup_unwind_code 1-*
%assign offs_in_prologue 0
%xdefine unwindcode
%rep %0
%rotate 1
win64_get_regnum %1
; unwind code:
; CodeOffset=offs_in_prologue
; UnwindOp=UWOP_PUSH_NONVOL=0, OpInfo=<register number>
%xdefine unwindcode offs_in_prologue,(regnum<<4)|0,unwindcode
; push for upper 8 GPRs is 2 bytes, lower 8 is 1 byte
%if regnum >= 8
%assign offs_in_prologue offs_in_prologue+2
%else
%assign offs_in_prologue offs_in_prologue+1
%endif
%endrep
%endmacro
; leaf_func_with_prologue name: declares a public leaf function, generates a prologue, and on Win64
; also sets up unwind data.
%macro leaf_func_with_prologue 1
%ifdef WIN64_ABI ; the fun never stops!
[section .pdata rdata align=4] ; RUNTIME_FUNC description
dd %1 wrt ..imagebase ; start of func
dd %1%+.fn_end_mark wrt ..imagebase ; end of func
dd unwind_%+%1 wrt ..imagebase ; ptr to unwind data
[section .xdata rdata align=8] ; UNWIND_INFO stuff
unwind_%+%1:
win64_setup_unwind_code CALLEE_SAVE_GPRS
db 1 ; version 1, no exception handler
db offs_in_prologue ; size of prologue in bytes
db (.unwind_end-.unwind_begin)/2 ; number of unwind codes
db 0 ; no frame register
.unwind_begin:
db unwindcode
.unwind_end:
%undef offs_in_prologue
%undef unwindcode
%undef regnum
%endif
[section .text]
cglobal %1
%1:
prologue_push CALLEE_SAVE_GPRS
%endmacro
; leaf_func_epilogue_and_ret: must be the last thing in a leaf function
%macro leaf_func_epilogue_and_ret 0
epilogue_pop CALLEE_SAVE_GPRS
ret
.fn_end_mark: ; for Win64 unwind info
%endmacro
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment