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
28 * Copied from https://github.com/dlfcn-win32/dlfcn-win32 and modified only to
29 * fit Heimdal's lib/roken.
33 #define _WIN32_WINNT 0x0501
36 #define _CRTDBG_MAP_ALLOC
45 /* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
46 #pragma intrinsic(_ReturnAddress)
48 /* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
49 #ifndef _ReturnAddress
50 #define _ReturnAddress() (__builtin_extract_return_addr(__builtin_return_address(0)))
54 #ifdef DLFCN_WIN32_SHARED
55 #define DLFCN_WIN32_EXPORTS
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
{
66 struct local_object
*previous
;
67 struct local_object
*next
;
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
;
80 for( pobject
= &first_object
; pobject
; pobject
= pobject
->next
)
81 if( pobject
->hModule
== hModule
)
87 static BOOL
local_add( HMODULE hModule
)
89 local_object
*pobject
;
90 local_object
*nobject
;
95 pobject
= local_search( hModule
);
97 /* Do not add object again if it's already on the list */
101 for( pobject
= &first_object
; pobject
->next
; pobject
= pobject
->next
);
103 nobject
= (local_object
*) malloc( sizeof( local_object
) );
107 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
111 pobject
->next
= nobject
;
112 nobject
->next
= NULL
;
113 nobject
->previous
= pobject
;
114 nobject
->hModule
= hModule
;
119 static void local_rem( HMODULE hModule
)
121 local_object
*pobject
;
123 if( hModule
== NULL
)
126 pobject
= local_search( hModule
);
132 pobject
->next
->previous
= pobject
->previous
;
133 if( pobject
->previous
)
134 pobject
->previous
->next
= pobject
->next
;
139 /* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
141 * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
144 static char error_buffer
[65535];
145 static BOOL error_occurred
;
147 static void save_err_str( const char *str
)
153 dwMessageId
= GetLastError( );
155 if( dwMessageId
== 0 )
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>
166 error_buffer
[pos
++] = '"';
167 memcpy( error_buffer
+pos
, str
, 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
);
178 /* When FormatMessageA() fails it returns zero and does not touch buffer
179 * so add trailing null byte */
181 error_buffer
[pos
] = '\0';
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];
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
);
219 if( !EnumProcessModulesPtr
)
221 psapi
= LoadLibraryA( "Psapi.dll" );
223 EnumProcessModulesPtr
= (BOOL (WINAPI
*)(HANDLE
, HMODULE
*, DWORD
, LPDWORD
)) GetProcAddress( psapi
, "EnumProcessModules" );
224 if( !EnumProcessModulesPtr
)
228 return EnumProcessModulesPtr( hProcess
, lphModule
, cb
, lpcbNeeded
);
231 ROKEN_LIB_FUNCTION
void * ROKEN_LIB_CALL
232 dlopen( const char *file
, int mode
)
237 error_occurred
= FALSE
;
239 /* Do not let Windows display the critical-error-handler message box */
240 uMode
= SetErrorMode( SEM_FAILCRITICALERRORS
);
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
);
257 save_err_str( "(null)" );
262 DWORD dwProcModsBefore
, dwProcModsAfter
;
263 char lpFileName
[MAX_PATH
];
266 len
= strlen( file
);
268 if( len
>= sizeof( lpFileName
) )
270 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
271 save_err_str( file
);
276 /* MSDN says backslashes *must* be used instead of forward slashes. */
277 for( i
= 0; i
< len
; i
++ )
280 lpFileName
[i
] = '\\';
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
296 hModule
= LoadLibraryExA( lpFileName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
300 save_err_str( lpFileName
);
304 if( MyEnumProcessModules( hCurrentProc
, NULL
, 0, &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
316 if( (mode
& RTLD_LOCAL
) && dwProcModsBefore
!= dwProcModsAfter
)
318 if( !local_add( hModule
) )
320 save_err_str( lpFileName
);
321 FreeLibrary( hModule
);
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
;
345 error_occurred
= FALSE
;
347 ret
= FreeLibrary( hModule
);
349 /* If the object was loaded with RTLD_LOCAL, remove it from list of local
353 local_rem( hModule
);
355 save_err_ptr_str( handle
);
357 /* dlclose's return value in inverted in relation to FreeLibrary's. */
363 __declspec(noinline
) /* Needed for _ReturnAddress() */
364 ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
365 dlsym( void *handle
, const char *name
)
372 error_occurred
= FALSE
;
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.
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
) )
401 if( handle
!= RTLD_NEXT
)
403 symbol
= GetProcAddress( (HMODULE
) handle
, name
);
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
)
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
);
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
] )
440 if( local_search( modules
[i
] ) )
442 symbol
= GetProcAddress( modules
[i
], name
);
455 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
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
475 /* If this is the second consecutive call to dlerror, return NULL */
476 if( !error_occurred
)
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
;
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
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 )
501 optionalHeader
= (IMAGE_OPTIONAL_HEADER
*) ( (BYTE
*) module
+ dosHeader
->e_lfanew
+ 24 );
503 if( optionalHeader
->Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
)
506 if( index
< 0 || index
> IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR
)
509 if( optionalHeader
->DataDirectory
[index
].Size
== 0 || optionalHeader
->DataDirectory
[index
].VirtualAddress
== 0 )
513 *size
= optionalHeader
->DataDirectory
[index
].Size
;
515 *ptr
= (void *)( (BYTE
*) module
+ optionalHeader
->DataDirectory
[index
].VirtualAddress
);
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
)
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
) )
540 if( (void *) thunkIAT
->u1
.Function
> addr
|| candidateAddr
>= (void *) thunkIAT
->u1
.Function
)
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
;
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
)
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 );
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
);
601 /* On 32 bit the offset is absolute
602 * 4019b4: ff 25 90 71 40 00 jmp *0x40719
604 BYTE
*ptr
= (BYTE
*) offset
;
607 if( !is_valid_address( ptr
) || ptr
< (BYTE
*) iat
|| ptr
> (BYTE
*) iat
+ iat_size
)
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
)
621 if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
, addr
, &hModule
) || hModule
== NULL
)
624 dwSize
= GetModuleFileNameA( hModule
, module_filename
, sizeof( module_filename
) );
626 if( dwSize
== 0 || dwSize
== sizeof( module_filename
) )
629 info
->dli_fbase
= (void *) hModule
;
630 info
->dli_fname
= module_filename
;
635 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
636 dladdr( void *addr
, Dl_info
*info
)
638 void *realAddr
, *funcAddress
= NULL
;
640 IMAGE_IMPORT_DESCRIPTOR
*iid
;
643 if( addr
== NULL
|| info
== NULL
)
646 hModule
= GetModuleHandleA( NULL
);
647 if( hModule
== NULL
)
650 if( !is_valid_address( addr
) )
655 if( !get_image_section( hModule
, IMAGE_DIRECTORY_ENTRY_IMPORT
, (void **) &iid
, &iidSize
) )
658 if( is_import_thunk( addr
) )
661 void *iatAddr
= NULL
;
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 )
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
)
683 if( !fill_module_info( realAddr
, info
) )
686 info
->dli_sname
= get_symbol_name( hModule
, iid
, realAddr
, &funcAddress
);
688 info
->dli_saddr
= !info
->dli_sname
? NULL
: funcAddress
? funcAddress
: realAddr
;
692 #ifdef DLFCN_WIN32_SHARED
693 BOOL WINAPI
DllMain( HINSTANCE hinstDLL
, DWORD fdwReason
, LPVOID lpvReserved
)