Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Interoperability: Linux (Segmentation fault)

Maia__Nycholas
Beginner
1,706 Views

Hi @Steve,

Thank you so much for your `module str_routines`. It can convert C char array to a Fortran string and works great in Windows x64 (Microsoft Visual Studio 2019 + Intel Visual Fortran 2019).

Now, I'm trying to use it on Linux (Ubuntu 18 x64) but I got a segmentation fault error inside of it and I don't know why it's happening.

Here is my C++ source-code:

extern "C" {
	void f_subroutine(const char* c_str);
}

int main(void)
{
	const char c_str[5] = "test";

	f_subroutine(c_str);

	return 0;
}

Here is my Fortran source-code:

module str_routines
    contains        
        subroutine C_F_STRPOINTER (STRARRAY, FSTRPTR, MAXLEN)
            use, intrinsic :: ISO_C_BINDING

            implicit none
            character, dimension(*), target, intent(in) :: STRARRAY
            character(:), pointer, intent(out) :: FSTRPTR
            integer, intent(in), optional :: MAXLEN

            integer :: curlen

            curlen = 0

            do
                curlen = curlen +1
                if (PRESENT(MAXLEN)) THEN
                    if (curlen > MAXLEN) exit
                end if

                if (STRARRAY(CURLEN) == CHAR(0)) exit
            end do

            call doassign(C_LOC(STRARRAY), FSTRPTR, curlen-1)

            contains
                subroutine doassign(CSTRPTR, FSTRPTR, STRLEN)
                    type(C_PTR), intent(in) :: CSTRPTR
                    character(:), pointer, intent(out) :: FSTRPTR
                    integer, intent(in) :: STRLEN
                    character(STRLEN), pointer :: p

                    call C_F_POINTER(CSTRPTR, p)
                    FSTRPTR => p
                    return
                end subroutine doassign
        end subroutine C_F_STRPOINTER
end module str_routines

subroutine f_subroutine(c_str) bind(C)
    use, intrinsic :: ISO_C_BINDING
    use str_routines

    implicit none
    character(kind=C_CHAR), target, intent(in) :: c_str(*)
    character(kind=C_CHAR, len=:), pointer :: f_str

    ! Convert C char array to Fortran string:
    call C_F_STRPOINTER (c_str, f_str)
end subroutine f_subroutine

Compile and link sequence:

// Compile the Fortran file to a object:
gfortran -c -g fortran.90

// Create a Linux static library
ar -cr libmylib.a fortran.o

// Compile C++ and link it with the Fortran static library
g++ -Wall main.cpp -L. -lmylib -lgfortran -o app

// Run it:
./app

// Console error:
Segmentation fault (core dumped)

To inspect what it causing this run-time error, I debugged it using Qt Creator IDE on Linux.
Here is my debug session and local variables values:

seg_fault_linux.png

As you can see, I got a "Segmentation Fault" error in the line 34.

FSTRPTR => p

Could you please help me to solve it?

Thank you again,
Nycholas Maia

0 Kudos
1 Solution
Steve_Lionel
Honored Contributor III
1,706 Views

Did you rebuild the Fortran library after changing the setting? If yes and you still have the problem, add ifmodintr.lib to "Additional Dependencies" in the C++ project's Linker properties.

View solution in original post

0 Kudos
16 Replies
Steve_Lionel
Honored Contributor III
1,706 Views

You're using gfortran - I can't help with that. It may be that gfortran doesn't fully or properly implement deferred-length character pointers. I notice in your screenshot that the debugger claims that the type of _fstrptr is integer(4) - that doesn't seem right.

Come back here if it doesn't work in Intel Fortran.

0 Kudos
FortranFan
Honored Contributor II
1,706 Views

This is for other readers who may be open to advice such as the one provided by Jim Dempsey where he states, "As it stands, a helper function is required on one side or the other. My preference is to do this on the C/C++ side.", in this other thread by OP: https://software.intel.com/en-us/forums/intel-fortran-compiler/topic/815905 which then can lead to really short and simple code that is easier on both readers as well as processors:

#include <cstring>

extern "C" {
	void f_subroutine(const char* , size_t);
}

int main(void)
{
	char c_str[] = "Test"; //<-- Note no hard-wired length

	f_subroutine(c_str, strlen(c_str));

	return 0;
}
subroutine f_subroutine(c_str, lens) bind(C, name="f_subroutine")
! Module procedures are recommended
   use, intrinsic :: iso_c_binding, only : c_loc, c_f_pointer, c_char, c_size_t

   ! Argument list
   character(kind=c_char, len=1), target, intent(in) :: c_str(*)
   integer(kind=c_size_t), intent(in), value         :: lens

   if ( lens > 0 ) then
      block
         character(kind=c_char, len=lens), pointer :: pfstr
         call c_f_pointer( c_loc(c_str), fptr=pfstr )
         print *, "In f_subroutine: Fortran incarnation of c_str: ", pfstr
         pfstr => null()
      end block
   end if

   return

end subroutine f_subroutine

