/*
* dlfcn-win32
* Copyright (c) 2007-2009 Ramiro Polla
* Copyright (c) 2014 Tiancheng "Timothy" Gu
* Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
*
* dlfcn-win32 is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* dlfcn-win32 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with dlfcn-win32; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <wchar.h>
#include <io.h>
#include <fcntl.h>
#include <direct.h>
#include "dlfcn.h"
/* If these dlclose's fails, we don't care as the handles are going to be
closed eventually when the program ends. */
#define CLOSE_LIB dlclose( library )
#define CLOSE_GLOBAL dlclose( global )
#define RETURN_ERROR printf("From line %d\n", __LINE__); return 1
#define RUNFUNC do { \
ret = function (); \
if( ret != 0) { \
CLOSE_LIB; \
CLOSE_GLOBAL; \
RETURN_ERROR; \
} \
} while( 0 )
/* This is what this test does:
* - Open library with RTLD_GLOBAL
* - Get global object
* - Get symbol from library through library object <- works
* - Run function if it worked
* - Get nonexistent symbol from library through library object <- fails
* - Get symbol from library through global object <- works
* - Run function if it worked
* - Get nonexistent symbol from library through global object <- fails
* - Close library
* - Open library with RTLD_LOCAL
* - Get symbol from library through library object <- works
* - Run function if it worked
* - Get nonexistent symbol from library through library object <- fails
* - Get local symbol from library through global object <- fails
* - Get nonexistent local symbol from library through global object <- fails
* - Open library again (without closing it first) with RTLD_GLOBAL
* - Get symbol from library through global object <- works
* - Get nonexistent symbol from library through global object <- fails
* - Close library
* - Close global object
*
* If one test fails, the program terminates itself.
*/
int main()
{
void *global;
void *library2;
void *library;
char *error;
int (*function)( void );
int (*function2_from_library2)( void );
size_t (*fwrite_local) ( const void *, size_t, size_t, FILE * );
size_t (*fputs_default) ( const char *, FILE * );
int (*nonexistentfunction)( void );
int fd;
int ret;
HMODULE library3;
char toolongfile[32767];
DWORD code;
char nonlibraryfile[MAX_PATH];
char bytename[sizeof("testdll2.dll")+4];
WCHAR widename[sizeof("testdll2.dll")+1];
DWORD length;
HANDLE tempfile;
DWORD dummy;
UINT uMode;
#ifdef _DEBUG
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);
#endif
length = GetTempPathA( sizeof( nonlibraryfile ) - sizeof( "temp.dll" ), nonlibraryfile );
if( length == 0 || length > sizeof( nonlibraryfile ) - sizeof( "temp.dll" ) )
{
printf( "ERROR\tGetTempPath failed\n" );
RETURN_ERROR;
}
memcpy( nonlibraryfile + length, "temp.dll", sizeof( "temp.dll" ) );
tempfile = CreateFileA( (LPCSTR) nonlibraryfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
if( tempfile == INVALID_HANDLE_VALUE )
{
printf( "ERROR\tCannot create temporary file %s: %lu\n", nonlibraryfile, (unsigned long)GetLastError( ) );
RETURN_ERROR;
}
WriteFile( tempfile, "test content", 12, &dummy, NULL );
CloseHandle( tempfile );
uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
library3 = LoadLibraryA( nonlibraryfile );
code = GetLastError( );
SetErrorMode( uMode );
if( library3 )
{
printf( "ERROR\tNon-library file %s was opened via WINAPI\n", nonlibraryfile );
CloseHandle( library3 );
DeleteFileA( nonlibraryfile );
RETURN_ERROR;
}
else if( code != ERROR_BAD_EXE_FORMAT )
{
printf( "ERROR\tNon-library file %s was processed via WINAPI: %lu\n", nonlibraryfile, (unsigned long)code );
DeleteFileA( nonlibraryfile );
RETURN_ERROR;
}
else
printf( "SUCCESS\tCould not open non-library file %s via WINAPI: %lu\n", nonlibraryfile, (unsigned long)code );
library = dlopen( nonlibraryfile, RTLD_GLOBAL );
if( library )
{
printf( "ERROR\tNon-library file %s was opened via dlopen\n", nonlibraryfile );
dlclose( library );
DeleteFileA( nonlibraryfile );
RETURN_ERROR;
}
error = dlerror( );
if( !error )
{
printf( "ERROR\tNo error from dlopen for non-library file\n" );
DeleteFileA( nonlibraryfile );
RETURN_ERROR;
}
else
printf( "SUCCESS\tCould not open non-library file %s: %s\n", nonlibraryfile, error );
DeleteFileA( nonlibraryfile );
library = dlopen( "nonexistentfile.dll", RTLD_GLOBAL );
if( library )
{
printf( "ERROR\tNon-existent file nonexistentfile.dll was opened via dlopen\n" );
RETURN_ERROR;
}
error = dlerror( );
if( !error )
{
printf( "ERROR\tNo error from dlopen for non-existent file\n" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tCould not open non-existent file nonexistentfile.dll: %s\n", error );
memset( toolongfile, 'X', sizeof( toolongfile ) - 5 );
memcpy( toolongfile + sizeof( toolongfile ) - 5, ".dll", 5 );
library = dlopen( toolongfile, RTLD_GLOBAL );
if( library )
{
printf( "ERROR\tFile with too long file name was opened via dlopen\n" );
RETURN_ERROR;
}
error = dlerror( );
if( !error )
{
printf( "ERROR\tNo error from dlopen for file with too long file name\n" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tCould not open file with too long file name: %s\n", error );
uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
library3 = LoadLibraryA( toolongfile );
code = GetLastError( );
SetErrorMode( uMode );
if( library3 )
{
printf( "ERROR\tFile with too long file name was opened via WINAPI\n" );
RETURN_ERROR;
}
else if( code != ERROR_FILENAME_EXCED_RANGE )
{
printf( "ERROR\tFile with too long file name was processed via WINAPI: %lu\n", (unsigned long)code );
RETURN_ERROR;
}
else
printf( "SUCCESS\tCould not open file with too long file name via WINAPI: %lu\n", (unsigned long)code );
fd = _open( "testdll2.dll", _O_RDONLY | _O_BINARY );
if( fd < 0 )
{
if( _chdir( "Debug" ) == 0 )
{
fd = _open( "testdll2.dll", _O_RDONLY | _O_BINARY );
if( fd < 0 )
_chdir( ".." );
}
}
if( fd < 0 )
{
if( _chdir( "Release" ) == 0 )
{
fd = _open( "testdll2.dll", _O_RDONLY | _O_BINARY );
if( fd < 0 )
_chdir( ".." );
}
}
if( fd < 0 )
{
printf( "ERROR\tCould not open library2 file: %s\n", strerror( errno ) );
RETURN_ERROR;
}
_close( fd );
library2 = dlopen( "testdll2.dll", RTLD_GLOBAL );
if( !library2 )
{
error = dlerror( );
printf( "ERROR\tCould not open library2 globally: %s\n", error ? error : "" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tOpened library2 globally: %p\n", library2 );
widename[0] = 0xE1; /* wide non-ASCII character mappable to most ANSI codepages */
wcscpy( widename+1, L"testdll2.dll" );
if( !CopyFileW( L"testdll2.dll", widename, FALSE ))
{
printf( "ERROR\tCould not copy file testdll2.dll: %lu\n", (unsigned long)GetLastError( ) );
RETURN_ERROR;
}
else
printf( "SUCCESS\tCopied testdll2.dll to wide name %ls\n", widename );
ret = WideCharToMultiByte( CP_ACP, 0, widename, -1, bytename, sizeof( bytename ), NULL, NULL );
/* if we cannot convert widename to current codepage (used by _open() function), skip this test */
if( ret > 0 )
{
bytename[ret] = 0;
fd = _wopen( widename, _O_RDONLY | _O_BINARY );
if( fd < 0 )
{
printf( "ERROR\tCould not open copied wide library2 file %ls: %s\n", widename, strerror( errno ) );
DeleteFileW( widename );
RETURN_ERROR;
}
_close( fd );
fd = _open( bytename, _O_RDONLY | _O_BINARY );
if( fd < 0 )
{
printf( "ERROR\tCould not open copied wide library2 file %s: %s\n", bytename, strerror( errno ) );
DeleteFileW( widename );
RETURN_ERROR;
}
_close( fd );
dlclose( library2 );
library2 = dlopen( bytename, RTLD_GLOBAL );
if( !library2 )
{
error = dlerror( );
printf( "ERROR\tCould not open copied wide library2 file %s globally: %s\n", bytename, error ? error : "" );
DeleteFileW( widename );
RETURN_ERROR;
}
else
printf( "SUCCESS\tOpened copied wide library2 file %s globally: %p\n", bytename, library2 );
dlclose( library2 );
DeleteFileW( widename );
library2 = dlopen( "testdll2.dll", RTLD_GLOBAL );
if( !library2 )
{
error = dlerror( );
printf( "ERROR\tCould not open library2 globally: %s\n", error ? error : "" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tOpened library2 globally: %p\n", library2 );
}
library = dlopen( "testdll.dll", RTLD_GLOBAL );
if( !library )
{
error = dlerror( );
printf( "ERROR\tCould not open library globally: %s\n", error ? error : "" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tOpened library globally: %p\n", library );
global = dlopen( 0, RTLD_GLOBAL );
if( !global )
{
error = dlerror( );
printf( "ERROR\tCould not open global handle: %s\n", error ? error : "" );
CLOSE_LIB;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot global handle: %p\n", global );
*(void **) (&fwrite_local) = dlsym( global, "fwrite" );
if (!fwrite_local)
{
error = dlerror();
printf("ERROR\tCould not get symbol from global handle: %s\n",
error ? error : "");
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot symbol from global handle: %p\n", *(void **) (&fwrite_local) );
{
const char *hello_world = "Hello world from local fwrite!\n";
fwrite_local( hello_world, sizeof( char ), strlen( hello_world ), stderr );
fflush( stderr );
}
*(void **) (&fputs_default) = dlsym( RTLD_DEFAULT, "fputs" );
if (!fputs_default)
{
error = dlerror();
printf("ERROR\tCould not get symbol from default handle: %s\n",
error ? error : "");
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot symbol from default handle: %p\n", *(void **) (&fputs_default) );
{
const char *hello_world_fputs = "Hello world from default fputs!\n";
fputs_default( hello_world_fputs, stderr );
fflush( stderr );
}
*(void **) (&function) = dlsym( library, "function" );
if( !function )
{
error = dlerror( );
printf( "ERROR\tCould not get symbol from library handle: %s\n",
error ? error : "" );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot symbol from library handle: %p\n", *(void **) (&function) );
RUNFUNC;
*(void **) (&function2_from_library2) = dlsym( library2, "function2" );
if( !function2_from_library2 )
{
error = dlerror( );
printf( "ERROR\tCould not get symbol from library2 handle: %s\n",
error ? error : "" );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot symbol from library2 handle: %p\n", *(void **) (&function2_from_library2) );
ret = function2_from_library2 ();
if( ret != 2 )
{
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
*(void **) (&nonexistentfunction) = dlsym( library, "nonexistentfunction" );
if( nonexistentfunction )
{
error = dlerror( );
printf( "ERROR\tGot nonexistent symbol from library handle: %p\n", *(void **) (&nonexistentfunction) );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
error = dlerror( );
if( !error )
{
printf( "ERROR\tNo error from dlsym for nonexistent symbol\n" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tCould not get nonexistent symbol from library handle: %s\n", error );
*(void **) (&function) = dlsym( global, "function" );
if( !function )
{
error = dlerror( );
printf( "ERROR\tCould not get symbol from global handle: %s\n",
error ? error : "" );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot symbol from global handle: %p\n", *(void **) (&function) );
RUNFUNC;
*(void **) (&nonexistentfunction) = dlsym( global, "nonexistentfunction" );
if( nonexistentfunction )
{
error = dlerror( );
printf( "ERROR\tGot nonexistent symbol from global handle: %p\n", *(void **) (&nonexistentfunction) );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
error = dlerror( );
if( !error )
{
printf( "ERROR\tNo error from dlsym for nonexistent symbol\n" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tCould not get nonexistent symbol from global handle: %s\n", error );
ret = dlclose( library );
if( ret )
{
error = dlerror( );
printf( "ERROR\tCould not close library: %s\n", error ? error : "" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tClosed library.\n" );
ret = dlclose( library2 );
if( ret )
{
error = dlerror( );
printf( "ERROR\tCould not close library2: %s\n", error ? error : "" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tClosed library2.\n" );
library = dlopen( "testdll.dll", RTLD_LOCAL );
if( !library )
{
error = dlerror( );
printf( "ERROR\tCould not open library locally: %s\n", error ? error : "" );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tOpened library locally: %p\n", library );
*(void **) (&function) = dlsym( library, "function" );
if( !function )
{
error = dlerror( );
printf( "ERROR\tCould not get symbol from library handle: %s\n",
error ? error : "" );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot symbol from library handle: %p\n", *(void **) (&function) );
RUNFUNC;
*(void **) (&nonexistentfunction) = dlsym( library, "nonexistentfunction" );
if( nonexistentfunction )
{
error = dlerror( );
printf( "ERROR\tGot nonexistent symbol from library handle: %p\n", *(void **) (&nonexistentfunction) );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
error = dlerror( );
if( !error )
{
printf( "ERROR\tNo error from dlsym for nonexistent symbol\n" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tCould not get nonexistent symbol from library handle: %s\n", error );
*(void **) (&function) = dlsym( global, "function" );
if( function )
{
error = dlerror( );
printf( "ERROR\tGot local symbol from global handle: %s @ %p\n",
error ? error : "", *(void **) (&function) );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tDid not get local symbol from global handle.\n" );
*(void **) (&nonexistentfunction) = dlsym( global, "nonexistentfunction" );
if( nonexistentfunction )
{
error = dlerror( );
printf( "ERROR\tGot nonexistent local symbol from global handle: %p\n", *(void **) (&nonexistentfunction) );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
error = dlerror( );
if( !error )
{
printf( "ERROR\tNo error from dlsym for nonexistent symbol\n" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tDid not get nonexistent local symbol from global handle: %s\n", error );
library = dlopen( "testdll.dll", RTLD_GLOBAL );
if( !library )
{
error = dlerror( );
printf( "ERROR\tCould not open library globally without closing it first: %s\n", error ? error : "" );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tOpened library globally without closing it first: %p\n", library );
*(void **) (&function) = dlsym( global, "function" );
if( !function )
{
error = dlerror( );
printf( "ERROR\tCould not get symbol from global handle: %s\n",
error ? error : "" );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot symbol from global handle: %p\n", *(void **) (&function) );
RUNFUNC;
*(void **) (&nonexistentfunction) = dlsym( global, "nonexistentfunction" );
if( nonexistentfunction )
{
error = dlerror( );
printf( "ERROR\tGot nonexistent symbol from global handle: %p\n", *(void **) (&nonexistentfunction) );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
error = dlerror( );
if( !error )
{
printf( "ERROR\tNo error from dlsym for nonexistent symbol\n" );
RETURN_ERROR;
}
else
{
printf( "SUCCESS\tCould not get nonexistent symbol from global handle: %s\n", error );
/* Test that the second call to dlerror() returns null as in the specs
See https://github.com/dlfcn-win32/dlfcn-win32/issues/34 */
error = dlerror( );
if( error == NULL )
{
printf( "SUCCESS\tSecond consecutive call to dlerror returned NULL\n");
}
else
{
printf( "ERROR\tSecond consecutive call to dlerror returned a non-NULL pointer: %p\n", error );
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
}
*(void **) (&function) = dlsym( global, "fwrite" );
if (!function)
{
error = dlerror();
printf("ERROR\tCould not get symbol from global handle: %s\n",
error ? error : "");
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot symbol from global handle: %p\n", *(void **) (&function) );
uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
library3 = LoadLibraryA("testdll3.dll");
SetErrorMode( uMode );
if (!library3)
{
printf( "ERROR\tCould not open library3 via WINAPI\n" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tOpened library3 via WINAPI: %p\n", (void *)library3 );
ret = dlclose( library );
if( ret )
{
error = dlerror( );
printf( "ERROR\tCould not close library: %s\n", error ? error : "" );
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tClosed library.\n" );
*(void **) (&function) = dlsym( global, "function3" );
if (!function)
{
error = dlerror();
printf("ERROR\tCould not get symbol from global handle: %s\n",
error ? error : "");
CLOSE_LIB;
CLOSE_GLOBAL;
RETURN_ERROR;
}
else
printf( "SUCCESS\tGot symbol from global handle: %p\n", *(void **) (&function) );
RUNFUNC;
ret = dlclose( global );
if( ret )
{
error = dlerror( );
printf( "ERROR\tCould not close global handle: %s\n", error ? error : "" );
RETURN_ERROR;
}
else
printf( "SUCCESS\tClosed global handle.\n" );
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif
return 0;
}