emscripten-core / emscripten-core/emscripten
[BUG] Dlclose behaves differently as compared to native counterpart
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
> I don't think you can currently expect dlclose followed by dlopen to replace symbols in the global namespace. I think the first dll to provide I symbol will currently win and that symbol will remain for the life of the program.
>
> If that native behaviour is different to this then I think we would consider a patch to make our behaviour match, in which case please open a bug for that.
_Originally posted by @sbc100 in [#23793](https://github.com/emscripten-core/emscripten/issues/23793#issuecomment-2711234732)_
Here's an example testing the above on my macos
1) main.cpp
```
#include
#include
typedef int (*getXFunc)();
int main() {
// Load lib1
void* handle1 = dlopen("./lib1.dylib", RTLD_NOW | RTLD_GLOBAL);
if (!handle1) {
std::cerr << "Failed to load lib1: " << dlerror() << std::endl;
return -1;
}
getXFunc getX1 = (getXFunc)dlsym(handle1, "getX");
if (!getX1) {
std::cerr << "Failed to get symbol from lib1: " << dlerror() << std::endl;
return -1;
}
std::cout << "Initial x from lib1: " << getX1() << std::endl;
// Close lib1
if (dlclose(handle1) != 0) {
std::cerr << "Failed to close lib1: " << dlerror() << std::endl;
} else {
std::cout << "Closed lib1.so" << std::endl;
}
// Load lib2
void* handle2 = dlopen("./lib2.dylib", RTLD_NOW | RTLD_GLOBAL);
if (!handle2) {
std::cerr << "Failed to load lib2: " << dlerror() << std::endl;
return -1;
}
getXFunc getX2 = (getXFunc)dlsym(handle2, "getX");
if (!getX2) {
std::cerr << "Failed to get symbol from lib2: " << dlerror() << std::endl;
return -1;
}
std::cout << "x after loading lib2: " << getX2() << std::endl;
dlclose(handle2);
return 0;
}
```
2) lib1.cpp
```
#include
int x = 10;
extern "C" int getX() {
return x;
}
__attribute__((constructor))
void init() {
std::cout << "lib1 loaded, x = " << x << std::endl;
}
```
3) lib2.cpp
```
#include
#include
extern "C" int getX(); // Forward declaration
// Define a pointer to x
int* x_ptr = nullptr;
// getX function
extern "C" int getX() {
return *x_ptr;
}
// Constructor runs when lib2 is loaded
__attribute__((constructor))
void init() {
std::cout << "lib2 loaded, checking for symbol 'x'..." << std::endl;
// Look for symbol 'x' in global namespace
void* sym = dlsym(RTLD_DEFAULT, "x");
if (sym) {
std::cout << "Symbol 'x' found from another module." << std::endl;
x_ptr = (int*)sym;
} else {
std::cout << "Symbol 'x' NOT found. Defining x = 99 locally." << std::endl;
static int local_x = 99; // Define our own
x_ptr = &local_x;
}
}
```
Output
```
(xeus-cpp) anutosh491@Anutoshs-MacBook-Air test_undo % clang++ -fPIC -shared -o lib1.dylib lib1.cpp
(xeus-cpp) anutosh491@Anutoshs-MacBook-Air test_undo % clang++ -fPIC -shared -o lib2.dylib lib2.cpp
(xeus-cpp) anutosh491@Anutoshs-MacBook-Air test_undo % clang++ -o main main.cpp
(xeus-cpp) anutosh491@Anutoshs-MacBook-Air test_undo % ./main
lib1 loaded, x = 10
Initial x from lib1: 10
Closed lib1.so
lib2 loaded, checking for symbol 'x'...
Symbol 'x' NOT found. Defining x = 99 locally.
x after loading lib2: 99
```
So I tried finding the symbol `x` in `lib2` once `dlclose` is being applied on `lib1` and if the symbol is not found I print a local x. Does this mean if we use `dlclose` that completely removes the presence of `x` from the global namespace ?
Contributor guide
Assessment
This issue has not been assessed yet.