Upon execution of code built with either gfortran or Intel Fortran, the output is as follows:

 In f_subroutine: Fortran incarnation of c_str: Test

 

0 Kudos
Maia__Nycholas
Beginner
1,706 Views

Hi Steve and FortranFan,

Thank you all for the informations.

I tried the FortranFan solution and it works perfect using gfortran. Just "copy and paste" and it works great!
Now, when I did the same thing using Intel Visual Fortran 2019 I got a compile-time error related with this Fortran source-code line:
 

call c_f_pointer( c_loc(c_str), fptr=pfstr )

So, Visual Studio 2019 give me theses erros below:

unresolved external symbol ISO_C_BINDING_mp_C_LOC_PRIVATE referenced in function f_subroutine

unresolved external symbol c_f_pointer_set_scalar referenced in function f_subroutine

Question 1:
Do you have any ideas why it's happening when I use IVF 2019? How can I solve it?

Question 2:

FortranFan, in the C++ side, your source-code exemple have this C string observation:

char c_str[] = "Test"; //<-- Note no hard-wired length

It works good!
But this modified version below works good too (at moment, only tested using gfortran).
 

char c_str[30] = "Test";

It prints the correct C string with no blank spaces like the original one.

So, why should I use the original "hard-wired length"? Is there any C-Fortran interoperability advantages?

Thank you all,

0 Kudos
Steve_Lionel
Honored Contributor III
1,706 Views

You did not get a compile-time error. This is a link-time error and indicates you did not supply the proper libraries when you linked your code. You need to add ifmodintr.lib if you've disabled default library search rules.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,706 Views

>>unresolved external symbol

Either bug or linking in incorrect Fortran runtime library.

BTW - c_loc should be an intrinsic function resolved at compile time, not an external function executed at runtime. This would seem to indicate bug.

Try adding just below BLOCK:

        use, intrinsic :: iso_c_binding, only : c_loc, c_f_pointer, c_char

>>So, why should I use the original "hard-wired length"? Is there any C-Fortran interoperability advantages?

In the event you want to receive text. In this case though you would likely pass sizeof(c_str) as opposed to strlen(c_str), and modify helper function to, for input, use lesser value of the search for NULL or passed in length.

Jim Dempsey

0 Kudos
Maia__Nycholas
Beginner
1,706 Views

@Steve, you are right!
Now, I change the Fortran project configuration to:
disabled default library search rules: NO

And that "unresolved external" old error is gone.

So, in Visual Studio 2019, I have 1 solution with 2 projects:

  • C++ console app
  • Fortran Static Library

With this help from Steve, now I can compile the Fortran library project with no errros. Good!

So, the last linker error is when I try to build and link the C++ project with the Fortran library file. I got this linker error:

cannot open file 'ifconsol.lib'

I already added in the C++ project properties:

Linker -> General -> Additional Library Directories: 

  • C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.4.245\windows\compiler\lib\intel64_win
  • C:\Users\lamar\Desktop\f_str_lib\x64\Debug 

Linker -> General -> Link Library Dependencies: YES
Linker -> Input -> Additional Dependecies: f_str_lib.lib; %(AdditionalDependencies)

 

2- Right-click on Solution name -> Project Dependencies: and set the correct dependency order.

 

AND In my Fortran Static Library project I setted:

Tools -> Options -> Intel Compilers and Tools -> Visual Fortran -> Compilers -> x64:
Includes:

C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.4.245\windows\compiler\include

Libraries:

C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2019.4.245\windows\compiler\lib\intel64_win

Finally, I followed this Intel tutorial:

https://software.intel.com/en-us/articles/configuring-visual-studio-for-mixed-language-applications

What more I have to do to link the 'ifconsol.lib' to my C++ application?

0 Kudos
Maia__Nycholas
Beginner
1,706 Views

UPDATE 1:

I followed the Intel Tutorial about mixed-language applications and set the configurations for use Intel Visual Fortran 2019 for build x64 applications.

After this, I cleaned my Solution and rebuild.

The Fortran Static library project compiles Ok! No errors!
But the C++ linker shows me these 2 old erros:

unresolved external symbol ISO_C_BINDING_mp_C_LOC_PRIVATE referenced in function f_subroutine    c_str_test

unresolved external symbol c_f_pointer_set_scalar referenced in function f_subroutine    c_str_test

   
Before this configuration I had only the linker error about 'ifconsol.lib'...and if I go back to the old configurations (without Intel Tutorial) these 2 erros above still the same.

What I'm doing wrong?

0 Kudos
Steve_Lionel
Honored Contributor III
1,706 Views

Same problem as above. If you're building a mixed language application where the main program is not Fortran, set the "Disable default library search rules" to No in the Fortran Library project. It defaults to Yes as this is generally what you want when the main is Fortran.

0 Kudos
Maia__Nycholas
Beginner
1,706 Views

I already did it...but it doesn't resolved the C++ linker problem.

fortran1.jpg

Do you have any idea?

0 Kudos
Steve_Lionel
Honored Contributor III
1,707 Views

