gcc編譯時文件擴展名為.S和.s的區別

  • 2020 年 2 月 17 日
  • 筆記

gcc編譯時,文件擴展名為.S和.s的區別是,.S支援預處理,而.s不支援。

gcc編譯一般分為四個階段,分別是預處理、編譯、彙編、鏈接。

下面我們用一個小例子看下這四個階段的作用,示例程式碼:

#ifndef __LIB_H

預處理的作用是宏展開和頭文件替換:

$ gcc -E main.c -o main.i  $ cat main.i  // 刪除一些無關內容  extern int add(int a, int b);    int main() {    return add(1, 2);  }  

編譯的作用是把c程式碼轉成彙編程式碼:

$ 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, %edi  	call	add@PLT  	popq	%rbp  	ret  	.size	main, .-main  	.ident	"GCC: (GNU) 9.2.0"  	.section	.note.GNU-stack,"",@progbits  

彙編的作用是將彙編程式碼轉成對應的二進位形式的cpu指令:

$ 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  

鏈接的作用是把程式碼之間的引用關係關聯起來,最終生成一個完整的程式:

$ 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結尾。

寫個例子看下:

$ 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後,該彙編程式碼可正常編譯並執行。

希望對你有所幫助。