Hello everyone, I am a dynamic link library!

I’m sure you’ve heard the name before.

I was instrumental in the early days of computers, when memory was scarce!

No matter in The Windows system, or in the Unix platform, I can be seen everywhere, because I can save a lot of resources for everyone, resource is RMB!





For example, my host wrote this simple code:

C #include <stdio.h> int func_in_lib(int k) {printf("func_in_lib is called \n"); return k + 1; }Copy the code

Just compile it with the following command, and I have lib.so, which is a dynamically linked library:

$ gcc -m32 -fPIC --shared -o lib.so lib.c
Copy the code

At this point, I can serve whoever my host throws me at, as long as he calls my func_in_lib function.

Although the function I provided is very simple, the principle is the same. If I have a chance later, I will calculate the trajectory of the robot in this function. Here you have a look!

For example, John wrote a piece of code today and needs to call my function.

Zhang SAN this person likes SAO operation, obviously he is compiling executable program, put me a dynamic link on it, like the following:

$ gcc -m32 -o main main.c ./lib.so
Copy the code

But John didn’t do that. To show off, he used DloPen dynamic loading to load me from the hard drive into the process.

Let’s take a look at the executable program code written by Zhang SAN:

H > #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> typedef int (*pfunc)(int); int main(int argc, char *agv[]) { int a = 1; int b; Void *handle = dlopen("./lib.so", RTLD_NOW); If (handle) {pfunc func = (pfunc) dlsym(handle, "func_in_lib"); if (func) { b = func(a); printf("b = %d \n", b); } else { printf("dlsym failed! \n"); } dlclose(handle); } else { printf("dlopen failed! \n"); } return 0; }Copy the code

As you can see from the code, John knew the function in my head was func_in_lib, so he used the system function DLSYM (handle, “func_in_lib”); To find the memory loading address of the function, and then you can call the function directly.

After compiling the executable file main, the execution result is completely correct, very happy!





But one day, I encountered an annoying thing, my master said: you this service function calculation process is too monotonous, for you to find some fun, you in the implementation of ah, to call a function in another external module.

As soon as I said that, I was thrown a function name: void func_in_main(void); .

In other words, I need to call a function from another module in my service function, like the following:

#include <stdio.h> // Void func_in_main(void); int func_in_lib(int k) { printf("func_in_lib is called \n"); // Call the external function func_in_main(); return k + 1; }Copy the code

So where is this function? Oh, my God, how the hell do I know what this function is? How can I find it hidden in the corner of memory (address)?

However, after modifying the code, the host compiled me smoothly:

$ gcc -m32 -fPIC --shared -o lib.so lib.c
Copy the code

The compile instructions are completely unchanged.

Because I’m just a dynamically linked library, I can compile func_in_main without knowing its address.

Only I will mark this guy: whoever wants to use me must tell me where this guy’s address is! Otherwise, don’t blame me for lying.





My host said to Zhang SAN: brother, my dynamic link library upgraded, more powerful function oh, do you want to try?

Zhang SAN thought: I am using dlopen way to load dynamic library files, do not need to re-compile or link to the executable program, directly run it!

So without saying a word, he just took me, dropped me in his executable directory, and ran main.

This time, however, he saw the result:

dlopen failed! 
Copy the code

Why did the load fail? It was executed normally last time! Zhang SAN one face meng force!

Actually, it’s not my fault at all! I thought I said: Anyone who wants to use me must tell me the address of func_in_main!

But in this process, I can’t find the address of this function anywhere. If you can’t satisfy me, THEN I can’t satisfy you!





My application code did not change a bit, why did not change the new dynamic link library you gave?

I forgot to tell you one thing: the dynamic library needs one more thing: provide a function called func_in_main in your program, and that’s it.

Zhang 31 thought: this is easy to do, add a function is.

Since the executable had only one main.c file, he added a new function:

void func_in_main(void)
{
    printf("func_in_main \n");
}
Copy the code

Then we compile and execute, and the operation is like a tiger:

# gcc -m32 -o main main.c -ldl
# ./main
dlopen failed! 
Copy the code

Yi? How or failure? ! Func_in_main was added as required? !

Yes, you did add this function to main.c, but you only added it to your executable, and I can’t see it at all.

If you don’t believe me, check the compiled executable to see if it exports the func_in_main symbol. If I don’t export it, how can I see it?

