Compiling library version into .so file - c

I have a C linux API library that I distribute both to end users and to servers. When a user needs to use this library, they compile and build a .so file that they send to our servers to be run. I would like a way to compile in the version number of the library into their .so file such that my server can check what version they compiled on. This way if the server is incompatible with the user's .so file, I can refuse to load the library. I'm not sure what options I even have to achieve this and was hoping for any type of suggestion. Please let me know if any more information would be helpful in solving this issue.

It's common for libraries to have a getLibraryVersion function that returns some constant value, be it a string, integer, or whatever. This would get you the version you linked against (i.e. your .so version). You could have an additional macro to get the version you compiled against (i.e. your server's version).
For example, SDL's API has a version struct and the following function defined in one of its headers:
#define SDL_MAJOR_VERSION 1
#define SDL_MINOR_VERSION 2
#define SDL_PATCHLEVEL 15
typedef struct SDL_version {
Uint8 major;
Uint8 minor;
Uint8 patch;
} SDL_version;
/**
* This macro can be used to fill a version structure with the compile-time
* version of the SDL library.
*/
#define SDL_VERSION(X) \
{ \
(X)->major = SDL_MAJOR_VERSION; \
(X)->minor = SDL_MINOR_VERSION; \
(X)->patch = SDL_PATCHLEVEL; \
}
/**
* Use this function to get the version of SDL that is linked against
* your program.
*/
extern void SDL_GetVersion(SDL_version* ver);
In one of your .so's .c files:
void SDL_GetVersion(SDL_version* ver)
{
SDL_VERSION(ver);
}
Example use:
SDL_version compiled;
SDL_version linked;
SDL_VERSION(&compiled);
SDL_GetVersion(&linked);
printf("We compiled against SDL version %d.%d.%d ...\n",
compiled.major, compiled.minor, compiled.patch);
printf("We are linking against SDL version %d.%d.%d.\n",
linked.major, linked.minor, linked.patch);
On a side note; it's a little dangerous to be running somebody else's code on your servers.

Related

Optional dynamic library

Background
Trying to profile an executable, I experimented the profiler Intel VTune and I learn that there is an API library (ITT) that provide utility to start/stop profiling. Its basic functions __itt_resume() and __itt_pause(). What triggers me is that the library is optional, i.e. if the runtime library of ITT is not loaded, these functions are basically noops.
Optional library?
I want to know (first of all on Linux)
Does a process checks that the dynamic library he is linking to is loaded when he starts or when each symbol, or the first symbol of the library is called at runtime (i.e. lazy initialization)? I think on Windows it's at startup because of can't find XXX.dll messages, but I am not sure on Linux. Also, with the example, I don't get any compilation & execution issues even if the symbol is not defined in some_process.c.
How to implement this on Linux? Looking at the Github repo of ITT, among many macro trickery, I feel like the key is here:
#define ITTNOTIFY_VOID(n) (!ITTNOTIFY_NAME(n)) ? (void)0 : ITTNOTIFY_NAME(n)
Basically it wraps every function call with a function pointer call if its not NULL.
How to implement this in a cross-platform way (Windows, Mac, Linux) ?
I end up with a minimal example that looks like the code linked here, but it does not work as it should. In the linked version, my_api_hello_impl() is not called as it should. Also, there is no crash checking the value of the extern symbol api_hello_ptr() when the library is not linked.
my_api.c
#include "my_api.h"
#include <stdio.h>
void(*api_hello_ptr)();
void api_hello_impl()
{
printf("Hello\n");
}
__attribute__((constructor))
static void init()
{
printf("linked\n");
api_hello_ptr = api_hello_impl;
}
my_api.h
#pragma once
extern void(*api_hello_ptr)();
inline void api_hello() { if(api_hello_ptr) api_hello_ptr(); }
some_process.c
#include "my_api.h"
int main()
{
// NOOPS of not linked at runtime
api_hello();
}
Makefile
# my_api is not linked to some_process
some_process: some_process.c my_api.h
$(CC) -o $# $<
my_api.so: my_api.c my_api.h
$(CC) -shared -fPIC -o $# $<
test_linked: some_process my_api.so
LD_PRELOAD="$(shell pwd)/my_api.so" ./some_process
test_unlinked: some_process my_api.so
./some_process
.PHONY: test_linked test_unlinked
Output:
$ make test_linked
LD_PRELOAD="/tmp/tmp.EkrQbILrNg/my_api.so" ./some_process
linked
$ make test_unlinked
./some_process
Does a process checks that the dynamic library he is linking to is loaded when he starts
Yes, it does. If a dynamic library is linked, then it is a runtime requirement and the system loader will not start execution of a program without finding and loading the library first. There are mechanisms for delayed-loading, but it is not the norm on Linux, they are done manually or using custom libraries. By default, all dynamically linked objects need to be loaded before execution starts.
Note: I'm assuming we are talking about ELF executables here since we are on Linux.
How to implement this on Linux?
You can do it using macros or wrapper functions, plus libdl (link with -ldl), with dlopen() + dlsym(). Basically, in each one of those wrappers, the first thing you do is check if the library was already loaded, and if not, load it. Then, find and call the needed symbol.
Something like this:
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
static void *libfoo_handle = NULL;
static int (*libfoo_func_a)(int, int);
static void load_libfoo_if_needed(void) {
if (!libfoo_handle) {
// Without "/" in the path, this will look in all standard system
// dynamic library directories.
libfoo_handle = dlopen("libfoo.so", RTLD_LAZY | RTLD_GLOBAL);
if (!libfoo_handle) {
perror("failed to load libfoo.so");
_exit(1);
}
// Optionally use dlsym() here to initialize a set of global
// function pointers, so that you don't have to do it later.
void *tmp = dlsym(libfoo_handle, "func_a");
if (!tmp) {
perror("no symbol func_a in libfoo.so");
_exit(1);
}
*((void**)&libfoo_func_a) = tmp;
}
}
int wrapper_libfoo_func_a(int a, int b) {
load_libfoo_if_needed();
return libfoo_func_a(a, b);
}
// And so on for every function you need. You could use macros as well.
How to implement this in a cross-platform way (Windows, Mac, Linux)?
For macOS, you should have dlopen() and dlsym() just like in Linux.
Not sure how to exactly do this on Windows, but I know there is LoadLibrary() available in different flavors (e.g. one, two, etc.), which should be more or less the equivalent of dlopen() and GetProcAddress(), which should be the equivalent of dlsym().
See also: Loading a library dynamically in Linux or OSX?

PostgreSQL: compile C function on Windows 8 64bit

I'm learning how to compile a C trigger to load on PostgreSQL
When compile the "trigf.c" (in the example at http://www.postgresql.org/docs/9.3/interactive/trigger-example.html), I get some issue related to int64 error (c.h header)
#ifdef HAVE_LONG_INT_64
/* Plain "long int" fits, use it */
#ifndef HAVE_INT64
typedef long int int64;
#endif
#ifndef HAVE_UINT64
typedef unsigned long int uint64;
#endif
#elif defined(HAVE_LONG_LONG_INT_64)
/* We have working support for "long long int", use that */
#ifndef HAVE_INT64
typedef long long int int64;
#endif
#ifndef HAVE_UINT64
typedef unsigned long long int uint64;
#endif
#else
/* neither HAVE_LONG_INT_64 nor HAVE_LONG_LONG_INT_64 */
#error must have a working 64-bit integer datatype
#endif
-> [Error] #error must have a working 64-bit integer datatype
I don't know how to solve that problem, because clearly that there is a working 64 bit integer datatype that I can use.
Edit: I installed pgsql from binary. The C compiler I used for compile the C function file is MinGW GCC 4.7.2. (Using the path of Dev-cpp mingw gcc).
The command line is :
gcc -fpic -c "D:\trigf.c"
At the first time, it showed an error that in c.h: not found libintl.h (no such file or directory). Then I download the Lib Intl - 0.14.4 (library for native language support). The installation create a folder: C:\Program Files (x86)\GnuWin32.
I edited the environment variable CPATH, added C:\Program Files (x86)\GnuWin32\include folder, which contained libintl.h.
I ran the command again, and I met with the above error.
Update: It turns out not to be too hard to build extensions stand-alone with MSVC on Windows. I wrote a blog post detailing the process today.
The usual way to build extensions on Windows is to do it inside a working PostgreSQL build tree.
See these instructions on the PostgreSQL wiki.
You might be able to do it using MinGW and PGXS using a suitable Makefile instead.
Just trying to compile a standalone .c file is unlikely to work as there are a variety of paths and preprocessor definitions required.
It doesn't help that the current PostgreSQL packages don't include headers for public dependencies, which is really rather frustrating. You can safely compile without ENABLE_NLS defined even if the target PostgreSQL was built with ENABLE_NLS, though, and in this case libintl.h won't be required.

Use PKCS#11 in Visual Studio

I need to connect to a token using the standard PKCS#11.
In my C program, wrote with Visual Studio, I included PKCS#11 headers, downloaded from RSA site and some macros.
//define macros
#define CK_PTR *
#define CK_DEFINE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)
#ifndef NULL_PTR
#define NULL_PTR 0
#endif
#include "pkcs11.h"
int main(int argc, char * argv[]) {
[..]
C_Initialize(NULL_PTR);
[..]
When i build it, i get "undefined reference to C_Initialize". The same error for all the pkcs11 function I used.
In the program directory there are also the other pkcs11 headers. What is the wrong with it?
For static linking you need to have a corresponding .lib file. You can have it if you link the application with the SDK of particular hardware device.
The generic approach is to load the DLL given by the end user dynamically. To do this your code needs to use LoadLibrary() and GetProcAddress() Windows API functions to obtain addresses of each function of the library (yes, there are more than 50 functions there if memory serves).
You're not linking with whatever object file or library that C_Initialize function is defined in.

Link against a Windows .dll+.lib file combination with GCC under Cygwin?

I know how to link against libraries in Unix-ish contexts: If I'm working with .a or .so files, I specify the root search directory with -L/my/path/to/lib/ and for libMylib I add -lMyLib.
But what if I have
a .dll (e.g. in the Windows\System32 directory)?
a .dll (in Windows\System32) and a .lib (someplace else)?
These DLLs are by some other party; I don't have access to their sources - but do have access to the corresponding include files, against which I manage to compile.
If you can link against a .lib in Cygwin or MinGW, then you can (indirectly) link against a DLL.
In the MSVC world, it is not unusual to create an import library along with a DLL. It is a static library (.lib) that loads the DLL and wraps the interface of the DLL. You just call the wrapper functions in the (static) import library and let the import library do all the DLL-related things.
For the Windows API, there are import libraries in the WindowsSDK.
For your own MSVC DLLs, MSVC can automatically generate the import libraries when you build the DLL.
For a third party DLL, you can build a static wrapper library based on the corresponding header files.
Linking against the .lib file in Cygwin or MinGW is possible. Example:
g++ -o myprg myprg.o -lShlwapi
This links against Shlwapi.lib. (The library must be in the local directory or in the library path of the linker.)
Linking against import libraries of DLLs works the same way.
Note 1: Keep in mind the different ABIs and name mangeling. However, calling plain C functions in DLL or LIB files will work in most cases.
Note 2: Keep in mind that g++ requires the libraries to be specified in the correct order.
#einpoklum Converting my comment to an answer: #n.18e9 is correct in that you must use the full path name for the lib file without any -L or -l options.
g++ -o foo.exe foo.o c:\something\somethingelse\some.lib. You can also link directly to the Windows DLL file g++ -o foo.exe foo.o c:\something\somethingelse\some.dll.
Important - make sure you are linking to a lib file (and associated dll) generated for a 64-bit platform (on MSVC target X64, not Win32).
OK you wanted an example, well let's go.
Here are two examples using gcc/g++ to link to a Windows native DLL which exports plain C functions (using here x86_64-w64-mingw32/8.3.0 on Windows 10).
I'm using my own free xmlsq library as an example https://www.cryptosys.net/xmlsq.
You can download the core native DLL and all the source code quoted below. Make sure you use the 64-bit DLL.
The native Windows DLL diXmlsq.dll is written entirely in plain C code and exports simple C functions (extern "C").
In particular, for this example, it exports a XMLSQ_Gen_Version function that returns an integer value.
The DLL was compiled using MSVC 12.0 targetting the X64 platform. The associated library file generated by MSVC is diXmlsq.lib.
I should add that this DLL works exactly the same as a Windows "Win32 API" DLL, so the instructions here should work for the standard Windows libraries in Windows\System32 (again make sure you link against the 64-bit version).
Example 1. A plain C interface.
Both these commands compile without warning on my system:
> gcc -o test-ver test-ver.c "C:\fullpath\to\x64\diXmlsq.lib"
> gcc -o test-ver test-ver.c "C:\fullpath\to\x64\diXmlsq.dll"
diXmlsq.dll is compiled using the following definition file.
(You could alternatively use __declspec(dllexport))
Ref: https://learn.microsoft.com/en-us/cpp/build/exporting-from-a-dll?view=msvc-160
diXmlsq.def
LIBRARY "diXmlsq"
EXPORTS
XMLSQ_Gen_Version
diXmlsq.h - the C interface to diXmlsq.dll
#ifdef __cplusplus
extern "C" {
#endif
long __stdcall XMLSQ_Gen_Version(void);
#ifdef __cplusplus
}
#endif
To call the core function in a plain C program:
test-ver.c
#include <stdio.h>
#include "diXmlsq.h"
int main(void)
{
long n;
n = XMLSQ_Gen_Version();
printf("Version = %ld\n", n);
return 0;
}
Example 2. A C++ interface.
Both these commands compile without warning using g++ .
> g++ -o test-simple test-simple.cpp xmlsq.cpp "C:\fullpath\to\x64\diXmlsq.lib"
> g++ -o test-simple test-simple.cpp xmlsq.cpp "C:\fullpath\to\x64\diXmlsq.dll"
The idea of the C++ interface is to be an interface to the plain C library using the more convenient STL types like std::string and std::vector.
To keep things simple we'll just demonstrate the Gen::Version method.
Extracts of the C++ code follow:
test-simple.cpp - a test C++ program.
#include <iostream>
#include "xmlsq.hpp"
int main()
{
std::cout << "xmlsq::Gen::Version=" << xmlsq::Gen::Version() << std::endl;
}
xmlsq.hpp - the C++ interface
namespace xmlsq
{
class Gen {
private:
Gen() {} // Static methods only, so hide constructor.
public:
/** Get version number of core diXmlsq DLL. */
static int Version();
};
}
xmlsq.cpp - the C++ implementation.
#include "diXmlsq.h"
#include "xmlsq.hpp"
namespace xmlsq
{
int Gen::Version() {
int n = XMLSQ_Gen_Version();
return n;
}
}
Example 3. Attempting to link to the 32-bit library by mistake.
> gcc -o test-ver test-ver.c "C:\fullpath\to\Win32\diXmlsq.lib"
C:/Strawberry/c/bin/../lib/gcc/x86_64-w64-mingw32/8.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
C:\Users\user\AppData\Local\Temp\cce27Dhl.o:test-ver.c:(.text+0xe):
undefined reference to `XMLSQ_Gen_Version'
collect2.exe: error: ld returned 1 exit status

Probably bad using structures and typedef in header

Hello how to rewrite probably bad construction?
I tryed to ask how to fix it to make it work there,but maybe it's all the bad conception.
Any other solution to do that?
It's compiled in Eclipse using GCC for linux, compiled as C code.
file first.h
#ifndef FIRST_H_
#define FIRST_H_
typedef struct foo
{
int a;
char *c;
} foo_struct;
#endif /* FIRST_H_ */
file second.h:
#ifndef SECOND_H_
#define SECOND_H_
#include "first.h"
typedef struct wtf
{
foo_struct *poleFOO[5];
}wtf_struct;
#endif /* SECOND_H_ */
Concretely in file second.h row foo_struct *poleFOO[5]; throws: "foo_struct could not be resolved"
I work on Linux Ubuntu 11.10 using gcc in editor Eclipse for C and C++.
Okay, this is not an error from the compiler but from Eclipse. Simply Googl'ing the error "could not be resolved" points me to articles talking about Eclipse CDT (the eclipse subsystem for C/C++ development).
So it has something to do with Eclipse, your C headers look syntactically right. I believe that without a C file but only headers, Eclipse does not know how to parse the headers only to create its own index database (must be used for intellisense, symbols list, etc.)
I suggest you insert a simple C file including second.h, and with a main() function so that the link step passes as well, for example:
#include "second.h"
int main() {
wtf_struct my_variable;
return 0;
}
I had the same Error: "vuint16 - could not be resolved" for a wrapped type within typedef (mentioned below) and it was successfully resolved by rebuilding the Index in Eclipse Environment(right click on project -> Index -> Rebuild)
Note: It is not a compiler error!
[headerfile1: can.h]
#define vuint8 uint8
[headerfile2: can_local.h]
#include "Can.h" /* include all needed types */
typedef struct sCanRxFullInfoStruct
{
vuint16 objectNumber; //error line
} tCanRxFullInfoStruct;
I suspect that you've created (and included) two headers that both have the same header guard
#ifndef FIRST_H_
#define FIRST_H_
I had the same problem after creating a set of typedefs that wrap other types.
The solution was to rebuild Eclipse Index - right click on project -> Index -> Rebuild
I landed here because I had a similar problem and Eclipse was also to blame (I reproduced using only GCC and it works). I tried to reproduce the OP's issue, but it compiled OK in my environment :/.
So I will show what worked for me instead.
The only thing that helped was to make the extern declaration using struct, and the actual definition in the source file using the new type I defined with typedef, like so:
lib1.h:
typedef struct sMyStruct
{
// struct fill
}tMyStruct;
lib2.h:
extern struct sMyStruct my_struct, *p_my_struct;
//extern tMyStruct my_struct, *p_my_struct; // This wouldn't work
lib2.c:
tMyStruct my_struct, *p_my_struct;
With extern tMyStruct in lib2.h it would not compile (the typedef was not recognized), no matter what. Cleaning the project wouldn't help, neither would rebuilding the index, etc.
Astonishingly, I tried to reproduce the problem minimally in a new project... and it compiles fine! With both versions! So, I don't know, Eclipse got hung up on some wicked ghost file or something.
I was stuck for days, the only thing that worked on the actual project was what I showed above.

Resources