GNU Dynamic Loader search directories

本文深入探讨了Linux系统中动态链接器的工作原理,包括其如何处理程序与共享库之间的交互、如何解析未解决的符号,以及搜索共享库的具体路径。通过实际案例展示了不同编译选项对动态链接的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The GNU dynamic loader is one of the main components of the user space in Linux based systems (file name: /lib/ld-linux.so.2). Whenever a program is executed, the dynamic loader is loaded into the process’ address space and called by the kernel before the control is passed to the program’s “main” function (basically, it is not the “main” function initially called by the kernel, but this knowledge is sufficient for a high-level understanding and is not part of the dynamic loader internals). The main task of the dynamic loader is to handle the interaction between the program and the system’s shared libraries by relocating unresolved symbols.To keep the programs as portable as possible across different GNU/Linux based systems, the program usually only records the shared library name (in fact the shared library’s internal soname to enable library versioning) while omitting the absolute path to the shared library.Before relocating unresolved symbols, the dynamic loader needs to find the appropriate shared library by searching different directories which is the focus of this short and high-leveled post.

The man page ld.so(8) [1] serves thereby as an entry point which directories are searched in which order:

  1. the DT_RPATH value of the program’s .dynamic ELF section (colon separated list of directories)
  2. the LD_LIBRARY_PATH environment variables (colon separated list of directories)
  3. the DT_RUNPATH value of the program’s .dynamic ELF section (colon separated list of directories)
  4. the dynamic loader cache file, usually /etc/ld.so.cache
  5. the default system library directories configured at compile-time, usually /lib and/usr/lib (skipped if the binary is linked with-z nodefaultlib)

(Note: the LD_PRELOAD environment variable could be used to specify shared libraries to loaded before any other shared libraries, but you need to specify the absolute library path instead of just a search directory as in the cases above)

下面分享一下关于上面这个话题的一个例子:

我编写了一个使用Mysql C API连接数据库的测试程序

#include <stdio.h>
#include <mysql.h>

int main()
{
    MYSQL mysql;

    mysql_init(&mysql);
    if (!mysql_real_connect(&mysql, "localhost", "root", "root", "test",0, NULL, 0)) 
    {   
        printf("Connect Fail!\n");
        return -1; 
    }   
    else
    {   
        printf("Connect Successfully!\n");
        mysql_close(&mysql);
        return 0;
    }   
}

首先,在/usr/lib目录下面没有libmysqlclient.so.18文件,我先执行一下 touch /usr/lib/libmysqlclient.so.18。

并且,执行 export LD_LIBRARY_PATH=/usr/lib。

第一次,我这么编译:

gcc -o mysql_client mysql_client.c -I/usr/local/mysql/include -L/usr/local/mysql/lib -lmysqlclient

运行./mysql_client 报错:

./mysql_client: error while loading shared libraries: /usr/lib/libmysqlclient.so.18: file too short

说明动态链接器ld找到的是/usr/lib 下面,刚刚创建的一个空白文件。

第二次编译:

gcc -o mysql_client mysql_client.c -I/usr/local/mysql/include -L/usr/local/mysql/lib -lmysqlclient -Wl,-rpath,/usr/local/mysql/lib

运行./mysql_client 结果:

Connect Successfully!

我们使用readelf -d mysql_client 查看一下可执行文件的动态链接段,发现有这么一个符号:

0x0000000f (RPATH)                      Library rpath: [/usr/local/mysql/lib]

第三次编译:

gcc -o mysql_client mysql_client.c -I/usr/local/mysql/include -L/usr/local/mysql/lib -lmysqlclient -Wl,-rpath,/usr/local/mysql/lib,-enable-new-dtags

运行./mysql_client 结果:

./mysql_client: error while loading shared libraries: /usr/lib/libmysqlclient.so.18: file too short

我们再来看一下可执行文件:

 0x0000000f (RPATH)                      Library rpath: [/usr/local/mysql/lib]
 0x0000001d (RUNPATH)                    Library runpath: [/usr/local/mysql/lib]

多了一个 RUNPATH。

据此可知,如果有这个符号存在,动态链接器还是会先去 LD_LIBRARY_PATH 目录中寻找所依赖的动态链接库文件。

