GCC 将包含程序集的静态库插入到动态库中

GCC insert a static library containing assembly into a dynamic lib

我有一个问题:我有 2 个库(一个在 ASM 中用 NASM 编译的静态库和一个在 C 中用 GCC 编译的动态库)。

我首先使用以下 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 $@ $<

然后我编译动态库,让它使用静态的函数:

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: symbol lookup error: ./libclib.so: undefined symbol: 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__

nm 在静态库上:

asm_function.o:
0000000000000000 T asm_function
000000000000001c t _end
0000000000000021 t _finded
0000000000000008 t _loop

在创建动态库(或通常 linking)时,依赖关系的顺序 很重要。

您的 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 之后,否则它将被忽略(link 对象必须从最依赖的对象开始到最不依赖的对象依赖对象)

我会把 linker 标志放在最后,所以静态库在最后:

$(NAME)             :   $(OBJ)
                        $(CC) -shared -o $(NAMEDYN) $(OBJ) $(LDFLAGS)

这解决了符号解析问题,但没有解决与位置无关的代码问题。

对于C语言,没有什么比设置-fPIC选项更容易的了(实际上,"modern"编译器默认是这样,所以不用费心),但是汇编语言没有那么高水平层。如果你加载一个有效地址,但你没有让它成为 pc-relative,你有一个 (一些汇编器可以为某些指令让它成为 pc-relative,但这并不涵盖每条指令)。

为确保您在汇编中生成与位置无关的代码,请修改您的代码并将其反汇编,直到您在反汇编中看不到外部重定位。我不是 x86 专家,但我在 68k 系列上做了很多。

这是不正确的:

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 — 标准化缩进样式的主要目的是使人们更容易在项目之间切换而不必 "re-train" 他们的眼睛阅读新代码。当然,您可以自由地继续使用任何有效的样式。$@$^ 的使用也是相当标准的。)