1/* ----------------------------------------------------------------------- 2 sysv.S - Copyright (c) 1998, 2008, 2011 Red Hat, Inc. 3 Copyright (c) 2011 Plausible Labs Cooperative, Inc. 4 5 ARM Foreign Function Interface 6 7 Permission is hereby granted, free of charge, to any person obtaining 8 a copy of this software and associated documentation files (the 9 ``Software''), to deal in the Software without restriction, including 10 without limitation the rights to use, copy, modify, merge, publish, 11 distribute, sublicense, and/or sell copies of the Software, and to 12 permit persons to whom the Software is furnished to do so, subject to 13 the following conditions: 14 15 The above copyright notice and this permission notice shall be included 16 in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 ----------------------------------------------------------------------- */ 27 28#ifdef __arm__ 29#define LIBFFI_ASM 30#include <fficonfig.h> 31#include <ffi.h> 32#include <ffi_cfi.h> 33#include "internal.h" 34 35/* GCC 4.8 provides __ARM_ARCH; construct it otherwise. */ 36#ifndef __ARM_ARCH 37# if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ 38 || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ 39 || defined(__ARM_ARCH_7EM__) 40# define __ARM_ARCH 7 41# elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ 42 || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \ 43 || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) \ 44 || defined(__ARM_ARCH_6M__) 45# define __ARM_ARCH 6 46# elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) \ 47 || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) \ 48 || defined(__ARM_ARCH_5TEJ__) 49# define __ARM_ARCH 5 50# else 51# define __ARM_ARCH 4 52# endif 53#endif 54 55/* Conditionally compile unwinder directives. */ 56#ifdef __ARM_EABI__ 57# define UNWIND(...) __VA_ARGS__ 58#else 59# define UNWIND(...) 60#endif 61 62#if defined(HAVE_AS_CFI_PSEUDO_OP) && defined(__ARM_EABI__) 63 .cfi_sections .debug_frame 64#endif 65 66#define CONCAT(a, b) CONCAT2(a, b) 67#define CONCAT2(a, b) a ## b 68 69#ifdef __USER_LABEL_PREFIX__ 70# define CNAME(X) CONCAT (__USER_LABEL_PREFIX__, X) 71#else 72# define CNAME(X) X 73#endif 74#ifdef __ELF__ 75# define SIZE(X) .size CNAME(X), . - CNAME(X) 76# define TYPE(X, Y) .type CNAME(X), Y 77#else 78# define SIZE(X) 79# define TYPE(X, Y) 80#endif 81 82#define ARM_FUNC_START_LOCAL(name) \ 83 .align 3; \ 84 TYPE(CNAME(name), %function); \ 85 CNAME(name): 86 87#define ARM_FUNC_START(name) \ 88 .globl CNAME(name); \ 89 FFI_HIDDEN(CNAME(name)); \ 90 ARM_FUNC_START_LOCAL(name) 91 92#define ARM_FUNC_END(name) \ 93 SIZE(name) 94 95/* Aid in defining a jump table with 8 bytes between entries. */ 96/* ??? The clang assembler doesn't handle .if with symbolic expressions. */ 97#ifdef __clang__ 98# define E(index) 99#else 100# define E(index) \ 101 .if . - 0b - 8*index; \ 102 .error "type table out of sync"; \ 103 .endif 104#endif 105 106 .text 107 .syntax unified 108 .arm 109 110#ifndef __clang__ 111 /* We require interworking on LDM, which implies ARMv5T, 112 which implies the existance of BLX. */ 113 .arch armv5t 114#endif 115 116 /* Note that we use STC and LDC to encode VFP instructions, 117 so that we do not need ".fpu vfp", nor get that added to 118 the object file attributes. These will not be executed 119 unless the FFI_VFP abi is used. */ 120 121 @ r0: stack 122 @ r1: frame 123 @ r2: fn 124 @ r3: vfp_used 125 126ARM_FUNC_START(ffi_call_VFP) 127 UNWIND(.fnstart) 128 cfi_startproc 129 130 cmp r3, #3 @ load only d0 if possible 131#ifdef __clang__ 132 vldrle d0, [sp] 133 vldmgt sp, {d0-d7} 134#else 135 ldcle p11, cr0, [r0] @ vldrle d0, [sp] 136 ldcgt p11, cr0, [r0], {16} @ vldmgt sp, {d0-d7} 137#endif 138 add r0, r0, #64 @ discard the vfp register args 139 /* FALLTHRU */ 140ARM_FUNC_END(ffi_call_VFP) 141 142ARM_FUNC_START(ffi_call_SYSV) 143 stm r1, {fp, lr} 144 mov fp, r1 145 146 @ This is a bit of a lie wrt the origin of the unwind info, but 147 @ now we've got the usual frame pointer and two saved registers. 148 UNWIND(.save {fp,lr}) 149 UNWIND(.setfp fp, sp) 150 cfi_def_cfa(fp, 8) 151 cfi_rel_offset(fp, 0) 152 cfi_rel_offset(lr, 4) 153 154 mov sp, r0 @ install the stack pointer 155 mov lr, r2 @ move the fn pointer out of the way 156 ldr ip, [fp, #16] @ install the static chain 157 ldmia sp!, {r0-r3} @ move first 4 parameters in registers. 158 blx lr @ call fn 159 160 @ Load r2 with the pointer to storage for the return value 161 @ Load r3 with the return type code 162 ldr r2, [fp, #8] 163 ldr r3, [fp, #12] 164 165 @ Deallocate the stack with the arguments. 166 mov sp, fp 167 cfi_def_cfa_register(sp) 168 169 @ Store values stored in registers. 170 .align 3 171 add pc, pc, r3, lsl #3 172 nop 1730: 174E(ARM_TYPE_VFP_S) 175#ifdef __clang__ 176 vstr s0, [r2] 177#else 178 stc p10, cr0, [r2] @ vstr s0, [r2] 179#endif 180 pop {fp,pc} 181E(ARM_TYPE_VFP_D) 182#ifdef __clang__ 183 vstr d0, [r2] 184#else 185 stc p11, cr0, [r2] @ vstr d0, [r2] 186#endif 187 pop {fp,pc} 188E(ARM_TYPE_VFP_N) 189#ifdef __clang__ 190 vstm r2, {d0-d3} 191#else 192 stc p11, cr0, [r2], {8} @ vstm r2, {d0-d3} 193#endif 194 pop {fp,pc} 195E(ARM_TYPE_INT64) 196 str r1, [r2, #4] 197 nop 198E(ARM_TYPE_INT) 199 str r0, [r2] 200 pop {fp,pc} 201E(ARM_TYPE_VOID) 202 pop {fp,pc} 203 nop 204E(ARM_TYPE_STRUCT) 205 pop {fp,pc} 206 207 cfi_endproc 208 UNWIND(.fnend) 209ARM_FUNC_END(ffi_call_SYSV) 210 211 212/* 213 int ffi_closure_inner_* (cif, fun, user_data, frame) 214*/ 215 216ARM_FUNC_START(ffi_go_closure_SYSV) 217 cfi_startproc 218 stmdb sp!, {r0-r3} @ save argument regs 219 cfi_adjust_cfa_offset(16) 220 ldr r0, [ip, #4] @ load cif 221 ldr r1, [ip, #8] @ load fun 222 mov r2, ip @ load user_data 223 b 0f 224 cfi_endproc 225ARM_FUNC_END(ffi_go_closure_SYSV) 226 227ARM_FUNC_START(ffi_closure_SYSV) 228 UNWIND(.fnstart) 229 cfi_startproc 230 stmdb sp!, {r0-r3} @ save argument regs 231 cfi_adjust_cfa_offset(16) 232 233#if FFI_EXEC_TRAMPOLINE_TABLE 234 ldr ip, [ip] @ ip points to the config page, dereference to get the ffi_closure* 235#endif 236 ldr r0, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET] @ load cif 237 ldr r1, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+4] @ load fun 238 ldr r2, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+8] @ load user_data 2390: 240 add ip, sp, #16 @ compute entry sp 241 sub sp, sp, #64+32 @ allocate frame 242 cfi_adjust_cfa_offset(64+32) 243 stmdb sp!, {ip,lr} 244 245 /* Remember that EABI unwind info only applies at call sites. 246 We need do nothing except note the save of the stack pointer 247 and the link registers. */ 248 UNWIND(.save {sp,lr}) 249 cfi_adjust_cfa_offset(8) 250 cfi_rel_offset(lr, 4) 251 252 add r3, sp, #8 @ load frame 253 bl CNAME(ffi_closure_inner_SYSV) 254 255 @ Load values returned in registers. 256 add r2, sp, #8+64 @ load result 257 adr r3, CNAME(ffi_closure_ret) 258 add pc, r3, r0, lsl #3 259 cfi_endproc 260 UNWIND(.fnend) 261ARM_FUNC_END(ffi_closure_SYSV) 262 263ARM_FUNC_START(ffi_go_closure_VFP) 264 cfi_startproc 265 stmdb sp!, {r0-r3} @ save argument regs 266 cfi_adjust_cfa_offset(16) 267 ldr r0, [ip, #4] @ load cif 268 ldr r1, [ip, #8] @ load fun 269 mov r2, ip @ load user_data 270 b 0f 271 cfi_endproc 272ARM_FUNC_END(ffi_go_closure_VFP) 273 274ARM_FUNC_START(ffi_closure_VFP) 275 UNWIND(.fnstart) 276 cfi_startproc 277 stmdb sp!, {r0-r3} @ save argument regs 278 cfi_adjust_cfa_offset(16) 279 280#if FFI_EXEC_TRAMPOLINE_TABLE 281 ldr ip, [ip] @ ip points to the config page, dereference to get the ffi_closure* 282#endif 283 ldr r0, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET] @ load cif 284 ldr r1, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+4] @ load fun 285 ldr r2, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+8] @ load user_data 2860: 287 add ip, sp, #16 288 sub sp, sp, #64+32 @ allocate frame 289 cfi_adjust_cfa_offset(64+32) 290#ifdef __clang__ 291 vstm sp, {d0-d7} 292#else 293 stc p11, cr0, [sp], {16} @ vstm sp, {d0-d7} 294#endif 295 stmdb sp!, {ip,lr} 296 297 /* See above. */ 298 UNWIND(.save {sp,lr}) 299 cfi_adjust_cfa_offset(8) 300 cfi_rel_offset(lr, 4) 301 302 add r3, sp, #8 @ load frame 303 bl CNAME(ffi_closure_inner_VFP) 304 305 @ Load values returned in registers. 306 add r2, sp, #8+64 @ load result 307 adr r3, CNAME(ffi_closure_ret) 308 add pc, r3, r0, lsl #3 309 cfi_endproc 310 UNWIND(.fnend) 311ARM_FUNC_END(ffi_closure_VFP) 312 313/* Load values returned in registers for both closure entry points. 314 Note that we use LDM with SP in the register set. This is deprecated 315 by ARM, but not yet unpredictable. */ 316 317ARM_FUNC_START_LOCAL(ffi_closure_ret) 318 cfi_startproc 319 cfi_rel_offset(sp, 0) 320 cfi_rel_offset(lr, 4) 3210: 322E(ARM_TYPE_VFP_S) 323#ifdef __clang__ 324 vldr s0, [r2] 325#else 326 ldc p10, cr0, [r2] @ vldr s0, [r2] 327#endif 328 ldm sp, {sp,pc} 329E(ARM_TYPE_VFP_D) 330#ifdef __clang__ 331 vldr d0, [r2] 332#else 333 ldc p11, cr0, [r2] @ vldr d0, [r2] 334#endif 335 ldm sp, {sp,pc} 336E(ARM_TYPE_VFP_N) 337#ifdef __clang__ 338 vldm r2, {d0-d3} 339#else 340 ldc p11, cr0, [r2], {8} @ vldm r2, {d0-d3} 341#endif 342 ldm sp, {sp,pc} 343E(ARM_TYPE_INT64) 344 ldr r1, [r2, #4] 345 nop 346E(ARM_TYPE_INT) 347 ldr r0, [r2] 348 ldm sp, {sp,pc} 349E(ARM_TYPE_VOID) 350 ldm sp, {sp,pc} 351 nop 352E(ARM_TYPE_STRUCT) 353 ldm sp, {sp,pc} 354 cfi_endproc 355ARM_FUNC_END(ffi_closure_ret) 356 357#if FFI_EXEC_TRAMPOLINE_TABLE 358 359#ifdef __MACH__ 360#include <mach/machine/vm_param.h> 361 362.align PAGE_MAX_SHIFT 363ARM_FUNC_START(ffi_closure_trampoline_table_page) 364.rept PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE 365 adr ip, #-PAGE_MAX_SIZE @ the config page is PAGE_MAX_SIZE behind the trampoline page 366 sub ip, #8 @ account for pc bias 367 ldr pc, [ip, #4] @ jump to ffi_closure_SYSV or ffi_closure_VFP 368.endr 369ARM_FUNC_END(ffi_closure_trampoline_table_page) 370#endif 371 372#else 373 374ARM_FUNC_START(ffi_arm_trampoline) 3750: adr ip, 0b 376 ldr pc, 1f 3771: .long 0 378ARM_FUNC_END(ffi_arm_trampoline) 379 380#endif /* FFI_EXEC_TRAMPOLINE_TABLE */ 381#endif /* __arm__ */ 382 383#if defined __ELF__ && defined __linux__ 384 .section .note.GNU-stack,"",%progbits 385#endif 386