符号可见,异常运行时错误(Symbol visibility, exceptions, runtim

2019-08-06 08:56发布

我试着去理解符号可见性更好。 该GCC维基( http://gcc.gnu.org/wiki/Visibility )具有约“问题与C ++异常”部分。 根据GCC维基则可能是有,因为没有出口的异常运行时错误。 没有编译时错误/警告运行时错误是相当危险的,所以我想更好地理解这个问题。 我做了一些实验,但我仍然不能复制。 任何想法如何重现问题?

维基提及使用三个其它的库,所以我做了三个小库。

我运行以下命令:

没有虚函数表Exception类(按预期工作):

make
./dsouser

Exception类有虚函数表,但它并没有出口(甚至不编译):

make HAS_VIRTUAL=1

Exception类出口虚函数表(按预期工作):

make HAS_VIRTUAL=1 EXCEPTION_VISIBLE=1
./dsouser

Makefile文件:

CXX=g++-4.7.1
CFLAGS=-ggdb -O0 -fvisibility=hidden
ifdef EXCEPTION_VISIBLE
  CFLAGS+=-DEXCEPTION_VISIBLE
endif
ifdef HAS_VIRTUAL
  CFLAGS+=-DHAS_VIRTUAL
endif
all: dsouser

libmydso.so: mydso.cpp mydso.h
    $(CXX) $(CFLAGS) -fPIC -shared -Wl,-soname,$@ -o $@ $<

libmydso2.so: mydso2.cpp mydso.h mydso2.h libmydso.so
    $(CXX) $(CFLAGS) -L.  -fPIC -shared -Wl,-soname,$@ -o $@ $< -lmydso

libmydso3.so: mydso3.cpp mydso.h mydso2.h mydso3.h libmydso2.so
    $(CXX) $(CFLAGS) -L.  -fPIC -shared -Wl,-soname,$@ -o $@ $< -lmydso -lmydso2

dsouser: dsouser.cpp libmydso3.so
    $(CXX) $< $(CFLAGS) -L. -o $@ -lmydso -lmydso2 -lmydso3

clean:
    rm -f *.so *.o dsouser

.PHONY: all clean

mydso.h:

#ifndef DSO_H_INCLUDED
#define DSO_H_INCLUDED
#include <exception>
#define SYMBOL_VISIBLE __attribute__ ((visibility ("default")))
namespace dso
{
  class
#ifdef EXCEPTION_VISIBLE
    SYMBOL_VISIBLE
#endif
    MyException : public std::exception
  {
  public:
#ifdef HAS_VIRTUAL
    virtual void dump();
#endif
    void SYMBOL_VISIBLE foo();
  };
}
#endif

mydso.cpp:

#include <iostream>
#include "mydso.h"
namespace dso
{

#ifdef HAS_VIRTUAL
void MyException::dump()
{
}
#endif

void MyException::foo()
{
#ifdef HAS_VIRTUAL
  dump();
#endif
}

}

mydso2.h:

#ifndef DSO2_H_INCLUDED
#define DSO2_H_INCLUDED
#define SYMBOL_VISIBLE __attribute__ ((visibility ("default")))
namespace dso2
{
  void SYMBOL_VISIBLE some_func();
}
#endif

mydso2.cpp:

#include <iostream>
#include "mydso.h"
#include "mydso2.h"
namespace dso2
{
  void some_func()
  {
    throw dso::MyException();
  }
}

mydso3.h:

#ifndef DSO3_H_INCLUDED
#define DSO3_H_INCLUDED
#define SYMBOL_VISIBLE __attribute__ ((visibility ("default")))
namespace dso3
{
  void SYMBOL_VISIBLE some_func();
}
#endif

mydso3.cpp:

#include <iostream>

#include "mydso2.h"
#include "mydso3.h"

#include <iostream>

namespace dso3
{

  void some_func()
  {
    try
    {
      dso2::some_func();
    } catch (std::exception e)
    {
      std::cout << "Got exception\n";
    }
  }

}

dsouser.cpp:

#include <iostream>
#include "mydso3.h"
int main()
{
  dso3::some_func();
  return 0;
}

谢谢,达尼

Answer 1:

我原来的补丁GCC添加类可视性支持的作者,我这GCC克隆原来的HOWTO是http://www.nedprod.com/programs/gccvisibility.html 。 我要感谢VargaD通过电子邮件发送我亲自告诉我这太问题。

你观察到的行为是有效的GCC的最近的,但它并非总是如此。 当我最初修补GCC早在2004年,我提交了一份请求,GCC的Bugzilla海合会异常处理运行时通过他们的错位符号的字符串比较来比较抛出类型,而不是比较这些字符串的地址 -这是当时由GCC拒绝作为维护者不可接受的运行成本,尽管这种行为是什么呢MSVC,尽管这期间表现异常抛出一般不考虑重要,因为他们应该是罕见的。 因此,我不得不给特定异常添加到我的知名度指导说,任何抛出的类型必须永远不会被隐藏,不是一次,在一个二进制的“隐藏性”王牌“默认”,所以只是一个单一的隐藏符号声明保证覆盖所有病例在给定的二元同一符号。

接下来发生的事情,我想我们没有预料 - KDE非常公开地拥抱我的贡献功能。 这级联到几乎每一个大使用GCC项目在极短的时间。 突然符号隐藏是常态,而不是例外。

不幸的是,有少数人没有正确地运用我的指南抛出的异常类型,以及有关在GCC不正确的交叉共享对象异常处理不断bug报告最终引起了GCC维护者放弃,很多年后的字符串比较补丁对于抛出类型匹配,因为我原先要求。 因此,在新的GCC的情况有所好转。 我没有改变我的指导也没有,因为这种方法是最安全的还是每GCC 4.0版以来的说明,而新GCC的是在处理异常更可靠的抛出,由于现在使用的字符串比较,以下指南的规定不伤害那。

这使我们到所属类别的问题。 一个大问题是,最好的做法C ++要求你总是 几乎继承抛出类型的,因为如果你撰写两个异常类型都从性病::例外继承(比方说),有两个等距离的std ::例外基类会引起抓(性病::例外&)自动呼叫终止(),因为它无法解析,以匹配其基类,所以你必须永远只能有一个标准::异常的基类,与以往同样的理由适用于任何抛出可能组成类型。 此最佳实践在任何C ++库特别需要,因为你无法知道什么是第三方用户将与您的异常类型做。

换言之,这意味着,在最佳实践中,所有被抛出的异常类型将总是为每个基类连续RTTI的链,并且该异常匹配是现在的内部做一个成功的dynamic_cast <>到的类型匹配的情况下,一个O(基础类的数量)操作。 而对于的dynamic_cast <>工作在虚拟继承类型的链条,你猜对了,你需要这个链条有默认的可见的每一个 。 如果连一个是从执行捕获()的代码隐藏,全堆进肚子了,你会得到一个终止()。 我会很感兴趣,如果你返工上面几乎继承和看看会发生什么你的示例代码 - 您的意见之一说,它拒绝的链接,这是伟大的。 但是,假设DLL A定义A型,DLL乙子类型A到B,DLLç子类B型为C,并计划d试图赶上A型的一个异常时,C型被抛出。 计划d将有一个可用的类型信息,而是试图也许吧,不过,最近GCC的有固定这也为B型和C取RTTI时,应错吗? 我不知道,我近年来的注意力都在铿锵因为这是未来所有的C ++编译器。

显然,这是一个烂摊子,但它是一个特定的ELF-混乱 - 这一切都不影响PE或大男子主义,这两份首先不使用进程全局符号表获得以上所有权利。 然而,WG21 SG2模组研究组对C ++ 17的工作必须切实落实出口模板模块,以努力解决ODR违法行为,C ++ 17是第一个提出的标准,我看出与LLVM中写心神。 换句话说,C ++编译器17将必须转储复杂AST到盘状铛一样。 这意味着什么RTTI提供担保的大幅增加 - 事实上,这就是为什么我们有SG7思考研究小组,因为C ++模块的AST能够在可能的自我反省的机会大幅增加。 换句话说,希望上述问题与C ++ 17采纳很快离开去。

因此,简而言之,继续跟随我原来的指南了。 而且事情会希望得到在未来十年好得多。 并感谢苹果公司资助该解决方案,它已经在未来很长一段时间,由于它是多么邪恶的努力。

尼尔



文章来源: Symbol visibility, exceptions, runtime error