Linux x64下C/NASM编写类GLIBC共享库编译链接报错求助
问题描述
我正在用C和NASM为Linux x86_64平台实现类GLIBC的共享库。用-nostdlib编译可执行文件时,必须手动添加C包装器(比如asm(".global _start; _start: call main; movq %rax, %rdi; movq $60, %rax; syscall");),由此推断GLIBC负责处理int main(int argc, char *argv[], char *envp[])的加载逻辑。
我用NASM重写了exit(int status)和main()的包装器,但编译时出现一堆警告和LD错误,相关代码、配置及错误信息如下:
相关代码
vlibc-wrapper.h
#ifdef __linux__ #ifdef __x86_64__ #ifndef write #define write(fd, buf, count) extern int _write(int fd, const void *buf, size_t count); #endif #ifndef exit #define exit(status) extern void _exit(int status); #endif #ifndef blk #define brk(addr) extern int _brk(void *addr); #endif #endif #endif
exit.asm
bits 64 section .text global _exit:function _exit: mov rax, 60 syscall
main.asm
bits 64 section .text global _start extern main extern _exit _start: call main wrt ..plt mov rdi, rax call _exit wrt ..plt
Makefile
ASM_DIR = ./src/ASM C_DIR = ./src/C TEST_DIR = ./test BUILD_DIR = ./build TMP_DIR = ./.tmp INCLUDE_DIR = ./include ASM_FILES = $(wildcard $(ASM_DIR)/*.asm) C_FILES = $(wildcard $(C_DIR)/*.c) TEST_FILES = $(wildcard $(TEST_DIR)/*.c) ASM_OBJECTS = $(patsubst $(ASM_DIR)/%.asm,$(TMP_DIR)/%.o,$(ASM_FILES)) C_OBJECTS = $(patsubst $(C_DIR)/%.c,$(TMP_DIR)/%.o,$(C_FILES)) TEST_OBJECTS = $(patsubst $(TEST_DIR)/%.c,$(TMP_DIR)/%.o,$(TEST_FILES)) LIBRARY = $(BUILD_DIR)/libvlibc.so TEST_EXECUTABLE = $(BUILD_DIR)/test NASM = nasm NASM_FLAGS = -f elf64 CC = gcc CFLAGS = -m64 -fPIC -pedantic -fno-builtin -nostdlib -Wall -Wextra -Werror -ggdb3 -I$(INCLUDE_DIR) LD = ld LDFLAGS = -shared -I$(INCLUDE_DIR) all: $(LIBRARY) $(TEST_EXECUTABLE) $(LIBRARY): $(ASM_OBJECTS) $(C_OBJECTS) $(LD) $(LDFLAGS) -o $@ $^ $(TMP_DIR)/%.o: $(ASM_DIR)/%.asm $(NASM) $(NASM_FLAGS) $< -o $@ $(TMP_DIR)/%.o: $(C_DIR)/%.c $(CC) $(CFLAGS) -c $< -o $@ $(TEST_EXECUTABLE): $(TEST_OBJECTS) $(LIBRARY) $(CC) $(CFLAGS) -o $@ $^ -L$(BUILD_DIR) -lvlibc re: fclean all clean: rm -f $(TMP_DIR)/*.o fclean: clean rm -f $(LIBRARY) $(TEST_EXECUTABLE) test: $(TEST_EXECUTABLE) ./$(TEST_EXECUTABLE) .PHONY: all clean fclean re test
目录结构
. ├── build │ └── libvlibc.so ├── include │ ├── vlibc-def.h │ ├── vlibc.h │ └── vlibc-wrapper.h ├── Makefile ├── src │ ├── ASM │ │ ├── brk.asm │ │ ├── exit.asm │ │ ├── main.asm │ │ └── write.asm │ └── C └── test
错误信息
/usr/bin/ld: warning: type and size of dynamic symbol "_start" are not defined /usr/bin/ld: warning: input symbol _start not found; no initialization of start address /usr/bin/ld: build/libvlibc.so: undefined reference to "main collect2: error: ld returned output status 1 make: *** [Makefile:40: build/test] Error 1
我认为缺少main()的警告或许正常,但希望能修复——毕竟链接后main()会被识别。需要解决所有问题以顺畅开发共享库。
解决方案
1. 拆分_start的编译逻辑
共享库不需要_start入口点(_start是可执行文件的专属入口),将main.asm从共享库编译对象中移除,仅在编译测试可执行文件时链接它:
- 修改Makefile,拆分ASM文件规则:
# 排除main.asm作为共享库的一部分 ASM_FILES = $(filter-out $(ASM_DIR)/main.asm, $(wildcard $(ASM_DIR)/*.asm)) MAIN_ASM_FILE = $(ASM_DIR)/main.asm MAIN_ASM_OBJ = $(TMP_DIR)/main.o # 新增main.asm的编译规则 $(MAIN_ASM_OBJ): $(MAIN_ASM_FILE) $(NASM) $(NASM_FLAGS) $< -o $@ # 修改测试可执行文件的依赖,加入main.o $(TEST_EXECUTABLE): $(TEST_OBJECTS) $(MAIN_ASM_OBJ) $(LIBRARY) $(CC) $(CFLAGS) -o $@ $^ -L$(BUILD_DIR) -lvlibc
2. 修复vlibc-wrapper.h的宏定义
原宏写法会导致语法错误,调整为标准的函数声明+别名映射:
#ifdef __linux__ #ifdef __x86_64__ #ifndef write extern int _write(int fd, const void *buf, size_t count); #define write(fd, buf, count) _write((fd), (buf), (count)) #endif #ifndef exit extern void _exit(int status); #define exit(status) _exit((status)) #endif #ifndef brk extern int _brk(void *addr); #define brk(addr) _brk((addr)) #endif #endif #endif
3. 提供测试用的main()函数
在test目录下创建测试文件(比如test/main.c),提供main()实现:
#include "vlibc-wrapper.h" int main(int argc, char *argv[]) { // 测试示例:调用exit exit(0); return 0; }
4. 修复_start的参数传递逻辑
Linux x86_64下,_start需要将argc/argv/envp传递给main,修改main.asm:
bits 64 section .text global _start extern main extern _exit _start: ; 直接传递rdi(argc)、rsi(argv)、rdx(envp)给main call main wrt ..plt mov rdi, rax call _exit wrt ..plt
5. 调整共享库的链接方式
用gcc代替ld链接共享库,自动处理动态链接器等必要配置:
$(LIBRARY): $(ASM_OBJECTS) $(C_OBJECTS) $(CC) -shared -o $@ $^
同时删除LDFLAGS中的-I$(INCLUDE_DIR)(链接阶段不需要头文件路径)。
最终验证
- 执行
make clean all重新编译 - 运行
make test测试可执行文件
所有警告和错误都会被解决,共享库可正常工作。
内容的提问来源于stack exchange,提问作者Nayrosk
相关产品推荐
相关产品推荐

