Patrick
Patrick

Reputation: 1939

Fortran & Matlab linking -- undefined symbols

I have inherited this mex code written in fortran 90 that I can't manage to properly build. All the object files compile nicely, but the linking fails. Fortunately, I have several leads to go on to figure out the problem.

The only complaint from the linker is

Undefined symbols for architecture x86_64:
  "_mxgetstring_", referenced from:
      _mexfunction_ in qgstep_mex.o
ld: symbol(s) not found for architecture x86_64

Now mxGetStringis only called in one place in qgstep_mex.f90

status = mxGetString(prhs(3), prmfname, STRLEN)

So if I change this to

status = 0

and hardcode prmfname, the package builds just fine. But of course I don't want to hardcode it.

I have, however, managed to build another program that uses mxGetString(), namely the Matlab-supplied example file revord.f

Both of these programs are linked by the same command made by mex

gcc -O -Wl,-twolevel_namespace -undefined error -arch x86_64 -Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/ -mmacosx-version-min=10.5 -bundle -Wl,-exported_symbols_list,/Applications/MATLAB_R2012a.app/extern/lib/maci64/fexport.map  -o  "revord.mexmaci64"  revord.o  -L/Applications/MATLAB_R2012a.app/bin/maci64 -lmx -lmex -lmat -L/opt/local/lib/gcc47/gcc/x86_64-apple-darwin12/4.7.3/../../.. -lgfortran -L/opt/local/lib/gcc47/gcc/x86_64-apple-darwin12/4.7.3 -lgfortranbegin

except that revord.F is substituted by qgstep_mex.f90 and similarly for the output name.

There is an include statement in revord.F

#include "fintrf.h"

which is pointed to during the compilation by the flag

-I/Applications/MATLAB_R2012a.app/extern/include

However, if I try to put this in qgstep_mex.f90, the linker just complains with

Warning: qgstep_mex.f90:1: Illegal preprocessor directive.

So, does anyone know how to get this working?

Update (in response to M.S.B.'s answer)

Changing the case of the extension to .F90 had no effect, but the -cpp flag seems to have done the trick. However, now it gives an error (during compilation, not linking) that I do not quite understand (I'm not very good with Fortran)

$ gfortran-mp-4.7 -c -I/Applications/MATLAB_R2012a.app/extern/include -I/Applications/MATLAB_R2012a.app/simulink/include -fexceptions -m64 -fbackslash -DMX_COMPAT_32 -O -cpp "qgstep_mex.f90"
qgstep_mex.f90:102.11:

  status = mxGetString700(prhs(3), prmfname, STRLEN)
           1
Error: Function 'mxgetstring700' at (1) has no IMPLICIT type

The relevant lines from the include file seems to be

#if defined(MX_COMPAT_32)
.
.
.
#define mxGetString mxGetString700

Update in response to M.S.B.'s update

Aha. It seems I'm not supposed to include fintrf.h after all. There's another file in the project directory, mexf90.f90, containing

module mexf90_mod
  interface
     function mxGetString(p, string, STRLEN)
       integer(8) :: mxGetString
       integer(8) :: p
       character*(*) :: string
       integer(4) :: STRLEN
     end function mxGetString

     function mxGetPr(pm)
       integer(8), pointer :: mxGetPr
       integer(8) :: pm
     end function mxGetPr
.
.

The strange thing is that mxGetPr is called just fine in qgstep_mex.f90, before mxGetString, but calling mxGetString fails, with the error message as noted originally above. Here's the linking command I use (linebreaks inserted for clarity)

gfortran-mp-4.7 -O -Wl,-twolevel_namespace -undefined error -arch x86_64
-Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/
-mmacosx-version-min=10.5 -bundle -Wl,-exported_symbols_list,/Applications/MATLAB_R2012a.app/extern/lib/maci64/fexport.map
-o "QG_step_f.mexmaci64" utils.o parameters.o helmholtz.o calc.o qgflux.o qgstep.o mexf90.o qgstep_mex.o
-L/Applications/MATLAB_R2012a.app/bin/maci64 -lmx -lmex -lmat -L/opt/local/lib/gcc47/gcc/x86_64-apple-darwin12/4.7.3/../../..
-lgfortran -L/opt/local/lib/gcc47/gcc/x86_64-apple-darwin12/4.7.3 -lgfortranbegin

As can be seen, the object files do seem to be in the right order as well.

Update

The package builds just fine on a linux system. Hence I guess it must be some difference between the compilers. Here's the linking code used on the linux system:

gfortran -O  -pthread -shared -Wl,--version-script,/ufs/local/matlab-2012a/extern/lib/glnxa64/fexport.map -Wl,--no-undefined
-o  "QG_step_f.mexa64"  utils.o parameters.o helmholtz.o calc.o qgflux.o qgstep.o mexf90.o qgstep_mex.o
-Wl,-rpath-link,/ufs/local/matlab-2012a/bin/glnxa64 -L/ufs/local/matlab-2012a/bin/glnxa64 -lmx -lmex -lmat -lm

Upvotes: 0

Views: 1212

Answers (2)

M. S. B.
M. S. B.

Reputation: 29401

Try changing the filename qgstep_mex.f90 to qgstep_mex.F90. That will cause gfortran to run the C preprocessor. There is also a compiler option to cause that.

EDIT in response to the question update: The compiler wants the function mxGetString7001 to be typed. There are several ways that you can do this. The ideal approach is to have the source code of the function in a module. Then when the caller uses that module, the return type of the function and the types of its arguments are known to the compiler. Perhaps this exists and you need to add the source code file to your compile statement ... before the file that uses this function. If not, you could write an interface block providing this information. Or you could declare the function and use an external statement, but this is really a work around, using FORTRAN 77 methods.

Upvotes: 1

cup
cup

Reputation: 8267

The include statement in fortran does not have a #

include "fintrf.h"

When building, why not use gfortran instead of gcc. Saves the compiler having to figure out what you are compiling.

Upvotes: 0

Related Questions