Shared libraries with GCC on Linux

本文详细介绍如何使用GCC在Linux环境下创建共享库,包括编译位置独立代码、生成共享库文件及链接程序等步骤,并探讨了解决常见加载问题的方法。

foo.h:

1
2
3
4
5
6
#ifndef foo_h__
#define foo_h__
extern void foo( void );
#endif // foo_h__

foo.c:

1
2
3
4
5
6
7
#include <stdio.h>
void foo( void )
{
puts ( "Hello, I'm a shared library" );
}

main.c:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include "foo.h"
int main( void )
{
puts ( "This is a shared library test..." );
foo();
return 0;
}

foo.h defines the interface to our library, a single function, foo(). foo.c contains the implementation of that function, and main.c is a driver program that uses our library.

For the purposes of this example, everything will happen in /home/username/foo

Step 1: Compiling with Position Independent Code

We need to compile our library source code into position-independent code (PIC):1

$ gcc -c -Wall -Werror -fpic foo.c

Step 2: Creating a shared library from an object file

Now we need to actually turn this object file into a shared library. We’ll call it libfoo.so:

gcc -shared -o libfoo.so foo.o

Step 3: Linking with a shared library

As you can see, that was actually pretty easy. We have a shared library. Let’s compile our main.c and link it with libfoo. We’ll call our final program “test.” Note that the -lfoo option is not looking for foo.o, but libfoo.so. GCC assumes that all libraries start with ‘lib’ and end with .so or .a (.so is for shared object or shared libraries, and .a is for archive, or statically linked libraries).

$ gcc -Wall -o test main.c -lfoo
/usr/bin/ld: cannot find -lfoo
collect2: ld returned 1 exit status

Telling GCC where to find the shared library

Uh-oh! The linker doesn’t know where to find libfoo. GCC has a list of places it looks by default, but our directory is not in that list.2We need to tell GCC where to find libfoo.so. We will do that with the -L option. In this example, we will use the current directory, /home/username/foo:

$ gcc -L/home/username/foo -Wall -o test main.c -lfoo

Step 4: Making the library available at runtime

Good, no errors. Now let’s run our program:

$ ./test
./test: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory

Oh no! The loader can’t find the shared library.3We didn’t install it in a standard location, so we need to give the loader a little help. We have a couple of options: we can use the environment variable LD_LIBRARY_PATH for this, or rpath. Let’s take a look first at LD_LIBRARY_PATH:

Using LD_LIBRARY_PATH

$ echo $LD_LIBRARY_PATH

There’s nothing in there. Let’s fix that by prepending our working directory to the existing LD_LIBRARY_PATH:

$ LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH
$ ./test
./test: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory

What happened? Our directory is in LD_LIBRARY_PATH, but we didn’t export it. In Linux, if you don’t export the changes to an environment variable, they won’t be inherited by the child processes. The loader and our test program didn’t inherit the changes we made. Thankfully, the fix is easy:

$ export LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH
$ ./test
This is a shared library test...
Hello, I'm a shared library

Good, it worked! LD_LIBRARY_PATH is great for quick tests and for systems on which you don’t have admin privileges. As a downside, however, exporting the LD_LIBRARY_PATH variable means it may cause problems with other programs you run that also rely on LD_LIBRARY_PATH if you don’t reset it to its previous state when you’re done.

Using rpath

Now let’s try rpath (first we’ll clear LD_LIBRARY_PATH to ensure it’s rpath that’s finding our library). Rpath, or the run path, is a way of embedding the location of shared libraries in the executable itself, instead of relying on default locations or environment variables. We do this during the linking stage. Notice the lengthy “-Wl,-rpath=/home/username/foo” option. The -Wl portion sends comma-separated options to the linker, so we tell it to send the -rpath option to the linker with our working directory.

$ unset LD_LIBRARY_PATH
$ gcc -L/home/username/foo -Wl,-rpath=/home/username/foo -Wall -o test main.c -lfoo
$ ./test
This is a shared library test...
Hello, I'm a shared library

Excellent, it worked. The rpath method is great because each program gets to list its shared library locations independently, so there are no issues with different programs looking in the wrong paths like there were for LD_LIBRARY_PATH.

rpath vs. LD_LIBRARY_PATH

There are a few downsides to rpath, however. First, it requires that shared libraries be installed in a fixed location so that all users of your program will have access to those libraries in those locations. That means less flexibility in system configuration. Second, if that library refers to a NFS mount or other network drive, you may experience undesirable delays–or worse–on program startup.

