text: /**
   * dlfcn-win32
   * Copyright (c) 2007 Ramiro Polla
   * Copyright (c) 2015 Tiancheng "Timothy" Gu
   * Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
   * Copyright (c) 2020 Ralf Habacker <ralf.habacker@freenet.de>
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  
  #ifdef _DEBUG
  #define _CRTDBG_MAP_ALLOC
  #include <stdlib.h>
  #include <crtdbg.h>
  #endif
  #include <windows.h>
  #include <stdio.h>
  #include <stdlib.h>
  
  /* Older versions do not have this type */
  #if _WIN32_WINNT < 0x0500
  typedef ULONG ULONG_PTR;
  #endif
  
  /* Older SDK versions do not have these macros */
  #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
  #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4
  #endif
  #ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
  #define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2
  #endif
  
  #ifdef _MSC_VER
  /* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
  #pragma intrinsic( _ReturnAddress )
  #else
  /* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
  #ifndef _ReturnAddress
  #define _ReturnAddress( ) ( __builtin_extract_return_addr( __builtin_return_address( 0 ) ) )
  #endif
  #endif
  
  #ifdef DLFCN_WIN32_SHARED
  #define DLFCN_WIN32_EXPORTS
  #endif
  #include "dlfcn.h"
  
  #if defined( _MSC_VER ) && _MSC_VER >= 1300
  /* https://docs.microsoft.com/en-us/cpp/cpp/noinline */
  #define DLFCN_NOINLINE __declspec( noinline )
  #elif defined( __GNUC__ ) && ( ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) )
  /* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */
  #define DLFCN_NOINLINE __attribute__(( noinline ))
  #else
  #define DLFCN_NOINLINE
  #endif
  
  /* Note:
   * MSDN says these functions are not thread-safe. We make no efforts to have
   * any kind of thread safety.
   */
  
  typedef struct local_object {
      HMODULE hModule;
      struct local_object *previous;
      struct local_object *next;
  } local_object;
  
  static local_object first_object;
  
  /* These functions implement a double linked list for the local objects. */
  static local_object *local_search( HMODULE hModule )
  {
      local_object *pobject;
  
      if( hModule == NULL )
          return NULL;
  
      for( pobject = &first_object; pobject; pobject = pobject->next )
          if( pobject->hModule == hModule )
              return pobject;
  
      return NULL;
  }
  
  static BOOL local_add( HMODULE hModule )
  {
      local_object *pobject;
      local_object *nobject;
  
      if( hModule == NULL )
          return TRUE;
  
      pobject = local_search( hModule );
  
      /* Do not add object again if it's already on the list */
      if( pobject != NULL )
          return TRUE;
  
      for( pobject = &first_object; pobject->next; pobject = pobject->next );
  
      nobject = (local_object *) malloc( sizeof( local_object ) );
  
      if( !nobject )
          return FALSE;
  
      pobject->next = nobject;
      nobject->next = NULL;
      nobject->previous = pobject;
      nobject->hModule = hModule;
  
      return TRUE;
  }
  
  static void local_rem( HMODULE hModule )
  {
      local_object *pobject;
  
      if( hModule == NULL )
          return;
  
      pobject = local_search( hModule );
  
      if( pobject == NULL )
          return;
  
      if( pobject->next )
          pobject->next->previous = pobject->previous;
      if( pobject->previous )
          pobject->previous->next = pobject->next;
  
      free( pobject );
  }
  
  /* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
   * static buffer.
   * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
   * the limit.
   */
  static char error_buffer[65535];
  static BOOL error_occurred;
  
  static void save_err_str( const char *str, DWORD dwMessageId )
  {
      DWORD ret;
      size_t pos, len;
  
      len = strlen( str );
      if( len > sizeof( error_buffer ) - 5 )
          len = sizeof( error_buffer ) - 5;
  
      /* Format error message to:
       * "<argument to function that failed>": <Windows localized error message>
        */
      pos = 0;
      error_buffer[pos++] = '"';
      memcpy( error_buffer + pos, str, len );
      pos += len;
      error_buffer[pos++] = '"';
      error_buffer[pos++] = ':';
      error_buffer[pos++] = ' ';
  
      ret = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId,
          MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
          error_buffer + pos, (DWORD) ( sizeof( error_buffer ) - pos ), NULL );
      pos += ret;
  
      /* When FormatMessageA() fails it returns zero and does not touch buffer
       * so add trailing null byte */
      if( ret == 0 )
          error_buffer[pos] = '\0';
  
      if( pos > 1 )
      {
          /* POSIX says the string must not have trailing <newline> */
          if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' )
              error_buffer[pos-2] = '\0';
      }
  
      error_occurred = TRUE;
  }
  
  static void save_err_ptr_str( const void *ptr, DWORD dwMessageId )
  {
      char ptr_buf[2 + 2 * sizeof( ptr ) + 1];
      char num;
      size_t i;
  
      ptr_buf[0] = '0';
      ptr_buf[1] = 'x';
  
      for( i = 0; i < 2 * sizeof( ptr ); i++ )
      {
          num = (char) ( ( ( (ULONG_PTR) ptr ) >> ( 8 * sizeof( ptr ) - 4 * ( i + 1 ) ) ) & 0xF );
          ptr_buf[2 + i] = num + ( ( num < 0xA ) ? '0' : ( 'A' - 0xA ) );
      }
  
      ptr_buf[2 + 2 * sizeof( ptr )] = 0;
  
      save_err_str( ptr_buf, dwMessageId );
  }
  
  static HMODULE MyGetModuleHandleFromAddress( const void *addr )
  {
      static BOOL (WINAPI *GetModuleHandleExAPtr)(DWORD, LPCSTR, HMODULE *) = NULL;
      static BOOL failed = FALSE;
      HMODULE kernel32;
      HMODULE hModule;
      MEMORY_BASIC_INFORMATION info;
      SIZE_T sLen;
  
      if( !failed && GetModuleHandleExAPtr == NULL )
      {
          kernel32 = GetModuleHandleA( "Kernel32.dll" );
          if( kernel32 != NULL )
              GetModuleHandleExAPtr = (BOOL (WINAPI *)(DWORD, LPCSTR, HMODULE *)) GetProcAddress( kernel32, "GetModuleHandleExA" );
          if( GetModuleHandleExAPtr == NULL )
              failed = TRUE;
      }
  
      if( !failed )
      {
          /* If GetModuleHandleExA is available use it with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS */
          if( !GetModuleHandleExAPtr( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) )
              return NULL;
      }
      else
      {
          /* To get HMODULE from address use undocumented hack from https://stackoverflow.com/a/2396380
           * The HMODULE of a DLL is the same value as the module's base address.
           */
          sLen = VirtualQuery( addr, &info, sizeof( info ) );
          if( sLen != sizeof( info ) )
              return NULL;
          hModule = (HMODULE) info.AllocationBase;
      }
  
      return hModule;
  }
  
  /* Load Psapi.dll at runtime, this avoids linking caveat */
  static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded )
  {
      static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD) = NULL;
      static BOOL failed = FALSE;
      UINT uMode;
      HMODULE psapi;
  
      if( failed )
          return FALSE;
  
      if( EnumProcessModulesPtr == NULL )
      {
          /* Windows 7 and newer versions have K32EnumProcessModules in Kernel32.dll which is always pre-loaded */
          psapi = GetModuleHandleA( "Kernel32.dll" );
          if( psapi != NULL )
              EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "K32EnumProcessModules" );
  
          /* Windows Vista and older version have EnumProcessModules in Psapi.dll which needs to be loaded */
          if( EnumProcessModulesPtr == NULL )
          {
              /* Do not let Windows display the critical-error-handler message box */
              uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
              psapi = LoadLibraryA( "Psapi.dll" );
              if( psapi != NULL )
              {
                  EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "EnumProcessModules" );
                  if( EnumProcessModulesPtr == NULL )
                      FreeLibrary( psapi );
              }
              SetErrorMode( uMode );
          }
  
          if( EnumProcessModulesPtr == NULL )
          {
              failed = TRUE;
              return FALSE;
          }
      }
  
      return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded );
  }
  
  DLFCN_EXPORT
  void *dlopen( const char *file, int mode )
  {
      HMODULE hModule;
      UINT uMode;
  
      error_occurred = FALSE;
  
      /* Do not let Windows display the critical-error-handler message box */
      uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
  
      if( file == NULL )
      {
          /* POSIX says that if the value of file is NULL, a handle on a global
           * symbol object must be provided. That object must be able to access
           * all symbols from the original program file, and any objects loaded
           * with the RTLD_GLOBAL flag.
           * The return value from GetModuleHandle( ) allows us to retrieve
           * symbols only from the original program file. EnumProcessModules() is
           * used to access symbols from other libraries. For objects loaded
           * with the RTLD_LOCAL flag, we create our own list later on. They are
           * excluded from EnumProcessModules() iteration.
           */
          hModule = GetModuleHandle( NULL );
  
          if( !hModule )
              save_err_str( "(null)", GetLastError( ) );
      }
      else
      {
          HANDLE hCurrentProc;
          DWORD dwProcModsBefore, dwProcModsAfter;
          char lpFileName[MAX_PATH];
          size_t i, len;
  
          len = strlen( file );
  
          if( len >= sizeof( lpFileName ) )
          {
              save_err_str( file, ERROR_FILENAME_EXCED_RANGE );
              hModule = NULL;
          }
          else
          {
              /* MSDN says backslashes *must* be used instead of forward slashes. */
              for( i = 0; i < len; i++ )
              {
                  if( file[i] == '/' )
                      lpFileName[i] = '\\';
                  else
                      lpFileName[i] = file[i];
              }
              lpFileName[len] = '\0';
  
              hCurrentProc = GetCurrentProcess( );
  
              if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 )
                  dwProcModsBefore = 0;
  
              /* POSIX says the search path is implementation-defined.
               * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
               * to UNIX's search paths (start with system folders instead of current
               * folder).
               */
              hModule = LoadLibraryExA( lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
  
              if( !hModule )
              {
                  save_err_str( lpFileName, GetLastError( ) );
              }
              else
              {
                  if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 )
                      dwProcModsAfter = 0;
  
                  /* If the object was loaded with RTLD_LOCAL, add it to list of local
                   * objects, so that its symbols cannot be retrieved even if the handle for
                   * the original program file is passed. POSIX says that if the same
                   * file is specified in multiple invocations, and any of them are
                   * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
                   * symbols will remain global. If number of loaded modules was not
                   * changed after calling LoadLibraryEx(), it means that library was
                   * already loaded.
                   */
                  if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter )
                  {
                      if( !local_add( hModule ) )
                      {
                          save_err_str( lpFileName, ERROR_NOT_ENOUGH_MEMORY );
                          FreeLibrary( hModule );
                          hModule = NULL;
                      }
                  }
                  else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter )
                  {
                      local_rem( hModule );
                  }
              }
          }
      }
  
      /* Return to previous state of the error-mode bit flags. */
      SetErrorMode( uMode );
  
      return (void *) hModule;
  }
  
  DLFCN_EXPORT
  int dlclose( void *handle )
  {
      HMODULE hModule = (HMODULE) handle;
      BOOL ret;
  
      error_occurred = FALSE;
  
      ret = FreeLibrary( hModule );
  
      /* If the object was loaded with RTLD_LOCAL, remove it from list of local
       * objects.
       */
      if( ret )
          local_rem( hModule );
      else
          save_err_ptr_str( handle, GetLastError( ) );
  
      /* dlclose's return value in inverted in relation to FreeLibrary's. */
      ret = !ret;
  
      return (int) ret;
  }
  
  DLFCN_NOINLINE /* Needed for _ReturnAddress() */
  DLFCN_EXPORT
  void *dlsym( void *handle, const char *name )
  {
      FARPROC symbol;
      HMODULE hCaller;
      HMODULE hModule;
      DWORD dwMessageId;
  
      error_occurred = FALSE;
  
      symbol = NULL;
      hCaller = NULL;
      hModule = GetModuleHandle( NULL );
      dwMessageId = 0;
  
      if( handle == RTLD_DEFAULT )
      {
          /* The symbol lookup happens in the normal global scope; that is,
           * a search for a symbol using this handle would find the same
           * definition as a direct use of this symbol in the program code.
           * So use same lookup procedure as when filename is NULL.
           */
          handle = hModule;
      }
      else if( handle == RTLD_NEXT )
      {
          /* Specifies the next object after this one that defines name.
           * This one refers to the object containing the invocation of dlsym().
           * The next object is the one found upon the application of a load
           * order symbol resolution algorithm. To get caller function of dlsym()
           * use _ReturnAddress() intrinsic. To get HMODULE of caller function
           * use MyGetModuleHandleFromAddress() which calls either standard
           * GetModuleHandleExA() function or hack via VirtualQuery().
           */
          hCaller = MyGetModuleHandleFromAddress( _ReturnAddress( ) );
  
          if( hCaller == NULL )
          {
              dwMessageId = ERROR_INVALID_PARAMETER;
              goto end;
          }
      }
  
      if( handle != RTLD_NEXT )
      {
          symbol = GetProcAddress( (HMODULE) handle, name );
  
          if( symbol != NULL )
              goto end;
      }
  
      /* If the handle for the original program file is passed, also search
       * in all globally loaded objects.
       */
  
      if( hModule == handle || handle == RTLD_NEXT )
      {
          HANDLE hCurrentProc;
          HMODULE *modules;
          DWORD cbNeeded;
          DWORD dwSize;
          size_t i;
  
          hCurrentProc = GetCurrentProcess( );
  
          /* GetModuleHandle( NULL ) only returns the current program file. So
           * if we want to get ALL loaded module including those in linked DLLs,
           * we have to use EnumProcessModules( ).
           */
          if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 )
          {
              modules = malloc( dwSize );
              if( modules )
              {
                  if( MyEnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded )
                  {
                      for( i = 0; i < dwSize / sizeof( HMODULE ); i++ )
                      {
                          if( handle == RTLD_NEXT && hCaller )
                          {
                              /* Next modules can be used for RTLD_NEXT */
                              if( hCaller == modules[i] )
                                  hCaller = NULL;
                              continue;
                          }
                          if( local_search( modules[i] ) )
                              continue;
                          symbol = GetProcAddress( modules[i], name );
                          if( symbol != NULL )
                          {
                              free( modules );
                              goto end;
                          }
                      }
  
                  }
                  free( modules );
              }
              else
              {
                  dwMessageId = ERROR_NOT_ENOUGH_MEMORY;
                  goto end;
              }
          }
      }
  
  end:
      if( symbol == NULL )
      {
          if( !dwMessageId )
              dwMessageId = ERROR_PROC_NOT_FOUND;
          save_err_str( name, dwMessageId );
      }
  
      return *(void **) (&symbol);
  }
  
  DLFCN_EXPORT
  char *dlerror( void )
  {
      /* If this is the second consecutive call to dlerror, return NULL */
      if( !error_occurred )
          return NULL;
  
      /* POSIX says that invoking dlerror( ) a second time, immediately following
       * a prior invocation, shall result in NULL being returned.
       */
      error_occurred = FALSE;
  
      return error_buffer;
  }
  
  /* 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
   * for details */
  
  /* Get specific image section */
  static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *size )
  {
      IMAGE_DOS_HEADER *dosHeader;
      IMAGE_OPTIONAL_HEADER *optionalHeader;
  
      dosHeader = (IMAGE_DOS_HEADER *) module;
  
      if( dosHeader->e_magic != 0x5A4D )
          return FALSE;
  
      optionalHeader = (IMAGE_OPTIONAL_HEADER *) ( (BYTE *) module + dosHeader->e_lfanew + 24 );
  
      if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC )
          return FALSE;
  
      if( index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR )
          return FALSE;
  
      if( optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0 )
          return FALSE;
  
      if( size != NULL )
          *size = optionalHeader->DataDirectory[index].Size;
  
      *ptr = (void *)( (BYTE *) module + optionalHeader->DataDirectory[index].VirtualAddress );
  
      return TRUE;
  }
  
  /* Return symbol name for a given address from export table */
  static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, const void *addr, void **func_address )
  {
      DWORD i;
      void *candidateAddr = NULL;
      int candidateIndex = -1;
      BYTE *base = (BYTE *) module;
      DWORD *functionAddressesOffsets = (DWORD *) (base + ied->AddressOfFunctions);
      DWORD *functionNamesOffsets = (DWORD *) (base + ied->AddressOfNames);
      USHORT *functionNameOrdinalsIndexes = (USHORT *) (base + ied->AddressOfNameOrdinals);
  
      for( i = 0; i < ied->NumberOfFunctions; i++ )
      {
          if( (void *) ( base + functionAddressesOffsets[i] ) > addr || candidateAddr >= (void *) ( base + functionAddressesOffsets[i] ) )
              continue;
  
          candidateAddr = (void *) ( base + functionAddressesOffsets[i] );
          candidateIndex = i;
      }
  
      if( candidateIndex == -1 )
          return NULL;
  
      *func_address = candidateAddr;
  
      for( i = 0; i < ied->NumberOfNames; i++ )
      {
          if( functionNameOrdinalsIndexes[i] == candidateIndex )
              return (const char *) ( base + functionNamesOffsets[i] );
      }
  
      return NULL;
  }
  
  static BOOL is_valid_address( const void *addr )
  {
      MEMORY_BASIC_INFORMATION info;
      SIZE_T result;
  
      if( addr == NULL )
          return FALSE;
  
      /* check valid pointer */
      result = VirtualQuery( addr, &info, sizeof( info ) );
  
      if( result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS )
          return FALSE;
  
      return TRUE;
  }
  
  /* Return state if address points to an import thunk
   *
   * An import thunk is setup with a 'jmp' instruction followed by an
   * absolute address (32bit) or relative offset (64bit) pointing into
   * the import address table (iat), which is partially maintained by
   * the runtime linker.
   */
  static BOOL is_import_thunk( const void *addr )
  {
      return *(short *) addr == 0x25ff ? TRUE : FALSE;
  }
  
  /* Return adress from the import address table (iat),
   * if the original address points to a thunk table entry.
   */
  static void *get_address_from_import_address_table( void *iat, DWORD iat_size, const void *addr )
  {
      BYTE *thkp = (BYTE *) addr;
      /* Get offset from thunk table (after instruction 0xff 0x25)
       *   4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00
       */
      ULONG offset = *(ULONG *)( thkp + 2 );
  #ifdef _WIN64
      /* On 64 bit the offset is relative
       *      4018c8:   ff 25 4a 8a 00 00    jmpq    *0x8a4a(%rip)    # 40a318 <__imp_VirtualQuery>
       * And can be also negative (MSVC in WDK)
       *   100002f20:   ff 25 3a e1 ff ff    jmpq   *-0x1ec6(%rip)    # 0x100001060
       * So cast to signed LONG type
       */
      BYTE *ptr = (BYTE *)( thkp + 6 + (LONG) offset );
  #else
      /* On 32 bit the offset is absolute
       *   4019b4:    ff 25 90 71 40 00    jmp    *0x40719
       */
      BYTE *ptr = (BYTE *) offset;
  #endif
  
      if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size )
          return NULL;
  
      return *(void **) ptr;
  }
  
  /* Holds module filename */
  static char module_filename[2*MAX_PATH];
  
  static BOOL fill_info( const void *addr, Dl_info *info )
  {
      HMODULE hModule;
      DWORD dwSize;
      IMAGE_EXPORT_DIRECTORY *ied;
      void *funcAddress = NULL;
  
      /* Get module of the specified address */
      hModule = MyGetModuleHandleFromAddress( addr );
  
      if( hModule == NULL )
          return FALSE;
  
      dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) );
  
      if( dwSize == 0 || dwSize == sizeof( module_filename ) )
          return FALSE;
  
      info->dli_fname = module_filename;
      info->dli_fbase = (void *) hModule;
  
      /* Find function name and function address in module's export table */
      if( get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void **) &ied, NULL ) )
          info->dli_sname = get_export_symbol_name( hModule, ied, addr, &funcAddress );
      else
          info->dli_sname = NULL;
  
      info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : (void *) addr;
  
      return TRUE;
  }
  
  DLFCN_EXPORT
  int dladdr( const void *addr, Dl_info *info )
  {
      if( info == NULL )
          return 0;
  
      if( !is_valid_address( addr ) )
          return 0;
  
      if( is_import_thunk( addr ) )
      {
          void *iat;
          DWORD iatSize;
          HMODULE hModule;
  
          /* Get module of the import thunk address */
          hModule = MyGetModuleHandleFromAddress( addr );
  
          if( hModule == NULL )
              return 0;
  
          if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) )
          {
              /* Fallback for cases where the iat is not defined,
               * for example i586-mingw32msvc-gcc */
              IMAGE_IMPORT_DESCRIPTOR *iid;
              DWORD iidSize;
  
              if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) )
                  return 0;
  
              if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 )
                  return 0;
  
              iat = (void *)( (BYTE *) hModule + iid->FirstThunk );
              /* We assume that in this case iid and iat's are in linear order */
              iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid );
          }
  
          addr = get_address_from_import_address_table( iat, iatSize, addr );
  
          if( !is_valid_address( addr ) )
              return 0;
      }
  
      if( !fill_info( addr, info ) )
          return 0;
  
      return 1;
  }
  
  #ifdef DLFCN_WIN32_SHARED
  BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
  {
      (void) hinstDLL;
      (void) fdwReason;
      (void) lpvReserved;
      return TRUE;
  }
  #endif
  