Mercurial > pub > dyncall > dyncall
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 |