为什么此代码链接在英特尔编译器2015而非英特尔编译器2018?

sti*_*tix 15 c++ intel linker-errors

我的团队最近从2015英特尔编译器(并行工作室)升级到2018年版本,我们遇到了一个链接器问题,每个人都在撕扯他们的头发.

我有以下类(为了简洁而适度编辑)来处理子进程的包装和相关的文件描述符以便与它们交谈:

class SubprocWrapper
{
public:
    static const int PASSTHRU_FD = 0;
    static const int MAKE_PIPE = -1;

    typedef std::map<std::string, std::string> EnvMapType;

    static EnvMapType getMyEnv();

    SubprocWrapper(
        int stdin_fd_req,
        int stdout_fd_req,
        int stderr_fd_req,
        const std::string & execPath,
        const std::vector<std::string> & args,
        const std::set<int> & dont_close_fds,
        const EnvMapType * env = 0);
};
Run Code Online (Sandbox Code Playgroud)

然后我用以下代码调用它:

std::string runCmd = "/run/some/file.bin";
std::vector<std::string> args(2);
args[0] = "-c";
args[1] = runCmd;

SubprocWrapper::EnvMapType env_vars = SubprocWrapper::getMyEnv();

SubprocWrapper subproc(
    SubprocWrapper::PASSTHRU_FD,
    SubprocWrapper::PASSTHRU_FD,
    SubprocWrapper::PASSTHRU_FD,
    std::string("/bin/sh"),
    args,
    std::set<int>(), //dont_close_fds = null means "close all fds"
    &env_vars
);
Run Code Online (Sandbox Code Playgroud)

在2015年和2018年的英特尔编译器上,上面的代码编译得很好.

但是,在2018年的英特尔编译器中,上面的代码无法链接,而在2015年的英特尔编译器中,它链接得很好.

错误似乎是链接器无法找到构造函数符号,因为我得到以下错误:

    SourceFile.o: in function <MangledName> SourceFile.hh:<LineNum>: undefined reference to 
`SubprocWrapper::SubprocWrapper(int, int, int, std::string const&,
 std::vector<std::string, std::allocator<std::string> > const&,
 std::set<int, std::less<int>, std::allocator<int> > const&,
 std::map<std::string, std::string, std::less<std::string>,
 std::allocator<std::pair<std::string const, std::string> > > const*)'
Run Code Online (Sandbox Code Playgroud)

请注意,SubprocWrapper类正在编译为.a文件,并静态链接到调用它的代码中.在生成的.a文件上执行nm似乎确认SubprocWrapper构造函数的符号存在,但代码仅链接在2015下,即使原始.a是使用2018编译的.我们已确认正在传入正确的.a文件在链接器行(我们的构建过程没有改变),我们尝试在链接顺序中移动.a无济于事.

在我链接的.a文件上执行nm会显示与构造函数关联的以下签名(lib.a和SourceFile.o之间不同):

lib.a:

0000000000001020 T _ZN4beau5posix14SubprocWrapperC1EiiiRKSsRKSt6vectorISsSaISsEERKSt3setIiSt4lessIiESaIiEEPKSt3mapISsSsSA_ISsESaISt4pairIS2_SsEEE
0000000000000084 r _ZN4beau5posix14SubprocWrapperC1EiiiRKSsRKSt6vectorISsSaISsEERKSt3setIiSt4lessIiESaIiEEPKSt3mapISsSsSA_ISsESaISt4pairIS2_SsEEE$$LSDA
0000000000001010 T _ZN4beau5posix14SubprocWrapperC2EiiiRKSsRKSt6vectorISsSaISsEERKSt3setIiSt4lessIiESaIiEEPKSt3mapISsSsSA_ISsESaISt4pairIS2_SsEEE
Run Code Online (Sandbox Code Playgroud)

SourceFile.o:

U _ZN4beau5posix14SubprocWrapperC1EiiiRKSsRKSt6vectorISsSaISsEERKSt3setIiSt4lessIiESaIiEEPKSt3mapISsSsSA_ISsESaISt4pairIKSsSsEEE
Run Code Online (Sandbox Code Playgroud)

请注意,构造函数的两个受损名称不匹配!

我假设代码符合标准,因为它编译得很好,它只是无法链接.代码在大约7年左右没有改变,所有以前版本的英特尔编译器都使用它很好.

为什么此代码在英特尔2015而非2018下工作?

Operating system: RHEL 7.4
GCC version: 4.8.5
libstdc++ version: 4.8.5

Intel compiler versions: 
2015.3.187 (Works!)
2018.1.163 (Fails to link!)
Run Code Online (Sandbox Code Playgroud)

编辑: 这个问题似乎是某种竞争条件.当使用非常大的作业批处理(make -j30)进行编译时,我们在Intel 2015编译器上也注意到了它.问题突然出现,经过进一步检查,我们发现其中一个.o文件是使用符号的KS版本而不是IS2版本编译的.在交换了几个咒骂之后,我们删除了有问题的.o文件并再次编译没有作业批处理(只是make),并且令我们惊讶地发现,编译器这次为该函数生成了一个不同的符号(IS2版本) ).这似乎非常奇怪,因为无论作业批号如何,编译器都应始终生成相同的符号.不幸的是,这种行为不容易重复,因为我们做了一个make clean,并且再次运行了一个高工作批号,却发现它在那个时候工作了.

ach*_*ach 4

抱歉,这实际上不是一个答案,只是一条评论,但我无法将其放入评论框中。

如果您分析这两个损坏的名称,您会发现它们只有一个片段不同:KSsvs S2_。KSs意味着std::string const(或者,更准确地说,std::basic_string<char, std::char_traits<char>, std::allocator<char> > const)。S2_是对前面遇到的名称的第四个可替换组件的压缩引用,它实际上是相同的类型。看起来 GNU 修改方案在这个问题上是不明确的,因为它没有规定在哪里使用或不使用压缩引用,而只是将它们描述为一个可用选项。

然而,如果编译器没有以一致的方式修改名称,因为它会破坏合法代码的链接,那么这似乎是一个错误。您应该向编译器报告错误。