I wish to make use of a subroutine in a DLL for my research. The dll is provided to me as a blackbox and can be used in a leading FE software.
I executed the following code for the dll
dumpbin /EXPORTS UDSM_HPS.dll > UDSM_HPS.exports
and ended up with the following subroutine
2 0 00020D50 _getmodelcount#4
4 1 00020D60 _getmodelname#12
6 2 00020E50 _getparamcount#8
8 3 00020E80 _getparamname#16
10 4 00021AE0 _getparamunit#16
12 5 00001010 _user_mod#124
1 6 00020D50 getmodelcount
3 7 00020D60 getmodelname
5 8 00020E50 getparamcount
7 9 00020E80 getparamname
9 A 00021AE0 getparamunit
11 B 00001010 user_mod
My interest lies in the 'user_mod' subroutine. I created a .lib file from the .def file, by adding 'EXPORTS' in the beginning of the file and isolating the subroutine names, using the following code
lib /def:UDSM_HPS.def /out:UDSM_HPS.lib
and attached the .lib file to the 'Resourse Files' to my Visual Studio 2013, while placing a copy of my .dll to my 'Debug' folder.
No surprises here, the 'user_mod' subroutine is not seen by the compiler.
Now My question, am I missing something, or is there a differnt way of utilising the subroutine from the dll?
The dll is written in FORTRAN too, and I have written a simple piece of FE code in FORTRAN to run this subroutine.
Long story short: Any assistance is figuring out how to utilise a FORTRAN dll in FORTRAN program is much appreciated.
Edit 1:
I am using the Intel Fortran compiler - Intel(R) Visual Fortran Compiler XE 15.0.6.285 [IA-32]. I had tried compiling the same piece of code in Intel(R) Fortran Compiler 10.1.021 [IA-32], but in vain.
The subroutine I intend to call is
call user_mod(IDTask, iMod, IsUndr, iStep, iTer,&
iEl, Int, X, Y, Z, Time0, dTime, Props, Sig0, Swp0, StVar0,&
dEps, D, BulkW, Sig, Swp, StVar, ipl, nStat, NonSym, iStrsDep,&
iTimeDep, iTang, iAbort)
To clarify my rather vague sentence of 'not seen by the compiler', I meant this error
Error 1 error LNK2019: unresolved external symbol USER_MOD referenced in function PLAXIS_DLL_INTF interface_files.obj
Edit 2:
Thanks for the all the help.
Adding
!DEC$ ATTRIBUTES STDCALL,REFERENCE :: USER_MOD
was all that was needed to get my code going. Code is behaving like how it should now.
Thanks for providing the error message - that is the key. It isn't the compiler that can't see user_mod, it's the linker. Note that the error message refers to USER_MOD, but your dump of the symbols says user_mod. Case matters!
It is also important that the routine you're calling has the STDCALL calling mechanism. This is not the Intel Fortran default, and getting this wrong leads to stack corruption.
The solution is to add the line:
!DEC$ ATTRIBUTES STDCALL,REFERENCE :: USER_MOD
to the routine calling USER_MOD - in with the declarations. This tells Intel Fortran that USERMOD is a STDCALL routine, and downcases the routine name. Also make sure that you are passing the correct number and type of arguments when you call USER_MOD. If you get this wrong, you'll get link errors. Last, I am guessing about the use of REFERENCE here, but I will assume it is correct since I don't know how USER_MOD was built. At least this will get you past the link error.
Related
I have a Fortran program which uses a routine in a module to resize a matrix like:
module resizemod
contains
subroutine ResizeMatrix(A,newSize,lindx)
integer,dimension(:,:),intent(inout),pointer :: A
integer,intent(in) :: newSize(2)
integer,dimension(:,:),allocatable :: B
integer,optional,intent(in) :: lindx(2)
integer :: i,j
allocate(B(lbound(A,1):ubound(A,1),lbound(A,2):ubound(A,2)))
forall (i=lbound(A,1):ubound(A,1),j=lbound(A,2):ubound(A,2))
B(i,j)=A(i,j)
end forall
if (associated(A)) deallocate(A)
if (present(lindx)) then
allocate(A(lindx(1):lindx(1)+newSize(1)-1,lindx(2):lindx(2)+newSize(2)-1))
else
allocate(A(newSize(1),newSize(2)))
end if
do i=lbound(B,1),ubound(B,1)
do j=lbound(B,2), ubound(B,2)
A(i,j)=B(i,j)
end do
end do
deallocate(B)
end subroutine ResizeMatrix
end module resizemod
The main program looks like:
program resize
use :: resizemod
implicit none
integer,pointer :: mtest(:,:)
allocate(mtest(0:1,3))
mtest(0,:)=[1,2,3]
mtest(1,:)=[1,4,5]
call ResizeMatrix(mtest,[3,3],lindx=[0,1])
mtest(2,:)=0
print *,mtest(0,:)
print *,mtest(1,:)
print *,mtest(2,:)
end program resize
I use ifort 14.0 to compile the codes. The issue that I am facing is that sometimes I don't get the desired result:
1 0 0
1 0 5
0 0 -677609912
Actually I couldn't reproduce the issue (which is present in my original program) using the minimal test codes. But the point that I noticed was that when I remove the compiler option -fast, this problem disappears.
Then my question would be
If the pieces of code that I use are completely legal?
If any other method for resizing the matrices would be recommended which is better than the one presented in here?
The relevance of the described issue and the compiler option "-fast".
If I've read the code right it's legal but incorrect. In your example you've resized a 2x3 array into 3x3 but the routine ResizeMatrix doesn't do anything to set the values of the extra elements. The strange values you see, such as -677609912, are the interpretation, as integers. of whatever bits were lying around in memory when the memory location corresponding to the unset array element was read (so that it's value could be written out).
The relevance of -fast is that it is common for compilers in debug or low-optimisation modes, to zero-out memory locations but not to bother when higher optimisation is switched on. The program is legal in the sense that it contains no compiler-determinable syntax errors. But it is incorrect in the sense that reading a variable whose value has not been initialised or assigned is not something you regularly ought to do; doing so makes your program, essentially, non-deterministic.
As for your question 2, it raises the possibility that you are not familiar with the intrinsic functions reshape or (F2003) move_alloc. The latter is almost certainly what you want, the former may help too.
As an aside: these days I rarely use pointer on arrays, allocatable is much more useful and generally easier and safer too. But you may have requirements of which I wot not.
I'm trying to do some interesting orbit mechanics, I've found some related code in Fortan and I'm going through line by line moving it to Visual Basic. I can't understand what this is though:
IF(ABS(EPW-TEMP2) .LE. E6A) GO TO 140
It's not a variable. I figure E6 might be 10^6 but what's the 'A' mean?
Thanks!
When I google that line of code, I end up with some "Spacetrack Report No.3" with the fortran code listing. And E6A is defined as 1.E-6 in the routine DRIVER (Page. 73)
DATA DE2RA,E6A,PI,PIO2,QO,SO,TOTHRD,TWOPI,X3PIO2,XJ2,XJ3,
1 XJ4,XKE,XKMPER,XMNPDA,AE/.174532925E-1,1.E-6,
2 3.14159265,1.57079633,120.0,78.0,.66666667,
4 6.2831853,4.71238898,1.082616E-3,-.253881E-5,
5 -1.65597E-6,.743669161E-1,6378.135,1440.,1.
I see this code has already been converted to Java and C, perhaps you should use those as reference.
Here is the situation. I am using MS Visual Sturio 2005 and Excel 2007. I create a dll containing a function
extern "C" VARIANT WINAPI MyFunc()
{
VARIANT res;
memset(&res, 0, sizeof(VARIANT));
::VariantInit(&res);
return res;
}
That is called from Excel VBA. This function seems to work correctly.
But there is a problem: when VBA returns from my function and tries to execute the next floating point instruction I see a window:
runtime error 6. Overflow.
This error is pretty strange. I have been investigating it the last couple of days, here are the "facts" I have collected:
1) The error shows up only after the first call to the dll. All successive calls to this function don't cause this error.
2) The error is triggered by the first (seemingly harmless) floating-point instruction in the VBA code after the control returns from the dll:
Dim dMinValue As Double
dMinValue = 10000000#
3) The project from which the dll is built contains 4 files: mydll.cpp, mydll.def, cSomeClass.cpp and cSomeClass.h.
cSomeClass is a fairly complicated class that calls code from my other libs. BUT mydll.cpp does not use cSomeClass in any way. Here is the code of mydll.cpp:
#include <OAIdl.h>
#include <float.h>
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
return TRUE;
}
extern "C" VARIANT WINAPI MyFunc()
{
unsigned int u;
u = _controlfp(0, 0);
_controlfp(u & ~(_EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT), _MCW_EM);
VARIANT res;
memset(&res, 0, sizeof(VARIANT));
::VariantInit(&res);
return res;
}
The initial version of MyFunc() didn't have the first 3 lines (the ones messing with _controlfp()), it was causing "Runtime Error 6, Overflow" in VBA. Then I started suspecting that this error could be related to the FPU registeres (I know very little about those). When I added these 3 lines an exception was thrown when I call _controlfp() - "0xC0000090: Floating-point invalid operation.". If I leave the code as it is presented above the exception causes stack unwind (we don't get to the VARIANT res; line) and a window saying "runtime error 6. Overflow." is displayed in Excel. To sum it up, adding these 3 lines caused the floating point exception to be thrown earlier. If we catch the exception thrown in the 3rd line (with an __except clause) and ignore it (by callng _clearfp()) then no errors are reported in Excel.
A NASTY DETAIL: if I remove the files cSomeClass.cpp and cSomeClass.h from the Visual Studio project the error is not reproduced. cSomeClass.h is not includeed into mydll.cpp, but if I remove cSomeClass.* files from the project the dll size is significantly reduced.
My best guess at this point is that there are some static objects in the LIB-s referenced by cSineClass.cpp that cause this. Maybe these objects are initialized (constructed) when the dll is being loaded (according to my experiments, before to DllMain) and this somehow causes an error flag for "0xC0000090: Floating-point invalid operation." being set. When we return to Visual Basic from the dll, somethis calls _controlfp and enables the floating-point exceptions (they are turned off in С++) when raises "0xC0000090: Floating-point invalid operation." that somehow gets converted to "Runtime Error 6. Overflow." that I see in Excel. This is just a guess. Up unti now I haven't found any static objects that could be doing this.
Unfortunately I haven't been able to create a small example reproducing this error, because it shows up only when I have cSomeFile.* as part of the project. And those files require all of my libs...
If anyone knows what could be the cause of such behaviour or have suggestions about how to advance with my investigations that would be greatly appreciated.
So I have at various points had similar random issues with Excel, and you are my hero.
I found that by calling _clearfp(), it will quiet the error and return the value properly. I think the issue arises from the fact that I use _HUGE values all over the place to signify invalid floats. Which sets the flag, and causes Excel (and only excel) to barf. I've never run into any issues with runtime exceptions before in any other program.
1) Apparently, when I get into the DLL code from VBA the FPU exceptions are turned off, like if someone called
u = _controlfp( 0, 0 );
_controlfp( u & ((_EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT )), _MCW_EM );
before passing control to my dll code.
2) Calling std::numeric_limits::signaling_NaN() at any point causes the _EM_INVALID flag to be set in the floating point status word (can be seen by calling _statusfp()).
3) When the control is returned from the DLL the FPU exceptions seem to be turned on. As if someone called:
u = _controlfp( 0, 0 );
_controlfp( u & (~(_EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | EM_UNDERFLOW | _EM_INEXACT )), _MCW_EM );
This causes a runtime error.
I am using Xilinx ISE 10.1 to run some verilog code. In the code I want to write the register values of 3 registers in a file, cipher.txt. The following is the code snippet:
if (clk_count==528) begin
f1 = $fopen("cipher.txt", "w");
$fwrite(f1, "clk: %d", clk_count[11:0]);
$fwrite(f1, "plain: %h", plain[31:0]);
$fwrite(f1, "cipher: %h", cipher[31:0]);
$fclose(f1);
end
At the end of execution, the contents of cipher.txt is found as:
clk: %dplain: %hcipher: %h
There is no other error encountered, but a warning comes up corresponding to the 3 fwrite's:
Parameter 3 is not constant in call of system task $fwrite.
Parameter 3 is not constant in call of system task $fwrite.
Parameter 3 is not constant in call of system task $fwrite.
The values of the registers clk_count and cipher change on every clock cycle (value of register plain remains constant throughout), and the values are written to cipher.txt when clk_count equals 528 (indicated by the if statement)
Can anybody provide some insight and/or help me get past this hurdle?
Thanks.
It appears that ISE expects the arguments to $fwrite to be constant. The warnings are referring to clk_count[11:0], plain[31:0], and cipher[31:0], which are not constant. By definition they are changing each cycle so they are not known at compile time. This also explains why they are not printing and you are seeing %d and %h in the output.
There is nothing to my knowledge in the Verilog spec that requires the arguments to $fwrite be constant. The same code works as expected with Cadence Incisive. My guess is that it's a limitation of ISE, so you may want to check with Xilinx.
Possible work-arounds:
1) Use $swrite to create a string with the proper formatting. Then write the string to the file.
2) Try using an intermediate variable in the calls to $fwrite. Maybe the part-selects are throwing it off. e.g.
integer foo;
foo = clk_count[11:0];
$fwrite(... , foo , ...);
Either of those might work, or not.
Out of curiosity, if you remove the part-selects, and try to print clk_count without the [11:0] , do you get the same warnings?
Stupid question that I'm sure is some bit of syntax that's not right. How do I get dlsym to work with a function that returns a value? I'm getting the error 'invalid conversion of void* to LSError (*)()' in the following code - trying to get the compile the linux lightscribe sample program hoping that I can link it against the OSX dylib (why the hell won't HP release an actual Cocoa SDK? LS has only been around for what? 6 or 7 years now?):
void* LSHandle = dlopen("liblightscribe.1.dylib", RTLD_LOCAL|RTLD_LAZY);
if (LSHandle) {
LSError (*LS_DiscPrinter_ReleaseExclusiveUse)() = dlsym(LSHandle, "LS_DiscPrinter_ReleaseExclusiveUse");
..
lsError = LS_DiscPrinter_ReleaseExclusiveUse( pDiscPrinter);
The C standard does not actually define behaviour for converting to and from function pointers. Explanations vary as to why; the most common being that not all architectures implement function pointers as simple pointers to data. On some architectures, functions may reside in an entirely different segment of memory that is unaddressable using a pointer to void.
The “recommended” way to use dlsym is:
LSError (*LS_DiscPrinter_ReleaseExclusiveUse)(LS_DiscPrinterHandle);
*(void **)&LS_DiscPrinter_ReleaseExclusiveUse = dlsym("LS_DiscPrinter_ReleaseExclusiveUse");
Read the rationale and example on the POSIX page for dlsym for more information.