vst3sdk
vst3sdk copied to clipboard
pluginfactory_constexpr.h produces wrong factory.classInfos() on GCC 10.2.1/Raspberry Pi OS.
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).
- load the adelay.vst3 plugin.
- unload the adelay.vst3 plugin. (optional Unloading doesn't make a difference)
- Load the mda-vst3vst3 plugin, and obtain the class factory.
- Call factory..classInfos().
Expected result:
a
ClassInfos
structure containing theClassInfo
data 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).
plugin.sdk/source/main/pluginfactory_constexpr.h:
<
namespace Steinberg {
>
#if __GNUC__ > 4
// prevent template-class static initializers from being shared on GCC 10.1/aarch64
#define HIDDEN_CLASS __attribute__ ((visibility ("hidden")))
#else
#define HIDDEN_CLASS
#endif
namespace Steinberg {
------------------
<
#define BEGIN_FACTORY_DEF(company, url, email, noClasses) \
struct FactoryData \
>
#define BEGIN_FACTORY_DEF(company, url, email, noClasses) \
struct HIDDEN_CLASS FactoryData \
--------------------
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.
#include <string>
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/iparameterclient.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h"
using namespace std;
using namespace VST3::Hosting;
using namespace Steinberg;
using namespace Steinberg::Vst;
PluginFactory::ClassInfos GetVst3ClassInfos(const std::string& pluginPath)
{
std::string error;
Module::Ptr module = Module::create(pluginPath.c_str(), error);
if (!module)
{
assert(false && "Vst3 load failed.");
}
const PluginFactory &factory = module->getFactory();
return factory.classInfos();
}
static std::string HomePath() { return getenv("HOME"); }
void BugReportTest() { auto _ = GetVst3ClassInfos(HomePath() + "/.vst3/adelay.vst3"); PluginFactory::ClassInfos classInfos = GetVst3ClassInfos(HomePath() + "/.vst3/mda-vst3.vst3");
// 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 constexpr
initialization 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.
Thanks for the detailed report. We will make a fix for it in the next SDK update.
Fixed with v3.7.6_build_18