View Issue Details

IDProjectCategoryView StatusLast Update
0000469fileGeneralpublic2023-08-05 14:40
Reporterjoveler Assigned Tochristos  
PrioritynormalSeveritymajorReproducibilityalways
Status resolvedResolutionfixed 
Platformx86, x64, arm64OSWindows 10OS Version22H2
Product Version5.45 
Fixed in VersionHEAD 
Summary0000469: dllexport keyword required for building Win32 DLL
Description[*] Main issue

In Windows, compiled libmagic-1.dll exports too many/empty function symbols.
- MSYS2 MinGW-w64: Exports 134 functions
- llvm-mingw: Exports 0 functions

libmagic does not define any visibility keywords in Win32 builds.

As a solution, `file_public` macro must be defined as `__declspec(dllexport)` in Win32.
After the patch, clang-compiled libmagic-1.dll contains an almost correct export function table.

- llvm-mingw: Exports 20 functions


[*] Symbol export visibility issue

In POSIX GCC, symbol export visibility is controlled by `__attribute__ ((__visibility__(...)))` keyword.

- `__attribute__ ((__visibility__("default")))`: Always export function symbol.
- `__attribute__ ((__visibility__("hidden")))`: Must not export symbol.
- No Keyword: Decided by compiler policy.

In Win32 MSVC, symbol export visibility is more strict.

- `__declspec(dllexport)`: Expose function symbol to export table (EAT)
- No keyword: Always prevent symbol from being exposed to EAT

In MSYS2 MinGW-w64 or llvm-mingw, they have a relaxed policy on the `No Keyword` case.
It is because they try to replicate POSIX behavior to some degree.

- Latest MSYS2 MinGW-w64 has been exposing every function, at least in `libmagic-1.dll`.
- Latest llvm-mingw follows MSVC policy, hiding every function symbol.
    - llvm-mingw had the same policy as MinGW-w64, but it seems to be changed in 2023.

In Windows, all libmagic functions were built without any visibility keyword.
Thus MinGW-w64 build is exposing symbols that should have been hidden, like `file_apprentice`.
LLVM/Clang build is not exposing any symbols.


[*] Solution

`__attribute__ ((__visibility__("default")))` and `__declspec(dllexport)` keywords are equivalent.
And many other cross-platform libraries are mapping these two.

I tested `__declspec(dllexport)` keyword in the Win32 build, and got llvm-mingw working.
However, MinGW-w64 still exposes all functions. It would need more investigation.
In the meantime, the attached patch will solve the issue at least on LLVM/Clang.

The patch uses `WIN32` macro to detect Windows environment because existing code uses it.
It may be changed to other macros, such as `__MINGW32__`.
Steps To Reproduce[Reproduce Instructions for Windows]
1. Install MSYS2, and download llvm-mingw, and radare2 from its GitHub page.
2. Unpack file-5.45-Win32-reproduce.tar.xz
3. Run `libmagic-msys2.sh` in MSYS2 shell, for example:
  $ ./libmagic-msys2.sh -t /c/Tools/llvm-mingw-20230614-ucrt-x86_64 -a x86_64 /c/Build/file-5.45
4. Check EAT of `build-x86_64\libmagic-1.dll`. It can be done with radare2:
  > C:\radare2\bin\r2 build-x86_64\libmagic-1.dll
  > [0xYYYYYYYY]> is~
5. Apply `Win32_visibility_dllexport.diff` patch, and retry compiling.
Additional InformationI have attached lists of exported functions on Linux/Win32 binaries.
Look through `symbols_results.zip`.
TagsEAT, export, MinGW, Win32

Activities

joveler

2023-08-03 14:31

reporter  

symbols_results.zip (8,025 bytes)
Win32_visibility_dllexport.diff (781 bytes)   
diff --git a/src/file.h b/src/file.h
index 2e0494d..a811d8d 100644
--- a/src/file.h
+++ b/src/file.h
@@ -107,11 +107,18 @@
 
 #define file_private static
 
-#if HAVE_VISIBILITY && !defined(WIN32)
-#define file_public  __attribute__ ((__visibility__("default")))
-#ifndef file_protected
-#define file_protected __attribute__ ((__visibility__("hidden")))
-#endif
+#if HAVE_VISIBILITY
+	#if defined(WIN32)
+		#define file_public  __declspec(dllexport)
+		#ifndef file_protected
+		#define file_protected
+		#endif
+	#else
+		#define file_public  __attribute__ ((__visibility__("default")))
+		#ifndef file_protected
+		#define file_protected __attribute__ ((__visibility__("hidden")))
+		#endif
+	#endif
 #else
 #define file_public
 #ifndef file_protected

christos

2023-08-05 14:40

manager   ~0003972

Applied, thanks!

Issue History

Date Modified Username Field Change
2023-08-03 14:31 joveler New Issue
2023-08-03 14:31 joveler Tag Attached: EAT
2023-08-03 14:31 joveler Tag Attached: export
2023-08-03 14:31 joveler Tag Attached: MinGW
2023-08-03 14:31 joveler Tag Attached: Win32
2023-08-03 14:31 joveler File Added: symbols_results.zip
2023-08-03 14:31 joveler File Added: Win32_visibility_dllexport.diff
2023-08-03 14:31 joveler File Added: file-5.45-Win32-reproduce.tar.xz
2023-08-05 14:40 christos Assigned To => christos
2023-08-05 14:40 christos Status new => assigned
2023-08-05 14:40 christos Status assigned => resolved
2023-08-05 14:40 christos Resolution open => fixed
2023-08-05 14:40 christos Fixed in Version => HEAD
2023-08-05 14:40 christos Note Added: 0003972