From 0382965825caa27deaac3ab6a25145c896b9053b Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 18 Nov 2022 21:47:04 +0100 Subject: libpci: windows: Fix usage of GetModuleFileName() Module file name can have arbitrary length despite all MS examples say about MAX_PATH upper limit. This limit does not apply for example when executable is running from network disk with very long UNC paths or when using "\\??\\" prefix for specifying executable binary path. So handle buffer truncatenation by retrying GetModuleFileName() call with larger buffer. Fixes loading of pci.ids file when lspci.exe binary is running from network drive with path longer than 260 bytes. --- lib/init.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/init.c b/lib/init.c index e6efb8b..9cb64f3 100644 --- a/lib/init.c +++ b/lib/init.c @@ -223,12 +223,33 @@ pci_init_name_list_path(struct pci_access *a) else { char *path, *sep; - DWORD len; + size_t len; + size_t size; + + /* + * Module file name can have arbitrary length despite all MS examples say + * about MAX_PATH upper limit. This limit does not apply for example when + * executable is running from network disk with very long UNC paths or + * when using "\\??\\" prefix for specifying executable binary path. + * Function GetModuleFileName() returns passed size argument when passed + * buffer is too small and does not signal any error. In this case retry + * again with larger buffer. + */ + size = 256; /* initial buffer size (more than sizeof(PCI_IDS)) */ +retry: + path = pci_malloc(a, size); + len = GetModuleFileNameA(module, path, size-sizeof(PCI_IDS)); + if (len >= size-sizeof(PCI_IDS)) + { + free(path); + size *= 2; + goto retry; + } + else if (len == 0) + path[0] = '\0'; - path = pci_malloc(a, MAX_PATH+1); - len = GetModuleFileNameA(NULL, path, MAX_PATH+1); - sep = (len > 0) ? strrchr(path, '\\') : NULL; - if (len == 0 || len == MAX_PATH+1 || !sep || MAX_PATH-(size_t)(sep+1-path) < sizeof(PCI_IDS)) + sep = strrchr(path, '\\'); + if (!sep) { free(path); pci_set_name_list_path(a, PCI_IDS, 0); -- cgit 1.2.3-korg From a7a7aaeaa9feca5377a0da891d2d444db20cdb7d Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 18 Nov 2022 21:54:53 +0100 Subject: libpci: windows: Fix locating path to pci.ids file for DLL builds When using shared libpci DLL library, it is expected that pci.ids file is stored in directory where is also libpci DLL library and not in directory where is application executable. Based on the build mode and compile options, choose the appropriate function for retrieving path to the libpci DLL library or application executable. Also pass correct module argument to GetModuleFileName() call. --- lib/init.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/lib/init.c b/lib/init.c index 9cb64f3..590dfb4 100644 --- a/lib/init.c +++ b/lib/init.c @@ -14,7 +14,56 @@ #include "internal.h" #ifdef PCI_OS_WINDOWS + #include + +/* Force usage of ANSI (char*) variant of GetModuleFileName() function */ +#ifdef _WIN32 +#ifdef GetModuleFileName +#undef GetModuleFileName +#endif +#define GetModuleFileName GetModuleFileNameA +#endif + +/* Define __ImageBase for all linkers */ +#ifdef _WIN32 +/* GNU LD provides __ImageBase symbol since 2.19, in previous versions it is + * under name _image_base__, so add weak alias for compatibility. */ +#ifdef __GNUC__ +asm(".weak\t" PCI_STRINGIFY(__MINGW_USYMBOL(__ImageBase)) "\n\t" + ".set\t" PCI_STRINGIFY(__MINGW_USYMBOL(__ImageBase)) "," PCI_STRINGIFY(__MINGW_USYMBOL(_image_base__))); +#endif +/* + * MSVC link.exe provides __ImageBase symbol since 12.00 (MSVC 6.0), for + * previous versions resolve it at runtime via GetModuleHandleA() which + * returns base for main executable or via VirtualQuery() for DLL builds. + */ +#if defined(_MSC_VER) && _MSC_VER < 1200 +static HMODULE +get_current_module_handle(void) +{ +#ifdef PCI_SHARED_LIB + MEMORY_BASIC_INFORMATION info; + size_t len = VirtualQuery(&get_current_module_handle, &info, sizeof(info)); + if (len != sizeof(info)) + return NULL; + return (HMODULE)info.AllocationBase; +#else + return GetModuleHandleA(NULL); +#endif +} +#define __ImageBase (*(IMAGE_DOS_HEADER *)get_current_module_handle()) +#else +extern IMAGE_DOS_HEADER __ImageBase; +#endif +#endif + +#if defined(_WINDLL) +extern HINSTANCE _hModule; +#elif defined(_WINDOWS) +extern HINSTANCE _hInstance; +#endif + #endif static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = { @@ -224,8 +273,20 @@ pci_init_name_list_path(struct pci_access *a) { char *path, *sep; size_t len; + +#if defined(_WIN32) || defined(_WINDLL) || defined(_WINDOWS) + + HMODULE module; size_t size; +#if defined(_WIN32) + module = (HINSTANCE)&__ImageBase; +#elif defined(_WINDLL) + module = _hModule; +#elif defined(_WINDOWS) + module = _hInstance; +#endif + /* * Module file name can have arbitrary length despite all MS examples say * about MAX_PATH upper limit. This limit does not apply for example when @@ -238,7 +299,7 @@ pci_init_name_list_path(struct pci_access *a) size = 256; /* initial buffer size (more than sizeof(PCI_IDS)) */ retry: path = pci_malloc(a, size); - len = GetModuleFileNameA(module, path, size-sizeof(PCI_IDS)); + len = GetModuleFileName(module, path, size-sizeof(PCI_IDS)); if (len >= size-sizeof(PCI_IDS)) { free(path); @@ -248,9 +309,26 @@ retry: else if (len == 0) path[0] = '\0'; +#else + + const char *exe_path; + + exe_path = _pgmptr; + + len = strlen(exe_path); + path = pci_malloc(a, len+sizeof(PCI_IDS)); + memcpy(path, exe_path, len+1); + +#endif + sep = strrchr(path, '\\'); if (!sep) { + /* + * If current module path (current executable for static builds or + * current DLL library for shared build) cannot be determined then + * fallback to the current directory. + */ free(path); pci_set_name_list_path(a, PCI_IDS, 0); } -- cgit 1.2.3-korg From 43fd29c72dea38d080be5681056b6a9e536e590b Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 18 Nov 2022 21:57:50 +0100 Subject: libpci: djgpp: Allow to specify empty IDSDIR= Like for windows builds this will cause to load pci.ids file from the same directory where is stored application binary. Code is same as for Windows, just djgpp uses global symbol __dos_argv0 instead of _pgmptr. Tested with following compile command: make CROSS_COMPILE=i586-pc-msdosdjgpp- HOST=i586-djgpp ZLIB=no DNS=no IDSDIR= --- lib/init.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/init.c b/lib/init.c index 590dfb4..8c8f973 100644 --- a/lib/init.c +++ b/lib/init.c @@ -13,6 +13,10 @@ #include "internal.h" +#ifdef PCI_OS_DJGPP +#include /* for __dos_argv0 */ +#endif + #ifdef PCI_OS_WINDOWS #include @@ -262,7 +266,7 @@ pci_get_method_name(int index) return pci_methods[index]->name; } -#ifdef PCI_OS_WINDOWS +#if defined(PCI_OS_WINDOWS) || defined(PCI_OS_DJGPP) static void pci_init_name_list_path(struct pci_access *a) @@ -274,7 +278,7 @@ pci_init_name_list_path(struct pci_access *a) char *path, *sep; size_t len; -#if defined(_WIN32) || defined(_WINDLL) || defined(_WINDOWS) +#if defined(PCI_OS_WINDOWS) && (defined(_WIN32) || defined(_WINDLL) || defined(_WINDOWS)) HMODULE module; size_t size; @@ -309,11 +313,15 @@ retry: else if (len == 0) path[0] = '\0'; -#else +#elif defined(PCI_OS_DJGPP) || defined(PCI_OS_WINDOWS) const char *exe_path; +#ifdef PCI_OS_DJGPP + exe_path = __dos_argv0; +#else exe_path = _pgmptr; +#endif len = strlen(exe_path); path = pci_malloc(a, len+sizeof(PCI_IDS)); -- cgit 1.2.3-korg From 6cf9052e88e23902a6fd1e6dcb8f07145383501c Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 18 Nov 2022 22:57:11 +0100 Subject: libpci: windows: Fix path returned by GetModuleFileName() GetModuleFileName() on Windows 10 has bugs and returns bogus path. Implement fixups to make path usable for later fopen() call. --- lib/init.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/init.c b/lib/init.c index 8c8f973..a310cc5 100644 --- a/lib/init.c +++ b/lib/init.c @@ -300,11 +300,11 @@ pci_init_name_list_path(struct pci_access *a) * buffer is too small and does not signal any error. In this case retry * again with larger buffer. */ - size = 256; /* initial buffer size (more than sizeof(PCI_IDS)) */ + size = 256; /* initial buffer size (more than sizeof(PCI_IDS)-4) */ retry: path = pci_malloc(a, size); - len = GetModuleFileName(module, path, size-sizeof(PCI_IDS)); - if (len >= size-sizeof(PCI_IDS)) + len = GetModuleFileName(module, path, size-sizeof(PCI_IDS)-4); /* 4 for "\\\\?\\" */ + if (len >= size-sizeof(PCI_IDS)-4) { free(path); size *= 2; @@ -313,6 +313,39 @@ retry: else if (len == 0) path[0] = '\0'; + /* + * GetModuleFileName() has bugs. On Windows 10 it prepends current drive + * letter if path is just pure NT namespace (with "\\??\\" prefix). Such + * extra drive letter makes path fully invalid and unusable. So remove + * extra drive letter to make path valid again. + * Reproduce: CreateProcessW("\\??\\C:\\lspci.exe", ...) + */ + if (((path[0] >= 'a' && path[0] <= 'z') || + (path[0] >= 'A' && path[0] <= 'Z')) && + strncmp(path+1, ":\\??\\", 5) == 0) + { + memmove(path, path+2, len-2); + len -= 2; + path[len] = '\0'; + } + + /* + * GetModuleFileName() has bugs. On Windows 10 it does not add "\\\\?\\" + * prefix when path is in native NT UNC namespace. Such path is treated by + * WinAPI/DOS functions as standard DOS path relative to the current + * directory, hence something completely different. So prepend missing + * "\\\\?\\" prefix to make path valid again. + * Reproduce: CreateProcessW("\\??\\UNC\\10.0.2.4\\qemu\\lspci.exe", ...) + */ + if (strncmp(path, "\\UNC\\", 5) == 0 || + strncmp(path, "UNC\\", 4) == 0) + { + memmove(path+4, path, len); + memcpy(path, "\\\\?\\", 4); + len += 4; + path[len] = '\0'; + } + #elif defined(PCI_OS_DJGPP) || defined(PCI_OS_WINDOWS) const char *exe_path; -- cgit 1.2.3-korg From 64d7bab6f27418199d244481664255b24c7ffc20 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 18 Nov 2022 23:40:39 +0100 Subject: libpci: windows: Handle long paths generated by GetModuleFileName() C function fopen() implemented by msvcrt.dll requires special prefix "\\\\?\\" for paths longer than 260 bytes. Because GetModuleFileName() returns absolute path, it may be longer than 260 bytes. Add fixup to handle long paths. --- lib/init.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/init.c b/lib/init.c index a310cc5..4d28a2e 100644 --- a/lib/init.c +++ b/lib/init.c @@ -336,9 +336,16 @@ retry: * directory, hence something completely different. So prepend missing * "\\\\?\\" prefix to make path valid again. * Reproduce: CreateProcessW("\\??\\UNC\\10.0.2.4\\qemu\\lspci.exe", ...) + * + * If path starts with DOS drive letter and with appended PCI_IDS is + * longer than 260 bytes and is without "\\\\?\\" prefix then append it. + * This prefix is required for paths and file names with DOS drive letter + * longer than 260 bytes. */ if (strncmp(path, "\\UNC\\", 5) == 0 || - strncmp(path, "UNC\\", 4) == 0) + strncmp(path, "UNC\\", 4) == 0 || + (((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z')) && + len + sizeof(PCI_IDS) >= 260)) { memmove(path+4, path, len); memcpy(path, "\\\\?\\", 4); -- cgit 1.2.3-korg From 9001c06d1e15c9e0325d6036a0bb38fb1460239d Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 21 Nov 2022 01:20:30 +0100 Subject: libpci: windows: Define ERROR_NOT_FOUND Fix compile issues with older toolchain which does not define ERROR_NOT_FOUND macro. --- lib/win32-kldbg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/win32-kldbg.c b/lib/win32-kldbg.c index 3890b6b..4c8ee54 100644 --- a/lib/win32-kldbg.c +++ b/lib/win32-kldbg.c @@ -15,6 +15,10 @@ #include "internal.h" #include "i386-io-windows.h" +#ifndef ERROR_NOT_FOUND +#define ERROR_NOT_FOUND 1168 +#endif + #ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20 #endif -- cgit 1.2.3-korg