Using ldconfig to modify ld.so

What if we want to install our library so everybody on the system can use it? For that, you will need admin privileges. You will need this for two reasons: first, to put the library in a standard location, probably /usr/lib or /usr/local/lib, which normal users don’t have write access to. Second, you will need to modify the ld.so config file and cache. As root, do the following:

$ cp /home/username/foo/libfoo.so /usr/lib
$ chmod 0755 /usr/lib/libfoo.so

Now the file is in a standard location, with correct permissions, readable by everybody. We need to tell the loader it’s available for use, so let’s update the cache:

$ ldconfig

That should create a link to our shared library and update the cache so it’s available for immediate use. Let’s double check:

$ ldconfig -p | grep foo
libfoo.so (libc6) => /usr/lib/libfoo.so

Now our library is installed. Before we test it, we have to clean up a few things:

Clear our LD_LIBRARY_PATH once more, just in case:

$ unset LD_LIBRARY_PATH

Re-link our executable. Notice we don’t need the -L option since our library is stored in a default location and we aren’t using the rpath option:

$ gcc -Wall -o test main.c -lfoo

Let’s make sure we’re using the /usr/lib instance of our library using ldd:

$ ldd test | grep foo
libfoo.so => /usr/lib/libfoo.so (0x00a42000)

Good, now let’s run it:

$ ./test
This is a shared library test...
Hello, I'm a shared library

That about wraps it up. We’ve covered how to build a shared library, how to link with it, and how to resolve the most common loader issues with shared libraries–as well as the positives and negatives of different approaches.


  1. What is position independent code? PIC is code that works no matter where in memory it is placed. Because several different programs can all use one instance of your shared library, the library cannot store things at fixed addresses, since the location of that library in memory will vary from program to program.


  2. GCC first searches for libraries in /usr/local/lib, then in /usr/lib. Following that, it searches for libraries in the directories specified by the -L parameter, in the order specified on the command line.


  3. The default GNU loader, ld.so, looks for libraries in the following order:

    1. It looks in the DT_RPATH section of the executable, unless there is a DT_RUNPATH section.
    2. It looks in LD_LIBRARY_PATH. This is skipped if the executable is setuid/setgid for security reasons.
    3. It looks in the DT_RUNPATH section of the executable unless the setuid/setgid bits are set (for security reasons).
    4. It looks in the cache file /etc/ld/so/cache (disabled with the ‘-z nodeflib’ linker option).
    5. It looks in the default directories /lib then /usr/lib (disabled with the ‘-z nodeflib’ linker option).

