Linking C++ static libraries with QtCreator - linker

I am using QtCreator 4.2.1 with MinGW4.9.2 32 bit compiler (Win 8.1 platform).
My pgi project only has a main.cpp which uses objects and functions belonging to my PhygenicLib static library. This static library in turn relies on functions and classes and constants defined in the Mathlib2 static library along with the eigen3 template library (template-based, only headers, no .cpp!) found on the web.
Here is the PhygenicLib.pro which compiles nicely into the libPhygenicLib.a file (debug mode):
QT -= gui
TARGET = PhygenicLib
TEMPLATE = lib
CONFIG += staticlib
DEFINES += QT_DEPRECATED_WARNINGS
INCLUDEPATH += d:/Qt-apps/Mathlib2 d:/Qt-apps/eigen3
SOURCES += phygenic.cpp \ cinemeca_pg.cpp \ mecagen.cpp \
rc_perf_pg.cpp \ xploit_pg.cpp \ utilities.cpp \
Analex.cpp \ Anasyntax.cpp
HEADERS += phygenic.h \ cinemeca_pg.h \ mecagen.h \ rc_perf_pg.h \
xploit_pg.h \ utilities.h \ Analex.h \ Anasyntax.h
unix { target.path = /usr/lib
INSTALLS += target }
Here is the pgi.pro (debug mode):
QT += core
QT -= gui
CONFIG += c++11
TARGET = pgi
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
LIBS += d:/Qt-apps/build-Mathlib2-Desktop_Qt_5_5_1_MinGW_32bit-Release/release/libMathlib2.a \
d:/Qt-apps/build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/debug/libPhygenicLib.a
INCLUDEPATH += d:/Qt-apps/PhygenicLib \
d:/Qt-apps/Mathlib2 \
d:/Qt-apps/eigen3
SOURCES += main.cpp
HEADERS +=
DEFINES += QT_DEPRECATED_WARNINGS
Note that Mathlib2 is compiled in release mode not debug mode, but that should not be a problem (?). Now, here is my main.cpp:
#include <iostream>
#include "Anasyntax.h"
using namespace std;
int main(int, char**)
{
ofstream fdmp("test"+extFichier[DMP]);
try { // bloc try
fdmp<<"--- test généraux interpréteur ---"<<endl ;
Anasyntax asynt ;
asynt.interprete(fdmp);
} catch (std::exception const& err) {
fdmp << err.what()<< endl<<" - Erreur fatale." << endl ;
cerr << err.what()<< endl<<"- Erreur fatale." << endl ;
}
cout<<'\a' ;
fdmp << "--- FIN DE L'EXECUTION --- "<< std::endl;
fdmp.close() ;
return EXIT_SUCCESS ;
}
#include Anasyntax.h is pulling all the headers from the various libraries wherever required (root of the include tree, so to say).
Now when I try to compile and link the project, I get the following diagnostic (sorry the language, QtCreator installed in French):
14:48:12: Exécution des étapes pour le projet pgi...
14:48:12: Débute : "C:\Qt\Tools\mingw492_32\bin\mingw32-make.exe" clean
C:/Qt/Tools/mingw492_32/bin/mingw32-make -f Makefile.Debug clean
mingw32-make[1]: Entering directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
del debug\main.o
Impossible de trouver D:\Qt-apps\build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug\debug\main.o
mingw32-make[1]: Leaving directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
C:/Qt/Tools/mingw492_32/bin/mingw32-make -f Makefile.Release clean
mingw32-make[1]: Entering directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
del release\main.o
Impossible de trouver D:\Qt-apps\build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug\release\main.o
mingw32-make[1]: Leaving directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
14:48:13: Le processus "C:\Qt\Tools\mingw492_32\bin\mingw32-make.exe" s'est terminé normalement.
14:48:13: Configuration inchangée, étape qmake sautée.
14:48:13: Débute : "C:\Qt\Tools\mingw492_32\bin\mingw32-make.exe"
C:/Qt/Tools/mingw492_32/bin/mingw32-make -f Makefile.Debug
mingw32-make[1]: Entering directory 'D:/Qt-apps/build-pgi-Desktop_Qt_5_5_1_MinGW_32bit-Debug'
g++ -c -pipe -fno-keep-inline-dllexport -g -std=c++0x -frtti -Wall -Wextra -fexceptions -mthreads -DUNICODE -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_CORE_LIB -I..\pgi -I. -Id:\Qt-apps\PhygenicLib -Id:\Qt-apps\Mathlib2 -Id:\Qt-apps\eigen3 -IC:\Qt\5.5\mingw492_32\include -IC:\Qt\5.5\mingw492_32\include\QtCore -Idebug -IC:\Qt\5.5\mingw492_32\mkspecs\win32-g++ -o debug\main.o ..\pgi\main.cpp
g++ -Wl,-subsystem,console -mthreads -o debug\pgi.exe debug/main.o d:/Qt-apps/build-Mathlib2-Desktop_Qt_5_5_1_MinGW_32bit-Release/release/libMathlib2.a d:/Qt-apps/build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/debug/libPhygenicLib.a -LC:/Qt/5.5/mingw492_32/lib -lQt5Cored
d:/Qt-apps/build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/debug/libPhygenicLib.a(mecagen.o): In function `ZN10Solide_dyn4calcEv':
D:\Qt-apps\build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/../PhygenicLib/mecagen.cpp:220: undefined reference to `rotation_WM(V3)'
D:\Qt-apps\build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/../PhygenicLib/mecagen.cpp:221: undefined reference to `Mat_transp_3x3(Mat3x3)'
and so on, and so on...
It looks like g++ is trying to link mecagen.cpp (which is already compiled as part of libPhygenicLib.a) and not finding references to symbols which are part of Mathlib2 library (includepath= d:/Qt-apps/Mathlib2, LIB= d:/.../release/libMathlib2.a).
I found a walk-around solution: just inserting in the main.cpp a reference to any Mathlib2 object or function, such as function coupure hereafter, enables to build and run the project executable without any warning/error:
int main(int, char**)
{
ofstream fdmp("test"+extFichier[DMP]);
coupure(0.,0.,0.) ; // any object or function from Mathlib2 works as well!
try { // etc...
So the problem is overcome but it drives me nuts not to understand why it won't work without inserting a useless code line... I have to say I was too lazy to ever learn about g++, make, qmake and makeFile sort of things; just pushing QtCreator buttons is so easy and comfortable...

I read again some posts with somewhat similar issues and found one comment regarding the order of declaring static libraries which may be critical in some cases.
And indeed, if I declared the LIBS in my pgi.pro in the reverse order, as follows:
LIBS += d:/Qt-apps/build-PhygenicLib-Desktop_Qt_5_5_1_MinGW_32bit-Debug/debug/libPhygenicLib.a \
d:/Qt-apps/build-Mathlib2-Desktop_Qt_5_5_1_MinGW_32bit-Release/release/libMathlib2.a
the project then compiles and links fine without any extraneous reference to a Mathlib2 object or function in the main.cpp. I got the lesson and will now take care of declaring LIBS in the appropriate order...

Related

Trying to add SDL2_mixer and SDL2 as ExternalProject's in CMake

I am currently trying to fetch SDL2 and SDL2_mixer as external projects in my CMake project.
SDL2 seems to work fine, but I cannot make SDL2_mixer compile. It fails when trying to link the playwav binary. The problem are the CFLAGS and LDFLAGS variables in the ExternalProject_Add. The same problem occurs when adding these variables while building from the command line without CMake.
Here is my code so far:
cmake_minimum_required(VERSION 2.8)
include(ExternalProject)
project(sdl2_test)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
set(LIBS_DIR ${PROJECT_BINARY_DIR}/libs)
set(SDL2_VER "2.0.8")
set(SDL2_MIXER_VER "2.0.2")
# SDL library
ExternalProject_Add(sdl2_project
URL http://www.libsdl.org/release/SDL2-${SDL2_VER}.tar.gz
PREFIX ${LIBS_DIR}/SDL2
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(sdl2_project SOURCE_DIR)
ExternalProject_Get_Property(sdl2_project BINARY_DIR)
set(SDL2_SRC ${SOURCE_DIR})
set(SDL2_BIN ${BINARY_DIR})
file(GLOB SDL2_INCLUDE "${SDL2_SRC}/include/*")
file(COPY ${SDL2_INCLUDE} DESTINATION ${SDL2_BIN}/include/)
# SDL_mixer library
ExternalProject_Add(sdl2_mixer_project
URL https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-${SDL2_MIXER_VER}.tar.gz
DEPENDS sdl2_project
PREFIX ${LIBS_DIR}/SDL2_mixer
CONFIGURE_COMMAND
SDL2_CONFIG=${SDL2_BIN}/sdl2-config
CFLAGS=-I${SDL2_BIN}/include
LDFLAGS=-L${SDL2_BIN}
#LIBS=-ldl
<SOURCE_DIR>/configure
--prefix=<INSTALL_DIR>
--enable-shared=no
#--with-sdl-prefix=${SDL2_BIN}
--disable-sdltest
BUILD_COMMAND make
INSTALL_COMMAND ""
)
#file(GLOB SDL2_INCLUDE "${SDL2_SRC}/include/*")
#file(COPY ${SDL2_INCLUDE} DESTINATION ${SDL2_BIN}/include/)
ExternalProject_Get_Property(sdl2_mixer_project SOURCE_DIR)
ExternalProject_Get_Property(sdl2_mixer_project BINARY_DIR)
set(SDL2_MIXER_SRC ${SOURCE_DIR})
set(SDL2_MIXER_BIN ${BINARY_DIR})
include_directories(${SDL2_SRC}/include)
include_directories(${SDL2_MIXER_SRC}/include)
set(SOURCE sdl2test.cc)
add_executable(sdl_test ${SOURCE})
add_dependencies(sdl_test sdl2_project sdl2_mixer_project)
target_link_libraries(sdl_test ${SDL2_BIN}/libSDL2.a)
The file sdl2test.cc is just a dummy file:
#include <iostream>
int main()
{
std::cout << "Hooray" << std::endl;
return 0;
}

How to make visual studio compile exports for C DLL the way MinGW does [duplicate]

With VS2005, I want to create a DLL and automatically export all symbols without adding __declspec(dllexport) everywhere, and without hand-creating .def files. Is there a way to do this?
Short answer
You can do it with help of the new version of the CMake (any version cmake-3.3.20150721-g9cd2f-win32-x86.exe or higher).
Currently it's in the dev branch.
Later, the feature will be added in the release version of the cmake-3.4.
Link to the cmake dev:
cmake_dev
Link to an article which describe the technic:
Create dlls on Windows without declspec() using new CMake export all feature
Link to an example project:
cmake_windows_export_all_symbols
Long answer
Caution:
All information below is related to the MSVC compiler or Visual Studio.
If you use other compilers like gcc on Linux or MinGW gcc compiler on Windows you don't have linking errors due to not exported symbols, because gcc compiler export all symbols in a dynamic library (dll) by default instead of MSVC or Intel windows compilers.
In windows you have to explicitly export symbol from a dll.
More info about this is provided by links:
Exporting from a DLL
HowTo: Export C++ classes from a DLL
So if you want to export all symbols from dll with MSVC (Visual Studio compiler) you have two options:
Use the keyword __declspec(dllexport) in the class/function's definition.
Create a module definition (.def) file and use the .def file when building the DLL.
1. Use the keyword __declspec(dllexport) in the class/function's definition
1.1. Add "__declspec(dllexport) / __declspec(dllimport)" macros to a class or method you want to use. So if you want to export all classes you should add this macros to all of them
More info about this is provided by link:
Exporting from a DLL Using __declspec(dllexport)
Example of usage (replace "Project" by real project name):
// ProjectExport.h
#ifndef __PROJECT_EXPORT_H
#define __PROJECT_EXPORT_H
#ifdef USEPROJECTLIBRARY
#ifdef PROJECTLIBRARY_EXPORTS
#define PROJECTAPI __declspec(dllexport)
#else
#define PROJECTAPI __declspec(dllimport)
#endif
#else
#define PROJECTAPI
#endif
#endif
Then add "PROJECTAPI" to all classes.
Define "USEPROJECTLIBRARY" only if you want export/import symbols from dll.
Define "PROJECTLIBRARY_EXPORTS" for the dll.
Example of class export:
#include "ProjectExport.h"
namespace hello {
class PROJECTAPI Hello {}
}
Example of function export:
#include "ProjectExport.h"
PROJECTAPI void HelloWorld();
Caution: don't forget to include "ProjectExport.h" file.
1.2. Export as C functions.
If you use C++ compiler for compilation code is written on C, you could add extern "C" in front of a function to eliminate name mangling
More info about C++ name mangling is provided by link:
Name Decoration
Example of usage:
extern "C" __declspec(dllexport) void HelloWorld();
More info about this is provided by link:
Exporting C++ Functions for Use in C-Language Executables
2. Create a module definition (.def) file and use the .def file when building the DLL
More info about this is provided by link:
Exporting from a DLL Using DEF Files
Further I describe three approach about how to create .def file.
2.1. Export C functions
In this case you could simple add function declarations in the .def file by hand.
Example of usage:
extern "C" void HelloWorld();
Example of .def file (__cdecl naming convention):
EXPORTS
_HelloWorld
2.2. Export symbols from static library
I tried approach suggested by "user72260".
He said:
Firstly, you could create static library.
Then use "dumpbin /LINKERMEMBER" to export all symbols from static library.
Parse the output.
Put all results in a .def file.
Create dll with the .def file.
I used this approach, but it's not very convinient to always create two builds (one as a static and the other as a dynamic library). However, I have to admit, this approach really works.
2.3. Export symbols from .obj files or with help of the CMake
2.3.1. With CMake usage
Important notice: You don't need any export macros to a classes or functions!
Important notice: You can't use /GL (Whole Program Optimization) when use this approach!
Create CMake project based on the "CMakeLists.txt" file.
Add the following line to the "CMakeLists.txt" file:
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
Then create Visual Studio project with help of "CMake (cmake-gui)".
Compile the project.
Example of usage:
Root folder
CMakeLists.txt (Root folder)
cmake_minimum_required(VERSION 2.6)
project(cmake_export_all)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(dir ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}/bin")
set(SOURCE_EXE main.cpp)
include_directories(foo)
add_executable(main ${SOURCE_EXE})
add_subdirectory(foo)
target_link_libraries(main foo)
main.cpp (Root folder)
#include "foo.h"
int main() {
HelloWorld();
return 0;
}
Foo folder (Root folder / Foo folder)
CMakeLists.txt (Foo folder)
project(foo)
set(SOURCE_LIB foo.cpp)
add_library(foo SHARED ${SOURCE_LIB})
foo.h (Foo folder)
void HelloWorld();
foo.cpp (Foo folder)
#include <iostream>
void HelloWorld() {
std::cout << "Hello World!" << std::endl;
}
Link to the example project again:
cmake_windows_export_all_symbols
CMake uses the different from "2.2. Export symbols from static library" approach.
It does the following:
1) Create "objects.txt" file in the build directory with information of .obj files are used in a dll.
2) Compile the dll, that is create .obj files.
3) Based on "objects.txt" file information extract all symbols from .obj file.
Example of usage:
DUMPBIN /SYMBOLS example.obj > log.txt
More info about this is provided by link:
/SYMBOLS
4) Parse extracted from .obj file information.
In my opinion I would use calling convection, for example "__cdecl/__fastcall", "SECTx/UNDEF" symbol field (the third column), "External/Static" symbol field (the fifth column), "??", "?" information for parsing an .obj files.
I don't know how exactly CMake parse an .obj file.
However, CMake is open source, so you could find out if it's interested for you.
Link to the CMake project:
CMake_github
5) Put all exported symbols in a .def file.
6) Link a dll with usage of a .def created file.
Steps 4)-5), that is parse .obj files and create a .def file before linking and using the .def file CMake does with help of "Pre-Link event".
While "Pre-Link event" fires you could call any program you want.
So in case of "CMake usage" "Pre-Link event" call the CMake with the following information about where to put the .def file and where the "objects.txt" file and with argument "-E __create_def".
You could check this information by creating CMake Visusal Studio project with "set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)" and then check the ".vcxproj" project file for dll.
If you try to compile a project without "set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)" or with "set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)" you will get linking errors, due to the fact that symbols are not exported from a dll.
More info about this is provided by link:
Understanding Custom Build Steps and Build Events
2.3.2. Without CMake usage
You simple could create a small program for parsing .obj file by youself without CMake usege. Hovewer, I have to admit that CMake is very usefull program especially for cross-platform development.
It can be done...
The way we do it here is to use the /DEF option of the linker to pass a "module definition file" containing a list of our exports. I see from your question that you know about these files. However, we do not do it by hand. The list of exports itself is created by the dumpbin /LINKERMEMBER command, and manipulating the output via a simple script to the format of a module definition file.
It is a lot of work to setup, but it allows us to compile code created without dllexport declarations for Unix on Windows.
I want to create a DLL and automatically export all symbols without adding __declspec(dllexport) everywhere and without hand-creating .def files. Is threre a way to do this?
This is a late answer, but it provides the details for Maks's answer in Section (2). It also avoids scripts and uses a C++ program called dump2def. The source code for dump2def is below.
Finally, the steps below assume you are working from a Visual Studio Developer Prompt, which is a Windows Terminal where vcvarsall.bat has been run. You need to ensure the build tools like cl.exe, lib.exe, link.exe and nmake.exe are on-path.
More info about this is provided by link:
Exporting from a DLL Using DEF
Files
...
The instruction below use:
static.lib - static library archive (*.a file on Linux)
dynamic.dll - dynamic library (*.so file on Linux)
import.lib - dynamic library (import library on Windows)
Also note that though you are exporting everything from the DLL, clients still must use declspec(dllimport) on all symbols (classes, functions and data) that they use. Also see on MSDN.
First, take your objects and create a static archive:
AR = lib.exe
ARFLAGS = /nologo
CXX_SRCS = a.cpp b.cpp c.cpp ...
LIB_OBJS = a.obj b.obj c.obj ...
static.lib: $(LIB_OBJS)
$(AR) $(ARFLAGS) $(LIB_OBJS) /out:$#
Second, run dumpbin.exe /LINKERMEMEBER on the archive to create a *.dump file:
dynamic.dump:
dumpbin /LINKERMEMBER static.lib > dynamic.dump
Third, run dump2def.exe on the *.dump file to produce the *.def file. The source code for dump2def.exe is below.
dynamic.def: static.lib dynamic.dump
dump2def.exe dynamic.dump dynamic.def
Fourth, build the DLL:
LD = link.exe
LDFLAGS = /OPT:REF /MACHINE:X64
LDLIBS = kernel32.lib
dynamic.dll: $(LIB_OBJS) dynamic.def
$(LD) $(LDFLAGS) /DLL /DEF:dynamic.def /IGNORE:4102 $(LIB_OBJS) $(LDLIBS) /out:$#
/IGNORE:4102 is used to avoid this warning. It is expected in this case:
dynamic.def : warning LNK4102: export of deleting destructor 'public: virtual v
oid * __ptr64 __cdecl std::exception::`scalar deleting destructor'(unsigned int)
__ptr64'; image may not run correctly
When the dynamic.dll recipe is invoked, it creates a dynamic.lib import file and dynamic.exp file, too:
> cls && nmake /f test.nmake dynamic.dll
...
Creating library dynamic.lib and object dynamic.exp
And:
C:\Users\Test\testdll>dir *.lib *.dll *.def *.exp
Volume in drive C is Windows
Volume Serial Number is CC36-23BE
Directory of C:\Users\Test\testdll
01/06/2019 08:33 PM 71,501,578 static.lib
01/06/2019 08:33 PM 11,532,052 dynamic.lib
Directory of C:\Users\Test\testdll
01/06/2019 08:35 PM 5,143,552 dynamic.dll
Directory of C:\Users\Test\testdll
01/06/2019 08:33 PM 1,923,070 dynamic.def
Directory of C:\Users\Test\testdll
01/06/2019 08:35 PM 6,937,789 dynamic.exp
5 File(s) 97,038,041 bytes
0 Dir(s) 139,871,186,944 bytes free
Gluing it together here is what the Nmake makefile looks like. It is part of a real Nmake file:
all: test.exe
test.exe: pch.pch static.lib $(TEST_OBJS)
$(LD) $(LDFLAGS) $(TEST_OBJS) static.lib $(LDLIBS) /out:$#
static.lib: $(LIB_OBJS)
$(AR) $(ARFLAGS) $(LIB_OBJS) /out:$#
dynamic.map:
$(LD) $(LDFLAGS) /DLL /MAP /MAPINFO:EXPORTS $(LIB_OBJS) $(LDLIBS) /out:dynamic.dll
dynamic.dump:
dumpbin.exe /LINKERMEMBER static.lib /OUT:dynamic.dump
dynamic.def: static.lib dynamic.dump
dump2def.exe dynamic.dump
dynamic.dll: $(LIB_OBJS) dynamic.def
$(LD) $(LDFLAGS) /DLL /DEF:dynamic.def /IGNORE:4102 $(LIB_OBJS) $(LDLIBS) /out:$#
clean:
$(RM) /F /Q pch.pch $(LIB_OBJS) pch.obj static.lib $(TEST_OBJS) test.exe *.pdb
And here is the source code for dump2def.exe:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <set>
typedef std::set<std::string> SymbolMap;
void PrintHelpAndExit(int code)
{
std::cout << "dump2def - create a module definitions file from a dumpbin file" << std::endl;
std::cout << " Written and placed in public domain by Jeffrey Walton" << std::endl;
std::cout << std::endl;
std::cout << "Usage: " << std::endl;
std::cout << " dump2def <infile>" << std::endl;
std::cout << " - Create a def file from <infile> and write it to a file with" << std::endl;
std::cout << " the same name as <infile> but using the .def extension" << std::endl;
std::cout << " dump2def <infile> <outfile>" << std::endl;
std::cout << " - Create a def file from <infile> and write it to <outfile>" << std::endl;
std::exit(code);
}
int main(int argc, char* argv[])
{
// ******************** Handle Options ******************** //
// Convenience item
std::vector<std::string> opts;
for (size_t i=0; i<argc; ++i)
opts.push_back(argv[i]);
// Look for help
std::string opt = opts.size() < 3 ? "" : opts[1].substr(0,2);
if (opt == "/h" || opt == "-h" || opt == "/?" || opt == "-?")
PrintHelpAndExit(0);
// Add <outfile> as needed
if (opts.size() == 2)
{
std::string outfile = opts[1];
std::string::size_type pos = outfile.length() < 5 ? std::string::npos : outfile.length() - 5;
if (pos == std::string::npos || outfile.substr(pos) != ".dump")
PrintHelpAndExit(1);
outfile.replace(pos, 5, ".def");
opts.push_back(outfile);
}
// Check or exit
if (opts.size() != 3)
PrintHelpAndExit(1);
// ******************** Read MAP file ******************** //
SymbolMap symbols;
try
{
std::ifstream infile(opts[1].c_str());
std::string::size_type pos;
std::string line;
// Find start of the symbol table
while (std::getline(infile, line))
{
pos = line.find("public symbols");
if (pos == std::string::npos) { continue; }
// Eat the whitespace after the table heading
infile >> std::ws;
break;
}
while (std::getline(infile, line))
{
// End of table
if (line.empty()) { break; }
std::istringstream iss(line);
std::string address, symbol;
iss >> address >> symbol;
symbols.insert(symbol);
}
}
catch (const std::exception& ex)
{
std::cerr << "Unexpected exception:" << std::endl;
std::cerr << ex.what() << std::endl;
std::cerr << std::endl;
PrintHelpAndExit(1);
}
// ******************** Write DEF file ******************** //
try
{
std::ofstream outfile(opts[2].c_str());
// Library name, cryptopp.dll
std::string name = opts[2];
std::string::size_type pos = name.find_last_of(".");
if (pos != std::string::npos)
name.erase(pos);
outfile << "LIBRARY " << name << std::endl;
outfile << "DESCRIPTION \"Crypto++ Library\"" << std::endl;
outfile << "EXPORTS" << std::endl;
outfile << std::endl;
outfile << "\t;; " << symbols.size() << " symbols" << std::endl;
// Symbols from our object files
SymbolMap::const_iterator it = symbols.begin();
for ( ; it != symbols.end(); ++it)
outfile << "\t" << *it << std::endl;
}
catch (const std::exception& ex)
{
std::cerr << "Unexpected exception:" << std::endl;
std::cerr << ex.what() << std::endl;
std::cerr << std::endl;
PrintHelpAndExit(1);
}
return 0;
}
I've written a small program to parse the output of "dumpbin /linkermember" on the .lib file. I have upwards of 8,000 function references to export from one DLL.
The problem with doing it on a DLL is that you have to link the DLL without the exported definitions once to create the .lib file, then generate the .def which means you now have to relink the DLL again with the .def file to actually have the references exported.
Working with static libraries is easier. Compile all your sources into static libs, run dumbin, generate a .def with your little program, then link the libs together into a DLL now that the export names are available.
Unfortunately my company won't allow me to show you the source. The work involved is recognizing which "public symbols" in the dump output are not needed in your def file. You have to throw away a lot of those references, NULL_IMPORT_DESCRIPTOR, NULL_THUNK_DATA, __imp*, etc.
Thanks #Maks for the detailed answer.
Below is an example of what I used in Pre-Link event to generate def file from obj. I hope it will be helpful for someone.
dumpbin /SYMBOLS $(Platform)\$(Configuration)\mdb.obj | findstr /R "().*External.*mdb_.*" > $(Platform)\$(Configuration)\mdb_symbols
(echo EXPORTS & for /F "usebackq tokens=2 delims==|" %%E in (`type $(Platform)\$(Configuration)\mdb_symbols`) do #echo %%E) > $(Platform)\$(Configuration)\lmdb.def
Basically I just took one of objects (mdb.obj) and grepped mdb_* functions. Then parsed output to keep just names taking into account amount of spaces for indentation (one after splitting into tokens and another in echo. I don't know if it's matter though).
Real world script probably will kind of more complex though.
Perhaps somebody finds useful my Python script for converting .dump to .def.
import sys, os
functions = []
startPoint = False
# Exclude standard API like sprintf to avoid multiple definition link error
excluded_functions = [ 'sprintf', 'snprintf', 'sscanf', 'fprintf' ]
if len(sys.argv) < 2:
print('Usage: %s <Input .dump file> <Output .def file>.' % sys.argv[0])
print('Example: %s myStaticLib.dump exports.def' % sys.argv[0])
sys.exit(1)
print('%s: Processing %s to %s' % (sys.argv[0], sys.argv[1], sys.argv[2]))
fin = open(sys.argv[1], 'r')
lines = fin.readlines()
fin.close()
# Reading
for l in lines:
l_str = l.strip()
if (startPoint == True) and (l_str == 'Summary'): # end point
break
if (startPoint == False) and ("public symbols" in l_str):
startPoint = True
continue
if (startPoint == True) and l_str is not '':
funcName = l_str.split(' ')[-1]
if funcName not in excluded_functions:
functions.append(" " + funcName)
# Writing
fout = open(sys.argv[2], 'w')
fout.write('EXPORTS\n')
for f in functions:
fout.write('%s\n' % f)
fout.close()
With this script you can get the .def file for your .lib in two steps:
dumpbin /LINKERMEMBER:1 myStaticLib.lib > myExports.dump
python dump2def.py myExports.dump myExports.def
No, you will need a macro that resolves to __declspec(dllexport) when it's included by the .cpp file that implements the exported functions, and resolves to __declspec(dllimport) otherwise.

Inlining code from distinct files in LLVM

I need to inline a couple of functions during runtime using LLVM. The complication is that such functions are defined in separate bitcode files.
During runtime, I need to generate code for a function such as
void snippet1(); //declaring that snippet1 and 2 are defined in snippet1.c and snippet2.c
void snippet2();
void combo12(){
snippet1();
snippet1();
snippet2();
snippet2();
}
From separate LLVM bitcode files compiled from combo12.c, snippet1.c and snippet2.c. The thing is, I need to inline all calls to snippet1 and snippet2 in combo12. I tried to do this using the following code (main.cpp):
OwningPtr<MemoryBuffer> MB, MB2, MB3;
Module *M1, *M2, *MC12, *MOUT;
LLVMContext Context;
std::string ErrorStr;
MemoryBuffer::getFile("snippet1.bc", MB);
M1 = ParseBitcodeFile(MB.get(), Context);
MemoryBuffer::getFile("snippet2.bc", MB2);
M2 = ParseBitcodeFile(MB2.get(), Context);
MemoryBuffer::getFile("combo12.bc", MB3);
MC12 = ParseBitcodeFile(MB3.get(), Context);
Linker* L;
L = new Linker("testprog", M1, 0);
L->setFlags(llvm::Linker::Verbose);
if (!(L->LinkInModule(M2, &ErrorStr)))
std::cout << ErrorStr;
if (!(L->LinkInModule(MC12, &ErrorStr)))
std::cout << ErrorStr;
MOUT = L->getModule();
MOUT->dump();
PassManager *PM;
PM = new PassManager();
PM->add(createInternalizePass(true));
PM->add(createAlwaysInlinerPass());
if (PM->run(*MOUT)){
std::cout << "\n\n\nCode was altered!\n\n\n" << std::endl;
MOUT->dump();
std::cout << "\n\n ALTERED BEAST \n\n" << std::endl;
}
snippet1.c:
//What this function does is irrelevant
#include "post_opt.h" //contains the struct exstr declaration
extern struct exstr a;
inline void snippet1() __attribute((always_inline));
void snippet1(){
int x, y;
a.b = 10;
x = 2;
if(x < a.a){
y = x + 1;
}
}
I compiled snippet1.c, snippet2.c and combo12.c using
clang -c -emit-llvm snippet1.c -o snippet1.bc -O0
clang -c -emit-llvm snippet2.c -o snippet2.bc -O0
clang -c -emit-llvm combo12.c -o combo12.bc -O0
And main.cpp with
clang++ -g main.cpp `llvm-config --cppflags --ldflags --libs --cppflags --ldflags --libs core jit native linker transformutils ipo bitreader` -O0 -o main
When I run ./main, It does not inline the snippet code, although I explicitly mark the function with the always_inline attribute, and use the AlwaysInline pass. It never prints ALTERED BEAST onscreen.
Why does this happen? I thought that, by linking all modules together and applying an IPO pass (AlwaysInline) this would work out ok.
Thanks for any insight!
Inlining occurs during compilation and not linking and inlining is only available with completly defined function at compilation time. So, it is impossible to inlining function from other files ('cause when compiling a file, other one are ignored). The only solution is to define function in every files which need inline thoses functions.

Linking against older symbol version in a .so file

Using gcc and ld on x86_64 linux I need to link against a newer version of a library (glibc 2.14) but the executable needs to run on a system with an older version (2.5). Since the only incompatible symbol is memcpy (needing memcpy#GLIBC_2.2.5 but the library providing memcpy#GLIBC_2.14), I would like to tell the linker that instead of taking the default version for memcpy, it should take an old version I specify.
I found a quite arkward way to do it: simply specify a copy of the old .so file at the linker command line. This works fine, but I don't like the idea of having multiple .so files (I could only make it work by specifying all old libraries I link to that also have references to memcpy) checked into the svn and needed by my build system.
So I am searching for a way to tell the linker to take the old versioned symbol.
Alternatives that don't work (well) for me are:
Using asm .symver (as seen on Web Archive of Trevor Pounds' Blog) since this would require me to make sure the symver is before all the code that is using memcpy, which would be very hard (complex codebase with 3rd party code)
Maintaining a build environment with the old libraries; simply because I want to develop on my desktop system and it would be a pita to sync stuff around in our network.
When thinking about all the jobs a linker does, it doesn't seem like a hard thing to imlpement, after all it has some code to figure out the default version of a symbol too.
Any other ideas that are on the same complexity level as a simple linker command line (like creating a simple linker script etc.) are welcome too, as long as they are not weird hacks like editing the resulting binary...
edit:
To conserve this for the future readers, additionally to the below ideas I found the option --wrap to the linker, which might be useful sometimes too.
I found the following working solution. First create file memcpy.c:
#include <string.h>
/* some systems do not have newest memcpy##GLIBC_2.14 - stay with old good one */
asm (".symver memcpy, memcpy#GLIBC_2.2.5");
void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
return memcpy(dest, src, n);
}
No additional CFLAGS needed to compile this file. Then link your program with -Wl,--wrap=memcpy.
Just link memcpy statically - pull memcpy.o out of libc.a ar x /path/to/libc.a memcpy.o (whatever version - memcpy is pretty much a standalone function) and include it in your final link. Note that static linking may complicate licensing issues if your project is distributed to the public and not open-source.
Alternatively, you could simply implement memcpy yourself, though the hand-tuned assembly version in glibc is likely to be more efficient
Note that memcpy#GLIBC_2.2.5 is mapped to memmove (old versions of memcpy consistently copied in a predictable direction, which led to it sometimes being misused when memmove should have been used), and this is the only reason for the version bump - you could simply replace memcpy with memmove in your code for this specific case.
Or you could go to static linking, or you could ensure that all systems on your network have the same or better version than your build machine.
I had a similar issue. A third party library we use needs the old memcpy#GLIBC_2.2.5. My solution is an extended approach #anight posted.
I also warp the memcpy command, but i had to use a slightly different approach, since the solution #anight posted did not work for me.
memcpy_wrap.c:
#include <stddef.h>
#include <string.h>
asm (".symver wrap_memcpy, memcpy#GLIBC_2.2.5");
void *wrap_memcpy(void *dest, const void *src, size_t n) {
return memcpy(dest, src, n);
}
memcpy_wrap.map:
GLIBC_2.2.5 {
memcpy;
};
Build the wrapper:
gcc -c memcpy_wrap.c -o memcpy_wrap.o
Now finally when linking the program add
-Wl,--version-script memcpy_wrap.map
memcpy_wrap.o
so that you will end up with something like:
g++ <some flags> -Wl,--version-script memcpy_wrap.map <some .o files> memcpy_wrap.o <some libs>
I had a similar problem. Trying to install some oracle components on RHEL 7.1, I got this:
$ gcc -o /some/oracle/bin/foo .... -L/some/oracle/lib ...
/some/oracle/lib/libfoo.so: undefined reference to `memcpy#GLIBC_2.14'
It seems that (my) RHEL's glibc only defines memcpy#GLIBC_2.2.5:
$ readelf -Ws /usr/lib/x86_64-redhat-linux6E/lib64/libc_real.so | fgrep memcpy#
367: 000000000001bfe0 16 FUNC GLOBAL DEFAULT 8 memcpy##GLIBC_2.2.5
1166: 0000000000019250 16 FUNC WEAK DEFAULT 8 wmemcpy##GLIBC_2.2.5
So, I managed to get around this, by first creating a memcpy.c file without wrapping, as follows:
#include <string.h>
asm (".symver old_memcpy, memcpy#GLIBC_2.2.5"); // hook old_memcpy as memcpy#2.2.5
void *old_memcpy(void *, const void *, size_t );
void *memcpy(void *dest, const void *src, size_t n) // then export memcpy
{
return old_memcpy(dest, src, n);
}
and a memcpy.map file that exports our memcpy as memcpy#GLIBC_2.14:
GLIBC_2.14 {
memcpy;
};
I then compiled my own memcpy.c into a shared lib like this:
$ gcc -shared -fPIC -c memcpy.c
$ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc
, moved libmemcpy-2.14.so into /some/oracle/lib (pointed to by -L arguments in my linking), and linked again by
$ gcc -o /some/oracle/bin/foo .... -L/some/oracle/lib ... /some/oracle/lib/libmemcpy-2.14.so -lfoo ...
(which compiled without errors) and verified it by:
$ ldd /some/oracle/bin/foo
linux-vdso.so.1 => (0x00007fff9f3fe000)
/some/oracle/lib/libmemcpy-2.14.so (0x00007f963a63e000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f963a428000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f963a20c000)
librt.so.1 => /lib64/librt.so.1 (0x00007f963a003000)
libc.so.6 => /lib64/libc.so.6 (0x00007f9639c42000)
/lib64/ld-linux-x86-64.so.2 (0x00007f963aa5b000)
This worked for me. I hope it does it for you, too.
I'm clearly a little late responding to this but I recently upgraded (more reasons to never upgrade) my Linux OS to XUbuntu 14.04 which came with the new libc. I compile a shared library on my machine which is used by clients who, for whatever legitimate reasons, have not upgraded their environment from 10.04. The shared library I compiled no longer loaded in their environment because gcc put a dependency on memcpy glibc v. 2.14 (or higher). Let's leave aside the insanity of this. The workaround across my whole project was three fold:
added to my gcc cflags: -include glibc_version_nightmare.h
created the glibc_version_nightmare.h
created a perl script to verify the symbols in the .so
glibc_version_nightmare.h:
#if defined(__GNUC__) && defined(__LP64__) /* only under 64 bit gcc */
#include <features.h> /* for glibc version */
#if defined(__GLIBC__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 14)
/* force mempcy to be from earlier compatible system */
__asm__(".symver memcpy,memcpy#GLIBC_2.2.5");
#endif
#undef _FEATURES_H /* so gets reloaded if necessary */
#endif
perl script fragment:
...
open SYMS, "nm $flags $libname |";
my $status = 0;
sub complain {
my ($symbol, $verstr) = #_;
print STDERR "ERROR: $libname $symbol requires $verstr\n";
$status = 1;
}
while (<SYMS>) {
next unless /\#\#GLIBC/;
chomp;
my ($symbol, $verstr) = (m/^\s+.\s(.*)\#\#GLIBC_(.*)/);
die "unable to parse version from $libname in $_\n"
unless $verstr;
my #ver = split(/\./, $verstr);
complain $symbol, $verstr
if ($ver[0] > 2 || $ver[1] > 10);
}
close SYMS;
exit $status;
Minimal runnable self contained example
GitHub upstream.
main.c
#include <assert.h>
#include <stdlib.h>
#include "a.h"
#if defined(V1)
__asm__(".symver a,a#LIBA_1");
#elif defined(V2)
__asm__(".symver a,a#LIBA_2");
#endif
int main(void) {
#if defined(V1)
assert(a() == 1);
#else
assert(a() == 2);
#endif
return EXIT_SUCCESS;
}
a.c
#include "a.h"
__asm__(".symver a1,a#LIBA_1");
int a1(void) {
return 1;
}
/* ## means "default version". */
__asm__(".symver a2,a##LIBA_2");
int a2(void) {
return 2;
}
a.h
#ifndef A_H
#define A_H
int a(void);
#endif
a.map
LIBA_1{
global:
a;
local:
*;
};
LIBA_2{
global:
a;
local:
*;
};
Makefile
CC := gcc -pedantic-errors -std=c89 -Wall -Wextra
.PHONY: all clean run
all: main.out main1.out main2.out
run: all
LD_LIBRARY_PATH=. ./main.out
LD_LIBRARY_PATH=. ./main1.out
LD_LIBRARY_PATH=. ./main2.out
main.out: main.c libcirosantilli_a.so
$(CC) -L'.' main.c -o '$#' -lcirosantilli_a
main1.out: main.c libcirosantilli_a.so
$(CC) -DV1 -L'.' main.c -o '$#' -lcirosantilli_a
main2.out: main.c libcirosantilli_a.so
$(CC) -DV2 -L'.' main.c -o '$#' -lcirosantilli_a
a.o: a.c
$(CC) -fPIC -c '$<' -o '$#'
libcirosantilli_a.so: a.o
$(CC) -Wl,--version-script,a.map -L'.' -shared a.o -o '$#'
libcirosantilli_a.o: a.c
$(CC) -fPIC -c '$<' -o '$#'
clean:
rm -rf *.o *.a *.so *.out
Tested on Ubuntu 16.04.
This workaround seem not compatible with -flto compile option.
My solution is calling memmove. memove does exactly the same jobs than memcpy.
The only difference is when src and dest zone overlap, memmove is safe and memcpy is unpredictable. So we can safely always call memmove instead memcpy
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
return memmove(dest, src, n);
}
#ifdef __cplusplus
}
#endif
For nim-lang, I elaborated on a solution I found using the C compiler --include= flag as follows:
Create a file symver.h with:
__asm__(".symver fcntl,fcntl#GLIBC_2.4");
Build your program with nim c ---passC:--include=symver.h
As for me I'm cross compiling too. I compile with nim c --cpu:arm --os:linux --passC:--include=symver.h ... and I can get symbol versions using arm-linux-gnueabihf-objdump -T ../arm-libc.so.6 | grep fcntl
I had to remove ~/.cache/nim at some point. And it seems to work.
I think you can get away with making a simple C file containing the symver statement and perhaps a dummy function calling memcpy. Then you just have to ensure that the resulting object file is the first file given to linker.
I suggest you either link memcpy() statically; or find the source of memcpy( ) and compile it as your own library.
It may caused by old ld (gnu link) version.
For following simple problem:
#include <string.h>
#include <stdio.h>
int main(int argc,char **argv)
{
char buf[5];
memset(buf,0,sizeof(buf));
printf("ok\n");
return 0;
}
When I use ld 2.19.1, memset is relocated to: memset##GLIBC_2.0, and cause crash.
After upgraded to 2.25, it is: memset#plt, and crash solved.
We had a similar issue, but instead of one older GLIBC symbol, we have to provide in our .so libs a mix of newer ones with necessary functionality and older ones our libs may be referencing but are not available. This situation occurs because we are shipping to customers high performance codec libs with vectorized math functions and we cannot impose requirements on what version of OS distro, gcc, or glibc they use. As long as their machine has appropriate SSE and AVX extensions, the libs should work. Here is what we did:
Include glibc 2.35 libmvec.so.1 and libm.so.6 files in a separate subfolder. These contain the necessary vectorized math functions. In a "hello codec" application example, we reference these in the link target depending on what distro, gcc, and glibc versions are found by the Makefile. More or less, for anything with glibc v2.35 or higher the high performance libs are referenced, otherwise slower libs are referenced.
To deal with missing symbols -- the subject of this thread -- we used a modification of Ortwin Anermeier's solution, in turn based on anight's solution, but without using the -Wl,--wrap=xxx option.
The .map file looks like:
GLIBC_2.35 {
hypot;
:
: (more function symbols as needed)
};
GLIBC_2.32 {
exp10;
:
: (more function symbols as needed)
};
:
: (more version nodes as needed)
and in a "stublib" .so we have:
#define _GNU_SOURCE
#include <math.h>
asm(".symver hypot_235, hypot#GLIBC_2.35");
asm(".symver exp10_232, exp10f#GLIBC_2.32");
/* ... more as needed */
double hypot_235(double x, double y) { return hypot(x, y); }
double exp10_232(double x) { return exp10(x); }
/* ... more as needed */
-lstublib.so is then included in the app build as the last link item, even after -lm.
This answer and this one also offer clues, but they not handling the general case of a .so flexible enough to be used on a wide variety of systems.

undefined reference to function declared in *.h file

I am a unskilled programmer and new to linux, I run into a problem when complining. I have two files 'ex_addinst.c' and 'lindo.h' in the same folder, I input command :
g++ -c ex_addinst.c
then, a object file ex_addinst.o is genetated with a warning:
ex_addinst.c: In function ‘int main()’:
ex_addinst.c:80: warning: deprecated conversion from string constant to ‘char*’
then I leak them with
g++ -Wall -o ex_addinst ex_addinst.o
and get the following info:
ex_addinst.o: In function `main':
ex_addinst.c:(.text+0x2b): undefined reference to `LSloadLicenseString'
ex_addinst.c:(.text+0x75): undefined reference to `LSgetVersionInfo'
ex_addinst.c:(.text+0xae): undefined reference to `LScreateEnv'
ex_addinst.c:(.text+0x10a): undefined reference to `LSgetErrorMessage'
...
...
ex_addinst.c:(.text+0x1163): undefined reference to `LSdeleteEnv'
collect2: ld returned 1 exit status
I guess that the header file 'lindo.h' is not complied into the .o file, but I have no idea what to do now. I have tried gcc, but get the same error. the version of my g++ and gcc is 4.4.5. I am using Ubuntu 10.10.
All the functions and structures have been declared in 'lindo.h'.
part of ex_addinst.c is as follows:
#include <stdio.h>
#include <stdlib.h>
/* LINDO API header file */
#include "lindo.h"
enter code here
int CALLTYPE LSwriteMPIFile(pLSmodel pModel,
char *pszFname);
/* Define a macro to declare variables for
error checking */
#define APIERRORSETUP \
int nErrorCode; \
char cErrorMessage[LS_MAX_ERROR_MESSAGE_LENGTH] \
/* Define a macro to do our error checking */
#define APIERRORCHECK \
if (nErrorCode) \
{ \
if ( pEnv) \
{ \
LSgetErrorMessage( pEnv, nErrorCode, \
cErrorMessage); \
printf("nErrorCode=%d: %s\n", nErrorCode, \
cErrorMessage); \
} else {\
printf( "Fatal Error\n"); \
} \
exit(1); \
} \
#define APIVERSION \
{\
char szVersion[255], szBuild[255];\
LSgetVersionInfo(szVersion,szBuild);\
printf("\nLINDO API Version %s built on %s\n",szVersion,szBuild);\
}\
/* main entry point */
int main()
{
APIERRORSETUP;
pLSenv pEnv;
pLSmodel pModel;
char MY_LICENSE_KEY[1024];
/*****************************************************************
* Step 1: Create a model in the environment.
*****************************************************************/
nErrorCode = LSloadLicenseString("home/li/work/tools/lindo/lindoapi/license/lndapi60.lic", MY_LICENSE_KEY);
if ( nErrorCode != LSERR_NO_ERROR)
{
printf( "Failed to load license key (error %d)\n",nErrorCode);
exit( 1);
}
......
......
......
APIERRORCHECK;
{
int nStatus;
double objval=0.0, primal[100];
/* Get the optimization result */
nErrorCode = LSgetInfo(pModel, LS_DINFO_GOP_OBJ, &objval);
APIERRORCHECK;
LSgetMIPPrimalSolution( pModel, primal) ;
APIERRORCHECK;
printf("\n\nObjective = %f \n",objval);
printf("x[0] = %f \n",primal[0]);
printf("x[1] = %f \n",primal[1]);
/* Get the linearity of the solved model */
nErrorCode = LSgetInfo (pModel, LS_IINFO_GOP_STATUS, &nStatus);
APIERRORCHECK;
/* Report the status of solution */
if (nStatus==LS_STATUS_OPTIMAL || nStatus==LS_STATUS_BASIC_OPTIMAL)
printf("\nSolution Status: Globally Optimal\n");
else if (nStatus==LS_STATUS_LOCAL_OPTIMAL)
printf("\nSolution Status: Locally Optimal\n\n");
else if (nStatus==LS_STATUS_INFEASIBLE)
printf("\nSolution Status: Infeasible\n\n");
}
/* >>> Step 7 <<< Delete the LINDO environment */
LSdeleteEnv(&pEnv);
/* Wait until user presses the Enter key */
printf("Press <Enter> ...");
getchar();
}
part of 'lindo.h' is:
/*********************************************************************
* Structure Creation and Deletion Routines (4) *
*********************************************************************/
pLSenv CALLTYPE LScreateEnv(int *pnErrorcode,
char *pszPassword);
pLSmodel CALLTYPE LScreateModel(pLSenv pEnv,
int *pnErrorcode);
int CALLTYPE LSdeleteEnv(pLSenv *pEnv);
int CALLTYPE LSdeleteModel(pLSmodel *pModel);
int CALLTYPE LSloadLicenseString(char *pszFname, char *pachLicense);
void CALLTYPE LSgetVersionInfo(char *pachVernum, char *pachBuildDate);
Thank you!
Thank you guys answering my problem. As you suggested, I need to link the library when complining. I have gotten the executable file with:
gcc -o ex_addinst ./ex_addinst.o -L/home/li/work/tools/lindo/lindoapi/bin/linux64 -m64 -llindo64 -lmosek64 -lconsub3 -lc -ldl -lm -lguide -lpthread -lsvml -limf -lirc
but there comes another problem when run the executable file ex_addinst: after run:
./ex_addinst
there comes:
./ex_addinst: error while loading shared libraries: liblindo64.so.6.0: cannot open shared object file: No such file or directory
The tricky thing is, liblindo64.so.6.0 is in the lib folder which contains:
libconsub3.so libirc.so liblindojni.so libmosek64.so.5.0 lindo.par
libguide.so liblindo64.so liblindojni.so.6.0.3 libsvml.so placeholder
libimf.so liblindo64.so.6.0 libmosek64.so lindoapivars.sh runlindo
I have created symbolic links between liblindo64.so.6.0 and liblindo64.so with
ln -sf liblindo64.so.6.0 liblindo64.so
but it doesn't help.
Can anyone tell me what is wrong here?
(I am not sure I should put this question in a new post, but I think currently it is better to follow the old one)
Ok, lindo.h contains the prototypes for those functions, but where are the functions actually defined? If they're in another C file you need to compile that one too, and link both the object files together.
If the functions are part of another static library, you need to tell the linker to link that library along with your object file.
If they're defined with a shared library, you can probably get g++ to still link to it at compile time, and take care of the library loading etc. Otherwise you'll need to load the library at runtime and reference the functions from the library. This Wikipedia article on dynamic loading of shared libraries contains some example code.
Try
g++ -Wall -o ex_addinst ex_addinst.c
instead of
g++ -Wall -o ex_addinst ex_addinst.o
You want to compile the .c file, not the .o file.
You need to tell gcc to link with the library or object file(s) that contain the LS... functions you're using. The header file tells the compiler how to call them, but the linker needs to know where to get the compiled code from.
undefined reference to ... is not a declaration problem. The compiler fails because it can't find symbols (objects) which are related to those declared functions.
In your case, you use the Limbo API, and include the header file, but you don't tell the compiler to link with the library : that's why it doesn't find symbols.
EDIT : I had forgotten the part when you say you're new to Linux. To link with the library, you need to use the -L/-l options of g++. man g++ is always a good read, and the Limbo's documentation should be, too.

Resources