1. Don’t directly dlopen the .so file. Copy it to a temporary file and dlopen that. Much fewer headaches that way when the original .so file changes. Bonus points for using Linux’s memfd or FreeBSD’s SHM_ANON.
2. Rely as little on global/thread-local state as possible in the plug-in. Minimize the use of non-reentrant function calls in exported functions.
3. Don’t use musl. Its dlclose is a no-op and will cause memory leaks.
4. If the plug-in is to be accessed across multiple threads, copy the symbols you’ll need from the plug-in into a struct, and maintain a reference count alongside the struct. When reloading the plug-in, create a new struct for all new threads to use, and free the old struct when its reference count goes to zero.