summaryrefslogtreecommitdiff
path: root/rts/adjustor/Nativei386.c
blob: c613d361b3ce235b2db7ab0a6074fc2d0ddf37bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* -----------------------------------------------------------------------------
 * i386 architecture adjustor thunk logic.
 * ---------------------------------------------------------------------------*/

#include "rts/PosixSource.h"
#include "Rts.h"

#include "RtsUtils.h"
#include "StablePtr.h"
#include "Adjustor.h"

#if defined(_WIN32)
#include <windows.h>
#endif

extern void adjustorCode(void);

/* !!! !!! WARNING: !!! !!!
 * This structure is accessed from AdjustorAsm.s
 * Any changes here have to be mirrored in the offsets there.
 */

typedef struct AdjustorStub {
    unsigned char   call[8];
    StgStablePtr    hptr;
    StgFunPtr       wptr;
    StgInt          frame_size;
    StgInt          argument_size;
} AdjustorStub;

void*
createAdjustor(int cconv, StgStablePtr hptr,
               StgFunPtr wptr,
               char *typeString STG_UNUSED
    )
{
    switch (cconv)
    {
    case 0: /* _stdcall */
#if !defined(darwin_HOST_OS)
    /* Magic constant computed by inspecting the code length of
       the following assembly language snippet
       (offset and machine code prefixed):

     <0>:       58                popl   %eax              # temp. remove ret addr..
     <1>:       68 fd fc fe fa    pushl  0xfafefcfd        # constant is large enough to
                                                           # hold a StgStablePtr
     <6>:       50                pushl  %eax              # put back ret. addr
     <7>:       b8 fa ef ff 00    movl   $0x00ffeffa, %eax # load up wptr
     <c>:       ff e0             jmp    %eax              # and jump to it.
                # the callee cleans up the stack
    */

    {
        ExecPage *page = allocateExecPage();
        if (page == NULL) {
            barf("createAdjustor: failed to allocate executable page\n");
        }
        uint8_t *adj_code = (uint8_t *) page;
        adj_code[0x00] = 0x58;  /* popl %eax  */

        adj_code[0x01] = 0x68;  /* pushl hptr (which is a dword immediate ) */
        *((StgStablePtr*)(adj_code + 0x02)) = (StgStablePtr)hptr;

        adj_code[0x06] = 0x50; /* pushl %eax */

        adj_code[0x07] = 0xb8; /* movl  $wptr, %eax */
        *((StgFunPtr*)(adj_code + 0x08)) = (StgFunPtr)wptr;

        adj_code[0x0c] = 0xff; /* jmp %eax */
        adj_code[0x0d] = 0xe0;

        freezeExecPage(page);
        return page;
    }
#endif /* !defined(darwin_HOST_OS) */

    case 1: /* _ccall */
    {
        /*
          Most of the trickiness here is due to the need to keep the
          stack pointer 16-byte aligned (see #5250).  That means we
          can't just push another argument on the stack and call the
          wrapper, we may have to shuffle the whole argument block.

          We offload most of the work to AdjustorAsm.S.
        */
        ExecPage *page = allocateExecPage();
        if (page == NULL) {
            barf("createAdjustor: failed to allocate executable page\n");
        }
        AdjustorStub *adjustorStub = (AdjustorStub *) page;
        int sz = totalArgumentSize(typeString);

        adjustorStub->call[0] = 0xe8;
        *(long*)&adjustorStub->call[1] = ((char*)&adjustorCode) - ((char*)page + 5);
        adjustorStub->hptr = hptr;
        adjustorStub->wptr = wptr;

            // The adjustor puts the following things on the stack:
            // 1.) %ebp link
            // 2.) padding and (a copy of) the arguments
            // 3.) a dummy argument
            // 4.) hptr
            // 5.) return address (for returning to the adjustor)
            // All these have to add up to a multiple of 16.

            // first, include everything in frame_size
        adjustorStub->frame_size = sz * 4 + 16;
            // align to 16 bytes
        adjustorStub->frame_size = (adjustorStub->frame_size + 15) & ~15;
            // only count 2.) and 3.) as part of frame_size
        adjustorStub->frame_size -= 12;
        adjustorStub->argument_size = sz;

        freezeExecPage(page);
        return page;
    }

    default:
        barf("createAdjustor: Unsupported calling convention");
    }
}

void
freeHaskellFunctionPtr(void* ptr)
{
    if ( *(unsigned char*)ptr != 0xe8 &&
         *(unsigned char*)ptr != 0x58 ) {
        errorBelch("freeHaskellFunctionPtr: not for me, guv! %p\n", ptr);
        return;
    }
    if (*(unsigned char*)ptr == 0xe8) { /* Aha, a ccall adjustor! */
        freeStablePtr(((AdjustorStub*)ptr)->hptr);
    } else {
        freeStablePtr(*((StgStablePtr*)((unsigned char*)ptr + 0x02)));
    }

    freeExecPage((ExecPage *) ptr);
}