0
|
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
|