简体   繁体   English

libstdc++ 中的 Linux C++ 共享库构造函数段错误

[英]Linux C++ shared library constructor segfaults in libstdc++

I'm trying to learn and understand how to develop shared libraries for Linux.我正在尝试学习和了解如何为 Linux 开发共享库。 However, I encountered a segfault I can't explain.但是,我遇到了一个我无法解释的段错误。 I wonder if this is the right way to implement constructors in a shared library.我想知道这是否是在共享库中实现构造函数的正确方法。 The source code of my mock up library is the following.我的模型库的源代码如下。

helper.h:助手.h:

#ifndef HELPER_HELPER_H
#define HELPER_HELPER_H

namespace helper {

int GetHelp(int i=0);

}   // namespace helper

#endif  // HELPER_HELPER_H

helper.cxx:助手.cxx:

#include "helper.h"

#include <iostream>
#include <string>


namespace helper {

void __attribute__((constructor)) InitHelp(void)
{
    std::cout << "Setting up Helper" << std::endl;
}

void __attribute__((destructor)) FinishHelp(void)
{
    std::cout << "Leaving Helper" << std::endl;
}

int GetHelp(int i)
{
    return 2*i+1;
}

}   // namespace helper

main.cxx:主.cxx:

#include <iostream>
#include <cstdlib>

#include "helper.h"


int main(int argc, char** argv)
{
    std::cout << "Hello World" << std::endl;

    const int i = 3;
    std::cout << "GetHelp returned " << helper::GetHelp(i) << std::endl;

    std::cout << "Good bye World" << std::endl;

    return EXIT_SUCCESS;
}

I have put them into separate subdirectories and added the simplest possible cmake files.我已将它们放入单独的子目录中,并添加了最简单的 cmake 文件。

Root directory:根目录:

cmake_minimum_required(VERSION 3.19)

