Fix spelling
[heimdal.git] / lib / roken / dlfcn_w32.c
blob37a7d126e9cda2fdf78326854e20ececed3ad917
1 /*
2 * dlfcn-win32
3 * Copyright (c) 2007 Ramiro Polla
4 * Copyright (c) 2015 Tiancheng "Timothy" Gu
5 * Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
6 * Copyright (c) 2020 Ralf Habacker <ralf.habacker@freenet.de>
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
28 * Copied from https://github.com/dlfcn-win32/dlfcn-win32 and modified only to
29 * fit Heimdal's lib/roken.
32 #ifndef _WIN32_WINNT
33 #define _WIN32_WINNT 0x0501
34 #endif
35 #ifdef _DEBUG
36 #define _CRTDBG_MAP_ALLOC
37 #include <stdlib.h>
38 #include <crtdbg.h>
39 #endif
40 #include <windows.h>
41 #include <stdio.h>
42 #include <stdlib.h>
44 #ifdef _MSC_VER
45 /* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
46 #pragma intrinsic(_ReturnAddress)
47 #else
48 /* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
49 #ifndef _ReturnAddress
50 #define _ReturnAddress() (__builtin_extract_return_addr(__builtin_return_address(0)))
51 #endif
52 #endif
54 #ifdef DLFCN_WIN32_SHARED
55 #define DLFCN_WIN32_EXPORTS
56 #endif
57 #include "dlfcn.h"
59 /* Note:
60 * MSDN says these functions are not thread-safe. We make no efforts to have
61 * any kind of thread safety.
64 typedef struct local_object {
65 HMODULE hModule;
66 struct local_object *previous;
67 struct local_object *next;
68 } local_object;
70 static local_object first_object;
72 /* These functions implement a double linked list for the local objects. */
73 static local_object *local_search( HMODULE hModule )
75 local_object *pobject;
77 if( hModule == NULL )
78 return NULL;
80 for( pobject = &first_object; pobject; pobject = pobject->next )
81 if( pobject->hModule == hModule )
82 return pobject;
84 return NULL;
87 static BOOL local_add( HMODULE hModule )
89 local_object *pobject;
90 local_object *nobject;
92 if( hModule == NULL )
93 return TRUE;
95 pobject = local_search( hModule );
97 /* Do not add object again if it's already on the list */
98 if( pobject )
99 return TRUE;
101 for( pobject = &first_object; pobject->next; pobject = pobject->next );
103 nobject = (local_object*) malloc( sizeof( local_object ) );
105 if( !nobject )
107 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
108 return FALSE;
111 pobject->next = nobject;
112 nobject->next = NULL;
113 nobject->previous = pobject;
114 nobject->hModule = hModule;
116 return TRUE;
119 static void local_rem( HMODULE hModule )
121 local_object *pobject;
123 if( hModule == NULL )
124 return;
126 pobject = local_search( hModule );
128 if( !pobject )
129 return;
131 if( pobject->next )
132 pobject->next->previous = pobject->previous;
133 if( pobject->previous )
134 pobject->previous->next = pobject->next;
136 free( pobject );
139 /* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
140 * static buffer.
141 * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
142 * the limit.
144 static char error_buffer[65535];
145 static BOOL error_occurred;
147 static void save_err_str( const char *str )
149 DWORD dwMessageId;
150 DWORD ret;
151 size_t pos, len;
153 dwMessageId = GetLastError( );
155 if( dwMessageId == 0 )
156 return;
158 len = strlen( str );
159 if( len > sizeof( error_buffer ) - 5 )
160 len = sizeof( error_buffer ) - 5;
162 /* Format error message to:
163 * "<argument to function that failed>": <Windows localized error message>
165 pos = 0;
166 error_buffer[pos++] = '"';
167 memcpy( error_buffer+pos, str, len );
168 pos += len;
169 error_buffer[pos++] = '"';
170 error_buffer[pos++] = ':';
171 error_buffer[pos++] = ' ';
173 ret = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId,
174 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
175 error_buffer+pos, (DWORD) (sizeof(error_buffer)-pos), NULL );
176 pos += ret;
178 /* When FormatMessageA() fails it returns zero and does not touch buffer
179 * so add trailing null byte */
180 if( ret == 0 )
181 error_buffer[pos] = '\0';
183 if( pos > 1 )
185 /* POSIX says the string must not have trailing <newline> */
186 if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' )
187 error_buffer[pos-2] = '\0';
190 error_occurred = TRUE;
193 static void save_err_ptr_str( const void *ptr )
195 char ptr_buf[2 + 2 * sizeof( ptr ) + 1];
196 char num;
197 size_t i;
199 ptr_buf[0] = '0';
200 ptr_buf[1] = 'x';
202 for( i = 0; i < 2 * sizeof( ptr ); i++ )
204 num = ( ( (ULONG_PTR) ptr ) >> ( 8 * sizeof( ptr ) - 4 * ( i + 1 ) ) ) & 0xF;
205 ptr_buf[2+i] = num + ( ( num < 0xA ) ? '0' : ( 'A' - 0xA ) );
208 ptr_buf[2 + 2 * sizeof( ptr )] = 0;
210 save_err_str( ptr_buf );
213 /* Load Psapi.dll at runtime, this avoids linking caveat */
214 static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded )
216 static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD);
217 HMODULE psapi;
219 if( !EnumProcessModulesPtr )
221 psapi = LoadLibraryA( "Psapi.dll" );
222 if( psapi )
223 EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "EnumProcessModules" );
224 if( !EnumProcessModulesPtr )
225 return 0;
228 return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded );
231 ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
232 dlopen( const char *file, int mode )
234 HMODULE hModule;
235 UINT uMode;
237 error_occurred = FALSE;
239 /* Do not let Windows display the critical-error-handler message box */
240 uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
242 if( file == 0 )
244 /* POSIX says that if the value of file is 0, a handle on a global
245 * symbol object must be provided. That object must be able to access
246 * all symbols from the original program file, and any objects loaded
247 * with the RTLD_GLOBAL flag.
248 * The return value from GetModuleHandle( ) allows us to retrieve
249 * symbols only from the original program file. EnumProcessModules() is
250 * used to access symbols from other libraries. For objects loaded
251 * with the RTLD_LOCAL flag, we create our own list later on. They are
252 * excluded from EnumProcessModules() iteration.
254 hModule = GetModuleHandle( NULL );
256 if( !hModule )
257 save_err_str( "(null)" );
259 else
261 HANDLE hCurrentProc;
262 DWORD dwProcModsBefore, dwProcModsAfter;
263 char lpFileName[MAX_PATH];
264 size_t i, len;
266 len = strlen( file );
268 if( len >= sizeof( lpFileName ) )
270 SetLastError( ERROR_FILENAME_EXCED_RANGE );
271 save_err_str( file );
272 hModule = NULL;
274 else
276 /* MSDN says backslashes *must* be used instead of forward slashes. */
277 for( i = 0; i < len; i++ )
279 if( file[i] == '/' )
280 lpFileName[i] = '\\';
281 else
282 lpFileName[i] = file[i];
284 lpFileName[len] = '\0';
286 hCurrentProc = GetCurrentProcess( );
288 if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 )
289 dwProcModsBefore = 0;
291 /* POSIX says the search path is implementation-defined.
292 * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
293 * to UNIX's search paths (start with system folders instead of current
294 * folder).
296 hModule = LoadLibraryExA( lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
298 if( !hModule )
300 save_err_str( lpFileName );
302 else
304 if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 )
305 dwProcModsAfter = 0;
307 /* If the object was loaded with RTLD_LOCAL, add it to list of local
308 * objects, so that its symbols cannot be retrieved even if the handle for
309 * the original program file is passed. POSIX says that if the same
310 * file is specified in multiple invocations, and any of them are
311 * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
312 * symbols will remain global. If number of loaded modules was not
313 * changed after calling LoadLibraryEx(), it means that library was
314 * already loaded.
316 if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter )
318 if( !local_add( hModule ) )
320 save_err_str( lpFileName );
321 FreeLibrary( hModule );
322 hModule = NULL;
325 else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter )
327 local_rem( hModule );
333 /* Return to previous state of the error-mode bit flags. */
334 SetErrorMode( uMode );
336 return (void *) hModule;
339 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
340 dlclose( void *handle )
342 HMODULE hModule = (HMODULE) handle;
343 BOOL ret;
345 error_occurred = FALSE;
347 ret = FreeLibrary( hModule );
349 /* If the object was loaded with RTLD_LOCAL, remove it from list of local
350 * objects.
352 if( ret )
353 local_rem( hModule );
354 else
355 save_err_ptr_str( handle );
357 /* dlclose's return value in inverted in relation to FreeLibrary's. */
358 ret = !ret;
360 return (int) ret;
363 __declspec(noinline) /* Needed for _ReturnAddress() */
364 ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
365 dlsym( void *handle, const char *name )
367 FARPROC symbol;
368 HMODULE hCaller;
369 HMODULE hModule;
370 HANDLE hCurrentProc;
372 error_occurred = FALSE;
374 symbol = NULL;
375 hCaller = NULL;
376 hModule = GetModuleHandle( NULL );
377 hCurrentProc = GetCurrentProcess( );
379 if( handle == RTLD_DEFAULT )
381 /* The symbol lookup happens in the normal global scope; that is,
382 * a search for a symbol using this handle would find the same
383 * definition as a direct use of this symbol in the program code.
384 * So use same lookup procedure as when filename is NULL.
386 handle = hModule;
388 else if( handle == RTLD_NEXT )
390 /* Specifies the next object after this one that defines name.
391 * This one refers to the object containing the invocation of dlsym().
392 * The next object is the one found upon the application of a load
393 * order symbol resolution algorithm. To get caller function of dlsym()
394 * use _ReturnAddress() intrinsic. To get HMODULE of caller function
395 * use standard GetModuleHandleExA() function.
397 if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) _ReturnAddress( ), &hCaller ) )
398 goto end;
401 if( handle != RTLD_NEXT )
403 symbol = GetProcAddress( (HMODULE) handle, name );
405 if( symbol != NULL )
406 goto end;
409 /* If the handle for the original program file is passed, also search
410 * in all globally loaded objects.
413 if( hModule == handle || handle == RTLD_NEXT )
415 HMODULE *modules;
416 DWORD cbNeeded;
417 DWORD dwSize;
418 size_t i;
420 /* GetModuleHandle( NULL ) only returns the current program file. So
421 * if we want to get ALL loaded module including those in linked DLLs,
422 * we have to use EnumProcessModules( ).
424 if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 )
426 modules = malloc( dwSize );
427 if( modules )
429 if( MyEnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded )
431 for( i = 0; i < dwSize / sizeof( HMODULE ); i++ )
433 if( handle == RTLD_NEXT && hCaller )
435 /* Next modules can be used for RTLD_NEXT */
436 if( hCaller == modules[i] )
437 hCaller = NULL;
438 continue;
440 if( local_search( modules[i] ) )
441 continue;
442 symbol = GetProcAddress( modules[i], name );
443 if( symbol != NULL )
445 free( modules );
446 goto end;
451 free( modules );
453 else
455 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
456 goto end;
461 end:
462 if( symbol == NULL )
464 if( GetLastError() == 0 )
465 SetLastError( ERROR_PROC_NOT_FOUND );
466 save_err_str( name );
469 return *(void **) (&symbol);
472 ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
473 dlerror( void )
475 /* If this is the second consecutive call to dlerror, return NULL */
476 if( !error_occurred )
477 return NULL;
479 /* POSIX says that invoking dlerror( ) a second time, immediately following
480 * a prior invocation, shall result in NULL being returned.
482 error_occurred = FALSE;
484 return error_buffer;
487 /* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2
488 * for details */
490 /* Get specific image section */
491 static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *size )
493 IMAGE_DOS_HEADER *dosHeader;
494 IMAGE_OPTIONAL_HEADER *optionalHeader;
496 dosHeader = (IMAGE_DOS_HEADER *) module;
498 if( dosHeader->e_magic != 0x5A4D )
499 return FALSE;
501 optionalHeader = (IMAGE_OPTIONAL_HEADER *) ( (BYTE *) module + dosHeader->e_lfanew + 24 );
503 if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC )
504 return FALSE;
506 if( index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR )
507 return FALSE;
509 if( optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0 )
510 return FALSE;
512 if( size != NULL )
513 *size = optionalHeader->DataDirectory[index].Size;
515 *ptr = (void *)( (BYTE *) module + optionalHeader->DataDirectory[index].VirtualAddress );
517 return TRUE;
520 /* Return symbol name for a given address */
521 static const char *get_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid, void *addr, void **func_address )
523 int i;
524 void *candidateAddr = NULL;
525 const char *candidateName = NULL;
526 BYTE *base = (BYTE *) module; /* Required to have correct calculations */
528 for( i = 0; iid[i].Characteristics != 0 && iid[i].FirstThunk != 0; i++ )
530 IMAGE_THUNK_DATA *thunkILT = (IMAGE_THUNK_DATA *)( base + iid[i].Characteristics );
531 IMAGE_THUNK_DATA *thunkIAT = (IMAGE_THUNK_DATA *)( base + iid[i].FirstThunk );
533 for( ; thunkILT->u1.AddressOfData != 0; thunkILT++, thunkIAT++ )
535 IMAGE_IMPORT_BY_NAME *nameData;
537 if( IMAGE_SNAP_BY_ORDINAL( thunkILT->u1.Ordinal ) )
538 continue;
540 if( (void *) thunkIAT->u1.Function > addr || candidateAddr >= (void *) thunkIAT->u1.Function )
541 continue;
543 candidateAddr = (void *) thunkIAT->u1.Function;
544 nameData = (IMAGE_IMPORT_BY_NAME *)( base + (ULONG_PTR) thunkILT->u1.AddressOfData );
545 candidateName = (const char *) nameData->Name;
549 *func_address = candidateAddr;
550 return candidateName;
553 static BOOL is_valid_address( void *addr )
555 MEMORY_BASIC_INFORMATION info;
556 SIZE_T result;
558 if( addr == NULL )
559 return FALSE;
561 /* check valid pointer */
562 result = VirtualQuery( addr, &info, sizeof( info ) );
564 if( result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS )
565 return FALSE;
567 return TRUE;
570 /* Return state if address points to an import thunk
572 * An import thunk is setup with a 'jmp' instruction followed by an
573 * absolute address (32bit) or relative offset (64bit) pointing into
574 * the import address table (iat), which is partially maintained by
575 * the runtime linker.
577 static BOOL is_import_thunk( void *addr )
579 return *(short *) addr == 0x25ff ? TRUE : FALSE;
582 /* Return address from the import address table (iat),
583 * if the original address points to a thunk table entry.
585 static void *get_address_from_import_address_table( void *iat, DWORD iat_size, void *addr )
587 BYTE *thkp = (BYTE *) addr;
588 /* Get offset from thunk table (after instruction 0xff 0x25)
589 * 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00
591 ULONG offset = *(ULONG *)( thkp + 2 );
592 #ifdef _WIN64
593 /* On 64 bit the offset is relative
594 * 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery>
595 * And can be also negative (MSVC in WDK)
596 * 100002f20: ff 25 3a e1 ff ff jmpq *-0x1ec6(%rip) # 0x100001060
597 * So cast to signed LONG type
599 BYTE *ptr = (BYTE *)( thkp + 6 + (LONG) offset );
600 #else
601 /* On 32 bit the offset is absolute
602 * 4019b4: ff 25 90 71 40 00 jmp *0x40719
604 BYTE *ptr = (BYTE *) offset;
605 #endif
607 if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size )
608 return NULL;
610 return *(void **) ptr;
613 /* Holds module filename */
614 static char module_filename[2*MAX_PATH];
616 static BOOL fill_module_info( void *addr, Dl_info *info )
618 HMODULE hModule;
619 DWORD dwSize;
621 if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL )
622 return FALSE;
624 dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) );
626 if( dwSize == 0 || dwSize == sizeof( module_filename ) )
627 return FALSE;
629 info->dli_fbase = (void *) hModule;
630 info->dli_fname = module_filename;
632 return TRUE;
635 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
636 dladdr( void *addr, Dl_info *info )
638 void *realAddr, *funcAddress = NULL;
639 HMODULE hModule;
640 IMAGE_IMPORT_DESCRIPTOR *iid;
641 DWORD iidSize = 0;
643 if( addr == NULL || info == NULL )
644 return 0;
646 hModule = GetModuleHandleA( NULL );
647 if( hModule == NULL )
648 return 0;
650 if( !is_valid_address( addr ) )
651 return 0;
653 realAddr = addr;
655 if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) )
656 return 0;
658 if( is_import_thunk( addr ) )
660 void *iat;
661 void *iatAddr = NULL;
662 DWORD iatSize = 0;
664 if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) )
666 /* Fallback for cases where the iat is not defined,
667 * for example i586-mingw32msvc-gcc */
668 if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 )
669 return 0;
671 iat = (void *)( (BYTE *) hModule + iid->FirstThunk );
672 /* We assume that in this case iid and iat's are in linear order */
673 iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid );
676 iatAddr = get_address_from_import_address_table( iat, iatSize, addr );
677 if( iatAddr == NULL )
678 return 0;
680 realAddr = iatAddr;
683 if( !fill_module_info( realAddr, info ) )
684 return 0;
686 info->dli_sname = get_symbol_name( hModule, iid, realAddr, &funcAddress );
688 info->dli_saddr = !info->dli_sname ? NULL : funcAddress ? funcAddress : realAddr;
689 return 1;
692 #ifdef DLFCN_WIN32_SHARED
693 BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
695 (void) hinstDLL;
696 (void) fdwReason;
697 (void) lpvReserved;
698 return TRUE;
700 #endif