从C ++拦截Fortran STOP [英] Intercepting Fortran STOP from C++

查看:202
本文介绍了从C ++拦截Fortran STOP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我准备了一个C ++接口到一个旧版Fortran库。

遗留库中的一些子例程遵循丑陋但可用的状态码惯例来报告错误,并且我使用这样的状态码从我的C ++代码中抛出可读的异常:它另一方面,有时遗留库调用 STOP (它会终止程序)。即使条件是可恢复的,它也是经常这样做的。



我想 这个 STOP c $ c>从C ++中,到目前为止我一直没有成功。



以下代码很简单,但恰恰代表了手头的问题:



Fortran遗留库 fmodule.f90

  module fmodule 
使用iso_c_binding
包含
子程序fsub(x)bind(c,name =fsub)
real(c_double)x
如果(x> = 5)则
stop'x> = 5:杀死程序'
else
print *,x
end if
end subroutine fsub
end module fmodule

C ++接口 main.cpp

 #include< iostream> 

//外部Fortran子程序的原型
externC{
void fsub(double& x);
}

int main(){
double x;
while(std :: cin>> x){
fsub(x);
}
返回0;



$ b

编译行(GCC 4.8.1 / OS X 10.7.4; code> $ 表示命令提示符):

  $ gfortran -o libfmodule.so fmodule .f90 -shared -fPIC -Wall 
$ g ++ main.cpp -L。 -lfmodule -std = c ++ 11

运行:

  $ ./a.out 
1
1.0000000000000000
2
2.0000000000000000
3
3.0000000000000000
4
4.0000000000000000
5
STOP x> = 5:杀死程序

如何捕获 STOP ,并且请求另一个号码。请注意,我不想触摸Fortran代码



我曾尝试过:


  • std :: atexit :一旦我输入,就不能回来。
  • std :: signal STOP 似乎并没有引发我可以捕获的信号


解决方案

您可以通过拦截对 exit 函数。见下文。 a.out 是使用您的代码和您提供的编译行创建的。



第1步找出哪个函数叫做。启动 gdb

  $ gdb ./a.out 
GNU gdb(GDB)红帽企业Linux(7.2-60.el6_4.1)
[...]
(gdb)break fsub
断点1在0x400888
gdb)运行
启动程序:a.out
5

断点1,0x00007ffff7dfc7e4位于fsub()from ./libfmodule.so
(gdb)step
单步直到从函数fsub退出,
没有行号信息。 $ / $ b stop_string(string = 0x7ffff7dfc8d8x> = 5:this kill the programfmodule.f90,len = 30)at /usr/local/src/gcc-4.7.2/libgfortran/runtime/stop.c: 67

因此调用 stop_string 。我们需要知道这个函数对应哪个符号。

第2步:找到 stop_string 函数的确切名称。它必须位于其中一个共享库中。

  $ ldd ./a.out 
linux-vdso.so.1 => (0x00007fff54095000)
libfmodule.so => ./libfmodule.so(0x00007fa31ab7d000)
libstdc ++。so.6 => /usr/local/gcc/4.7.2/lib64/libstdc++.so.6(0x00007fa31a875000)
libm.so.6 => /lib64/libm.so.6(0x0000003da4000000)
libgcc_s.so.1 => /usr/local/gcc/4.7.2/lib64/libgcc_s.so.1(0x00007fa31a643000)
libc.so.6 => /lib64/libc.so.6(0x0000003da3c00000)
libgfortran.so.3 => /usr/local/gcc/4.7.2/lib64/libgfortran.so.3(0x00007fa31a32f000)
libquadmath.so.0 => /usr/local/gcc/4.7.2/lib64/libquadmath.so.0(0x00007fa31a0fa000)
/lib64/ld-linux-x86-64.so.2(0x0000003da3800000)

$ b我在fortran运行时发现它(并不意外)。

pre > $ readelf -s /usr/local/gcc/4.7.2/lib64/libgfortran.so.3|grep stop_string
1121:000000000001b320 63 FUNC GLOBAL DEFAULT 11 _gfortran_stop_string @@ GFORTRAN_1.0
2417:000000000001b320 63 FUNC GLOBAL DEFAULT 11 _gfortran_stop_string

第3步。编写一个函数替换该函数



我在源代码中查找函数的精确签名( /usr/local/src/gcc-4.7.2 /libgfortran/runtime/stop.c 参见 gdb 会话)

  $ cat my_exit.c 
#define _GNU_SOURCE
#include< stdio.h>

