All VST3 plugins that declare plugin factories using pluginfactory_constexpr.h end up using the class declarations from the first-loaded VST3 plugin.
In the vst3sdk, the mda-vst3.vst3 and adelay.vst3 plugins both use pluginfactory_constexpr.h.
The problem is most likely a subtle defect in how static members of template classes are initialized are shared between .so's with non-GLOBAL linkage. There is some reason to believe that it a problem with aarch64 linux (which doesn't support all ELF linkage types). But it could be a defect in any of GCC, Rasberry Pi OS, or the GCC runtime. However, if it's a problem with Raspberry Pi OS, it's almost certainly a problem on all Debian-derived AARCH64 Linuxen.
Steps to repro:
Install, compile and build vst3sdk
(code fragment given below).
1) load the adelay.vst3 plugin.
2) unload the adelay.vst3 plugin. (optional Unloading doesn't make a difference)
3) Load the mda-vst3vst3 plugin, and obtain the class factory.
4) Call factory..classInfos().
Expected result:
a ClassInfosstructure containing the ClassInfodata for 68 mda-vst3.vst3 plugin classes.
Actual result:
a ClassInfos with 68 entries, containing the three ClassInfo entries for adlay.vst3, and 65 garbage entries.
Proposed fix:
The following fix has been tested successfully on GCC 10.2.1/Raspbian.
Force GCC to declare FactoryData with hidden linkage (non-exported).
// right size.
assert(classInfos.size() == 68); // succeeds
// wrong classInfo (from adelay.vst3)
assert(classInfos[0].name() == "mda Ambience"); // actual result "ADelay"
}
Platform info:
Raspberry Pi OS
Linux raspberrypi 5.15.32-v8+ #1538 SMP PREEMPT Thu Mar 31 19:40:39 BST 2022 aarch64 GNU/Linux
gcc version 10.2.1 20210110 (Debian 10.2.1-6)
Built from vst3sdk 3.7.5 .zip package.using Visual Studio Code/CMake build.
Debug Notes
The bug has occurs by the time ModuleEntry() is called. I'm unable to get GDB to debug through the .so initialization code.
Instances of Steinberg::PluginFactory<FactoryData> factory have different addresses. FactoryData:classInfo does not appear to share the same address across plugin instances (array<>::size() is correct for both plugins). I'd have to guess that data used to perform constexprinitialization of FactoryData::classInfos ends up getting incorrectly shared between .so instances even though dlopen() flags do NOT specify global linkage.
Moving the declaration of static Steinberg::PluginFactory<FactoryData> factory; outside of the function does not correct the problem. The defect appears to be triggered by constexpr initialization regardless of the scope of the variable.
Prepending #define FactoryData FactoryData21341 before the #include of pluginfactory_constexpr.h does correct the problem. Apparently the issue is related to a lexical collision of a leaked declaration of Steinberg::PluginFactory<FactoryData>::FactoryData::classInfo::some_static_intitializer_method_or_data_ declaration between plugins.
Description
All VST3 plugins that declare plugin factories using pluginfactory_constexpr.h end up using the class declarations from the first-loaded VST3 plugin.
In the vst3sdk, the mda-vst3.vst3 and adelay.vst3 plugins both use pluginfactory_constexpr.h.
The problem is most likely a subtle defect in how static members of template classes are initialized are shared between .so's with non-GLOBAL linkage. There is some reason to believe that it a problem with aarch64 linux (which doesn't support all ELF linkage types). But it could be a defect in any of GCC, Rasberry Pi OS, or the GCC runtime. However, if it's a problem with Raspberry Pi OS, it's almost certainly a problem on all Debian-derived AARCH64 Linuxen.
Steps to repro:
Install, compile and build vst3sdk
(code fragment given below).
1) load the adelay.vst3 plugin. 2) unload the adelay.vst3 plugin. (optional Unloading doesn't make a difference) 3) Load the mda-vst3vst3 plugin, and obtain the class factory. 4) Call factory..classInfos().
Expected result:
Actual result:
Proposed fix:
The following fix has been tested successfully on GCC 10.2.1/Raspbian.
Force GCC to declare FactoryData with hidden linkage (non-exported).
Code sample to reproduce
On Raspberry PI os, build vst3sdk using VS Code with CMake support plugins installed.
Select the VSCode toolchain to be "GCC 10.2.1 aarch66-linux-gnu" (default on Raspberry Pi OS)
You'll have to sort out linkage yourself. Add a GCC include to the root of vst3sdk.
static std::string HomePath() { return getenv("HOME"); }
void BugReportTest() { auto _ = GetVst3ClassInfos(HomePath() + "/.vst3/adelay.vst3"); PluginFactory::ClassInfos classInfos = GetVst3ClassInfos(HomePath() + "/.vst3/mda-vst3.vst3");
}
Platform info:
Raspberry Pi OS Linux raspberrypi 5.15.32-v8+ #1538 SMP PREEMPT Thu Mar 31 19:40:39 BST 2022 aarch64 GNU/Linux gcc version 10.2.1 20210110 (Debian 10.2.1-6)
Built from vst3sdk 3.7.5 .zip package.using Visual Studio Code/CMake build.
Debug Notes
The bug has occurs by the time
ModuleEntry()
is called. I'm unable to get GDB to debug through the .so initialization code.Instances of
Steinberg::PluginFactory<FactoryData> factory
have different addresses.FactoryData:classInfo
does not appear to share the same address across plugin instances (array<>::size() is correct for both plugins). I'd have to guess that data used to performconstexpr
initialization ofFactoryData::classInfos
ends up getting incorrectly shared between .so instances even though dlopen() flags do NOT specify global linkage.Moving the declaration of
static Steinberg::PluginFactory<FactoryData> factory;
outside of the function does not correct the problem. The defect appears to be triggered by constexpr initialization regardless of the scope of the variable.Prepending
#define FactoryData FactoryData21341
before the#include
ofpluginfactory_constexpr.h
does correct the problem. Apparently the issue is related to a lexical collision of a leaked declaration ofSteinberg::PluginFactory<FactoryData>::FactoryData::classInfo::some_static_intitializer_method_or_data_
declaration between plugins.