gcc编译时,文件扩展名为.S和.s的区别是,.S支持预处理,而.s不支持。
gcc编译一般分为四个阶段,分别是预处理、编译、汇编、链接。
下面我们用一个小例子看下这四个阶段的作用,示例代码:
代码语言:javascript复制#ifndef __LIB_H
预处理的作用是宏展开和头文件替换:
代码语言:javascript复制$ gcc -E main.c -o main.i
$ cat main.i
// 删除一些无关内容
extern int add(int a, int b);
int main() {
return add(1, 2);
}
编译的作用是把c代码转成汇编代码:
代码语言:javascript复制$ gcc -fno-asynchronous-unwind-tables -S main.i
$ ls
lib.c lib.h main.c main.i main.s
$ cat main.s
.file "main.c"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
movl $2, %esi
movl $1, �i
call add@PLT
popq %rbp
ret
.size main, .-main
.ident "GCC: (GNU) 9.2.0"
.section .note.GNU-stack,"",@progbits
汇编的作用是将汇编代码转成对应的二进制形式的cpu指令:
代码语言:javascript复制$ gcc -c main.s
$ ls
lib.c lib.h main.c main.i main.o main.s
$ file main.o
main.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
链接的作用是把代码之间的引用关系关联起来,最终生成一个完整的程序:
代码语言:javascript复制$ gcc -c lib.c
$ ls
lib.c lib.h lib.o main.c main.i main.o main.s
$ gcc main.o lib.o
$ ls
a.out lib.c lib.h lib.o main.c main.i main.o main.s
$ ./a.out; echo $?
3
由上可见,文件扩展名为.s的文件其实就是汇编代码文件。
其实我们可以直接编写汇编代码,保存到以.s为后缀的文件里,然后再用gcc将其编译成可执行文件。
但.s为后缀的文件不支持预处理,如果我们想在汇编代码里使用宏或头文件,则保存该汇编代码的文件必须以.S结尾。
写个例子看下:
代码语言:javascript复制$ cat hello.s
#define MSG "hello"
.global main
.text
main:
mov $message, %rdi
call puts
ret
message:
.asciz MSG
$ gcc -no-pie hello.s
hello.s: Assembler messages:
hello.s:10: Error: junk at end of line, first unrecognized character is `M'
$ mv hello.s hello.S
$ gcc -no-pie hello.S
$ ./a.out
hello
由上可见,当文件扩展名为.s时,宏MSG是无法识别的,但扩展名改为.S后,该汇编代码可正常编译并执行。
希望对你有所帮助。