Perl捕获嵌入脚本的stdout,如果在dlopen中加载共享库,则失败

Perl capture stdout of embedded script, fails if in dlopen-loaded shared lib

本文关键字:加载 dlopen 共享 失败 如果 脚本 Perl stdout      更新时间:2023-10-16

我在C++程序中嵌入了一些Perl代码(Ubuntu14,Perl5.18,如果这很重要的话(,我像本例中那样执行Perl。

更具体地说,I:

  1. 创建perl实例,然后按照"perlbed"手册中的描述加载持久性代码
  2. 评估一些"准备"代码:

    static const char * redirPre = "$scriptOutput = "";n"
            "open(SCRIPTOUTPUT, '> :scalar', \$scriptOutput) || print STDERR "Failed to open scriptoutput: $!";n"
            "print SCRIPTOUTPUT "huhu\n";n"
            "select SCRIPTOUTPUT;n"
            "print SCRIPTOUTPUT "huhu2\n";n"
            "print STDOUT "huhu2a\n";n";
    printf("PRE: '%s'n", redirPre);
    eval_pv(redirPre, FALSE);
    
  3. 评估我的实际Perl片段

现在奇怪的是,如果我静态地链接调用所有Perl函数的代码,或者如果这些函数在直接链接到主程序的共享库中,这会很好地工作,而如果这些函数位于共享库中(主程序使用dlopen加载(,这就不起作用。

Perl代码段执行正常,只是获取stdout失败。准确地说,这部分不起作用:

open(SCRIPTOUTPUT, '> :scalar', $scriptOutput) ||
  print STDERR "Failed to open scriptoutput: $!";

不管我指定的是CCD_ 1还是仅指定CCD_。

有趣的是,输出也没有出现在stdout上,而是显示Failed to open scriptoutput:(但没有任何实际错误(。

想法?

编辑:使用完全相同的代码将stdout重新路由到/tmp/xx效果很好,唯一的区别是:

"open(SCRIPTOUTPUT, '>', "/tmp/xx") || print STDERR "Failed: $!";n"

我花了很长时间仔细研究这个问题,最终发现了问题:

最初,我用dlopen("mylib.so", RTLD_LAZY)加载了自己的共享库,该库又是用对libperl.so的引用构建的。dlopen手册页指出,当隐式加载依赖库时,标志会向下传播,因此加载libperl也只有RTLD_LAZY。

如上所述重新路由stdout涉及Perl库scalar.so,Perl在我的Perl代码段执行时加载它。库scalar.so包含对PL_no_modify的未解析引用,该引用再次在libperl.so中定义。现在,由于我在有效调用>:scalar1时没有指定RTLD_GLOBALscalar.so没有看到PL_no_modify,无法加载。

在我的代码中设置了RTLD_GLOBAL之后,它就工作了。

剩下的唯一问题是Perl为什么没有告诉我…:-|