# check export main symbol table $objdump - e - T | grep output func_in_main # here is emptyCopy the code

Since the output is empty, it is not exported! I don’t have to teach you that, do I?

The character “Hui” in anise beans is written in four ways…

Oh, no, there are two ways to export symbols:

Method 1: Export all symbols

$ gcc -m32 -rdynamic -o main main.c -ldl
Copy the code

Of course, the following command also works:

gcc -m32 -Wl,--export-dynamic -o main main.c -ldl
Copy the code

Method 2: Export the specified symbol

Define a file that lists all the symbols that need to be exported:

File: exported. TXT

{
    extern "C"
    {
        func_in_main;
    };
};
Copy the code

Then, specify the export file in the compile options:

gcc -m32 -Wl,-dynamic-list=./exported.txt -o main main.c -ldl
Copy the code

Use either of these two methods, and after compiling, use the objdump directive to look at the export symbols:

$ objdump -e main -T | grep func_in_main
080485bb g    DF .text	00000019  Base        func_in_main
Copy the code

Well, good, good! Zhang SAN hurriedly in accordance with such a way of operation, really successful implementation of the function!

$ ./main 
func_in_lib is called 
func_in_main 
b = 2 
Copy the code

That is, in my dynamic library file, I found the correct address of the function in the external module, and happily executed successfully!





Although the execution is successful, Zhang SAN still has a slightly uncomfortable feeling in his heart, every compilation has to export symbols, it’s really troublesome, can you optimize it?

So he went to my master and expressed his displeasure.

Master a look, personality! Since you don’t want to provide it, I will:

  1. First, provide a default function implementation (func_in_main_def) in the dynamic library;

  2. Then, a special register function (register_func) is provided. If the external module wants to provide func_in_main, it calls the register function to register it.

At this point, the latest lib.c code looks like this:

Void func_in_main_def(void) {printf(" The main is lazy, do NOT register me! \n"); Void (*func_in_main)() = func_in_main_def; void register_func(void (*pf)()) { func_in_main = pf; } int func_in_lib(int k) { printf("func_in_lib is called \n"); if (func_in_main) func_in_main(); return k + 1; }Copy the code

Then compile, and the new me is born lib.so again:

gcc -m32 -fPIC --shared -o lib.so lib.c
Copy the code

When the host dropped me off to John, he said, “Ok, meet your needs. This time you don’t need to provide func_in_main, and of course you don’t need to export symbols.”

However, if you ever change your mind and want to provide this function again, you should register your function through the register_func function in the dynamic library.

Have you got it? Just go and try again!

At this point, when John uses me again, he doesn’t need to export the func_in_main function in his main.c; he can actually remove it from the code!

Compile, execute, and Joe again acts like a tiger:

$ gcc -m32 -o main main.c -ldl
$ ./main
func_in_lib is called 
the main is lazy, do NOT register me! 
b = 2 
Copy the code

Well, it looks right.

Yi? The main is lazy, do NOT register me!

Are you questioning my technical abilities? Well, in that case, I can also satisfy you, not just register a function, simple:

C #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> typedef int (*pfunc)(int); typedef int (*pregister)(void (*)()); #define REG_FUNC #ifdef REG_FUNC void func_in_main(void) {printf("func_in_main \n"); } #endif int main(int argc, char *agv[]) { int a = 1; int b; Void *handle = dlopen("./lib.so", RTLD_NOW); Pregister register_func = (pregister) dlsym(handle, "register_func"); if (register_func) { register_func(func_in_main); Pfunc func = (pfunc) dlsym(handle, "func_in_lib"); if (func) { b = func(a); printf("b = %d \n", b); } else { printf("dlsym failed! \n"); } dlclose(handle); } else { printf("dlopen failed! \n"); } return 0; }Copy the code

Then compile and execute:

$ gcc -m32 -o main main.c -ldl
$ ./main 
func_in_lib is called 
func_in_main 
b = 2 
Copy the code

Perfect finish!





Let the knowledge flow, the more shared, the more lucky!

Hi, I’m Doug, an embedded development veteran. Star mark public number, can find me faster!





[1] C language pointer – from the underlying principle to the tricks, with graphics and code to help you explain thoroughly [2] step by step analysis – how to use C to achieve object-oriented programming [3] The original GDB underlying debugging principle is so simple [4] inline assembly is terrible? Finish this article and end it! [5] It is said that software architecture should be layered and divided into modules. What should be done specifically