Add Windows build with clang
This commit is contained in:
4
namecollision/.gitignore
vendored
4
namecollision/.gitignore
vendored
@@ -1,2 +1,6 @@
|
|||||||
libhello.so
|
libhello.so
|
||||||
|
libhello.dll
|
||||||
|
libhello.lib
|
||||||
main
|
main
|
||||||
|
main.exe
|
||||||
|
libhello.exp
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
`print_obj()`. `main.cpp` defines a `namespace nt` with a free function
|
`print_obj()`. `main.cpp` defines a `namespace nt` with a free function
|
||||||
`print()`, and calls both `nt::print()` and `print_obj()`.
|
`print()`, and calls both `nt::print()` and `print_obj()`.
|
||||||
|
|
||||||
`hello.cpp` is compiled into a shared library (`libhello.so`), and `main.cpp` is
|
`hello.cpp` is compiled into a shared library (`libhello.so` on Linux,
|
||||||
linked against it.
|
`libhello.dll` on Windows), and `main.cpp` is linked against it.
|
||||||
|
|
||||||
## The Problem
|
## The Problem
|
||||||
|
|
||||||
@@ -80,7 +80,34 @@ This flat namespace behavior means a weak symbol in a `.so` can be silently
|
|||||||
overridden by any strong symbol of the same name anywhere in the process — even
|
overridden by any strong symbol of the same name anywhere in the process — even
|
||||||
from the main executable.
|
from the main executable.
|
||||||
|
|
||||||
## The Fix
|
## Why Windows Is Not Affected
|
||||||
|
|
||||||
|
On Windows, this collision does not occur. The DLL symbol model is fundamentally
|
||||||
|
different:
|
||||||
|
|
||||||
|
- Only symbols explicitly marked `__declspec(dllexport)` are visible outside the
|
||||||
|
DLL — everything else is private by default
|
||||||
|
- The linker generates an import library (`.lib`) with stubs that reference the
|
||||||
|
DLL by name, so the loader knows exactly which DLL each symbol comes from
|
||||||
|
- There is no flat global symbol namespace — each DLL is its own isolated scope
|
||||||
|
|
||||||
|
This means the internal `class nt` in `hello.cpp` was never at risk on Windows
|
||||||
|
regardless of optimization level. The bug is Linux (and ELF) specific.
|
||||||
|
|
||||||
|
To keep `hello.cpp` portable, the `__declspec(dllexport)` on `print_obj` is
|
||||||
|
wrapped in a macro:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define EXPORT __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define EXPORT
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
On Linux `EXPORT` expands to nothing. On Windows it marks the symbol for export.
|
||||||
|
|
||||||
|
## The Linux Fix
|
||||||
|
|
||||||
Wrap internal-use classes in an **anonymous namespace** in `hello.cpp`:
|
Wrap internal-use classes in an **anonymous namespace** in `hello.cpp`:
|
||||||
|
|
||||||
@@ -103,6 +130,10 @@ The `_GLOBAL__N_1` prefix is the ABI encoding for the anonymous namespace,
|
|||||||
making it unique per translation unit and invisible to the dynamic linker. No
|
making it unique per translation unit and invisible to the dynamic linker. No
|
||||||
collision is possible.
|
collision is possible.
|
||||||
|
|
||||||
|
The Linux and Windows fixes solve the same problem from opposite defaults:
|
||||||
|
- Linux: **opt-in to hiding** symbols (anonymous namespace, `-fvisibility=hidden`)
|
||||||
|
- Windows: **opt-in to exporting** symbols (`__declspec(dllexport)`)
|
||||||
|
|
||||||
## Key Takeaways
|
## Key Takeaways
|
||||||
|
|
||||||
- Class and namespace names mangle identically in the Itanium C++ ABI
|
- Class and namespace names mangle identically in the Itanium C++ ABI
|
||||||
@@ -110,7 +141,9 @@ collision is possible.
|
|||||||
the executable
|
the executable
|
||||||
- ODR violations are UB with no required diagnostic — bugs may only appear at
|
- ODR violations are UB with no required diagnostic — bugs may only appear at
|
||||||
certain optimization levels
|
certain optimization levels
|
||||||
|
- This is a Linux/ELF-specific problem — Windows DLLs use explicit export tables
|
||||||
|
and are not affected
|
||||||
- Internal implementation details in shared libraries should use anonymous
|
- Internal implementation details in shared libraries should use anonymous
|
||||||
namespaces to prevent symbol leakage
|
namespaces to prevent symbol leakage on Linux
|
||||||
- Use `objdump -t` or `nm` to inspect symbol visibility and catch these issues
|
- Use `objdump -t` or `nm` to inspect symbol visibility and catch these issues
|
||||||
early
|
early
|
||||||
|
|||||||
15
namecollision/build.ps1
Normal file
15
namecollision/build.ps1
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
param(
|
||||||
|
[string]$Opt = "-O0"
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# Compile hello.cpp as a shared library (also generates libhello.lib import library)
|
||||||
|
clang++ -std=c++17 $Opt -shared -o libhello.dll hello.cpp "-Wl,/IMPLIB:libhello.lib"
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||||
|
|
||||||
|
# Compile and link main.cpp with the shared library
|
||||||
|
clang++ -std=c++17 $Opt -o main.exe main.cpp libhello.lib
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||||
|
|
||||||
|
Write-Host "Build successful"
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define EXPORT __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class nt {
|
class nt {
|
||||||
@@ -9,7 +15,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void print_obj() {
|
EXPORT void print_obj() {
|
||||||
cout << "Hello from function\n";
|
cout << "Hello from function\n";
|
||||||
nt obj;
|
nt obj;
|
||||||
obj.print();
|
obj.print();
|
||||||
|
|||||||
Reference in New Issue
Block a user