Last active
July 23, 2023 09:11
-
-
Save xianbaoqian/10dae2219db91122bac981263ea0c27c to your computer and use it in GitHub Desktop.
Demo code for https://xianbao-qian.medium.com/pybind11-demystified-ch-1-dynamic-library-loading-7879dc714e40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Compile each source file to object files with `-c` option (without linking) | |
| gcc -c main.c | |
| gcc -c lib.c | |
| # Check for symbols in the object file. | |
| # 0000000000000000 D GLOBAL_COUNTER Symbol type D means an initialized value (main.c sets this value to 100). | |
| # 000000000000005c T inc_global_counter T means that this symbol is in the text section and refers to the compiled machine code. | |
| # 0000000000000000 T main | |
| # U my_lib_func U means that this symbol is not defined in this file and must be loaded from another file. | |
| # U printf | |
| # U puts | |
| nm main.o | |
| # U GLOBAL_COUNTER | |
| # U inc_global_counter | |
| # 0000000000000000 B LOCAL_COUNTER B means that this value is zero-initialized (lib.c sets its value to 0). | |
| # 0000000000000000 T my_lib_func | |
| # U printf | |
| # U puts | |
| nm lib.o | |
| # As we can see from `nm` outputs, none of these are complete that contain all the symbols, so we need to link them together. | |
| gcc main.o lib.o | |
| # Now let's run the program and everything seems to be working (no unresolved symbols) | |
| # Main: | |
| # global counter: 100 | |
| # Lib: | |
| # local counter: 0 | |
| # global counter: 100 | |
| # processing... | |
| # local counter: 1 | |
| # global counter: 102 | |
| # global counter: 102 | |
| ./a.out | |
| # Bonus 1: hidden shared library glibc | |
| # You might notice some undefined symbols above like `printf` and `puts`. What happened to them? GCC is smart enough to find them. | |
| # Let's verify it. | |
| # We'll find the following two lines which mean that these symbols are going to be loaded from glibc. | |
| # U printf@GLIBC_VERSION | |
| # U puts@GLIBC_VERSION | |
| nm a.out | |
| # ldd can reveal the actual location of the libc library | |
| ldd a.out | |
| # You can also check the actual linker command passed to `ld` with `-v` flag | |
| gcc -v main.o lib.o | |
| # Bonus 2: build glibc statically into the executable | |
| # What if I don't want to depend on the shared library and want to instead compile everything in a single binary that can be deployed on any machine e.g. the one without glibc? | |
| # This is possible with `--static` linker flag. | |
| gcc -v --static main.o lib.o -o static.out | |
| # First let's make sure it's running correctly | |
| ./static.out | |
| # And let's verify that no shared library is used. | |
| ldd static.out | |
| # Static compiled binary is often much larger as it embeds code that is otherwise shared. | |
| ls -lh a.out static.out | |
| # We can verify that `printf` is already embedded by looking at the binary's symbol list. | |
| nm static.out | grep printf |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Let's build the shared library in the same way | |
| gcc -c -fPIC lib.c | |
| gcc --shared lib.o -o libmy.so | |
| # Now we build the main binary. | |
| gcc -c main_dynamic.c | |
| # -ldl to inject dl shared library as a dependency to the executable. | |
| # -rdynamic to allow the shared library loaded by `dlopen` to refer back to symbols in the binary file itself. | |
| # Check '-E' option in this doc to read more information related to -rdynamic. https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_chapter/ld_2.html | |
| gcc -rdynamic main_dynamic.o -ldl | |
| # And now we can execute the final binary | |
| LD_LIBRARY_PATH=. ./a.out | |
| # Bonus: module initializer | |
| # Attach the initializer and finalizer to the shared library | |
| gcc --shared lib.o -o libmy.so -Wl,-init,my_lib_func -Wl,-fini,my_lib_func | |
| # And now let's see the magic. Enjoy! | |
| LD_LIBRARY_PATH=. LD_DEBUG=files ./a.out |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "main.h" | |
| #include "lib.h" | |
| #include "stdio.h" | |
| // Memory allocation happens here. | |
| int LOCAL_COUNTER = 0; | |
| void my_lib_func() | |
| { | |
| printf("\t Lib: \n"); | |
| printf("\t \t local counter: %d\n", LOCAL_COUNTER); | |
| printf("\t \t global counter: %d\n", GLOBAL_COUNTER); | |
| printf("\t \t processing...\n"); | |
| LOCAL_COUNTER++; | |
| GLOBAL_COUNTER++; | |
| inc_global_counter(); | |
| printf("\t \t local counter: %d\n", LOCAL_COUNTER); | |
| printf("\t \t global counter: %d\n", GLOBAL_COUNTER); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| void my_lib_func(); | |
| // Declaration but do not allocate memory. | |
| extern int LOCAL_COUNTER; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Demonstrate the use of static compile and the use of shared library. | |
| // Note that either use case requires the implementation of the library at compile time. | |
| #include "main.h" | |
| #include "lib.h" | |
| #include <stdio.h> | |
| // Memory allocation happens here. | |
| int GLOBAL_COUNTER = 100; | |
| int main() | |
| { | |
| printf("Main: \n"); | |
| printf("\t global counter: %d\n", GLOBAL_COUNTER); | |
| my_lib_func(); | |
| printf("\t global counter: %d\n", GLOBAL_COUNTER); | |
| return 1; | |
| } | |
| void inc_global_counter() | |
| { | |
| GLOBAL_COUNTER++; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| void inc_global_counter(); | |
| // Declaration but do not allocate memory. | |
| extern int GLOBAL_COUNTER; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Same as main.c but loads the lib implementation from a dynamic library. | |
| #include "main.h" | |
| // Note that we don't have to depend on "lib.h" here but the developer needs to be cautious about the symbol names and their types. | |
| // #include "lib.h" | |
| #include <stdio.h> | |
| // Required for dynamic loading options. | |
| #include <dlfcn.h> | |
| // Memory allocation happens here. | |
| int GLOBAL_COUNTER = 100; | |
| void call_my_lib_func() | |
| { | |
| printf("Starting to load the library.\n"); | |
| void *dl_handle = dlopen("libmy.so", RTLD_LAZY); | |
| if (!dl_handle) | |
| { | |
| printf("Failed to load the library: %s.\n", dlerror()); | |
| return; | |
| } | |
| void (*func)() = dlsym(dl_handle, "my_lib_func"); | |
| if (!func) | |
| { | |
| printf("Failed to load the symbol: %s.\n", dlerror()); | |
| return; | |
| } | |
| (*func)(); | |
| dlclose(dl_handle); | |
| printf("Closing the dynamic library.\n"); | |
| } | |
| int main() | |
| { | |
| printf("Main: \n"); | |
| printf("\t global counter: %d\n", GLOBAL_COUNTER); | |
| call_my_lib_func(); | |
| printf("\t global counter: %d\n", GLOBAL_COUNTER); | |
| return 1; | |
| } | |
| void inc_global_counter() | |
| { | |
| GLOBAL_COUNTER++; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment