comparison dyncall/dyncall_call_x86_8a.s @ 0:3e629dc19168

initial from svn dyncall-1745
author Daniel Adler
date Thu, 19 Mar 2015 22:24:28 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:3e629dc19168
1 /*
2
3 Package: dyncall
4 Library: dyncall
5 File: dyncall/dyncall_call_x86_8a.s
6 Description: All x86 abi call kernel implementations in Plan9's assembler
7 License:
8
9 Copyright (c) 2007-2011 Daniel Adler <dadler@uni-goettingen.de>,
10 Tassilo Philipp <tphilipp@potion-studios.com>
11
12 Permission to use, copy, modify, and distribute this software for any
13 purpose with or without fee is hereby granted, provided that the above
14 copyright notice and this permission notice appear in all copies.
15
16 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23
24 */
25
26
27
28 /* 64 bit integer return value calls require caller to create space for return
29 value, and pass a pointer to it as 1st argument. This main function handles
30 all cases (32 and 64 bit integers, as well as floats and doubles), however,
31 it has to be called through the 2 corresponding functions at the end of
32 this file.
33 In order to keep things simple, we basically put the pointer to the
34 return value in EDX and call the other assembler code, above. If EDX
35 is not null, then the code below will push it to the stack as first
36 argument.
37 */
38
39 call_main:
40
41 /* Since all registers except SP are scratch, and we have a variable
42 argument size depending on the function to call, we have to find
43 a way to store and restore SP.
44 The idea is to replace the return address with a custom one on the
45 stack, and to put some logic there, jumping back to the real
46 return address. This allows us, to put the SP somewhere next to
47 the fake return address on the stack, so that we can get it back
48 with a fixed offset (relative to the program counter, in our case).
49
50 The only real issue with this approach would be a non-executable
51 stack. However, Plan9 doesn't support w^x at the time of writing.
52 */
53
54 /* On the stack at this point:
55 RETADDR 0(SP)
56 FUNPTR 4(SP)
57 ARGS 8(SP)
58 SIZE 12(SP)
59 */
60
61 MOVL SP, BP /* base pointer for convenience */
62 PUSHL SP /* save stack pointer */
63
64 MOVL 8(BP), SI /* SI = pointer on args */
65 MOVL 12(BP), CX /* CX = size of args */
66
67 SUBL $16, SP /* Make some room for our SP-refetch logic */
68 MOVL SP, BX /* Copy address to new, executable stack space to BX */
69
70 /* This part fills our executable stack space with instructions. We
71 need to get the program counter, first, with a little hack. */
72 MOVL $0x000003e8, 0(SP) /* Copy 'call (cur ip+8)' */
73 MOVL $0x00000000, 4(SP) /* '00' for call address, rest is garbage */
74 MOVL $0x5a909090, 8(SP) /* 'nop, nop, nop, pop edx' to get eip+5 in edx */
75 MOVL $0xc30b628b,12(SP) /* Restore stack ptr and return: 'mov [edx+11] to esp, ret' */
76
77 SUBL CX, SP /* allocate 'size' bytes on stack for args */
78 MOVL SP, DI /* DI = stack args */
79
80 SHRL $2, SP /* Align stack. */
81 SHLL $2, SP /* " " */
82
83 /* I didn't figure out how to use MOVSB with the 8a syntax. The following
84 can probably be written in a better way. */
85 JMP copy_loop_cmp
86 copy_loop:
87 MOVL 0(SI), AX /* Copy args. */
88 MOVL AX, 0(DI)
89 SUBL $4, CX
90 ADDL $4, SI
91 ADDL $4, DI
92 copy_loop_cmp:
93 CMPL CX, $0
94 JGT copy_loop
95
96 /* Check if we need to push a pointer to long long that might be used as
97 container for 64-bit return values. */
98 CMPL DX, $0
99 JEQ call_ffi
100 PUSHL DX
101
102 /* Now we try to fake a call, meaning setting up our fake return address,
103 and then jumping to the FFI call. This should call the function, but
104 the return will jump into our stack space we reserved above. */
105 call_ffi:
106 PUSHL BX
107 MOVL 4(BP), BX
108 JMP BX
109
110 /* Note that there is no return here, b/c the return is in the asm code
111 above, that has been generated on the fly. */
112
113
114 /* Main call for 32 bit integer return values and floating point arguments.
115 See call_main for explanation. */
116 TEXT dcCall_x86_plan9(SB), $0
117
118 MOVL $0, DX
119 JMP call_main
120
121
122 /* Call for 64 bit integer return values.
123 See call_main for explanation. */
124 TEXT dcCall_x86_plan9_ll(SB), $0
125
126 MOVL 16(SP), DX /* Copy pointer to variable for return value. */
127 JMP call_main