From c76a096a289c2939feb1ca235923f448096bc076 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Sun, 25 Jan 2026 21:09:14 -0600 Subject: [PATCH] roken: Add pread() for Windows --- cf/roken-frag.m4 | 1 + lib/roken/NTMakefile | 1 + lib/roken/dlfcn.c | 749 +++++++++++++++++++++++++++++++++++++++++++ lib/roken/pread.c | 109 +++++++ lib/roken/roken.h.in | 6 + 5 files changed, 866 insertions(+) create mode 100644 lib/roken/dlfcn.c create mode 100644 lib/roken/pread.c diff --git a/cf/roken-frag.m4 b/cf/roken-frag.m4 index a554dc8f0..2e7d333db 100644 --- a/cf/roken-frag.m4 +++ b/cf/roken-frag.m4 @@ -360,6 +360,7 @@ AC_BROKEN([ \ mergesort \ mergesort_r \ mkstemp \ + pread \ putenv \ rcmd \ readv \ diff --git a/lib/roken/NTMakefile b/lib/roken/NTMakefile index 55b7a5605..64557ab9b 100644 --- a/lib/roken/NTMakefile +++ b/lib/roken/NTMakefile @@ -90,6 +90,7 @@ libroken_la_OBJS = \ $(OBJ)\parse_bytes.obj \ $(OBJ)\parse_time.obj \ $(OBJ)\parse_units.obj \ + $(OBJ)\pread.obj \ $(OBJ)\realloc.obj \ $(OBJ)\rename.obj \ $(OBJ)\resolve.obj \ diff --git a/lib/roken/dlfcn.c b/lib/roken/dlfcn.c new file mode 100644 index 000000000..96452c3dc --- /dev/null +++ b/lib/roken/dlfcn.c @@ -0,0 +1,749 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * Copyright (c) 2015 Tiancheng "Timothy" Gu + * Copyright (c) 2019 Pali Rohár + * Copyright (c) 2020 Ralf Habacker + * + * 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. + */ + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif +#ifdef _DEBUG +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif +#include +#include +#include + +#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" + +/* 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 ) + return TRUE; + + for( pobject = &first_object; pobject->next; pobject = pobject->next ); + + nobject = (local_object*) malloc( sizeof( local_object ) ); + + if( !nobject ) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + 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 ) + 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; + + dwMessageId = GetLastError( ); + + if( dwMessageId == 0 ) + return; + + len = strlen( str ); + if( len > sizeof( error_buffer ) - 5 ) + len = sizeof( error_buffer ) - 5; + + /* Format error message to: + * "": + */ + 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 */ + 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 ) +{ + 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 = ( ( (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 ); +} + +/* 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); + HMODULE psapi; + + if( !EnumProcessModulesPtr ) + { + psapi = LoadLibraryA( "Psapi.dll" ); + if( psapi ) + EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "EnumProcessModules" ); + if( !EnumProcessModulesPtr ) + return 0; + } + + 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 == 0 ) + { + /* POSIX says that if the value of file is 0, 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)" ); + } + else + { + HANDLE hCurrentProc; + DWORD dwProcModsBefore, dwProcModsAfter; + char lpFileName[MAX_PATH]; + size_t i, len; + + len = strlen( file ); + + if( len >= sizeof( lpFileName ) ) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + save_err_str( file ); + 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 ); + } + 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 ); + 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 ); + + /* dlclose's return value in inverted in relation to FreeLibrary's. */ + ret = !ret; + + return (int) ret; +} + +__declspec(noinline) /* Needed for _ReturnAddress() */ +DLFCN_EXPORT +void *dlsym( void *handle, const char *name ) +{ + FARPROC symbol; + HMODULE hCaller; + HMODULE hModule; + HANDLE hCurrentProc; + + error_occurred = FALSE; + + symbol = NULL; + hCaller = NULL; + hModule = GetModuleHandle( NULL ); + hCurrentProc = GetCurrentProcess( ); + + 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 standard GetModuleHandleExA() function. + */ + if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) _ReturnAddress( ), &hCaller ) ) + 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 ) + { + HMODULE *modules; + DWORD cbNeeded; + DWORD dwSize; + size_t i; + + /* 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 + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto end; + } + } + } + +end: + if( symbol == NULL ) + { + if( GetLastError() == 0 ) + SetLastError( ERROR_PROC_NOT_FOUND ); + save_err_str( name ); + } + + 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 import table */ +static const char *get_import_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid, void *addr, void **func_address ) +{ + int i; + void *candidateAddr = NULL; + const char *candidateName = NULL; + BYTE *base = (BYTE *) module; /* Required to have correct calculations */ + + for( i = 0; iid[i].Characteristics != 0 && iid[i].FirstThunk != 0; i++ ) + { + IMAGE_THUNK_DATA *thunkILT = (IMAGE_THUNK_DATA *)( base + iid[i].Characteristics ); + IMAGE_THUNK_DATA *thunkIAT = (IMAGE_THUNK_DATA *)( base + iid[i].FirstThunk ); + + for( ; thunkILT->u1.AddressOfData != 0; thunkILT++, thunkIAT++ ) + { + IMAGE_IMPORT_BY_NAME *nameData; + + if( IMAGE_SNAP_BY_ORDINAL( thunkILT->u1.Ordinal ) ) + continue; + + if( (void *) thunkIAT->u1.Function > addr || candidateAddr >= (void *) thunkIAT->u1.Function ) + continue; + + candidateAddr = (void *) thunkIAT->u1.Function; + nameData = (IMAGE_IMPORT_BY_NAME *)( base + (ULONG_PTR) thunkILT->u1.AddressOfData ); + candidateName = (const char *) nameData->Name; + } + } + + *func_address = candidateAddr; + return candidateName; +} + +/* Return symbol name for a given address from export table */ +static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, 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( 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( 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, 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_module_info( HMODULE hModuleImport, void *addr, Dl_info *info ) +{ + HMODULE hModule; + DWORD dwSize; + IMAGE_EXPORT_DIRECTORY *ied; + IMAGE_IMPORT_DESCRIPTOR *iid; + const char *name; + void *funcAddress = NULL; + + /* Get module of the specified address */ + if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || 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; + info->dli_sname = NULL; + + /* First try to 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 ); + + /* If symbol name is not known and we know which module is importing this address + * then try to find symbol name in this module's import table as the last resort. */ + if( info->dli_sname == NULL && hModuleImport != NULL ) + { + if( get_image_section( hModuleImport, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, NULL ) ) + { + name = get_import_symbol_name( hModuleImport, iid, addr, &funcAddress ); + if( name != NULL ) + info->dli_sname = name; + } + } + + info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : addr; + + return TRUE; +} + +DLFCN_EXPORT +int dladdr( void *addr, Dl_info *info ) +{ + HMODULE hModule = NULL; + + if( addr == NULL || info == NULL ) + return 0; + + if( !is_valid_address( addr ) ) + return 0; + + if( is_import_thunk( addr ) ) + { + void *iat; + DWORD iatSize; + + /* Get module of the import thunk address */ + if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || 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( addr == NULL ) + return 0; + + if( !is_valid_address( addr ) ) + return 0; + } + + if( !fill_module_info( hModule, 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 diff --git a/lib/roken/pread.c b/lib/roken/pread.c new file mode 100644 index 000000000..3b642b279 --- /dev/null +++ b/lib/roken/pread.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include "roken.h" + +#ifdef _WIN32 +#include +#include + +/* + * This is a WIN32 implementation of the POSIX pread() function. + * It reads from the file descriptor at the specified offset without + * changing the file position. + */ +ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL +pread(int fd, void *buf, size_t nbytes, off_t off) +{ + OVERLAPPED ov; + HANDLE h; + DWORD nread = 0; + BOOL ret; + + h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + + if (off < 0) { + errno = EINVAL; + return -1; + } + + memset(&ov, 0, sizeof(ov)); + ov.Offset = ((uint64_t)off & 0xFFFFFFFF); + ov.OffsetHigh = (((uint64_t)off >> 32) & 0xFFFFFFFF); + + SetLastError(0); + ret = ReadFile(h, buf, (DWORD)nbytes, &nread, &ov); + if (ret) { + ssize_t bytes = nread; + + if (bytes >= 0 && nread != (DWORD)bytes) { + errno = EOVERFLOW; + return -1; + } + return bytes; + } + + /* + * Map common Windows errors to errno values + */ + switch (GetLastError()) { + case ERROR_HANDLE_EOF: + return 0; + case ERROR_INVALID_HANDLE: + errno = EBADF; + break; + case ERROR_ACCESS_DENIED: + errno = EACCES; + break; + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + case ERROR_NOT_ENOUGH_QUOTA: + errno = ENOMEM; + break; + case ERROR_OPERATION_ABORTED: + errno = EINTR; + break; + default: + errno = EIO; + break; + } + return -1; +} +#endif /* _WIN32 */ diff --git a/lib/roken/roken.h.in b/lib/roken/roken.h.in index 8a88d6f67..868e9595b 100644 --- a/lib/roken/roken.h.in +++ b/lib/roken/roken.h.in @@ -860,6 +860,12 @@ ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL readv(int, const struct iovec *, int); #endif +#ifndef HAVE_PREAD +#define pread rk_pread +ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL +pread(int, void *, size_t, off_t); +#endif + #ifdef NO_PIDFILES #define rk_pidfile(x) ((void) 0) #else