Linux下使用CMake构建含nasm汇编的C/C++项目
CMake的出现极大的方便了C/C++项目的编译管理,避免了手工写Makefile的繁琐。如果在C/C++项目中有需要使用到外联汇编语言,CMake也一样可以进行编译管理。
在Linux下常用的C/C++编译器为GCC。近些年,随着LLVM项目的发展,Clang也占有了一席之地。但它们在Linux平台下,背后默认的汇编器依然是GAS。GAS使用语法格式为AT&T,与我们平常学习的Intel格式截然不同。它们的差别这里就不赘述了,网络上很多这方面的资料。那我们能不能在Linux平台也使用我们熟悉的Intel语法格式的汇编呢?答案是肯定的。
这里就介绍一下使用CMake来构建C/C++程序,并且可以与NASM汇编程序进行互调。
笔者使用的环境是Windows下的VSCode远程连接到64位的Linux,在VSCode中新建一个CMake项目。创建好项目后,一般有两个文件:main.cpp、CMakeLists.txt,我们再创建一个test.asm,需要注意所有文件的编码格式一定要是utf-8
或者utf-8 with BOM
把main.cpp内容改为:
1#include <stdio.h>
2
3extern "C" void 汇编函数(); // 声明汇编语言中定义的函数
4
5int 输出() {
6 printf("C/C++输出\n");
7 return 0;
8}
9
10int main(int, char**) {
11 输出();
12 汇编函数();
13 return 0;
14}
CMakeLists.txt的内容:
1cmake_minimum_required(VERSION 3.0.0)
2project(demo VERSION 0.1.0)
3
4#下面两句非常重要,如果需要生成调试,第三句也是必须的
5SET(CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS asm) # 设置NASM的文件扩展名为asm
6ENABLE_LANGUAGE(ASM_NASM) # 让CMacke启用NASM的汇编
7SET(CMAKE_ASM_NASM_FLAGS "-g") # 让NASM生成调试信息
8
9add_executable(demo main.cpp test.asm)
test.asm的内容:
1extern printf ;声明printf,将调用C语言的printf函数
2
3section .rodata
4 msg db "这是汇编中的输出",0xa,0
5
6section .text
7global 汇编函数
8汇编函数:
9 push rbp
10 mov rbp, rsp
11 lea rdi, [rel msg]
12 call printf wrt ..plt ;也可以使用 call [rel printf wrt ..got]
13 mov eax, 0
14 leave
15 ret
我们先来看看运行情况:
在NASM汇编代码中需要特别注意的地方是下面两句:
1lea rdi, [rel msg]
2call printf wrt ..plt
因为GCC编译器默认会使用PIE(Position-Independent-Executable)模式(即位置无关的模式,这里就不深入探讨了,感兴趣的朋友可以网上查资料深入了解),所以64位的汇编也必须要使用PIE模式。如果我们直接按下面的方式来写:
1lea rdi, [msg]
就会出现链接错误:
1[build] /usr/bin/ld: CMakeFiles/demo.dir/test.asm.o: relocation R_X86_64_32S against `.rodata' can not be used when making a PIE object; recompile with -fPIE
2[build] /usr/bin/ld: failed to set dynamic section sizes: bad value
3[build] collect2: error: ld returned 1 exit status
或者调用的方式写成:
1call printf
也会出现链接错误:
1[build] /usr/bin/ld: CMakeFiles/demo.dir/test.asm.o: warning: relocation against `printf@@GLIBC_2.2.5' in read-only section `.text'
2[build] /usr/bin/ld: CMakeFiles/demo.dir/test.asm.o: relocation R_X86_64_PC32 against symbol `printf@@GLIBC_2.2.5' can not be used when making a PIE object; recompile with -fPIE
3[build] /usr/bin/ld: final link failed: bad value
4[build] collect2: error: ld returned 1 exit status
当然,也可以让GCC不使用PIE模式,在CMakeLists.txt中添加一句:
1SET(CMAKE_CXX_FLAGS "-no-pie")
这样就可以把前面两句写成:
1lea rdi, [msg]
2call printf
但是不建议这样做,具体原因可以网上查询相关资料。
参考资料: https://stackoverflow.com/questions/65912204/how-to-compile-nasm-program-calling-printf https://stackoverflow.com/questions/52126328/cant-call-c-standard-library-function-on-64-bit-linux-from-assembly-yasm-code https://stackoverflow.com/questions/8194141/how-to-print-a-number-in-assembly-nasm
- 原文作者:Witton
- 原文链接:https://wittonbell.github.io/posts/2022/2022-05-29-Linux下使用CMake构建含nasm汇编的C_C++项目/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。