提问人:steveire 提问时间:6/30/2021 最后编辑:steveire 更新时间:6/30/2021 访问量:124
零法则如何影响具有隐藏可见性的共享库?
How does rule-of-zero affect shared libraries with hidden visibility?
问:
注意:问题在底部。
我正在尝试了解如果将零规则与共享库和派生类型一起使用时可能出现的问题。
在下面的演示中,是否使用零规则进行编译,具体取决于预处理器的定义。然后,该脚本演示了出现的差异,即,如果在 cpp 文件中定义了密钥方法,则仅在共享库中发出 vtable,而如果没有密钥方法,则在每个使用 TU 中发出 vtable。DerivedType
DerivedType
baselib.h:
#ifndef BASELIB_H
#define BASELIB_H
#ifdef BUILD_BASE_LIB
#define BASELIB_EXPORTS __attribute__((visibility("default")))
#else
#define BASELIB_EXPORTS
#endif
#include <memory>
class BASELIB_EXPORTS BaseType
{
public:
BaseType() = default;
virtual ~BaseType();
BaseType(BaseType const&) = default;
BaseType(BaseType &&) = default;
BaseType& operator=(BaseType const&) = default;
BaseType& operator=(BaseType &&) = default;
};
class BASELIB_EXPORTS DerivedType : public BaseType
{
public:
#ifdef DERIVED_RULE_OF_FIVE
DerivedType() = default;
~DerivedType() override;
DerivedType(DerivedType const&) = default;
DerivedType(DerivedType &&) = default;
DerivedType& operator=(DerivedType const&) = default;
DerivedType& operator=(DerivedType &&) = default;
#endif
#ifdef DERIVED_TYPE_EXPLICIT_KEY
virtual void key();
#endif
};
BASELIB_EXPORTS std::unique_ptr<BaseType> makeBaseType();
#endif
baselib.cpp
#include "baselib.h"
#include <iostream>
BaseType::~BaseType() = default;
#ifdef DERIVED_RULE_OF_FIVE
DerivedType::~DerivedType() = default;
#endif
#ifdef DERIVED_TYPE_EXPLICIT_KEY
void DerivedType::key() {}
#endif
std::unique_ptr<BaseType> makeBaseType()
{
std::cout << "BASE LIB " << &typeid(DerivedType) << "\n";
return std::make_unique<DerivedType>();
}
otherlib.h:
#ifndef OTHERLIB_H
#define OTHERLIB_H
#ifdef BUILD_OTHER_LIB
#define OTHERLIB_EXPORTS __attribute__((visibility("default")))
#else
#define OTHERLIB_EXPORTS
#endif
#include "baselib.h"
class OTHERLIB_EXPORTS OtherDerivedType : public DerivedType
{
public:
OtherDerivedType() = default;
virtual ~OtherDerivedType();
OtherDerivedType(OtherDerivedType const&) = default;
OtherDerivedType(OtherDerivedType &&) = default;
OtherDerivedType& operator=(OtherDerivedType const&) = default;
OtherDerivedType& operator=(OtherDerivedType &&) = default;
std::unique_ptr<DerivedType> getDerivedType();
};
#endif
otherlib.cpp
#include "otherlib.h"
#include "baselib.h"
#include <iostream>
OtherDerivedType::~OtherDerivedType() = default;
std::unique_ptr<DerivedType> OtherDerivedType::getDerivedType()
{
std::cout << "OTHER LIB " << &typeid(DerivedType) << "\n";
auto bt = makeBaseType().release();
return std::unique_ptr<DerivedType>(dynamic_cast<DerivedType*>(bt));
}
main.cpp
#include "otherlib.h"
#include "baselib.h"
#include <iostream>
int main(int argc, char** argv)
{
std::cout << "MAIN " << &typeid(DerivedType) << "\n";
OtherDerivedType odt;
auto dt = odt.getDerivedType();
std::cout << "DT " << dt.get() << "\n";
return 0;
}
#!/bin/bash
$COMPILER_DRIVER -fvisibility=hidden -fvisibility-inlines-hidden -fPIC -DBUILD_BASE_LIB -o baselib1.o -c baselib.cpp
$COMPILER_DRIVER -shared -Wl,--no-undefined -o baselib1.so baselib1.o
$COMPILER_DRIVER -fvisibility=hidden -fvisibility-inlines-hidden -fPIC -DBUILD_OTHER_LIB -o otherlib1.o -c otherlib.cpp
$COMPILER_DRIVER -shared -Wl,--no-undefined -o otherlib1.so otherlib1.o baselib1.so
$COMPILER_DRIVER -o main1.o -c main.cpp
$COMPILER_DRIVER -o def_rule_zero_without_key main1.o otherlib1.so baselib1.so
$COMPILER_DRIVER -fvisibility=hidden -fvisibility-inlines-hidden -fPIC -DBUILD_BASE_LIB -DDERIVED_TYPE_EXPLICIT_KEY -o baselib2.o -c baselib.cpp
$COMPILER_DRIVER -shared -Wl,--no-undefined -o baselib2.so baselib2.o
$COMPILER_DRIVER -fvisibility=hidden -fvisibility-inlines-hidden -fPIC -DBUILD_OTHER_LIB -DDERIVED_TYPE_EXPLICIT_KEY -o otherlib2.o -c otherlib.cpp
$COMPILER_DRIVER -shared -Wl,--no-undefined -o otherlib2.so otherlib2.o baselib2.so
$COMPILER_DRIVER -o main2.o -c main.cpp
$COMPILER_DRIVER -o def_rule_zero_with_explicit_key main2.o otherlib2.so baselib2.so
$COMPILER_DRIVER -fvisibility=hidden -fvisibility-inlines-hidden -fPIC -DBUILD_BASE_LIB -DDERIVED_RULE_OF_FIVE -DDERIVED_TYPE_EXPLICIT_KEY -o baselib3.o -c baselib.cpp
$COMPILER_DRIVER -shared -Wl,--no-undefined -o baselib3.so baselib3.o
$COMPILER_DRIVER -fvisibility=hidden -fvisibility-inlines-hidden -fPIC -DBUILD_OTHER_LIB -DDERIVED_RULE_OF_FIVE -DDERIVED_TYPE_EXPLICIT_KEY -o otherlib3.o -c otherlib.cpp
$COMPILER_DRIVER -shared -Wl,--no-undefined -o otherlib3.so otherlib3.o baselib3.so
$COMPILER_DRIVER -o main3.o -c main.cpp
$COMPILER_DRIVER -o def_rule_five_explicit_key main3.o otherlib3.so baselib3.so
echo
echo "Runtime demonstration of difference:"
echo
echo "The typeid is different when using rule of zero without a key method"
LD_LIBRARY_PATH=. ./def_rule_zero_without_key
echo
echo "The typeid is the same with a non-dtor explicit key and a defaulted inline dtor"
LD_LIBRARY_PATH=. ./def_rule_zero_with_explicit_key
echo
echo "The typeid is the same when using rule of FIVE/SIX and an explicit key"
LD_LIBRARY_PATH=. ./def_rule_five_explicit_key
echo
echo "Static demonstration of difference (nm -o):"
echo
echo "DerivedType vtable is emitted in consumer when using rule of zero"
nm -o otherlib1.o | c++filt | grep vtable
echo
echo "DerivedType IS STILL visible (but externally defined) when using rule of zero with an explicit key"
nm -o otherlib2.o | c++filt | grep vtable
echo
echo "DerivedType not visible with an explicit dtor and another virtual"
nm -o otherlib3.o | c++filt | grep vtable
echo
echo "Static demonstration of difference (readelf -a):"
echo
echo "DerivedType vtable is emitted in consumer when using rule of zero"
readelf -a otherlib1.o | c++filt | grep vtable
echo
echo "DerivedType vtable still emitted when using a defaulted destructor and an explicit key, but is NOTYPE GLOBAL DEFAULT and UND instead of OBJECT WEAK HIDDEN"
readelf -a otherlib2.o | c++filt | grep vtable
echo
echo "DerivedType vtable is not emitted if the destructor is out of line"
readelf -a otherlib3.o | c++filt | grep vtable
echo
echo
echo "In otherlib1, the vtable is present but HIDDEN (Is this STV_HIDDEN?)"
使用 G++ 输出:
Runtime demonstration of difference:
The typeid is different when using rule of zero without a key method
MAIN 0x5580b2787d30
OTHER LIB 0x7f26b67f4dc8
BASE LIB 0x5580b2787d30
DT 0x559e26cc52c0
The typeid is the same with a non-dtor explicit key and a defaulted inline dtor
MAIN 0x55696d0f4d30
OTHER LIB 0x55696d0f4d30
BASE LIB 0x55696d0f4d30
DT 0x559e26cc52c0
The typeid is the same when using rule of FIVE/SIX and an explicit key
MAIN 0x5619fc118d30
OTHER LIB 0x5619fc118d30
BASE LIB 0x5619fc118d30
DT 0x559e26cc52c0
Static demonstration of difference (nm -o):
DerivedType vtable is emitted in consumer when using rule of zero
otherlib1.o:0000000000000000 V vtable for DerivedType
otherlib1.o:0000000000000000 V vtable for OtherDerivedType
otherlib1.o: U vtable for __cxxabiv1::__si_class_type_info
DerivedType IS STILL visible (but externally defined) when using rule of zero with an explicit key
otherlib2.o: U vtable for DerivedType
otherlib2.o:0000000000000000 V vtable for OtherDerivedType
otherlib2.o: U vtable for __cxxabiv1::__si_class_type_info
DerivedType not visible with an explicit dtor and another virtual
otherlib3.o:0000000000000000 V vtable for OtherDerivedType
otherlib3.o: U vtable for __cxxabiv1::__si_class_type_info
Static demonstration of difference (readelf -a):
DerivedType vtable is emitted in consumer when using rule of zero
COMDAT group section [ 35] `.group' [vtable for OtherDerivedType] contains 2 sections:
COMDAT group section [ 36] `.group' [vtable for DerivedType] contains 2 sections:
000000000013 00770000002a R_X86_64_REX_GOTP 0000000000000000 vtable for OtherDerivedType - 4
000000000013 007000000002 R_X86_64_PC32 0000000000000000 vtable for DerivedType + c
112: 0000000000000000 32 OBJECT WEAK HIDDEN 112 vtable for DerivedType
119: 0000000000000000 32 OBJECT WEAK DEFAULT 110 vtable for OtherDerivedType
DerivedType vtable still emitted when using a defaulted destructor and an explicit key, but is NOTYPE GLOBAL DEFAULT and UND instead of OBJECT WEAK HIDDEN
COMDAT group section [ 35] `.group' [vtable for OtherDerivedType] contains 2 sections:
000000000013 00710000002a R_X86_64_REX_GOTP 0000000000000000 vtable for OtherDerivedType - 4
000000000013 006b0000002a R_X86_64_REX_GOTP 0000000000000000 vtable for DerivedType - 4
107: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND vtable for DerivedType
113: 0000000000000000 40 OBJECT WEAK DEFAULT 107 vtable for OtherDerivedType
DerivedType vtable is not emitted if the destructor is out of line
COMDAT group section [ 34] `.group' [vtable for OtherDerivedType] contains 2 sections:
000000000013 00670000002a R_X86_64_REX_GOTP 0000000000000000 vtable for OtherDerivedType - 4
103: 0000000000000000 40 OBJECT WEAK DEFAULT 102 vtable for OtherDerivedType
In otherlib1, the vtable is present but HIDDEN (Is this STV_HIDDEN?)
注意:这是一个问题:
是否存在某种会失败的模式,或者如果在多个 TU 和共享库中发出 vtables 可能难以调试的其他故障模式?dynamic_cast
答: 暂无答案
评论
dynamic_cast<>
std::cout << dt.get() << "\n
dynamic_cast