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__)
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 */
52 #define THUNK_POOL_SIZE (4096 / sizeof(struct thunk_code))
56 struct thunk_code thunks
[THUNK_POOL_SIZE
];
59 AtlThunkData_t data
[THUNK_POOL_SIZE
];
62 C_ASSERT(FIELD_OFFSET(struct thunk_pool
, first_free
) == 4096);
64 static struct thunk_pool
*alloc_thunk_pool(void)
66 struct thunk_pool
*thunks
;
70 if (!(thunks
= VirtualAlloc(NULL
, sizeof(*thunks
), MEM_COMMIT
, PAGE_READWRITE
)))
73 for (i
= 0; i
< ARRAY_SIZE(thunks
->thunks
); i
++)
75 struct thunk_code
*thunk
= &thunks
->thunks
[i
];
76 #if defined(__x86_64__)
77 thunk
->mov_rip_rcx
[0] = 0x48; /* mov mov_offset(%rip), %rcx */
78 thunk
->mov_rip_rcx
[1] = 0x8b;
79 thunk
->mov_rip_rcx
[2] = 0x0d;
80 thunk
->mov_offset
= (const BYTE
*)&thunks
->data
[i
].arg
- (const BYTE
*)(&thunk
->mov_offset
+ 1);
81 thunk
->jmp_rip
= 0x25ff; /* jmp *jmp_offset(%rip) */
82 thunk
->jmp_offset
= (const BYTE
*)&thunks
->data
[i
].proc
- (const BYTE
*)(&thunk
->jmp_offset
+ 1);
83 #elif defined(__i386__)
84 thunk
->mov_data_addr_eax
= 0xa1; /* movl data_addr, %eax */
85 thunk
->data_addr
= (DWORD
)&thunks
->data
[i
].arg
;
86 thunk
->mov_eax_esp
= 0x04244489; /* movl %eax, 4(%esp) */
87 thunk
->jmp
= 0x25ff; /* jmp *jmp_addr */
88 thunk
->jmp_addr
= (DWORD
)&thunks
->data
[i
].proc
;
91 VirtualProtect(thunks
->thunks
, FIELD_OFFSET(struct thunk_pool
, first_free
), PAGE_EXECUTE_READ
, &old_protect
);
92 thunks
->first_free
= 0;
93 thunks
->free_count
= 0;
97 static struct thunk_pool
*current_pool
;
99 BOOL WINAPI
DllMain(HINSTANCE instance
, DWORD reason
, LPVOID reserved
)
103 case DLL_PROCESS_ATTACH
:
104 DisableThreadLibraryCalls(instance
);
106 case DLL_PROCESS_DETACH
:
108 if (current_pool
&& current_pool
->free_count
== current_pool
->first_free
)
109 VirtualFree(current_pool
, sizeof(*current_pool
), MEM_RELEASE
);
115 static CRITICAL_SECTION thunk_alloc_cs
;
116 static CRITICAL_SECTION_DEBUG thunk_alloc_cs_debug
= {
117 0, 0, &thunk_alloc_cs
,
118 { &thunk_alloc_cs_debug
.ProcessLocksList
,
119 &thunk_alloc_cs_debug
.ProcessLocksList
},
120 0, 0, { (DWORD_PTR
)(__FILE__
": thunk_alloc_cs") }
122 static CRITICAL_SECTION thunk_alloc_cs
= { &thunk_alloc_cs_debug
, -1, 0, 0, 0, 0 };
124 AtlThunkData_t
* WINAPI
AtlThunk_AllocateData(void)
126 AtlThunkData_t
*thunk
= NULL
;
128 EnterCriticalSection(&thunk_alloc_cs
);
130 if (!current_pool
) current_pool
= alloc_thunk_pool();
133 thunk
= ¤t_pool
->data
[current_pool
->first_free
];
134 thunk
->pool
= current_pool
;
137 if (++current_pool
->first_free
== ARRAY_SIZE(current_pool
->data
))
141 LeaveCriticalSection(&thunk_alloc_cs
);
145 WNDPROC WINAPI
AtlThunk_DataToCode(AtlThunkData_t
*thunk
)
147 WNDPROC code
= (WNDPROC
)&thunk
->pool
->thunks
[thunk
- thunk
->pool
->data
];
148 TRACE("(%p) -> %p\n", thunk
, code
);
152 void WINAPI
AtlThunk_FreeData(AtlThunkData_t
*thunk
)
154 if (InterlockedIncrement(&thunk
->pool
->free_count
) == ARRAY_SIZE(thunk
->pool
->thunks
))
155 VirtualFree(thunk
->pool
, sizeof(*thunk
->pool
), MEM_RELEASE
);
158 void WINAPI
AtlThunk_InitData(AtlThunkData_t
*thunk
, void *proc
, SIZE_T arg
)
164 #else /* __i386__ || __x86_64__ */
166 AtlThunkData_t
* WINAPI
AtlThunk_AllocateData(void)
168 FIXME("Unsupported architecture.\n");
172 WNDPROC WINAPI
AtlThunk_DataToCode(AtlThunkData_t
*thunk
)
177 void WINAPI
AtlThunk_FreeData(AtlThunkData_t
*thunk
)
181 void WINAPI
AtlThunk_InitData(AtlThunkData_t
*thunk
, void *proc
, SIZE_T arg
)
185 #endif /* __i386__ || __x86_64__ */