草庐IT

c++ - 符号可见性,异常,运行时错误

coder 2023-05-31 原文

我试图更好地理解符号的可见性。 GCC Wiki(http://gcc.gnu.org/wiki/Visibility)包含有关“C++异常问题”的部分。根据GCC Wiki,由于未导出异常,可能存在运行时错误。没有编译时错误/警告的运行时错误非常危险,因此我试图更好地理解问题。我做了一些实验,但仍然无法复制。任何想法如何重现该问题?

Wiki互相提及了三个库,因此我制作了三个小型库。

我运行以下命令:

没有vtable的异常类(按预期工作):

make
./dsouser

带vtable的异常类,但不导出(甚至不编译):
make HAS_VIRTUAL=1

异常类导出的vtable(按预期工作):
make HAS_VIRTUAL=1 EXCEPTION_VISIBLE=1
./dsouser

生成文件:
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;
}

谢谢,
丹妮

最佳答案

我是添加了类可见性支持的GCC原始补丁的作者,而GCC克隆的原始方法是在http://www.nedprod.com/programs/gccvisibility.html上。感谢VargaD亲自给我发送电子邮件以告诉我有关此问题的信息。

您观察到的行为对最近的GCC有效,但并非总是如此。当我最初在2004年修补GCC时,我向GCC Bugzilla提交了GCC异常处理运行时的请求,以通过比较其整齐符号的字符串来比较抛出的类型,而不是比较那些字符串的地址-当时被GCC拒绝尽管维护人员的行为是MSVC所做的,并且维护人员的开销是无法接受的,并且尽管异常抛出期间的性能通常认为不重要,但鉴于维护人员很少见,因此维护人员通常认为这并不重要。因此,我不得不在可见性指南中添加一个特定的异常(exception),以说任何抛出的类型都绝不能以二进制形式隐藏,而不是一次,因为“hiddenness”胜过“default”,因此只有一个隐藏的符号声明可以覆盖所有的情况。给定二进制文件中的相同符号。

接下来发生的事情我想我们都没想到-KDE非常公开地接受了我的贡献。在极短的时间内,它几乎渗入了几乎所有使用GCC的大型项目。突然,隐藏符号是正常现象,而不是异常(exception)。

不幸的是,少数人没有将我的指南正确地应用于引发异常的类型,并且不断出现的关于GCC中交叉共享对象异常处理不正确的错误报告最终导致GCC维护者放弃了,多年后又进行了字符串比较补丁按照我最初的要求进行抛出类型匹配。因此,在较新的海湾合作委员会中,情况要好一些。我没有更改指南或说明,因为自v4.0以来,该方法在每个GCC上仍是最安全的方法,并且由于现在使用字符串比较,较新的GCC在处理异常引发方面更可靠,因此遵循该指南的规则不会对您造成损害那。

这将我们带入typeinfo问题。一个大问题是,最佳实践C++要求您始终实际上以可抛出类型继承,因为如果您编写两个异常类型都从std::exception继承(比如说),则具有两个等距的std::exception基类将导致一个catch(std::exception&)来自动调用终止(),因为它无法解析匹配的基类,因此您必须永远只拥有一个std::exception基类,并且任何可能的组合都应遵循相同的原理可抛出类型。在任何C++库中,都特别需要这种最佳实践,因为您不知道第三方用户将如何处理您的异常类型。

换句话说,这意味着在最佳实践中,所有抛出的异常类型对于每个基类都将始终带有一连串的RTTI链,并且异常匹配现在是内部对匹配的类型成功执行dynamic_cast <>的情况, O(基类数)操作。而且,要使dynamic_cast <>能够处理一系列虚拟继承的类型,您需要知道,该链中的每个都需要才能具有默认可见性。如果在执行catch()的代码中甚至隐藏了一个代码,那么整个问题就变得无所适从,而您会得到一个Terminate()。如果您重新整理了上面的示例代码以实际上继承并查看发生了什么,我将非常感兴趣-您的评论之一说它拒绝链接,这很棒。但是,假设DLL A定义了类型A,DLL B将类型A子类化为B,DLL C将类型B子类化为C,并且程序D试图在引发类型C时捕获类型A的异常。程序D将具有A的类型信息,但是在尝试获取B和C类型的RTTI时应该出错。不过,也许最近的GCC也已经解决了这个问题?我不知道,最近几年我的注意力是clang,因为这是所有C++编译器的 future 。

显然,这是一团糟,但它是ELF特定的一团糟-这些都不会影响PE或MachO,这两者都通过不首先使用进程全局符号表来获得上述所有权利。但是,致力于C++ 17的WG21 SG2模块研究小组必须有效地实现模块的导出模板才能正常工作,以解决违反ODR的问题,而C++ 17是我见过的第一个提议的标准,是使用LLVM编写的。心神。换句话说,C++ 17编译器将必须像clang一样将复杂的AST转储到光盘上。这意味着对可用RTTI的保证将大大增加-的确,这就是我们设立SG7反射研究小组的原因,因为C++模块的AST极大地增加了可能的自我反射机会。换句话说,随着C++ 17的采用,上述问题将很快消失。

因此,简而言之,请暂时遵循我的原始指南。希望在 future 十年内情况会大大改善。还要感谢苹果公司为该解决方案提供的资金,由于它的邪恶程度,它已经走了很长时间了。

尼尔

关于c++ - 符号可见性,异常,运行时错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14268736/

有关c++ - 符号可见性,异常,运行时错误的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  4. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  5. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  6. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  7. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  8. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

    我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

  9. ruby - 即时确定方法的可见性 - 2

    我正在编写一个方法,它将在一个类中定义一个实例方法;类似于attr_accessor:classFoocustom_method(:foo)end我通过将custom_method函数添加到Module模块并使用define_method定义方法来实现它,效果很好。但我无法弄清楚如何考虑类(class)的可见性属性。例如,在下面的类中classFoocustom_method(:foo)privatecustom_method(:bar)end第一个生成的方法(foo)必须是公共(public)的,第二个(bar)必须是私有(private)的。我怎么做?或者,如何找到调用我的cust

  10. ruby-on-rails - Rails - 乐观锁定总是触发 StaleObjectError 异常 - 2

    我正在学习Rails,并阅读了关于乐观锁的内容。我已将类型为integer的lock_version列添加到我的articles表中。但现在每当我第一次尝试更新记录时,我都会收到StaleObjectError异常。这是我的迁移:classAddLockVersionToArticle当我尝试通过Rails控制台更新文章时:article=Article.first=>#我这样做:article.title="newtitle"article.save我明白了:(0.3ms)begintransaction(0.3ms)UPDATE"articles"SET"title"='dwdwd

随机推荐