[英]Why linker does not complain about multiple definitions when linking against shared and static
我最近在我正在开发的代码中遇到了 static 初始化失败的问题。 这让我意识到我缺乏关于链接过程和全局变量初始化的知识,所以我在 cppcon 上发现了这个非常有趣的关于全局变量、链接以及将 static 库嵌入共享和同时链接的可能问题(与全局变量定义)。 基本上说的是,当具有以下结构时:
//static.hpp
#pragma once
#include <string>
extern std::string globalString;
//static.cpp
#include "static.hpp"
std::string globalString;
//shared.hpp
#pragma once
#include "static.hpp"
#include <string>
std::string& getGlobalString();
//shared.cpp
#include "shared.hpp"
#include "static.hpp"
std::string& getGlobalString(){
return globalString;
}
//main.cpp
#include "static.hpp"
#include "shared.hpp"
#include <iostream>
int main(){
std::cout << "Global variable: " << globalString << std::endl;
std::cout << "Global var acccesed through shared lib: " << getGlobalString() << std::endl;
return 0;
}
所有编译:
clang++ -c -std=c++14 -fpic static.cpp
clang++ -c -std=c++14 -fpic shared.cpp
clang++ -c -std=c++14 main.cpp
ar rsc libstatic.a static.o
clang++ -shared -o libshared.so shared.o -L./ -lstatic
clang++ -L./ -Wl,-rpath=./ main.o -lstatic -lshared
由于多次调用同一个 object 的构造函数和析构函数,导致分段错误。 这是令人惊讶的,因为我认为每个 object 构造函数都将被调用一次是不变的!
这是令人惊讶的,因为我认为每个 object 构造函数都将被调用一次是不变的!
在正确构造的二进制文件中,这是正确的。 在具有 ODR 违规的二进制文件中不一定正确。
关于多次调用构造函数/析构函数的标准是什么?
该标准规定,在正确构造的程序中,构造函数/析构函数只被调用一次。 在违反 ODR 的程序中,任何事情都可能发生(未定义的行为)。
如果在这种情况下,我将 static 的全局嵌入到 main.o 和 libshared.so 中,这是否意味着有一个定义的规则被打破了?
是的。
为什么 linker 在链接 main 时没有抱怨第二个定义可用?
就 linker 而言,你的程序没有问题。 从 linker 的角度来看,在 static 和共享库中定义一个全局是完美的。 另请参阅此答案。
当在单独的翻译单元中至少有两个全局时,建立构造和销毁顺序的建议方法之一是在第一次使用时进行初始化。 然而,据我所知,这只能确保施工顺序。
正确的。 这是针对不同问题的解决方案:两个全局变量 A 和 B 在单独的翻译单元中定义。
你没有那个问题,你有一个完全不同的问题(违反 ODR)。
毁灭令呢? 如何控制?
在正确构造的程序中,破坏顺序保证与构造顺序相反。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.