gezi@ubuntu:~/study/pubilc/camerademo6/curl-8.7.1$ ./configure --host=arm-linux-gnueabi --prefix=/home/gezi/study/pubilc/camerademo6/curl_arm --with-openssl checking whether to enable maintainer-specific portions of Makefiles... no checking whether make supports nested variables... yes checking whether to enable debug build options... no checking whether to enable compiler optimizer... (assumed) yes checking whether to enable strict compiler warnings... no checking whether to enable compiler warnings as errors... no checking whether to enable curl debug memory tracking... no checking whether to enable hiding of library internal symbols... yes checking whether to enable c-ares for DNS lookups... no checking whether to disable dependency on -lrt... (assumed no) checking whether to enable ECH support... no checking for path separator... : checking for sed... /bin/sed checking for grep... /bin/grep checking that grep -E works... yes checking for arm-linux-gnueabi-ar... /usr/bin/arm-linux-gnueabi-ar checking for a BSD-compatible install... /usr/bin/install -c checking for arm-linux-gnueabi-gcc... arm-linux-gnueabi-gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... yes checking for suffix of object files... o checking whether the compiler supports GNU C... yes checking whether arm-linux-gnueabi-gcc accepts -g... yes checking for arm-linux-gnueabi-gcc option to enable C11 features... none needed checking whether arm-linux-gnueabi-gcc understands -c and -o together... yes checking how to run the C preprocessor... arm-linux-gnueabi-gcc -E checking for stdio.h... yes checking for stdlib.h... yes checking for string.h... yes checking for inttypes.h... yes checking for stdint.h... yes checking for strings.h... yes checking for sys/stat.h... yes checking for sys/types.h... yes checking for unistd.h... yes checking for stdatomic.h... yes checking if _Atomic is available... yes checking for a sed that does not truncate output... (cached) /bin/sed checking for code coverage support... no checking whether build environment is sane... yes checking for arm-linux-gnueabi-strip... arm-linux-gnueabi-strip checking for a race-free mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking whether make supports the include directive... yes (GNU style) checking dependency style of arm-linux-gnueabi-gcc... gcc3 checking curl version... 8.7.1 checking for httpd... no checking for apache2... no checking for apachectl... no checking for apxs... no configure: httpd/apache2 not in PATH, http tests disabled configure: apxs not in PATH, http tests disabled checking for nghttpx... no checking for caddy... no checking build system type... x86_64-pc-linux-gnu checking host system type... arm-unknown-linux-gnueabi checking for grep that handles long lines and -e... (cached) /bin/grep checking for egrep... /bin/grep -E checking if OS is AIX (to define _ALL_SOURCE)... no checking if _THREAD_SAFE is already defined... no checking if _THREAD_SAFE is actually needed... no checking if _THREAD_SAFE is onwards defined... no checking if _REENTRANT is already defined... no checking if _REENTRANT is actually needed... no checking if _REENTRANT is onwards defined... no checking for special C compiler options needed for large files... no checking for _FILE_OFFSET_BITS value needed for large files... 64 checking how to print strings... printf checking for a sed that does not truncate output... (cached) /bin/sed checking for fgrep... /bin/grep -F checking for ld used by arm-linux-gnueabi-gcc... /usr/arm-linux-gnueabi/bin/ld checking if the linker (/usr/arm-linux-gnueabi/bin/ld) is GNU ld... yes checking for BSD- or MS-compatible name lister (nm)... /usr/bin/arm-linux-gnueabi-nm -B checking the name lister (/usr/bin/arm-linux-gnueabi-nm -B) interface... BSD nm checking whether ln -s works... yes checking the maximum length of command line arguments... 1572864 checking how to convert x86_64-pc-linux-gnu file names to arm-unknown-linux-gnueabi format... func_convert_file_noop checking how to convert x86_64-pc-linux-gnu file names to toolchain format... func_convert_file_noop checking for /usr/arm-linux-gnueabi/bin/ld option to reload object files... -r checking for arm-linux-gnueabi-file... no checking for file... file configure: WARNING: using cross tools not prefixed with host triplet checking for arm-linux-gnueabi-objdump... arm-linux-gnueabi-objdump checking how to recognize dependent libraries... pass_all checking for arm-linux-gnueabi-dlltool... no checking for dlltool... no checking how to associate runtime and link libraries... printf %s\n checking for arm-linux-gnueabi-ar... /usr/bin/arm-linux-gnueabi-ar checking for archiver @FILE support... @ checking for arm-linux-gnueabi-strip... (cached) arm-linux-gnueabi-strip checking for arm-linux-gnueabi-ranlib... arm-linux-gnueabi-ranlib checking command to parse /usr/bin/arm-linux-gnueabi-nm -B output from arm-linux-gnueabi-gcc object... ok checking for sysroot... no checking for a working dd... /bin/dd checking how to truncate binary pipes... /bin/dd bs=4096 count=1 checking for arm-linux-gnueabi-mt... no checking for mt... mt checking if mt is a manifest tool... no checking for dlfcn.h... yes checking for objdir... .libs checking if arm-linux-gnueabi-gcc supports -fno-rtti -fno-exceptions... no checking for arm-linux-gnueabi-gcc option to produce PIC... -fPIC -DPIC checking if arm-linux-gnueabi-gcc PIC flag -fPIC -DPIC works... yes checking if arm-linux-gnueabi-gcc static flag -static works... yes checking if arm-linux-gnueabi-gcc supports -c -o file.o... yes checking if arm-linux-gnueabi-gcc supports -c -o file.o... (cached) yes checking whether the arm-linux-gnueabi-gcc linker (/usr/arm-linux-gnueabi/bin/ld) supports shared libraries... yes checking whether -lc should be explicitly linked in... no checking dynamic linker characteristics... GNU/Linux ld.so checking how to hardcode library paths into programs... immediate checking whether stripping libraries is possible... yes checking if libtool supports shared libraries... yes checking whether to build shared libraries... yes checking whether to build static libraries... yes checking whether to build shared libraries with -version-info... yes checking whether to build shared libraries with -no-undefined... no checking whether to build shared libraries with -mimpure-text... no checking whether to build shared libraries with PIC... yes checking whether to build static libraries with PIC... no checking whether to build shared libraries only... no checking whether to build static libraries only... no checking for arm-linux-gnueabi-windres... no checking for windres... no checking for inline... inline checking if cpp -P is needed... yes checking if cpp -P works... yes checking if compiler is DEC/Compaq/HP C... no checking if compiler is HP-UX C... no checking if compiler is IBM C... no checking if compiler is Intel C... no checking if compiler is clang... no checking if compiler is GNU C... yes checking compiler version... gcc '700' (raw: '7') checking if compiler is SGI MIPSpro C... no checking if compiler is SGI MIPS C... no checking if compiler is SunPro C... no checking if compiler is Tiny C... no checking whether build target is a native Windows one... no checking if compiler accepts some basic options... yes configure: compiler options added: -Werror-implicit-function-declaration checking if compiler optimizer assumed setting might be used... yes checking if compiler accepts optimizer enabling options... yes configure: compiler options added: -O2 checking if compiler accepts strict warning options... yes configure: compiler options added: -Wno-system-headers checking if compiler halts on compilation errors... yes checking if compiler halts on negative sized arrays... yes checking if compiler halts on function prototype mismatch... yes checking if compiler supports hiding library internal symbols... yes checking whether build target supports WIN32 file API... no checking whether build target supports WIN32 crypto API... no checking for good-to-use Darwin CFLAGS... no checking whether to link macOS CoreFoundation, CoreServices, and SystemConfiguration frameworks... no checking to see if the compiler supports __builtin_available()... no checking whether to support http... yes checking whether to support ftp... yes checking whether to support file... yes checking whether to support ldap... yes checking whether to support ldaps... yes checking whether to support rtsp... yes checking whether to support proxies... yes checking whether to support dict... yes checking whether to support telnet... yes checking whether to support tftp... yes checking whether to support pop3... yes checking whether to support imap... yes checking whether to support smb... yes checking whether to support smtp... yes checking whether to support gopher... yes checking whether to support mqtt... no checking whether to build documentation... yes checking whether to provide built-in manual... yes checking whether to enable generation of C code... yes checking whether to use libgcc... no checking if X/Open network library is required... no checking for gethostbyname... yes checking whether build target is a native Windows one... (cached) no checking for proto/bsdsocket.h... no checking for connect in libraries... yes checking for sys/types.h... (cached) yes checking for sys/time.h... yes checking for monotonic clock_gettime... yes checking for clock_gettime in libraries... no additional lib required checking for sys/types.h... (cached) yes checking for sys/time.h... (cached) yes checking for raw monotonic clock_gettime... yes checking for arm-linux-gnueabi-pkg-config... no checking for pkg-config... /usr/bin/pkg-config checking for zlib options with pkg-config... found checking for zlib.h... yes configure: found both libz and libz.h header checking for BrotliDecoderDecompress in -lbrotlidec... no checking for brotli/decode.h... no checking for ZSTD_createDStream in -lzstd... no checking for zstd.h... no checking for lber.h... no checking for ldap.h... no checking for ldap_ssl.h... no checking for LDAP libraries... cannot find LDAP libraries configure: WARNING: Cannot find libraries for LDAP support: LDAP disabled checking whether to enable IPv6... yes checking if struct sockaddr_in6 has sin6_scope_id member... yes checking if argv can be written to... no configure: WARNING: the previous check could not be made default was used checking if GSS-API support is requested... no checking whether to enable Windows native SSL/TLS... no checking whether to enable Secure Transport... no checking whether to enable Amiga native SSL/TLS (AmiSSL v5)... no checking for arm-linux-gnueabi-pkg-config... /usr/bin/pkg-config checking for openssl options with pkg-config... found configure: pkg-config: SSL_LIBS: "-lssl -lcrypto" configure: pkg-config: SSL_LDFLAGS: "" configure: pkg-config: SSL_CPPFLAGS: "" checking for HMAC_Update in -lcrypto... no checking for HMAC_Init_ex in -lcrypto... no checking OpenSSL linking with -ldl... no checking OpenSSL linking with -ldl and -lpthread... no checking for SSL_set_quic_use_legacy_codepoint... no configure: OpenSSL version does not speak QUIC API configure: OPT_OPENSSL: yes configure: OPENSSL_ENABLED: configure: error: --with-openssl was given but OpenSSL could not be detected
10-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值