我有一个问题:我有2个库(ASM编译的静态NASM和用GCC编译的C动态)。
我首先使用以下Makefile编译ASM中的那个(我删除了部分以使其更具可读性):
ASM = nasm
NAME = libasmlib.a
SRC = [...all .asm files...]
OBJ = $(SRC:.asm=.o)
FLAGS = -f elf64 -g
all : $(NAME)
$(NAME) : $(OBJ)
ar rc $(NAME) $(OBJ)
ranlib $(NAME)
%.o : %.asm
$(ASM) $(FLAGS) -o $@ $<
然后我编译动态库,以便它使用static的函数:
CC = gcc
NAMEDYN = libclib.so
SRC = [...all .c files...]
OBJ = $(SRC:%.c=%.o)
CFLAGS = -W -Wall -Werror -pedantic -fPIC
LDFLAGS = -L./libs/ASM -lasmlib
$(NAME) : $(OBJ)
$(CC) $(LDFLAGS) -shared -o $(NAMEDYN) $(OBJ)
all : $(NAME)
我没有问题,所有内容都完美编译但是当我使用以下.c(使用 gcc maindyn.c -ldl )测试代码时:
#include <stdio.h>
#include <dlfcn.h>
int main(int ac, char **av)
{
int res;
void *handle;
int (*c_function)(char *str);
if (!(handle = dlopen("./libclib.so", RTLD_LAZY | RTLD_GLOBAL | RTLD_NOW)))
return 1;
c_function = dlsym(handle, "c_function");
res = c_function("Hi!");
printf("%d\n", res);
[...]
}
我收到此错误:
动态库上的./ a.out:符号查找错误:./ libclib.so:未定义符号:asm_function
nm:
U asm_function
0000000000202078 B __bss_start
0000000000202078 b completed.7558
<snip>
0000000000000ee0 T c_function
0000000000000e20 t register_tm_clones
U __stack_chk_fail@@GLIBC_2.4
0000000000202078 d __TMC_END__
静态lib上的nm:
asm_function.o:
0000000000000000 T asm_function
000000000000001c t _end
0000000000000021 t _finded
0000000000000008 t _loop
答案 0 :(得分:1)
创建动态库(或一般链接)时,依赖项的顺序很重要。
makefile的一部分:
$(NAME) : $(OBJ)
$(CC) $(LDFLAGS) -shared -o $(NAMEDYN) $(OBJ)
构建线(大致)扩展为:
gcc -L./libs/ASM -lasmlib -shared -o libdyn.so obj1.o obj2.o
问题是,asm_function
在.a
文件中定义,但在.o
个文件中使用。您必须将.a
文件放在 .o
文件之后,否则它将被忽略(链接对象必须从大多数依赖对象开始到最少依赖对象)
我将链接器标志放在最后,以便静态库在最后:
$(NAME) : $(OBJ)
$(CC) -shared -o $(NAMEDYN) $(OBJ) $(LDFLAGS)
解决了符号解析问题,但不解决与位置无关的代码问题。
使用C语言,没有什么比设置-fPIC
选项更容易了(实际上,它默认情况下是“现代”编译器,所以不需要打扰),但汇编语言没有那个高级层。如果你加载了一个有效的地址,而你没有使它成为pc相对的,那么你有一个non-relocatable code(一些汇编程序可以使它与某些指令相关,但不包括每条指令)。< / p>
为确保您在装配中生成与位置无关的代码,请修改代码并反汇编,直到您在反汇编中看不到外部重定位。我不是x86专家,但我在68k系列中做了很多。
答案 1 :(得分:1)
这是不正确的:
LDFLAGS = -L./libs/ASM -lasmlib
$(NAME) : $(OBJ)
$(CC) $(LDFLAGS) -shared -o $(NAMEDYN) $(OBJ)
订单很重要。通常,-L
和-l
参数不应该是LDFLAGS
(链接器标志)的一部分,而是LIBS
(库)的一部分,它们按以下顺序出现在命令行中:
# No interesting LDFLAGS now, but maybe you want --gc-sections or --as-needed
LDFLAGS =
# Libraries go here
LIBS = -L./libs/ASM -lasmlib
libclib.so: $(OBJ)
$(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS)
订单之所以重要,是因为链接器只会解析命令行参数中以后定义的库中的符号,而不是它们被引用的位置。因此,所有-l
标志通常应该在所有.o
个文件之后。此规则不会影响.o
个文件,这些文件可以按任何顺序排列。
这也不适用于所有连接器。
(作为一个小调,由于其特殊的缩进风格,我发现难以阅读makefile - 标准化缩进样式的主要目的是让人们更容易在不需要“重新训练”的情况下在项目之间切换他们的目光是阅读新代码。当然,您可以继续使用任何风格的作品。使用$@
和$^
也是相当标准的。)