Kernel tells me that my dylib has an invalid digital sign, but for 100% it's a proper digital sign. Is this a bug, or a feature?
Minimal 100% reproducible example:
loader.cpp:
#include <cstdio>
#include <iostream>
#include <dlfcn.h>
int main() {
void* handle = dlopen("libtest.dylib", RTLD_NOW);
perror("dlopen");
return handle == nullptr;
}
test1.cpp:
#include <cstdio>
#include <iostream>
__attribute__((constructor)) void initfunc() {
std::cout << "hello from dylib1\n";
}
test2.cpp:
#include <cstdio>
#include <iostream>
__attribute__((constructor)) void initfunc() {
std::cout << "hello from dylib2\n";
}
Makefile:
all: libtest1.dylib libtest2.dylib loader
libtest1.dylib: test1.cpp
c++ -shared test1.cpp -o libtest1.dylib
libtest2.dylib: test2.cpp
c++ -shared test2.cpp -o libtest2.dylib
loader: loader.cpp
c++ loader.cpp -o loader
CMakeLists.txt instead of Makefile:
cmake_minimum_required(VERSION 3.5)
project(crash_test)
add_executable(loader loader.cpp)
add_library(test1 SHARED test1.cpp)
add_library(test2 SHARED test2.cpp)
Store all files into one directory and compile the binaries either by using make or cmake.
Reproduction scenario (100%):
Run Make or CMake to compile the source files,
Copy libtest1.dylib to libtest.dylib:
$ cp libtest1.dylib libtest.dylib
Run ./loader
Observe proper runtime without errors:
$ ./loader
hello from dylib1
dlopen: Undefined error: 0
Copy libtest2.dylib to libtest.dylib:
$ cp libtest2.dylib libtest.dylib
Run ./loader
Observe crash:
$ ./loader
[1] 96122 killed ./loader
Remove libtest.dylib
Repeat step 5: Copy libtest2.dylib to libtest.dylib:
$ cp libtest2.dylib libtest.dylib
Run ./loader and observe everything is OK:
$ ./loader
hello from dylib2
dlopen: Undefined error: 0
Is this a bug or a feature?
It seems to me that something is caching codesign information based on file inode. Replacing files seem to retain old inode while storing new data inside previously existing inode. Shouldn't replacing the file invalidate this cache, wherever it is?
Is this documented somewhere? Maybe macOS requires me to always remove dylibs before reusing their names? I couldn't see any documentation about that.
System information
I'm observing this on various macOS machines, on real machines and virtualized using Virtualization.framework. Currently I'm using this macOS version:
$ sw_vers
ProductName: macOS
ProductVersion: 12.6
BuildVersion: 21G115
2
0
1.1k