1
/***********************************************************************
2
This file contains functions that implement the delay loader on Windows.
4
This is a customized version of delay loader with limited functionalities.
8
* multiple delay loaded DLLs
9
* multiple loading of the same DLL
11
This delay loader is used only by the InnoDB plugin. Other components (DLLs)
12
can still use the default delay loader, provided by MSVC.
14
Several acronyms used by Microsoft:
15
* IAT: import address table
16
* INT: import name table
17
* RVA: Relative Virtual Address
19
See http://msdn.microsoft.com/en-us/magazine/bb985992.aspx for details of
23
***********************************************************************/
24
#if defined (__WIN__) && defined (MYSQL_DYNAMIC_PLUGIN)
25
# define WIN32_LEAN_AND_MEAN
27
# include <delayimp.h>
28
# include <mysql_priv.h>
32
# include "hash0hash.h"
35
/***********************************************************************
36
This following contains a list of externals that can not be resolved by
37
delay loading. They have to be resolved indirectly via their addresses
38
in the .map file. All of them are external variables. */
39
CHARSET_INFO* wdl_my_charset_bin;
40
CHARSET_INFO* wdl_my_charset_latin1;
41
CHARSET_INFO* wdl_my_charset_filename;
42
CHARSET_INFO** wdl_system_charset_info;
43
CHARSET_INFO** wdl_default_charset_info;
44
CHARSET_INFO** wdl_all_charsets;
45
system_variables* wdl_global_system_variables;
46
char* wdl_mysql_real_data_home;
47
char** wdl_mysql_data_home;
48
char** wdl_tx_isolation_names;
49
char** wdl_binlog_format_names;
51
pthread_mutex_t* wdl_LOCK_thread_count;
52
key_map* wdl_key_map_full;
53
MY_TMPDIR* wdl_mysql_tmpdir_list;
54
bool* wdl_mysqld_embedded;
55
uint* wdl_lower_case_table_names;
56
ulong* wdl_specialflag;
59
/***********************************************************************
60
The following is defined in ha_innodb.cc. It is used for copying the
61
system variables from the builtin innodb plugin to the dynamic plugin.
63
extern struct st_mysql_plugin* builtin_innobase_plugin_ptr;
65
/***********************************************************************
66
The preffered load-address defined in PE (portable executable format).*/
68
#pragma section(".base", long, read)
70
__declspec(allocate(".base"))
71
const IMAGE_DOS_HEADER __ImageBase;
74
const IMAGE_DOS_HEADER __ImageBase;
77
/***********************************************************************
78
A template function for converting a relative address (RVA) to an
79
absolute address (VA). This is due to the pointers in the delay
80
descriptor (ImgDelayDescr in delayimp.h) have been changed from
81
VAs to RVAs to work on both 32- and 64-bit platforms. */
84
return X(PBYTE(&__ImageBase) + rva);
87
/***********************************************************************
88
Convert to the old format for convenience. The structure as well as its
89
element names follow the definition of ImgDelayDescr in delayimp.h. */
90
struct InternalImgDelayDescr {
91
DWORD grAttrs; /* attributes */
92
LPCSTR szName; /* pointer to dll name */
93
HMODULE* phmod; /* address of module handle */
94
PImgThunkData pIAT; /* address of the IAT */
95
PCImgThunkData pINT; /* address of the INT */
96
PCImgThunkData pBoundIAT; /* address of the optional bound IAT */
97
PCImgThunkData pUnloadIAT; /* address of optional copy of
99
DWORD dwTimeStamp; /* 0 if not bound,
100
otherwise date/time stamp of DLL
101
bound to (Old BIND) */
104
typedef struct map_hash_chain_struct map_hash_chain_t;
106
struct map_hash_chain_struct {
107
char* symbol; /* pointer to a symbol */
108
ulint value; /* address of the symbol */
109
map_hash_chain_t* next; /* pointer to the next cell
110
in the same folder. */
111
map_hash_chain_t* chain; /* a linear chain used for
115
static HMODULE my_hmod = 0;
116
static struct hash_table_struct* m_htbl = NULL ;
117
static map_hash_chain_t* chain_header = NULL;
118
static ibool wdl_init = FALSE;
119
const ulint MAP_HASH_CELLS_NUM = 10000;
122
/***********************************************************************
123
In the dynamic plugin, it is required to call the following dbug functions
131
The plugin will get those function pointers during the initialization.
133
typedef void (__cdecl* pfn_db_enter_)(
137
const char** _sfunc_,
138
const char** _sfile_,
142
typedef void (__cdecl* pfn_db_return_)(
144
const char** _sfunc_,
145
const char** _sfile_,
148
typedef void (__cdecl* pfn_db_pargs_)(
150
const char* keyword);
152
typedef void (__cdecl* pfn_db_doprnt_)(
156
typedef void (__cdecl* pfn_db_dump_)(
159
const unsigned char* memory,
162
static pfn_db_enter_ wdl_db_enter_;
163
static pfn_db_return_ wdl_db_return_;
164
static pfn_db_pargs_ wdl_db_pargs_;
165
static pfn_db_doprnt_ wdl_db_doprnt_;
166
static pfn_db_dump_ wdl_db_dump_;
167
#endif /* !DBUG_OFF */
169
/*****************************************************************
170
Creates a hash table with >= n array cells. The actual number of cells is
171
chosen to be a prime number slightly bigger than n.
173
This is the same function as hash_create in hash0hash.c, except the
174
memory allocation. This function is invoked before the engine is
175
initialized, and buffer pools are not ready yet. */
180
/* out, own: created hash table */
181
ulint n) /* in: number of array cells */
187
prime = ut_find_prime(n);
189
table = (hash_table_t*) malloc(sizeof(hash_table_t));
194
array = (hash_cell_t*) malloc(sizeof(hash_cell_t) * prime);
200
table->array = array;
201
table->n_cells = prime;
202
table->n_mutexes = 0;
203
table->mutexes = NULL;
206
table->magic_n = HASH_TABLE_MAGIC_N;
208
/* Initialize the cell array */
209
hash_table_clear(table);
214
/*****************************************************************
215
Frees a hash table. */
220
hash_table_t* table) /* in, own: hash table */
223
ut_a(table->mutexes == NULL);
229
/***********************************************************************
230
Function for calculating the count of imports given the base of the IAT. */
235
/* out: number of imports */
236
PCImgThunkData pitd_base) /* in: base of the IAT */
239
PCImgThunkData pitd = pitd_base;
241
while (pitd->u1.Function) {
249
/***********************************************************************
250
Read Mapfile to a hashtable for faster access */
255
/* out: TRUE if the mapfile is
256
loaded successfully. */
257
const char* filename) /* in: name of the mapfile. */
260
const size_t nSize = 256;
265
ibool valid_load_addr = FALSE;
267
const char* tmp_string = " Preferred load address is %16llx";
269
const char* tmp_string = " Preferred load address is %08x";
272
fp = fopen(filename, "r");
278
/* Check whether to create the hashtable */
279
if (m_htbl == NULL) {
281
m_htbl = wdl_hash_create(MAP_HASH_CELLS_NUM);
283
if (m_htbl == NULL) {
290
/* Search start of symbol list and get the preferred load address */
291
while (fgets(tmp_buf, sizeof(tmp_buf), fp)) {
293
if (sscanf(tmp_buf, tmp_string, &load_addr) == 1) {
295
valid_load_addr = TRUE;
298
if (strstr(tmp_buf, "Rva+Base") != NULL) {
304
if (valid_load_addr == FALSE) {
306
/* No "Preferred load address", the map file is wrong. */
311
/* Read symbol list */
312
while (fgets(tmp_buf, sizeof(tmp_buf), fp))
314
map_hash_chain_t* map_cell;
322
func_name = strtok(tmp_buf, " ");
323
func_name = strtok(NULL, " ");
324
func_addr = strtok(NULL, " ");
326
if (func_name && func_addr) {
328
ut_snprintf(tmp_buf, nSize, "0x%s", func_addr);
329
if (*func_name == '_') {
334
map_cell = (map_hash_chain_t*)
335
malloc(sizeof(map_hash_chain_t));
336
if (map_cell == NULL) {
340
/* Chain all cells together */
341
map_cell->chain = chain_header;
342
chain_header = map_cell;
344
map_cell->symbol = strdup(func_name);
345
map_cell->value = (ulint) strtoull(tmp_buf, NULL, 0)
347
map_fold = ut_fold_string(map_cell->symbol);
349
HASH_INSERT(map_hash_chain_t,
362
/*****************************************************************
363
Cleanup.during DLL unload */
369
while (chain_header != NULL) {
370
map_hash_chain_t* tmp;
372
tmp = chain_header->chain;
373
free(chain_header->symbol);
378
if (m_htbl != NULL) {
380
wdl_hash_table_free(m_htbl);
384
/***********************************************************************
385
Load the mapfile mysqld.map. */
388
wdl_get_mysqld_mapfile(void)
389
/*========================*/
390
/* out: the module handle */
392
char file_name[MAX_PATH];
398
size_t nSize = MAX_PATH - strlen(".map") -1;
400
/* First find out the name of current executable */
401
my_hmod = GetModuleHandle(NULL);
407
err = GetModuleFileName(my_hmod, file_name, nSize);
414
ext = strrchr(file_name, '.');
418
strcat(file_name, ".map");
420
err = wdl_load_mapfile(file_name);
434
/***********************************************************************
435
Retrieves the address of an exported function. It follows the convention
436
of GetProcAddress(). */
439
wdl_get_procaddr_from_map(
440
/*======================*/
441
/* out: address of exported
443
HANDLE m_handle, /* in: module handle */
444
const char* import_proc) /* in: procedure name */
446
map_hash_chain_t* hash_chain;
449
map_fold = ut_fold_string(import_proc);
456
(ut_strcmp(hash_chain->symbol, import_proc) == 0));
458
if (hash_chain == NULL) {
461
/* On Win64, the leading '_' may not be taken out. In this
462
case, search again without the leading '_'. */
463
if (*import_proc == '_') {
468
map_fold = ut_fold_string(import_proc);
475
(ut_strcmp(hash_chain->symbol, import_proc) == 0));
477
if (hash_chain == NULL) {
479
if (wdl_init == TRUE) {
482
"InnoDB: the procedure pointer of %s"
493
return((FARPROC) ((ulint) m_handle + hash_chain->value));
496
/***********************************************************************
497
Retrieves the address of an exported variable.
498
Note: It does not follow the Windows call convention FARPROC. */
501
wdl_get_varaddr_from_map(
502
/*=====================*/
503
/* out: address of exported
505
HANDLE m_handle, /* in: module handle */
506
const char* import_variable) /* in: variable name */
508
map_hash_chain_t* hash_chain;
511
map_fold = ut_fold_string(import_variable);
518
(ut_strcmp(hash_chain->symbol, import_variable) == 0));
520
if (hash_chain == NULL) {
523
/* On Win64, the leading '_' may not be taken out. In this
524
case, search again without the leading '_'. */
525
if (*import_variable == '_') {
530
map_fold = ut_fold_string(import_variable);
537
(ut_strcmp(hash_chain->symbol, import_variable) == 0));
539
if (hash_chain == NULL) {
541
if (wdl_init == TRUE) {
544
"InnoDB: the variable address of %s"
555
return((void*) ((ulint) m_handle + hash_chain->value));
558
/***********************************************************************
559
Bind all unresolved external variables from the MySQL executable. */
562
wdl_get_external_variables(void)
563
/*============================*/
564
/* out: TRUE if successful */
566
HMODULE hmod = wdl_get_mysqld_mapfile();
573
#define GET_SYM(sym, var, type) \
574
var = (type*) wdl_get_varaddr_from_map(hmod, sym); \
575
if (var == NULL) return(FALSE)
577
#define GET_SYM2(sym1, sym2, var, type) \
578
var = (type*) wdl_get_varaddr_from_map(hmod, sym1); \
579
if (var == NULL) return(FALSE)
581
#define GET_SYM2(sym1, sym2, var, type) \
582
var = (type*) wdl_get_varaddr_from_map(hmod, sym2); \
583
if (var == NULL) return(FALSE)
585
#define GET_C_SYM(sym, type) GET_SYM(#sym, wdl_##sym, type)
586
#define GET_PROC_ADDR(sym) \
587
wdl##sym = (pfn##sym) wdl_get_procaddr_from_map(hmod, #sym)
589
GET_C_SYM(my_charset_bin, CHARSET_INFO);
590
GET_C_SYM(my_charset_latin1, CHARSET_INFO);
591
GET_C_SYM(my_charset_filename, CHARSET_INFO);
592
GET_C_SYM(default_charset_info, CHARSET_INFO*);
593
GET_C_SYM(all_charsets, CHARSET_INFO*);
594
GET_C_SYM(my_umask, int);
596
GET_SYM("?global_system_variables@@3Usystem_variables@@A",
597
wdl_global_system_variables, struct system_variables);
598
GET_SYM("?mysql_real_data_home@@3PADA",
599
wdl_mysql_real_data_home, char);
600
GET_SYM("?reg_ext@@3PADA", wdl_reg_ext, char);
601
GET_SYM("?LOCK_thread_count@@3U_RTL_CRITICAL_SECTION@@A",
602
wdl_LOCK_thread_count, pthread_mutex_t);
603
GET_SYM("?key_map_full@@3V?$Bitmap@$0EA@@@A",
604
wdl_key_map_full, key_map);
605
GET_SYM("?mysql_tmpdir_list@@3Ust_my_tmpdir@@A",
606
wdl_mysql_tmpdir_list, MY_TMPDIR);
607
GET_SYM("?mysqld_embedded@@3_NA",
608
wdl_mysqld_embedded, bool);
609
GET_SYM("?lower_case_table_names@@3IA",
610
wdl_lower_case_table_names, uint);
611
GET_SYM("?specialflag@@3KA", wdl_specialflag, ulong);
613
GET_SYM2("?system_charset_info@@3PEAUcharset_info_st@@EA",
614
"?system_charset_info@@3PAUcharset_info_st@@A",
615
wdl_system_charset_info, CHARSET_INFO*);
616
GET_SYM2("?mysql_data_home@@3PEADEA",
617
"?mysql_data_home@@3PADA",
618
wdl_mysql_data_home, char*);
619
GET_SYM2("?tx_isolation_names@@3PAPEBDA",
620
"?tx_isolation_names@@3PAPBDA",
621
wdl_tx_isolation_names, char*);
622
GET_SYM2("?binlog_format_names@@3PAPEBDA",
623
"?binlog_format_names@@3PAPBDA",
624
wdl_binlog_format_names, char*);
626
/* It is fine if builtin_innobase_plugin is not available. */
627
builtin_innobase_plugin_ptr = (struct st_mysql_plugin*)
628
wdl_get_varaddr_from_map(
630
"?builtin_innobase_plugin@@3PAUst_mysql_plugin@@A");
633
GET_PROC_ADDR(_db_enter_);
634
GET_PROC_ADDR(_db_return_);
635
GET_PROC_ADDR(_db_pargs_);
636
GET_PROC_ADDR(_db_doprnt_);
637
GET_PROC_ADDR(_db_dump_);
639
/* If any of the dbug functions is not available, just make them
640
all invalid. This is the case when working with a non-debug
641
version of the server. */
642
if (wdl_db_enter_ == NULL || wdl_db_return_ == NULL
643
|| wdl_db_pargs_ == NULL || wdl_db_doprnt_ == NULL
644
|| wdl_db_dump_ == NULL) {
646
wdl_db_enter_ = NULL;
647
wdl_db_return_ = NULL;
648
wdl_db_pargs_ = NULL;
649
wdl_db_doprnt_ = NULL;
652
#endif /* !DBUG_OFF */
663
/***********************************************************************
664
The DLL Delayed Loading Helper Function for resolving externals.
666
The function may fail due to one of the three reasons:
668
* Invalid parameter, which happens if the attributes in pidd aren't
670
* Failed to load the map file mysqld.map.
671
* Failed to find an external name in the map file mysqld.map.
673
Note: this function is called by run-time as well as __HrLoadAllImportsForDll.
674
So, it has to follow Windows call convention. */
679
/* out: the address of the imported
681
PCImgDelayDescr pidd, /* in: a const pointer to a
682
ImgDelayDescr, see delayimp.h. */
683
FARPROC* iat_entry) /* in/out: A pointer to the slot in
684
the delay load import address table
685
to be updated with the address of the
686
imported function. */
693
/* Set up data used for the hook procs */
694
InternalImgDelayDescr idd = {
696
PFromRva<LPCSTR>(pidd->rvaDLLName),
697
PFromRva<HMODULE*>(pidd->rvaHmod),
698
PFromRva<PImgThunkData>(pidd->rvaIAT),
699
PFromRva<PCImgThunkData>(pidd->rvaINT),
700
PFromRva<PCImgThunkData>(pidd->rvaBoundIAT),
701
PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT),
705
DelayLoadInfo dli = {
706
sizeof(DelayLoadInfo),
716
/* Check the Delay Load Attributes, log an error of invalid
717
parameter, which happens if the attributes in pidd are not
718
specified correctly. */
719
if ((idd.grAttrs & dlattrRva) == 0) {
721
sql_print_error("InnoDB: invalid parameter for delay loader.");
727
/* Calculate the index for the IAT entry in the import address table.
728
The INT entries are ordered the same as the IAT entries so the
729
calculation can be done on the IAT side. */
730
iIAT = (PCImgThunkData) iat_entry - idd.pIAT;
733
pitd = &(idd.pINT[iINT]);
735
dli.dlp.fImportByName = !IMAGE_SNAP_BY_ORDINAL(pitd->u1.Ordinal);
737
if (dli.dlp.fImportByName) {
739
dli.dlp.szProcName = (LPCSTR) (PFromRva<PIMAGE_IMPORT_BY_NAME>
740
((RVA) ((UINT_PTR) pitd->u1.AddressOfData))->Name);
743
dli.dlp.dwOrdinal = (ulint) IMAGE_ORDINAL(pitd->u1.Ordinal);
746
/* Now, load the mapfile, if it has not been done yet */
749
hmod = wdl_get_mysqld_mapfile();
753
/* LoadLibrary failed. */
754
PDelayLoadInfo rgpdli[1] = {&dli};
756
dli.dwLastError = ::GetLastError();
759
"InnoDB: failed to load mysqld.map with error %d.",
765
/* Store the library handle. */
768
/* Go for the procedure now. */
771
if (pidd->rvaBoundIAT && pidd->dwTimeStamp) {
773
/* Bound imports exist, check the timestamp from the target
775
PIMAGE_NT_HEADERS pinh;
777
pinh = (PIMAGE_NT_HEADERS) ((byte*) hmod
778
+ ((PIMAGE_DOS_HEADER) hmod)->e_lfanew);
780
if (pinh->Signature == IMAGE_NT_SIGNATURE
781
&& pinh->FileHeader.TimeDateStamp == idd.dwTimeStamp
782
&& (DWORD) hmod == pinh->OptionalHeader.ImageBase) {
784
/* We have a decent address in the bound IAT. */
785
fun = (FARPROC) (UINT_PTR)
786
idd.pBoundIAT[iIAT].u1.Function;
796
fun = wdl_get_procaddr_from_map(hmod, dli.dlp.szProcName);
807
/***********************************************************************
808
Unload a DLL that was delay loaded. This function is called by run-time. */
811
__FUnloadDelayLoadedDLL2(
812
/*=====================*/
813
/* out: TRUE is returned if the DLL is found
814
and the IAT matches the original one. */
815
LPCSTR module_name) /* in: DLL name */
820
/******************************************************************
821
Load all imports from a DLL that was specified with the /delayload linker
823
Note: this function is called by run-time. So, it has to follow Windows call
827
__HrLoadAllImportsForDll(
828
/*=====================*/
829
/* out: S_OK if the DLL matches, otherwise
830
ERROR_MOD_NOT_FOUND is returned. */
831
LPCSTR module_name) /* in: DLL name */
833
PIMAGE_NT_HEADERS img;
834
PCImgDelayDescr pidd;
835
IMAGE_DATA_DIRECTORY* image_data;
836
LPCSTR current_module;
837
HRESULT ret = ERROR_MOD_NOT_FOUND;
838
HMODULE hmod = (HMODULE) &__ImageBase;
840
img = (PIMAGE_NT_HEADERS) ((byte*) hmod
841
+ ((PIMAGE_DOS_HEADER) hmod)->e_lfanew);
843
&img->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT];
845
/* Scan the delay load IAT/INT for the DLL */
846
if (image_data->Size) {
848
pidd = PFromRva<PCImgDelayDescr>(image_data->VirtualAddress);
850
/* Check all of the listed DLLs we want to load. */
851
while (pidd->rvaDLLName) {
853
current_module = PFromRva<LPCSTR>(pidd->rvaDLLName);
855
if (stricmp(module_name, current_module) == 0) {
857
/* Found it, break out with pidd and
858
current_module set appropriately */
862
/* To the next delay import descriptor */
866
if (pidd->rvaDLLName) {
868
/* Found a matching DLL, now process it. */
872
iat_entry = PFromRva<FARPROC*>(pidd->rvaIAT);
873
count = wdl_import_count((PCImgThunkData) iat_entry);
875
/* now load all the imports from the DLL */
878
/* No need to check the return value */
879
__delayLoadHelper2(pidd, iat_entry);
891
/******************************************************************
892
The main function of a DLL */
897
/* out: TRUE if the call succeeds */
898
HINSTANCE hinstDLL, /* in: handle to the DLL module */
899
DWORD fdwReason, /* Reason code that indicates why the
900
DLL entry-point function is being
902
LPVOID lpvReserved) /* in: additional parameter based on
909
case DLL_PROCESS_ATTACH:
910
success = wdl_get_external_variables();
913
case DLL_PROCESS_DETACH:
922
/******************************************************************
923
Process entry point to user function. It makes the call to _db_enter_
924
in mysqld.exe. The DBUG functions are defined in my_dbug.h. */
925
extern "C" UNIV_INTERN
928
const char* _func_, /* in: current function name */
929
const char* _file_, /* in: current file name */
930
uint _line_, /* in: current source line number */
931
const char** _sfunc_, /* out: previous _func_ */
932
const char** _sfile_, /* out: previous _file_ */
933
uint* _slevel_, /* out: previous nesting level */
934
char*** _sframep_) /* out: previous frame pointer */
936
if (wdl_db_enter_ != NULL) {
938
wdl_db_enter_(_func_, _file_, _line_, _sfunc_, _sfile_,
939
_slevel_, _sframep_);
943
/******************************************************************
944
Process exit from user function. It makes the call to _db_return_()
946
extern "C" UNIV_INTERN
949
uint _line_, /* in: current source line number */
950
const char** _sfunc_, /* out: previous _func_ */
951
const char** _sfile_, /* out: previous _file_ */
952
uint* _slevel_) /* out: previous level */
954
if (wdl_db_return_ != NULL) {
956
wdl_db_return_(_line_, _sfunc_, _sfile_, _slevel_);
960
/******************************************************************
961
Log arguments for subsequent use. It makes the call to _db_pargs_()
963
extern "C" UNIV_INTERN
966
uint _line_, /* in: current source line number */
967
const char* keyword) /* in: keyword for current macro */
969
if (wdl_db_pargs_ != NULL) {
971
wdl_db_pargs_(_line_, keyword);
975
/******************************************************************
976
Handle print of debug lines. It saves the text into a buffer first,
977
then makes the call to _db_doprnt_() in the server. The text is
978
truncated to the size of buffer. */
979
extern "C" UNIV_INTERN
982
const char* format, /* in: the format string */
983
...) /* in: list of arguments */
988
if (wdl_db_doprnt_ != NULL) {
990
va_start(argp, format);
991
/* it is ok to ignore the trunction. */
992
_vsnprintf(buffer, sizeof(buffer), format, argp);
993
wdl_db_doprnt_(buffer);
998
/******************************************************************
999
Dump a string in hex. It makes the call to _db_dump_() in the server. */
1000
extern "C" UNIV_INTERN
1003
uint _line_, /* in: current source line
1005
const char* keyword, /* in: keyword list */
1006
const unsigned char* memory, /* in: memory to dump */
1007
size_t length) /* in: bytes to dump */
1009
if (wdl_db_dump_ != NULL) {
1011
wdl_db_dump_(_line_, keyword, memory, length);
1015
#endif /* !DBUG_OFF */
1016
#endif /* defined (__WIN__) && defined (MYSQL_DYNAMIC_PLUGIN) */