void _gfortran_stop_string(const char * string,int len)
{
printf(Let's keep on);

第4步。编译导出该符号的共享对象。

  gcc -Wall -fPIC -c -o my_exit.o my_exit.c 
gcc -shared -fPIC -Wl,-soname -Wl ,libmy_exit.so -o libmy_exit.so my_exit.o

第5步。使用LD_PRELOAD运行程序我们的新函数优先于运行时的一个形式。
$ b $ pre $ $ $ $ $ $ LD_PRELOAD =。/ libmy_exit.so ./a.out
1
1.0000000000000000
2
2.0000000000000000
3
3.0000000000000000
4
4.0000000000000000
5
让我们保持在5.0000000000000000
6
让我们继续6.0000000000000000
7
让我们继续7.0000000000000000

你去。


I prepared a C++ interface to a legacy Fortran library.

Some subroutines in the legacy library follow an ugly but usable status code convention to report errors, and I use such status codes to throw a readable exception from my C++ code: it works great.

On the other hand, sometimes the legacy library calls STOP (which terminates the program). And it often does it even though the condition is recoverable.

I would like to capture this STOP from within C++, and so far I have been unsuccessful.

The following code is simple, but exactly represents the problem at hand:

The Fortran legacy library fmodule.f90:

module fmodule
  use iso_c_binding
  contains
    subroutine fsub(x) bind(c, name="fsub")
      real(c_double) x
      if(x>=5) then 
         stop 'x >=5 : this kills the program'
      else
         print*, x
      end if
    end subroutine fsub    
end module fmodule

The C++ Interface main.cpp:

#include<iostream>

// prototype for the external Fortran subroutine
extern "C" {
  void fsub(double& x);  
}

int main() {  
  double x;
  while(std::cin >> x) {
    fsub(x);
  }
  return 0;
}

The compilation lines (GCC 4.8.1 / OS X 10.7.4; $ denotes command prompt ):

$ gfortran -o libfmodule.so fmodule.f90 -shared  -fPIC -Wall
$ g++ main.cpp -L. -lfmodule -std=c++11

The run:

$ ./a.out 
1
   1.0000000000000000     
2
   2.0000000000000000     
3
   3.0000000000000000     
4
   4.0000000000000000     
5
STOP x >=5 : this kills the program

How could I capture the STOP and, say, request another number. Notice that I do not want to touch the Fortran code.

What I have tried:

  • std::atexit: cannot "come back" from it once I have entered it
  • std::signal: STOP does not seem to throw a signal which I can capture

解决方案

You can solve your problem by intercepting the call to the exit function from the Fortran runtime. See below. a.out is created with your code and the compilation lines you give.

Step 1. Figure out which function is called. Fire up gdb

$ gdb ./a.out
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.el6_4.1)
[...]
(gdb) break fsub
Breakpoint 1 at 0x400888
(gdb) run
Starting program: a.out 
5

Breakpoint 1, 0x00007ffff7dfc7e4 in fsub () from ./libfmodule.so
(gdb) step
Single stepping until exit from function fsub,
which has no line number information.
stop_string (string=0x7ffff7dfc8d8 "x >=5 : this kills the programfmodule.f90", len=30) at /usr/local/src/gcc-4.7.2/libgfortran/runtime/stop.c:67

So stop_string is called. We need to know to which symbol this function corresponds.

Step 2. Find the exact name of the stop_string function. It must be in one of the shared libraries.

$ ldd ./a.out 
    linux-vdso.so.1 =>  (0x00007fff54095000)
    libfmodule.so => ./libfmodule.so (0x00007fa31ab7d000)
    libstdc++.so.6 => /usr/local/gcc/4.7.2/lib64/libstdc++.so.6 (0x00007fa31a875000)
    libm.so.6 => /lib64/libm.so.6 (0x0000003da4000000)
    libgcc_s.so.1 => /usr/local/gcc/4.7.2/lib64/libgcc_s.so.1 (0x00007fa31a643000)
    libc.so.6 => /lib64/libc.so.6 (0x0000003da3c00000)
    libgfortran.so.3 => /usr/local/gcc/4.7.2/lib64/libgfortran.so.3 (0x00007fa31a32f000)
    libquadmath.so.0 => /usr/local/gcc/4.7.2/lib64/libquadmath.so.0 (0x00007fa31a0fa000)
    /lib64/ld-linux-x86-64.so.2 (0x0000003da3800000)

I found it in (no surprise) the fortran runtime.

$ readelf -s /usr/local/gcc/4.7.2/lib64/libgfortran.so.3|grep stop_string
  1121: 000000000001b320    63 FUNC    GLOBAL DEFAULT   11 _gfortran_stop_string@@GFORTRAN_1.0
  2417: 000000000001b320    63 FUNC    GLOBAL DEFAULT   11 _gfortran_stop_string

Step 3. Write a function that will replace that function

I look for the precise signature of the function in the source code (/usr/local/src/gcc-4.7.2/libgfortran/runtime/stop.c see gdb session)

$ cat my_exit.c 
#define _GNU_SOURCE
#include <stdio.h>

void _gfortran_stop_string (const char *string, int len)
{
        printf("Let's keep on");
}

Step 4. Compile a shared object exporting that symbol.

gcc -Wall -fPIC -c -o my_exit.o my_exit.c
gcc -shared -fPIC -Wl,-soname -Wl,libmy_exit.so -o libmy_exit.so my_exit.o

Step 5. Run the program with LD_PRELOAD so that our new function has precedence over the one form the runtime

$ LD_PRELOAD=./libmy_exit.so ./a.out 
1
   1.0000000000000000     
2
   2.0000000000000000     
3
   3.0000000000000000     
4
   4.0000000000000000     
5
Let's keep on   5.0000000000000000     
6
Let's keep on   6.0000000000000000     
7
Let's keep on   7.0000000000000000   

There you go.

这篇关于从C ++拦截Fortran STOP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