project(MyProject VERSION 1.2.3
                  DESCRIPTION "Very nice project"
                  LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_subdirectory(libhelper)

add_subdirectory(pdemo)

libhelper directory: libhelper 目录:

add_library(helper)
target_sources(helper PUBLIC helper.h
                      PRIVATE helper.cxx)
target_include_directories(helper PUBLIC .)

install(TARGETS helper)

pdemo directory: pdemo 目录:

add_executable(pdemo)
target_sources(pdemo PRIVATE main.cxx)
target_link_libraries(pdemo PRIVATE helper)

install(TARGETS pdemo)

After creating a build directory, setting up with创建构建目录后,设置

cmake -DCMAKE_BUILD_TYPE='Debug' -DBUILD_SHARED_LIBS=ON ..

and compiling, the executable produces a segfault.和编译,可执行文件产生一个段错误。 Tracing back with gdb from the core file it turns out that the problem occurs in the InitHelp function, somewhere deep inside libstdc++.从核心文件中使用 gdb 进行追溯,结果发现问题出现在 InitHelp function 中,位于 libstdc++ 深处。 By removing the line std::cout << "Setting up... the problem disappears and the program produces the expected output. I have tested it with both GCC 5.4.0 and GCC 10.2.0 and both suffers the same problem. Oddly, if LTO is switched on the problem evaporates. By removing the line std::cout << "Setting up... the problem disappears and the program produces the expected output. I have tested it with both GCC 5.4.0 and GCC 10.2.0 and both suffers the same problem. Oddly ,如果打开 LTO,问题就会消失。

I expect that at the point the constructor is called during the initialisation of the executable all supporting libraries have been set up already, including the C++ standard library.我希望在初始化可执行文件期间调用构造函数时已经设置了所有支持库,包括 C++ 标准库。 Is this the case?是这样吗? If yes, what causes the segfault?如果是,是什么导致了段错误? Do I misunderstand the way constructors are intended to be used?我是否误解了构造函数的使用方式?

(Note: I understand that I could produce the same result by moving the library constructor to the constructor of a C++ class and instantiating a global object from this class. I guess the compiler would use the same mechanism with some additional steps maybe I'm not aware of. However, I want to understand what's going on and what are the real rules of implementing a shared library.) (Note: I understand that I could produce the same result by moving the library constructor to the constructor of a C++ class and instantiating a global object from this class. I guess the compiler would use the same mechanism with some additional steps maybe I'm不知道。但是,我想了解发生了什么以及实现共享库的真正规则是什么。)

Update :更新

After reading KamilCuk's answers, I started thinking and concluded that things are much worse than I thought before.在阅读了 KamilCuk 的答案后,我开始思考并得出结论,事情比我以前想象的要糟糕得多。 Consider the following program:考虑以下程序:

#include <iostream>
#include <cstdlib>

class Foo
{
public:
    Foo() {
        std::cout << "This is Foo speaking" << std::endl; }
};

Foo global_foo;

int main(int argc, char** argv)
{
    std::cout << "Hello World" << std::endl;

    return EXIT_SUCCESS;
}

Based on the things we discussed before this seemingly simple creature is an invalid C++ code.根据我们之前讨论的内容,这个看似简单的生物是无效的 C++ 代码。 It is clear that if it's statically linked to libstdc++, then it is a static initialization order failure, since there is no guarantee that std::cout, etc will be constructed before global_foo.很明显,如果它静态链接到 libstdc++,那么它就是一个 static 初始化命令失败,因为不能保证在 global_foo 之前构造 std::cout 等。 If it is dynamically linked, then there is no guarantee for proper order of initialization since the loading procedure of shared objects and executables do not define any predetermined order in which library initialization is done and library constructors are run in such a stage of the loading process at which no other libraries are guaranteed to be initialized.如果它是动态链接的,则无法保证正确的初始化顺序,因为共享对象和可执行文件的加载过程没有定义任何预定的库初始化完成顺序,并且库构造函数在加载过程的这个阶段运行保证不会初始化其他库。 Effectively it means that static initialization order fiasco is overextending the boundaries of the linking units (.so and executable files).实际上,这意味着 static 初始化命令惨败过度扩展了链接单元(.so 和可执行文件)的边界。

Looking at this from a practical viewpoint it means that instantiating static objects in a C++ program must be done with extreme care.从实际角度来看,这意味着在 C++ 程序中实例化 static 对象必须非常小心。 The developer must be sure that nothing directly or indirectly happening in the constructor will ever touch anything outside the control of this class, there will be no facilities in other libraries touched, no global variables involved, etc. Being conservative, the best approach is to completely avoid objects as global variables.开发人员必须确保构造函数中直接或间接发生的任何事情都不会触及此 class 控制之外的任何内容,不会触及其他库中的设施,不涉及全局变量等。保守的,最好的方法是完全避免将对象作为全局变量。 (I know, it's always important to avoid global variables, but sometimes some exceptions are necessary. Like std::cout:) ) (我知道,避免全局变量总是很重要的,但有时一些例外是必要的。像 std::cout :) )

Moreover, designing a shared library for either C or C++, the library constructor can not rely on any other libraries the library depends on.此外,为 C 或 C++ 设计共享库时,库构造函数不能依赖库所依赖的任何其他库。

Do I understand well?我理解的好吗? By the way, do MS Windows dll's have the same property?顺便说一句,MS Windows dll 是否具有相同的属性?

Is this the case?是这样吗?

No.不。

what's going on这是怎么回事

std::cout is initialized after your library, that's all. std::cout在您的库之后初始化,仅此而已。

what are the real rules什么是真正的规则

Just don't use global variables in constructors or make sure that these objects are constructed first.只是不要在构造函数中使用全局变量或确保首先构造这些对象。 Research "static initialization order fiasco".研究“静态初始化命令惨败”。

If I can't rely on any of the libraries my lib is dependent on while seting up, how could I set up my own library, if likely calls to those libraries are needed?如果我在设置时不能依赖我的库所依赖的任何库,如果可能需要调用这些库,我该如何设置自己的库?

Make sure all objects that you depend on are initialized.确保您依赖的所有对象都已初始化。

In case of global std::cout standard streams there is a std::ios_base::Init .在全局std::cout标准流的情况下,有一个std::ios_base::Init

Also do not use nonportable extensions when you do not need to.也不要在不需要时使用不可移植的扩展。 __attribute__((__constructor__)) and __attribute__((__destructor__)) are meant for use in C code to hook to startup/shutdown. __attribute__((__constructor__))__attribute__((__destructor__))用于 C 代码以挂钩到启动/关闭。 Just use actual C++ syntax that does exactly the same.只需使用完全相同的实际 C++ 语法。

struct InitHelper {
   InitHelper() {
       std::ios_base::Init some_name; // make sure std::cout is initialized
       std::cout << "Setting up Helper" << std::endl;
   }
   ~InitHelper() {
       std::cout << "Leaving Helper" << std::endl;
   }
};

static InitHelper inithelper; // will call constructor and destructor

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM