c shared library
source code
fourArithmeticOperation.h
1
2
3
4
5
6
7
8
9
| #ifndef __FOURARITHMETICOPERATION_
#define __FOURARITHMETICOPERATION_
int addition(int a, int b);
int subtraction(int a, int b);
int multiplication(int a, int b);
int division(int a, int b);
#endif
|
fourArithmeticOperation.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| #include "fourArithmeticOperation.h"
int addition(int a, int b)
{
return a+b;
}
int subtraction(int a, int b)
{
return a-b;
}
int multiplication(int a, int b)
{
return a*b;
}
int division(int a, int b)
{
return a/b;
}
|
create shared library
1
2
3
| $ gcc -Wall -fPIC -c fourArithmeticOperation.c
$ gcc -shared -o libfourArithmeticOperation.so.1 fourArithmeticOperation.o
$ ln -sf libfourArithmeticOperation.so.1 libfourArithmeticOperation.so
|
Here are options that is used when creating shared library.
- -shared : Produce a shared object which can be linked with other objects to form an executable.
- -fPIC: emit position-independent code, this is required when making a shared object
- -fpic: almost same with fPIC, but it has GOT(global offset table) size limitation.
- -Wl,options : Pass options to linker.(ex. -Wl,-soname,libfourArithmeticOperation.so.1 This passes “-soname=libfourArithmeticOperation.so.1” to linker. “-soname=name” set the internal DT_SONAME field to the specified name. When an executable is linked with a shared object which has a DT_SONAME field, then dynamic linker will attempt to load the shared object specified by the DT_SONAME field rather than the using the file name given to the linker.
- For more options, refer to gcc manual.
Executable file(linking at build time)
source code
main_shared.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| #include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include "fourArithmeticOperation.h"
int main(void)
{
int a = 0, b = 0;;
printf("Input 2 numbers : \n");
scanf("%d", &a);
getchar();
scanf("%d", &b);
getchar();
printf("add(%d, %d) = %d\n", a, b, addition(a, b));
printf("sub(%d, %d) = %d\n", a, b, subtraction(a, b));
printf("mul(%d, %d) = %d\n", a, b, multiplication(a, b));
printf("div(%d, %d) = %d\n", a, b, division(a, b));
return 0;
}
|
build executable file with “-l” option.
1
| gcc -Wall -o main_shared main_shared.c -L. -lfourArithmeticOperation
|
Here are options that is used when linking library
- -Ldir : search library specified by -l from “dir”
ex) -L. : search library from current directory(.)
- -llibraryname : specify library name to link.
ex) -lfourArithmeticOperation : the library to be linked is “libfourArithmeticOperation.so”
output
1
2
3
4
5
6
7
| $ LD_LIBRARY_PATH=. ./main_shared
Input 2 numbers :
3 2
add(3, 2) = 5
sub(3, 2) = 1
mul(3, 2) = 6
div(3, 2) = 1
|
Executable file(loading library at runtime)
source code
main_load_runtime.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| #include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
typedef int (*arithmetic_t)(int, int);
void checkError()
{
char *error = dlerror();
if (error != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
}
int main(void)
{
void *handle;
int a = 0, b = 0;;
handle = dlopen("./libfourArithmeticOperation.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror(); /* Clear any existing error */
arithmetic_t add = (arithmetic_t)dlsym(handle, "addition");
checkError();
arithmetic_t sub = (arithmetic_t)dlsym(handle, "subtraction");
checkError();
arithmetic_t mul = (arithmetic_t)dlsym(handle, "multiplication");
checkError();
arithmetic_t div = (arithmetic_t)dlsym(handle, "division");
checkError();
printf("Input 2 numbers : \n");
scanf("%d", &a);
getchar();
scanf("%d", &b);
getchar();
printf("add(%d, %d) = %d\n", a, b, add(a, b));
printf("sub(%d, %d) = %d\n", a, b, sub(a, b));
printf("mul(%d, %d) = %d\n", a, b, mul(a, b));
printf("div(%d, %d) = %d\n", a, b, div(a, b));
dlclose(handle);
return 0;
}
|
build executable file
1
| $ gcc -Wall -o main_load_runtime main_load_runtime.c -ldl
|
- -ldl : need to specify libdl.so library to use dlopen(), dlsym() function.
output
1
2
3
4
5
6
7
8
| $ ./main_load_runtime
Input 2 numbers :
4
3
add(4, 3) = 7
sub(4, 3) = 1
mul(4, 3) = 12
div(4, 3) = 1
|
c static library
source code
Let’s use same fourArithmeticOperation.c/h file to create static library
create static library
1
2
| $ gcc -Wall -c fourArithmeticOperation.c
$ ar -cvr libfourArithmeticOperation.a fourArithmeticOperation.o
|
Here are options that is used when creating static library.
main_static.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| #include <stdio.h>
#include <stdlib.h>
#include "fourArithmeticOperation.h"
int main(void)
{
int a = 0, b = 0;;
printf("Input 2 numbers : \n");
scanf("%d", &a);
getchar();
scanf("%d", &b);
getchar();
printf("add(%d, %d) = %d\n", a, b, addition(a, b));
printf("sub(%d, %d) = %d\n", a, b, subtraction(a, b));
printf("mul(%d, %d) = %d\n", a, b, multiplication(a, b));
printf("div(%d, %d) = %d\n", a, b, division(a, b));
return 0;
}
|
create executable
1
| $ gcc -o main_static main_static.c libfourArithmeticOperation.a
|
output
1
2
3
4
5
6
7
| $ ./main_static
Input 2 numbers :
2 3
add(2, 3) = 5
sub(2, 3) = -1
mul(2, 3) = 6
div(2, 3) = 0
|
c++ shared library
source code(used c++ class template)
fourArithmeticOperation.h
1
2
3
4
5
6
7
8
9
10
11
12
13
| #ifndef __FOURARITHMETICOPERATION_
#define __FOURARITHMETICOPERATION_
template <typename T>
class fourArithmeticOperation {
public :
T addition(T a, T b);
T subtraction(T a, T b);
T multiplication(T a, T b);
T division(T a, T b);
};
#endif
|
fourArithmeticOperation.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| #include "fourArithmeticOperation.h"
template class fourArithmeticOperation<float>;
template class fourArithmeticOperation<int>;
template <typename T>
T fourArithmeticOperation<T>::addition(T a, T b)
{
return a+b+a+b;
}
template <typename T>
T fourArithmeticOperation<T>::subtraction(T a, T b)
{
return a-b;
}
template <typename T>
T fourArithmeticOperation<T>::multiplication(T a, T b)
{
return a*b;
}
template <typename T>
T fourArithmeticOperation<T>::division(T a, T b)
{
return a/b;
}
|
create shared library
1
2
3
| $ g++ -Wall -fPIC -c fourArithmeticOperation.cpp
$ g++ -shared -o libfourArithmeticOperation.so.1 fourArithmeticOperation.o
$ ln -sf libfourArithmeticOperation.so.1 libfourArithmeticOperation.so
|
Executable file(linking at build time)
source code
main_shared.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| #include <iostream>
#include "fourArithmeticOperation.h"
int main(void)
{
fourArithmeticOperation<int> int_obj;
fourArithmeticOperation<float> float_obj;
std::cout<<"Input 2 numbers : "<<std::endl;
float a, b;
std::cin>>a>>b;
std::cout<<"shared library : int operation"<<std::endl;
int int_operation_result = int_obj.addition(a, b);
std::cout<<"addition("<<a<<", "<<b<<") = "<<int_operation_result<<std::endl;
int_operation_result = int_obj.subtraction(a, b);
std::cout<<"subtraction("<<a<<", "<<b<<") = "<<int_operation_result<<std::endl;
int_operation_result = int_obj.multiplication(a, b);
std::cout<<"multiplication("<<a<<", "<<b<<") = "<<int_operation_result<<std::endl;
int_operation_result = int_obj.division(a, b);
std::cout<<"division("<<a<<", "<<b<<") = "<<int_operation_result<<std::endl;
std::cout<<std::endl;
std::cout<<"shared library : float operation"<<std::endl;
float float_operation_result = float_obj.addition(a, b);
std::cout<<"addition("<<a<<", "<<b<<") = "<<float_operation_result<<std::endl;
float_operation_result = float_obj.subtraction(a, b);
std::cout<<"subtraction("<<a<<", "<<b<<") = "<<float_operation_result<<std::endl;
float_operation_result = float_obj.multiplication(a, b);
std::cout<<"multiplication("<<a<<", "<<b<<") = "<<float_operation_result<<std::endl;
float_operation_result = float_obj.division(a, b);
std::cout<<"division("<<a<<", "<<b<<") = "<<float_operation_result<<std::endl;
return 0;
}
|
build executable file with “-l” option.
1
| g++ -Wall -o main_shared main_shared.cpp -L. -lfourArithmeticOperation
|
output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| $ LD_LIBRARY_PATH=. ./main_shared
Input 2 numbers :
3 -4
shared library : int operation
addition(3, -4) = -1
subtraction(3, -4) = 7
multiplication(3, -4) = -12
division(3, -4) = 0
shared library : float operation
addition(3, -4) = -1
subtraction(3, -4) = 7
multiplication(3, -4) = -12
division(3, -4) = -0.75
|
Executable file(loading library at runtime)
In c++, shared library can be loaded at runtime by using dlopen(). By the way, one thing to note is that c++ supports function overloading. So, symbol in c++ is created with mangled name.(ex. in c, if function name is “addition”, then symbol name is same with function(“addition”), but in c++, several characters are added before and after the function name, so the symbol name is like “_ZN23additionIiE8”). To do call dlsym(), you need to know the symbol name of the function, but if it is created with a mangled name, you cannot know the symbol name exactly. To solve this, you need to create a symbol name in C type by using extern “C” in front of the function. In this case, you can’t use c++’s overloading feature.(This doesn’t mean that you can’t use c++ in library code, you only need to have a C type symbol for symbols that are exposed outside. The internal library function can be implemented in c++).
One more thing to say is that dlsym() is to obtain address of function symbol, not for class object. To load object and all its functions, we can do it by creating factory function which instantiates the class.
source code
calc.h
1
2
3
4
5
6
7
8
9
10
11
12
| #ifndef __CALC_
#define __CALC_
class calc {
public :
float addition(float a, float b);
float subtraction(float a, float b);
float multiplication(float a, float b);
float division(float a, float b);
};
#endif
|
calc.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| #include "calc.h"
#include <iostream>
extern "C" {
extern void helloWorld();
extern calc* createFactory();
extern void destroyFactory(calc *p);
}
float calc::addition(float a, float b)
{
return a+b;
}
float calc::subtraction(float a, float b)
{
return a-b;
}
float calc::multiplication(float a, float b)
{
return a*b;
}
float calc::division(float a, float b)
{
return a/b;
}
void helloWorld()
{
std::cout<<"helloWorld. I'm calc library!"<<std::endl;
}
calc* createFactory()
{
std::cout<<"new instance in createFactory. "<<std::endl;
return new calc;
}
void destroyFactory(calc *p)
{
std::cout<<"delete instance in destroyFactory. "<<std::endl;
if ( p) {
delete p;
p = nullptr;
}
}
|
create shared library
1
2
3
| $ gcc -Wall -fPIC -c calc.cpp
$ gcc -shared -o libcalc.so.1 calc.o
$ ln -sf libcalc.so.1 libcalc.so
|
main_runtime.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| #include <iostream>
#include <dlfcn.h>
#include "calc.h"
typedef float(*arithmetic_t)(float, float);
typedef void (*hello_t)(void);
typedef calc* (*createFactory_t)(void);
typedef void (*destroyFactory_t)(calc *p);
void checkError()
{
char *error = dlerror();
if (error != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
}
int main(void)
{
void *handle;
float a = 0, b = 0;;
handle = dlopen("./libcalc.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror(); /* Clear any existing error */
hello_t hello= (hello_t)dlsym(handle, "helloWorld");
checkError();
hello();
createFactory_t createCalc = (createFactory_t)dlsym(handle, "createFactory");
destroyFactory_t destroyCalc = (destroyFactory_t)dlsym(handle, "destroyFactory");
if (!createCalc) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
if (!createCalc) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
calc *calculator = createCalc();
std::cout<<"Input 2 numbers : "<<std::endl;
std::cin>>a>>b;
std::cout<<"add("<<a<<", "<<b<<") = "<<calculator->addition(a, b)<<std::endl;
std::cout<<"sub("<<a<<", "<<b<<") = "<<calculator->subtraction(a, b)<<std::endl;
std::cout<<"mul("<<a<<", "<<b<<") = "<<calculator->multiplication(a, b)<<std::endl;
std::cout<<"div("<<a<<", "<<b<<") = "<<calculator->division(a, b)<<std::endl;
destroyCalc(calculator);
dlclose(handle);
return 0;
}
|
build executable file with “-l” option.
1
| g++ -Wall -o main_runtime main_runtime.cpp -L. -lcalc
|
If we implement class methods in header file, then no need to add “-L. -lcalc” option. Otherwise, it need to specify where the methods are implemented by using “-l” option.
output
1
2
3
4
5
6
7
8
9
10
| $ LD_LIBRARY_PATH=. ./main_runtime
helloWorld. I'm calc library!
new instance in createFactory.
Input 2 numbers :
3 2
add(3, 2) = 5
sub(3, 2) = 1
mul(3, 2) = 6
div(3, 2) = 1.5
delete instance in destroyFactory.
|
c++ static library
source code
Let’s use same fourArithmeticOperation.c/h file to create static library
create static library
1
2
| $ gcc -Wall -c fourArithmeticOperation.c
$ ar -cvr libfourArithmeticOperation.a fourArithmeticOperation.o
|
main_static.c(same with main_shared.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| #include <iostream>
#include "fourArithmeticOperation.h"
int main(void)
{
fourArithmeticOperation<int> int_obj;
fourArithmeticOperation<float> float_obj;
std::cout<<"Input 2 numbers : "<<std::endl;
float a, b;
std::cin>>a>>b;
std::cout<<"shared library : int operation"<<std::endl;
int int_operation_result = int_obj.addition(a, b);
std::cout<<"addition("<<a<<", "<<b<<") = "<<int_operation_result<<std::endl;
int_operation_result = int_obj.subtraction(a, b);
std::cout<<"subtraction("<<a<<", "<<b<<") = "<<int_operation_result<<std::endl;
int_operation_result = int_obj.multiplication(a, b);
std::cout<<"multiplication("<<a<<", "<<b<<") = "<<int_operation_result<<std::endl;
int_operation_result = int_obj.division(a, b);
std::cout<<"division("<<a<<", "<<b<<") = "<<int_operation_result<<std::endl;
std::cout<<std::endl;
std::cout<<"shared library : float operation"<<std::endl;
float float_operation_result = float_obj.addition(a, b);
std::cout<<"addition("<<a<<", "<<b<<") = "<<float_operation_result<<std::endl;
float_operation_result = float_obj.subtraction(a, b);
std::cout<<"subtraction("<<a<<", "<<b<<") = "<<float_operation_result<<std::endl;
float_operation_result = float_obj.multiplication(a, b);
std::cout<<"multiplication("<<a<<", "<<b<<") = "<<float_operation_result<<std::endl;
float_operation_result = float_obj.division(a, b);
std::cout<<"division("<<a<<", "<<b<<") = "<<float_operation_result<<std::endl;
return 0;
}
|
build executable file(link static library)
1
| g++ -Wall -o main_static main_static.cpp libfourArithmeticOperation.a
|
output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| $ ./main_static
Input 2 numbers :
3 2
shared library : int operation
addition(3, 2) = 5
subtraction(3, 2) = 1
multiplication(3, 2) = 6
division(3, 2) = 1
shared library : float operation
addition(3, 2) = 5
subtraction(3, 2) = 1
multiplication(3, 2) = 6
division(3, 2) = 1.5
|
Leave a comment