# Makefile for GeekOS kernel, userspace, and tools # Copyright (c) 2004,2005 David H. Hovemeyer <daveho@cs.umd.edu> # $Revision: 1.45 $ # This is free software. You are permitted to use, # redistribute, and modify it as specified in the file "COPYING". # Required software to build GeekOS: # - GNU Make (http://www.gnu.org/software/make) # - gcc 2.95.2 generating code for target (i386/ELF) and host platforms # - nasm (http://nasm.sourceforge.net) # - Perl5, AWK (any version), egrep # # Cygwin (http://cygwin.com) may be used to build GeekOS. # Make sure that gcc, binutils, nasm, and perl are installed. # NOTES: # - This makefile has been written carefully to work correctly # with the -j (parallel make) option. I regularly use "make -j 2" # to speed the build process on 2 processor systems. PROJECT_ROOT := .. VPATH := $(PROJECT_ROOT)/src # Figure out if we're compiling with cygwin, http://cygwin.com SYSTEM_NAME := $(shell uname -s) ifeq ($(findstring CYGWIN,$(SYSTEM_NAME)),CYGWIN) SYM_PFX := _ EXTRA_C_OPTS := -DNEED_UNDERSCORE -DGNU_WIN32 EXTRA_NASM_OPTS := -DNEED_UNDERSCORE NON_ELF_SYSTEM := yes EXTRA_CC_USER_OPTS := -Dmain=geekos_main endif # ---------------------------------------------------------------------- # Configuration - # Various options specifying how GeekOS should be built, # what source files to build, which user programs to build, # etc. This is generally the only section of the makefile # that will need to be modified. # ---------------------------------------------------------------------- # List of targets to build by default. # These targets encompass everything needed to boot # and run GeekOS. ALL_TARGETS := fd.img # Kernel source files KERNEL_C_SRCS := idt.c int.c trap.c irq.c io.c \ keyboard.c screen.c timer.c \ mem.c crc32.c \ gdt.c tss.c segment.c \ bget.c malloc.c \ synch.c kthread.c \ main.c # Kernel object files built from C source files KERNEL_C_OBJS := $(KERNEL_C_SRCS:%.c=geekos/%.o) # Kernel assembly files KERNEL_ASM_SRCS := lowlevel.asm # Kernel object files build from assembler source files KERNEL_ASM_OBJS := \ $(KERNEL_ASM_SRCS:%.asm=geekos/%.o) # All kernel object files KERNEL_OBJS := $(KERNEL_C_OBJS) \ $(KERNEL_ASM_OBJS) # Common library source files. # This library is linked into both the kernel and user programs. # It provides string functions and generic printf()-style # formatted output. COMMON_C_SRCS := fmtout.c string.c memmove.c # Common library object files. COMMON_C_OBJS := $(COMMON_C_SRCS:%.c=common/%.o) # Base address of kernel KERNEL_BASE_ADDR := 0x00010000 # Kernel entry point function KERNEL_ENTRY = $(SYM_PFX)Main # ---------------------------------------------------------------------- # Tools - # This section defines programs that are used to build GeekOS. # ---------------------------------------------------------------------- # Uncomment if cross compiling #TARGET_CC_PREFIX := i386-elf- # Target C compiler. gcc 2.95.2 or later should work. TARGET_CC := $(TARGET_CC_PREFIX)gcc # Host C compiler. This is used to compile programs to execute on # the host platform, not the target (x86) platform. On x86/ELF # systems, such as Linux and FreeBSD, it can generally be the same # as the target C compiler. HOST_CC := gcc # Target linker. GNU ld is probably to only one that will work. TARGET_LD := $(TARGET_CC_PREFIX)ld # Target archiver TARGET_AR := $(TARGET_CC_PREFIX)ar # Target ranlib TARGET_RANLIB := $(TARGET_CC_PREFIX)ranlib # Target nm TARGET_NM := $(TARGET_CC_PREFIX)nm # Target objcopy TARGET_OBJCOPY := $(TARGET_CC_PREFIX)objcopy # Nasm (http://nasm.sourceforge.net) NASM := nasm # Tool to build PFAT filesystem images. BUILDFAT := tools/builtFat.exe # Perl5 or later PERL := perl # Pad a file so its size is a multiple of some unit (i.e., sector size) PAD := $(PERL) $(PROJECT_ROOT)/scripts/pad # Create a file filled with zeroes. ZEROFILE := $(PERL) $(PROJECT_ROOT)/scripts/zerofile # Calculate size of file in sectors NUMSECS := $(PERL) $(PROJECT_ROOT)/scripts/numsecs # ---------------------------------------------------------------------- # Definitions - # Options passed to the tools. # ---------------------------------------------------------------------- # Flags used for all C source files GENERAL_OPTS := -O -Wall $(EXTRA_C_OPTS) CC_GENERAL_OPTS := $(GENERAL_OPTS) -Werror # Flags used for kernel C source files CC_KERNEL_OPTS := -g -DGEEKOS -I$(PROJECT_ROOT)/include # Flags user for kernel assembly files NASM_KERNEL_OPTS := -I$(PROJECT_ROOT)/src/geekos/ -f elf $(EXTRA_NASM_OPTS) # Flags used for common library and libc source files CC_USER_OPTS := -I$(PROJECT_ROOT)/include -I$(PROJECT_ROOT)/include/libc \ $(EXTRA_CC_USER_OPTS) # Flags passed to objcopy program (strip unnecessary sections from kernel.exe) OBJCOPY_FLAGS := -R .dynamic -R .note -R .comment # ---------------------------------------------------------------------- # Rules - # Describes how to compile the source files. # ---------------------------------------------------------------------- # Compilation of kernel C source files geekos/%.o : geekos/%.c $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) $< -o geekos/$*.o # Compilation of kernel assembly source files geekos/%.o : geekos/%.asm $(NASM) $(NASM_KERNEL_OPTS) $< -o geekos/$*.o geekos/%.o : geekos/%.S $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) $< -o geekos/$*.o # Compilation of common library C source files common/%.o : common/%.c $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_USER_OPTS) $< -o common/$*.o # ---------------------------------------------------------------------- # Targets - # Specifies files to be built # ---------------------------------------------------------------------- # Default target - see definition of ALL_TARGETS in Configuration section all : $(ALL_TARGETS) # Standard floppy image - just boots the kernel fd.img : geekos/fd_boot.bin geekos/setup.bin geekos/kernel.bin cat geekos/fd_boot.bin geekos/setup.bin geekos/kernel.bin > $@ # Floppy boot sector (first stage boot loader). geekos/fd_boot.bin : geekos/setup.bin geekos/kernel.bin $(PROJECT_ROOT)/src/geekos/fd_boot.asm $(NASM) -f bin \ -I$(PROJECT_ROOT)/src/geekos/ \ -DNUM_SETUP_SECTORS=`$(NUMSECS) geekos/setup.bin` \ -DNUM_KERN_SECTORS=`$(NUMSECS) geekos/kernel.bin` \ $(PROJECT_ROOT)/src/geekos/fd_boot.asm \ -o $@ # Setup program (second stage boot loader). geekos/setup.bin : geekos/kernel.exe $(PROJECT_ROOT)/src/geekos/setup.asm $(NASM) -f bin \ -I$(PROJECT_ROOT)/src/geekos/ \ -DENTRY_POINT=0x`egrep 'Main$$' geekos/kernel.syms |awk '{print $$1}'` \ $(PROJECT_ROOT)/src/geekos/setup.asm \ -o $@ $(PAD) $@ 512 # Loadable (flat) kernel image. geekos/kernel.bin : geekos/kernel.exe $(TARGET_OBJCOPY) $(OBJCOPY_FLAGS) -S -O binary geekos/kernel.exe geekos/kernel.bin $(PAD) $@ 512 # The kernel executable and symbol map. geekos/kernel.exe : $(KERNEL_OBJS) $(COMMON_C_OBJS) $(TARGET_LD) -o geekos/kernel.exe -Ttext $(KERNEL_BASE_ADDR) -e $(KERNEL_ENTRY) \ $(KERNEL_OBJS) $(COMMON_C_OBJS) $(TARGET_NM) geekos/kernel.exe > geekos/kernel.syms # Clean build directories of generated files clean : for d in geekos common libc user tools; do \ (cd $$d && rm -f *); \ done # Build header file dependencies, so source files are recompiled when # header files they depend on are modified. depend : $(GENERATED_LIBC_SRCS) $(TARGET_CC) -M $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) \ $(KERNEL_C_SRCS:%.c=$(PROJECT_ROOT)/src/geekos/%.c) \ | $(PERL) -n -e 's,^(\S),geekos/$$1,;print' \ > depend.mak $(TARGET_CC) -M $(CC_GENERAL_OPTS) $(CC_USER_OPTS) \ $(COMMON_C_SRCS:%.c=$(PROJECT_ROOT)/src/common/%.c) \ | $(PERL) -n -e 's,^(\S),common/$$1,;print' \ >> depend.mak # By default, there are no header file dependencies. depend.mak : touch $@ include depend.mak 在哪里修改
最新发布
05-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值