Did you rebuild the Fortran library after changing the setting? If yes and you still have the problem, add ifmodintr.lib to "Additional Dependencies" in the C++ project's Linker properties.

0 Kudos
Maia__Nycholas
Beginner
1,706 Views

Thank you @Steve! You are great!
Yes, I just add the 'ifmodintr.lib' to  "Additional Dependencies" in the C++ project's Linker properties and all goes Ok!

Is there any documentation about this 'libraries dependencies'? In a future, how could I known which library name is missing in the "Additional Dependencies" field?

Thank you again!

0 Kudos
Steve_Lionel
Honored Contributor III
1,706 Views

Normally, when you compile a Fortran source, it adds linker directives to the object file naming the libraries it wants the linker to pull in. When you USE ISO_C_BINDING it adds a directive for ifmodintr.lib (you can see the OBJCOMMENT directive in the source for the module in the ifort include folder). 

There is a compiler option to disable adding these directives (/libdir:[no]auto), which is what the "Disable OBJCOMMENT Library Names" property adjusts. For static library projects the default is Yes (don't include OBJCOMMENT directives), as one typically wants the main program to control this.

With a non-Fortran main, however, there's nothing to specify which Fortran support libraries are pulled in other than the OBJCOMMENT directives, so you generally want to include those in your library build.

I still think that if you had rebuilt your library with the option properly set that it would have worked ok for you. I had thought there was documentation on the list of libraries, but I can't find it. I did find a page "Specifying Consistent Library Types", but it is not only incomplete but woefully out of date (mentions libc.lib, which no longer exists, plus says "ifcore.lib" which never existed.) I will report this to Intel.

If you want to see the list of libraries the compiler wants, do a:

dumpbin -directives

on a compiled .obj. You'll get something like this:

   Linker Directives
   -----------------
   -defaultlib:ifconsol
   -defaultlib:libifcoremt
   -defaultlib:libifport
   -defaultlib:libmmt
   -defaultlib:LIBCMT
   -defaultlib:libirc
   -defaultlib:svml_dispmt
   -defaultlib:OLDNAMES

I would ignore OLDNAMES - that's a historical artifact. Note that the list of libraries will vary depending on other compiler options and modules used.

Last, now that this forum has been merged for all platforms, I need to say that all of the above applies to Windows only. Linux/Mac doesn't have a similar mechanism, leaving you to explicitly name all of the support libraries needed if you're not using "ifort" to link.

0 Kudos
FortranFan
Honored Contributor II
1,706 Views

Maia, Nycholas wrote:

..

Question 2:
FortranFan, in the C++ side, your source-code exemple have this C string observation:

char c_str[] = "Test"; //<-- Note no hard-wired length

It works good!
But this modified version below works good too (at moment, only tested using gfortran).
 

char c_str[30] = "Test";

It prints the correct C string with no blank spaces like the original one.

So, why should I use the original "hard-wired length"? Is there any C-Fortran interoperability advantages?

..

Re: "why should I use the original "hard-wired length"?," yes, why should you!?  That was my point, that it's high-time coders moved beyond hard-wired string lengths that lead to so many issues: compile-time errors, run-time errors, wasted space.  As a matter of good coding practice, whether it be in Fortran or other languages, coders can try to utilize the capabilities to right-size their data.  And where Fortran falls short (like a true intrinsic derived type for strings, they should expect and request language improvements:

#include <iostream>
using namespace std;

extern "C" {
	void f_subroutine(const char* , size_t);
}

int main(void)
{
   // No guessing as to the length of string constant
	string s = "../files/json_files/my_json_file.json";
   cout << "length of string: " << s.size() << endl;

   f_subroutine( s.c_str(), s.size() );

	return 0;
}

Upon execution,

length of string: 37
 In f_subroutine: Fortran incarnation of c_str: ../files/json_files/my_json_file.json

 

0 Kudos
Maia__Nycholas
Beginner
1,706 Views

Thank you @FortranFan for your good explanation about this issue.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,706 Views

FWIW>>

   // No guessing as to the length of string constant
	string s = "../files/json_files/my_json_file.json";

s is not a string constant, it is declared as variable. Also note that the attribute of the segment(linker section) holding the buffer for s should contain Read/Write.

There is nothing to say that f_subroutine could not modify s. s could be declared with const and may place the buffer in a Read-Only segment when you really desire to have it a string constant.

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor II
1,706 Views

jimdempseyatthecove (Blackbelt) wrote:

FWIW>>

   // No guessing as to the length of string constant
	string s = "../files/json_files/my_json_file.json";

s is not a string constant, it is declared as variable. Also note that the attribute of the segment(linker section) holding the buffer for s should contain Read/Write.

There is nothing to say that f_subroutine could not modify s. s could be declared with const and may place the buffer in a Read-Only segment when you really desire to have it a string constant.

Jim Dempsey

Just to be clear: s is a variable, an object instance of 'string' class in C++.  It's the string literal on the right-hand side of the assignment is what I meant by constant; per Fortran standard terminology, that would be a "character literal constant"

0 Kudos
Reply