2 * Copyright 2019 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "wine/debug.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(atlthunk
);
25 #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
27 struct AtlThunkData_t
{
28 struct thunk_pool
*pool
;
33 /* Thunk replaces the first argument and jumps to provided proc. */
37 #if defined(__x86_64__)
38 BYTE mov_rip_rcx
[3]; /* mov mov_offset(%rip), %rcx */
40 WORD jmp_rip
; /* jmp *jmp_offset(%rip) */
42 #elif defined(__i386__)
43 BYTE mov_data_addr_eax
; /* movl data_addr, %eax */
45 DWORD mov_eax_esp
; /* movl %eax, 4(%esp) */
47 DWORD jmp_addr
; /* jmp *jmp_addr */
48 #elif defined(__aarch64__)
49 DWORD ldr_x0
; /* ldr x0,data_addr */
50 DWORD ldr_x16
; /* ldr x16,proc_addr */
51 DWORD br_x16
; /* br x16 */
57 #define THUNK_POOL_SIZE (4096 / sizeof(struct thunk_code))
61 struct thunk_code thunks
[THUNK_POOL_SIZE
];
64 AtlThunkData_t data
[THUNK_POOL_SIZE
];
67 C_ASSERT(FIELD_OFFSET(struct thunk_pool
, first_free
) == 4096);
69 static struct thunk_pool
*alloc_thunk_pool(void)
71 struct thunk_pool
*thunks
;
75 if (!(thunks
= VirtualAlloc(NULL
, sizeof(*thunks
), MEM_COMMIT
, PAGE_READWRITE
)))
78 for (i
= 0; i
< ARRAY_SIZE(thunks
->thunks
); i
++)
80 struct thunk_code
*thunk
= &thunks
->thunks
[i
];
81 #if defined(__x86_64__)
82 thunk
->mov_rip_rcx
[0] = 0x48; /* mov mov_offset(%rip), %rcx */
83 thunk
->mov_rip_rcx
[1] = 0x8b;
84 thunk
->mov_rip_rcx
[2] = 0x0d;
85 thunk
->mov_offset
= (const BYTE
*)&thunks
->data
[i
].arg
- (const BYTE
*)(&thunk
->mov_offset
+ 1);
86 thunk
->jmp_rip
= 0x25ff; /* jmp *jmp_offset(%rip) */
87 thunk
->jmp_offset
= (const BYTE
*)&thunks
->data
[i
].proc
- (const BYTE
*)(&thunk
->jmp_offset
+ 1);
88 #elif defined(__i386__)
89 thunk
->mov_data_addr_eax
= 0xa1; /* movl data_addr, %eax */
90 thunk
->data_addr
= (DWORD
)&thunks
->data
[i
].arg
;
91 thunk
->mov_eax_esp
= 0x04244489; /* movl %eax, 4(%esp) */
92 thunk
->jmp
= 0x25ff; /* jmp *jmp_addr */
93 thunk
->jmp_addr
= (DWORD
)&thunks
->data
[i
].proc
;
94 #elif defined(__aarch64__)
95 thunk
->ldr_x0
= 0x58000000 | (((DWORD
*)&thunks
->data
[i
].arg
- &thunk
->ldr_x0
) << 5);
96 thunk
->ldr_x16
= 0x58000010 | (((DWORD
*)&thunks
->data
[i
].proc
- &thunk
->ldr_x16
) << 5);
97 thunk
->br_x16
= 0xd61f0200;
100 VirtualProtect(thunks
->thunks
, FIELD_OFFSET(struct thunk_pool
, first_free
), PAGE_EXECUTE_READ
, &old_protect
);
101 thunks
->first_free
= 0;
102 thunks
->free_count
= 0;
106 static struct thunk_pool
*current_pool
;
108 BOOL WINAPI
DllMain(HINSTANCE instance
, DWORD reason
, LPVOID reserved
)
112 case DLL_PROCESS_ATTACH
:
113 DisableThreadLibraryCalls(instance
);
115 case DLL_PROCESS_DETACH
:
117 if (current_pool
&& current_pool
->free_count
== current_pool
->first_free
)
118 VirtualFree(current_pool
, sizeof(*current_pool
), MEM_RELEASE
);
124 static CRITICAL_SECTION thunk_alloc_cs
;
125 static CRITICAL_SECTION_DEBUG thunk_alloc_cs_debug
= {
126 0, 0, &thunk_alloc_cs
,
127 { &thunk_alloc_cs_debug
.ProcessLocksList
,
128 &thunk_alloc_cs_debug
.ProcessLocksList
},
129 0, 0, { (DWORD_PTR
)(__FILE__
": thunk_alloc_cs") }
131 static CRITICAL_SECTION thunk_alloc_cs
= { &thunk_alloc_cs_debug
, -1, 0, 0, 0, 0 };
133 AtlThunkData_t
* WINAPI
AtlThunk_AllocateData(void)
135 AtlThunkData_t
*thunk
= NULL
;
137 EnterCriticalSection(&thunk_alloc_cs
);
139 if (!current_pool
) current_pool
= alloc_thunk_pool();
142 thunk
= ¤t_pool
->data
[current_pool
->first_free
];
143 thunk
->pool
= current_pool
;
146 if (++current_pool
->first_free
== ARRAY_SIZE(current_pool
->data
))
150 LeaveCriticalSection(&thunk_alloc_cs
);
154 WNDPROC WINAPI
AtlThunk_DataToCode(AtlThunkData_t
*thunk
)
156 WNDPROC code
= (WNDPROC
)&thunk
->pool
->thunks
[thunk
- thunk
->pool
->data
];
157 TRACE("(%p) -> %p\n", thunk
, code
);
161 void WINAPI
AtlThunk_FreeData(AtlThunkData_t
*thunk
)
163 if (InterlockedIncrement(&thunk
->pool
->free_count
) == ARRAY_SIZE(thunk
->pool
->thunks
))
164 VirtualFree(thunk
->pool
, sizeof(*thunk
->pool
), MEM_RELEASE
);
167 void WINAPI
AtlThunk_InitData(AtlThunkData_t
*thunk
, void *proc
, SIZE_T arg
)
173 #else /* __i386__ || __x86_64__ || __aarch64__ */
175 AtlThunkData_t
* WINAPI
AtlThunk_AllocateData(void)
177 FIXME("Unsupported architecture.\n");
181 WNDPROC WINAPI
AtlThunk_DataToCode(AtlThunkData_t
*thunk
)
186 void WINAPI
AtlThunk_FreeData(AtlThunkData_t
*thunk
)
190 void WINAPI
AtlThunk_InitData(AtlThunkData_t
*thunk
, void *proc
, SIZE_T arg
)
194 #endif /* __i386__ || __x86_64__ || __aarch64__ */