共享库操作
- 前言
- Makefile
- Hotspot的Makefile
- 如何定义需要共享库
- 使用编译选项
- 使用链接器选项
- dlopen
- dlsym
前言
jvm启动时,libjli.so、libjvm.so、libjava.so这些共享库的加载操作,都是c语言的,我们记录一下这些操作函数。
Makefile
以下是一个简单的 Makefile,用于测试 $(CURDIR) 是否为当前目录的绝对路径
test:
@echo $(CURDIR)
@if [ "$(CURDIR)" = "$(abspath .)" ]; then \
echo "CURDIR is absolute path"; \
else \
echo "CURDIR is not absolute path"; \
fi
该 Makefile 包含一个名为 test 的伪目标。当运行 make test 时,它将打印出 $(CURDIR)
的值并检查是否与调用abspath .
返回的值相同。如果两者相同,则说明 $(CURDIR)
是当前目录的绝对路径,否则不是
$(CURDIR)是Makefile预定义的内置变量,它会自动展开为当前工作目录的绝对路径。因此,在Makefile中不需要重新定义CURDIR变量,以调用pwd命令进行路径展开,可以直接使用$(CURDIR)来表示当前工作目录的路径。
我们可以自定义一个变量完成它相同的功能,
CURRENT_DIR := $(shell pwd)
这个语句中使用了 pwd 命令来获取当前工作目录的路径,并通过 $(shell …) 内置函数将其传递给CURRENT_DIR变量。注意,在使用 $(shell …) 函数时需要将命令放在双引号中。
当你需要在 Makefile 中使用当前工作目录的路径时,可以直接使用 ${CURRENT_DIR} 来引用该变量。
Hotspot的Makefile
/home/wanghost/jdkCompile/openjdk/Makefile
# Locate this Makefile
ifeq ($(filter /%, $(lastword $(MAKEFILE_LIST))),)
#获取当前makefile工作目录/home/wanghost/jdkCompile/openjdk+'/'+makefile文件名
(去掉首尾空格的名字)。也就是获得makefile完整路径
makefile_path := $(CURDIR)/$(strip $(lastword $(MAKEFILE_LIST)))
else
makefile_path := $(lastword $(MAKEFILE_LIST))
endif
topdir := $(strip $(patsubst %/, %, $(dir $(makefile_path))))
topdir := $(strip $(patsubst %/, %, $(dir $(makefile_path))))
具体来说,这个语句包含以下几个函数:
$(makefile_path)
: 获取当前Makefile文件的完整路径。$(dir $(makefile_path))
: 获取当前Makefile文件所在的目录路径。$(patsubst %/, %, $(dir $(makefile_path)))
: 如果目录路径以/结尾,则去除最后一个字符/,否则返回原字符串。这个函数主要是为了去除目录路径的最后一个斜杠/,以便后续使用该路径时更加方便和规范。$(strip ...)
: 去除参数前后的空白符,返回纯净的值。这个函数主要是为了去除目录路径中可能存在的多余空格。
因此,将这些函数组合起来,就可以得到当前Makefile所在目录的上一级目录,即topdir变量的值/home/wanghost/jdkCompile/openjdk
即源码根目录。
如何定义需要共享库
-wl
最大优势应该是对间接引用库和直接引用库都能指定,-l
只能指定直接引用库。
在Linux/Unix系统中,通过gcc/g++编译程序时,使用-l选项可以指定直接引用的库。而使用-Wl,option选项可以将option指定的选项传递给链接器ld。
因此,-Wl选项的最大优势在于它可以将其他任意的选项传递给链接器ld,包括间接引用的库和直接引用的库。这意味着我们可以使用-Wl来指定任意的链接选项,从而实现更加灵活的链接操作。
例如,如果我们需要链接一个静态库,并且该库依赖于另外一个库,我们可以使用-Wl,-Bstatic选项来指定链接静态库。同时,我们还可以使用-Wl,-lxxx选项来指定依赖库xxx的链接方式。
总之,-Wl选项对于复杂的链接操作非常有用,因为它可以传递任意的链接选项给链接器ld,从而实现更加灵活的链接操作。
无论是编译器选项还是链接器选项,都只是指定搜索共享库的路径,而到了具体使用时系统会根据这个路径加载共享库。dlopen可以运行时立即加载共享库。
使用编译选项
使用-l
编译选项来指定程序需要链接的共享库名称。例如,如果你的程序需要链接 libcurl 库,那么可以使用以下命令进行编译:
gcc -o myprogram myprogram.c -lcurl
这将生成一个名为 myprogram 的可执行文件,该文件依赖于 libcurl 共享库。
注意,如果 libcurl 不在默认路径中,则还需要使用-L
选项来指定共享库的路径。例如,如果 libcurl 在 /usr/local/lib 目录下,则可以使用以下命令进行编译:
gcc -o myprogram myprogram.c -L/usr/local/lib -lcurl
但是编译器选项在编译时已经确定了程序的运行方式,包括使用哪些共享库。因此,在程序运行时无法更改编译器选项以加载其他共享库。
使用链接器选项
使用-wl
链接器选项也可以指定程序需要链接的共享库名称。例如,可以使用以下命令将libcurl链接到名为 myprogram 的可执行文件中:
gcc -o myprogram myprogram.o -Wl,-rpath=/usr/local/lib -lcurl
其中,-Wl
选项用于将后续参数传递给链接器。-rpath
选项表示在运行时搜索共享库文件的目录,这里指定为 /usr/local/lib 目录。注意,如果 libcurl 不在默认路径中,则必须使用 -rpath 选项来指定共享库的路径。
它和-l
主要区别时是它可以带连接器选项-Wl,option
- Bstatic:强制链接静态库。
- Bdynamic:强制链接动态库。
- export-dynamic:允许在动态库中导出符号。
- rpath=dir:指定运行时搜索共享库的路径。
- Ldir:添加搜索共享库的目录。
- lfile:链接名为“libfile.so”的共享库。
- no-as-needed:即使没有未解决的符号也链接共享库。
- as-needed:仅在有未解决的符号时才链接共享库。
- whole-archive:强制链接整个归档文件,而不仅仅是未解决的符号。
- no-whole-archive:关闭–whole-archive选项。
dlopen
dlopen
是一个 C 语言标准库函数,用于在程序运行时(runtime)动态加载共享库(shared library),也称为动态链接库(dynamic link library)。
void *dlopen(const char *filename, int flag);
其中 filename 参数是要加载的共享库文件名,flag 参数指定加载方式,比较常用的值有RTLD_NOW
(立即解析符号)和RTLD_LAZY
(在需要时再解析符号),RTLD_GLOBAL
(它的含义是使得库中的解析的定义变量在随后的其它的链接库中变得可以使用)。dlopen函数返回一个指向动态库句柄的指针。
通过 dlopen 加载的共享库中的函数可以使用 dlsym 函数动态获取。例如:
void *handle = dlopen("mylib.so", RTLD_LAZY);
if (handle) {
void (*foo)(void) = dlsym(handle, "foo");
if (foo) {
foo();
}
dlclose(handle);
}
这段代码尝试加载 mylib.so 文件,并获取其中名为 foo 的函数指针,如果成功则调用该函数。最后使用 dlclose 函数释放动态库句柄。
dlsym
dlsym是一个用于从共享库中获取符号地址的函数。它是在动态链接器库(例如linux下的libc.so)中定义的,通常通过使用 dlopen 打开共享库之后调用。具体来说,dlsym 函数可以通过在共享对象中查找相应名称的符号,并返回该符号的地址。这个地址可以被用来执行符号所代表的函数、变量等等。该函数的原型如下:
void* dlsym(void* handle, const char* symbol);
handle 参数是指向已打开共享对象的句柄的指针,而 symbol 参数则是要查找的符号名称的字符串。 如果找到了符号,则返回该符号的地址,否则返回 NULL。