【问题标题】:Visibility, Fortran common variables, runtime loading of shared libraries可见性、Fortran 公共变量、共享库的运行时加载
【发布时间】:2014-11-18 23:53:27
【问题描述】:

环境:英特尔 Linux、红帽 5。 编译器:gcc 3.4.6 (旧的东西,具有严重基础设施的遗留环境,抱歉)

我有一个从 Fortran 派生的特定共享库的多个版本(称之为“shared_lib.so”),它包含一个 COMMON 块和各种计算,并引用该 COMMON 中的变量。

我需要能够(从最终产品可执行文件中其他地方的 C 代码)在运行时使用 dlclose() 和 dlopen() 在这个库的版本之间切换(其中所有版本的 COMMON 内容都是相同的) .在某些情况下,相同的 COMMON 也会出现在作为静态库(称为“static_lib.a”)的一部分的代码中,该库也链接到可执行文件中,并且与我的项目分开维护,但具有与我的共享库。

我似乎看到 COMMON 的多个实例最终出现在可执行文件中,并且(更重要的是)静态库中实例中的变量值与“相同” 实例中的变量来自使用 dlopen() 拉入的共享库。

总而言之,我需要的是(在整个可执行文件中)加载 dlopen() 的 shared_lib.so 能够在 COMMON ABC 中设置/使用变量 XYZ,并让 static_lib.a 中的代码设置/使用 XYZ,并使其实际上是 XYZ 的同一个实例,或者至少使两者保持同步。这可能吗?

shared_lib.so 中源代码的编译命令格式如下:

g77 –c –g –m32 -fPIC –o shared_src.o shared_src.f

我构建 shared_lib.so 的命令格式如下:

gcc -g -m32 -fPIC -shared -o shared_lib.so *.o

我构建可执行文件的命令格式如下:

gcc –g -m32 –rdynamic –o exec exec.o static_lib.a shared_lib.so –lm –ldl –lg2c

我需要从表单的 C 代码中做一些事情:

handle1 = dlopen ("shared_lib.so", RTLD_NOLOAD);
dlclose (handle1);
handle2 = dlopen ("shared_lib2.so", RTLD_NOW | RTLD_GLOBAL);
...

初始启动配置在所需变量方面确实似乎运行正常,但后续 dlclose() 和 dlopen() 序列的结果却没有。也许根本问题是 dlopen() 缺乏 gcc 在链接时所拥有的一些智能。

【问题讨论】:

    标签: c fortran visibility dlopen fortran-common-block


    【解决方案1】:

    简答

    您是否/可以使用-fPIC 重新编译可执行文件?我发现有必要同时编译共享库和带有-fPIC 的可执行文件才能正确识别COMMON 块。

    长答案

    我最近在可执行文件和 FORTRAN 共享库之间共享 COMMON 块时遇到了一个稍微类似的问题。但是,我使用的是英特尔编译器而不是 GNU 编译器。可执行文件混合了C/C++ 和FORTRAN。

    代码的现有(工作)Windows 版本通过DLLEXPORT/DLLIMPORT ATTRIBUTE 指令在可执行文件和 DLL 之间共享公共块来工作。根据英特尔编译器文档,这些属性指令在 Linux 中无法识别。事实上,Linux Intel 编译器只会对这些指令产生警告。

    将代码从 Windows 转换到 Linux 的主要变化是将 Windows LoadLibrary 和 GetProcAddress 分别替换为 Linux 的 dlopen 和 dlsym 例程,使用 #ifdef 部分。共享库使用-fpic 编译并与-shared 链接。

    虽然共享库是用-fpic 编译的,但可执行文件不是。运行以这种方式编译的代码时,通过子程序调用传递给共享库的变量被正确传递,但是,COMMON 块变量设置不正确(或未初始化)。

    无奈之下,我终于尝试使用-fpic编译器选项编译可执行文件本身,然后COMMON块在共享库中被正确识别。

    【讨论】:

      【解决方案2】:

      这并不是真正的答案,但可能会对您有所帮助。

      以下是 2008 年标准对 COMMON 的说明:

      5.7.2.4 通用关联

      1 在一个程序中,所有非零大小的公共块的公共块存储序列具有相同的 name 有相同的第一个存储单元,和普通的块存储 所有具有相同名称的零大小公共块的序列是 相互关联的存储。在一个程序中,常见的 所有非零大小的空白公共块的块存储序列都有 相同的第一个存储单元和所有的存储顺序 零大小的空白公共块相互关联,并且 与任何非零大小的空白公共块的第一个存储单元。 这会导致对象在不同范围单元中的关联。 使用或宿主关联可能会导致这些关联对象 可在同一范围单元中访问。

      简而言之,同一程序中同名的COMMON段占用相同的存储空间。

      程序定义如下。

      2.2.2 程序

      1 一个程序应仅由一个主程序、任意数量(包括零)个其他类型的程序单元、任意 外部程序的数量(包括零),以及任何数量 (包括零)以其他方式定义的其他实体 Fortran。主程序应由 Fortran 主程序定义 程序单元或通过 Fortran 以外的方式,但不能同时使用。

      该标准没有说明静态链接与动态链接,也没有将前面的陈述限制为静态链接。因此,动态加载的库似乎应该与主程序共享COMMON 块(我不确定这在技术上是否可行),因此 GNU 实现不正确。

      另一方面,该标准也没有说明能够动态加载库。 “通过 Fortran 以外的方式定义的”程序单元应该包括 C 库,但这并不能告诉我们这些程序单元是如何连接到主程序的。一般来说,Fortran 不是一种非常动态的语言。

      当然,您可以通过不使用COMMON 块来解决所有这些问题。如果一个过程需要读/写一些数据,只需将其作为参数传入/输出即可。您还可以在派生类型中将数据分组在一起,并将其作为一个单元一起传递。现在(Fortran 2003+),你甚至可以使用面向对象编程,所以真的不再需要全局变量了。

      【讨论】:

      • 谢谢——我现在无法控制静态或动态部分的编码,只有运行它们的 C“执行程序”。
      • 另外,我已经验证(在调试器中,使用'dlsym')静态/main中的COMMON地址与shared中的同名COMMON地址不同目的。然而,每个源代码中的 include 语句都指向同一个文件,因此不存在 COMMON 是否匹配的问题。所以是的,我倾向于同意 dlopen() 所做的显然是不正确或不完整的。不过,再次感谢。
      猜你喜欢
      • 2014-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-27
      相关资源
      最近更新 更多