I have a .dll I suspect uses __stdcall functions, but totally undecorated names (not even an underscore or # sign). I wrote a .def file for it with aliases like
func1 = func1
func1#4 = func1
but I think lib (which I am using to create the import library from the .def file) is ignoring the aliases (the linker still says func1#4 is undefined)
How can I create an import library that will map func1#4 to func1?
I don't know if it possible to create right .lib file with lib.exe, so I suggest to create a DLL with stubs of functions exported by original DLL.
Example dll.cpp file:
void __stdcall foo(int) {}
dll.def file:
EXPORTS
foo=foo
Then you compile it, from IDE, or command line:
cl dll.cpp /link /dll /def:dll.def /noentry
It will create correct dll.lib file, so you can use it with code like this:
void __stdcall foo(int);
#pragma comment(lib, "dll")
int main()
{
foo(42);
}
You should be declaring the function prototype as extern "C" as you are (obviously) compiling with in C++ mode:
extern "C" void func1(void); // correct prototype, add __stdcall if necessary?
You can wrap entire sections of a header file inside
extern "C" {
// ... C compatible declarations
}
Related
I want to Create a Static-Library from Ada-Code and deploy it to Developers without the GNAT-Toolchain (for C/C++ Code).
I will get following Linker-Errors when I try to Link Ada-Library ('.a') with a C-Program:
undefined reference to `__gnat_rcheck_CE_Overflow_Check'
undefined reference to `ada__text_io__put_line__2'
How can I achieve this ? It seams that I should link against the Runtime-library, but how ?
Test-Code:
main.c:
#include <stdio.h>
extern void adaTest();
extern int add5(int);
int main(){
adaTest();
int b = add5(2);
printf("--> %d \ndone.\n", b);
return 0;
}
ada_lib_project.gpr:
library project ada_lib_project is
for Languages use ("Ada");
for Library_Name use "My_Ada_Lib";
for Library_Dir use "my_generated_lib";
for Library_Kind use "Static";
end ada_lib_project;
adatestpacket.ads:
with Interfaces.C; use Interfaces.C;
package adatestpacket is
procedure adatest with
Export, Convention => C, External_Name => "adaTest";
function add5(x: in int) return int with
Export, Convention => C, External_Name => "add5";
end adatestpacket;
adatestpacket.adb:
with Ada.Text_IO; use Ada.Text_IO;
with Interfaces.C; use Interfaces.C;
package body adatestpacket is
procedure adatest is
begin
Put_Line("This is executed ADA/SPARK-Code...");
null;
end adatest;
function add5(x: in int) return int is
begin
return x + 5;
end add5;
end adatestpacket;
Compiling:
gcc -c main.c -o main.o # .c -> .o
gprbuild -P ada_lib_project.gpr # .ad[sb] -> .a
gcc main.o -L my_generated_lib -l My_Ada_Lib -o a.out # Linking -- with undefined References
Probably the easiest way to do this is to simply also compile the C source with gprbuild (even if you can't do that in your target scenario, you can do it for testing and see with -v what GPRbuild does to get it to work):
with "ada_lib_project";
project My_Executable is
for Languages use ("C");
for Main use ("main.c");
end My_Executable;
You will also need to call adainit and adafinal to initialize / finalizate Ada packages:
#include <stdio.h>
extern void adainit();
extern void adafinal();
extern void adaTest();
extern int add5(int);
int main(){
adainit();
adaTest();
int b = add5(2);
printf("--> %d \ndone.\n", b);
adafinal();
return 0;
}
adainit and adafinal are generated by gnatbind for standalone libraries. I am not entirely sure whether GPRBuild takes care of this when seeing that you use an Ada library from a C executable; if not you'll need
package Binder is
for Default_Switches ("Ada") use ("-n");
end Binder;
in your library. After doing this, you should be able to do
gprbuild my_executable.gpr
If you want to do it without GPRbuild, the -n/adainit/adafinal part still applies and you need to link your executable with
-l<your-gnat-lib>
where <your-gnat-lib> is the Ada standard library of your GNAT version; last time I did this, it was something like gnat-2021. You may need to add a -L<directory-containing-that-lib> depending on where it's located.
(there may be mistakes in this answer since I cannot currently test it due to being on an M1)
Edit: If you really want to supply developers without any access to GNAT, you need to build an encapsulated, i.e. dynamic, library. This answer covers that process. If providing a static library is a requirement, you have to at least supply the GNAT standard library file.
For anyone whose interested in a Working-Implementation, these are the Changes from my Question:
main.c:
#include <stdio.h>
extern void adainit();
extern void adafinal();
extern void adaTest();
extern int add5(int);
int main(){
adainit();
adaTest();
int b = add5(2);
printf("--> %d \ndone.\n", b);
adafinal();
return 0;
}
ada_lib_project.gpr:
library project ada_lib_project is
for Languages use ("Ada");
for Library_Name use "My_Ada_Lib";
for Library_Dir use "my_generated_lib";
for Library_Kind use "static-pic";
for Library_Interface use ("adatestpacket");
package Binder is
-- "-Lada" set "ada" as Prefix for "init" and "final" Function
for Default_Switches ("Ada") use ("-n","-Lada");
end Binder;
end ada_lib_project;
Compiling:
gprbuild -P ada_lib_project.gpr # .adb -> .a
gcc main.c -L my_generated_lib -l My_Ada_Lib -l gnat_pic -ldl
For the last Command, I just need to Transfer the Library (My_Ada_Lib) and the Runtime (libgnat_pic.a) from GNAT/2021/lib/gcc/x86_64-pc-linux-gnu/10.3.1/rts-native/adalib to the remote Machine.
I have generated static binaries with -static. I don't know if something similar can work while generating your library or you will also need to have the GNAT runtime for linking with the C/C++ tools.
I am building a DLL which is having static reference to a library (.lib). Both the DLL and .lib has a function with same name but with different signature. I want to export the function written for the DLL. I am using .def file for exporting the function.
However, When I use dumpbin to export the function from the DLL, I dont see the decorated name for this function (having two function of the same name), for all other function I do see decorated name.
Function in A.cpp (DllName - ADLL)
uint __stdcall func(MSIHANDLE hHandle)
uint __stdcall func1(MSIHANDLE hHandle)
Function in .lib (B.lib) which is statically referenced in ADLL
DWORD WINAPI func(__in const bool input);
Format of def (ADLL.def) file :
LIBRARY "ADLL"
EXPORTS
func
func1
Snippet from .vcxProj
<ItemDefinitionGroup>
<Link>
<ModuleDefinitionFile>ADLL.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
Output from Dumpbin /Exports
ordinal hint RVA name
2 0 00035560 func = func
1 1 00001370 func1 = #ILT+875(?func1##YAIK#Z)
I am not able to understand why the function "func" doesn't have a decorated name like func1?
I have a .lib library which provides APIs which have global variables like file/device handles, these APIs are going to be used by the application(.exe) by linking with .lib statically. The application does some initialization using APIs provided by .lib (like opening device/file etc) and loads the .dll during run time to perform writes and reads to the device/file using APIs provided by .lib. The problem is when I link the static library with both .exe and .dll they have different copies of libraries and the initialization done by the application is not retained when .dll is opened during run-time (since .dll and .exe seem to be working with their own copy of .lib). This problem is solved when I make .dll instead of static library and export all APIs and import them in .exe and .dll.
But I want to make the .exe standalone without any .dll dependencies except for run-time .dll .
Here is sample code for scenario
main.c (.exe)
#include <stdio.h>
#include <windows.h>
#include "static_lib.h"
typedef void (*FNPTR)(void);
FNPTR functionname;
int main(){
HINSTANCE hLib;
LPTSTR dllname = "dynamic.dll";
hLib=LoadLibrary(dllname);
if(hLib==NULL)
{
printf("Unable to load dll %s\n", dllname);
return 0;
}
functionname=(FNPTR)GetProcAddress((HMODULE)hLib, (LPCSTR)"dyn_main");
if((functionname==NULL))
{
printf("unable to load test function %s\n", dllname);
FreeLibrary((HMODULE)hLib);
return 0;
}
printf("Var in main: ");
test_func();
printf("calling dyn_main:");
functionname();
printf("back to main:");
printvar();
FreeLibrary((HMODULE)hLib);
return 1;
}
static_lib.c (static library)
#include <stdio.h>
#include <windows.h>
#include "static_lib.h"
int var; //can be file or device handle
int test_func(){ //initializes the device
change_var();
printvar();
return 1;
}
void printvar(void){ //write/read device/file
printf("%d\n",var);
return;
}
void change_var(void){ //opens device/file
var = 1;
return;
}
static_lib.h
#ifndef _STATIC_LIB_H_
#define _STATIC_LIB_H_
#if defined (DLL_EXPORT)
#define DLL_IMPORT_EXPORT __declspec(dllexport)
#else
#define DLL_IMPORT_EXPORT __declspec(dllimport)
#endif
DLL_IMPORT_EXPORT void change_var(void);
DLL_IMPORT_EXPORT void printvar(void);
DLL_IMPORT_EXPORT int test_func();
#endif
dynamic.c (.dll)
#include <stdio.h>
#include <windows.h>
#include "static_lib.h"
__declspec(dllexport) void dyn_main(void){
printvar(); //uses the device
return;
}
The command line for compiling with dynamic linking is
cl /c /nologo main.c dynamic.c
cl /c /nologo /DDLL_EXPORT static_lib.c
LINK /nologo /DLL static_lib.obj
LINK /nologo /DLL dynamic.obj static_lib.lib
LINK /nologo main.obj static_lib.lib
The output is:
main.exe
Var in main: 1
calling dyn_main:1
back to main:1
But when I make a static library and link with .exe and .dll
cl /c /nologo main.c dynamic.c static_lib.c
LIB static_lib.obj
LINK /nologo /DLL dynamic.obj static_lib.lib
LINK /nologo main.obj static_lib.lib
The output is
main.exe
Var in main: 1
calling dyn_main:0
back to main:1
And in real scenario the program crashes as it is invalid device/file handle inside .dll.
Is there anyway to export APIs into .dll from .exe without linker giving unresolved symbol error and maintaining only one copy of static library used by .dll and .exe?
I have searched for the problem and I found this: Dynamic Loading of my DLL with Static Lib in Windows Environment
It says we cannot do this in windows environment. But I want to make sure before thinking of other alternatives.
I want to create a loadable DLL of some of my tcl methods. But I am not getting how to do this. For that I have taken a simple example of tcl api which adds two numbers and prints the sum. Now I want to create a loadable DLL for this to export this tcl functionality.
But I am not understanding how to do it in Visual Studio. I have written a C code which can call this tcl api and get the sum of two integers, but again I don't want it to do this way. I want to create a DLL file to use this tcl functionality. How can I create this DLL on Visual Studio 2010.
Below is my sample tcl program that I am using:
#!/usr/bin/env tclsh8.5
proc add_two_nos { } {
set a 10
set b 20
set c [expr { $a + $b } ]
puts " c is $c ......."
}
And here is the C code which can use this tcl functionality :
#include <tcl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
Tcl_Interp *interp;
int code;
char *result;
Tcl_FindExecutable(argv[0]);
interp = Tcl_CreateInterp();
code = Tcl_Eval(interp, "source myscript.tcl; add_two_nos");
/* Retrieve the result... */
result = Tcl_GetString(Tcl_GetObjResult(interp));
/* Check for error! If an error, message is result. */
if (code == TCL_ERROR) {
fprintf(stderr, "ERROR in script: %s\n", result);
exit(1);
}
/* Print (normal) result if non-empty; we'll skip handling encodings for now */
if (strlen(result)) {
printf("%s\n", result);
}
/* Clean up */
Tcl_DeleteInterp(interp);
exit(0);
}
I have successfully compiled this code with the below command
gcc simple_addition_wrapper_new.c -I/usr/include/tcl8.5/ -ltcl8.5 -o simple_addition_op
The above code is working with the expected output.
What steps do I need to take to create a loadable dll for this in Visual Studio 2010?
If you look at the answers to this question: here it gives the basic outline of the process you need to go through. There are links from my answer to some Microsoft MSDN articles on creating DLLs.
To go into this in a little more detail for a C++ dll that has Tcl embedded in it.
The first step is to create a new visual studio project with the correct type, one that is going to build a dll that exports symbols. My example project is called TclEmbeddedInDll and that name appears in code in symbols such as TCLEMBEDDEDINDLL_API that are generated by Visual Studio.
The dllmain.cpp look like this:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
allocInterp() ;
break ;
}
case DLL_THREAD_ATTACH:
break ;
case DLL_THREAD_DETACH:
break ;
case DLL_PROCESS_DETACH:
{
destroyInterp() ;
break;
}
}
return TRUE;
}
The allocInterp() and destroyInterp() functions are defined in the TclEmbeddedInDll.h, the reason for using functions here rather than creating the Tcl_Interp directly is that it keeps the details about Tcl away from the DLL interface. If you create the interp here then you have to include tcl.h and then things get complicated when you try and use the DLL in another program.
The TclEmbeddedInDll.h and .cpp are shown next, the function fnTclEmbeddedInDll() is the one that is exported from the DLL - I'm using C linkage for this rather than C++ as it makes it easier to call the function from other languages IMHO.
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the TCLEMBEDDEDINDLL_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// TCLEMBEDDEDINDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef TCLEMBEDDEDINDLL_EXPORTS
#define TCLEMBEDDEDINDLL_API __declspec(dllexport)
#else
#define TCLEMBEDDEDINDLL_API __declspec(dllimport)
#endif
extern "C" {
TCLEMBEDDEDINDLL_API void fnTclEmbeddedInDll(void);
}
void allocInterp() ;
void destroyInterp() ;
// TclEmbeddedInDll.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
extern "C" {
static Tcl_Interp *interp ;
// This is an example of an exported function.
TCLEMBEDDEDINDLL_API void fnTclEmbeddedInDll(void)
{
int code;
const char *result;
code = Tcl_Eval(interp, "source simple_addition.tcl; add_two_nos");
result = Tcl_GetString(Tcl_GetObjResult(interp));
}
}
void allocInterp()
{
Tcl_FindExecutable(NULL);
interp = Tcl_CreateInterp();
}
void destroyInterp()
{
Tcl_DeleteInterp(interp);
}
The implementation of allocInterp() and destroyInterp() is very naive, no error checking is done.
Finally for the Dll the stdafx.h file ties it all together like this:
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
// TODO: reference additional headers your program requires here
#include <tcl.h>
#include "TclEmbeddedInDll.h"
What is the difference in linking on various operating system?
For example the following code produces a linker error on Windows (compiled both with Vs2010 and gcc), but compiles successfully on Linux (Ubuntu,gcc):
extern int foo
int main() {
foo=1;
}
Gcc command:
gcc -shared filename.cpp
If you are trying to compile it as a windows shared library you need something like (code stolen from Wikipedia!) :-
#include <windows.h>
// DLL entry function (called on load, unload, ...)
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
// Exported function - adds two numbers
extern "C" __declspec(dllexport) double AddNumbers(double a, double b)
{
return a + b;
}
Windows shared modules (DLLs) require a DllMain entry point (executed the first time the module is loaded) and function names need to be exported via the declspec gobledygook before they can be used by another program.