C/C++ 코드는 시스템에서 어떻게 컴파일됩니까?
6884 단어 cpplinuxarchitectureprogramming
필요한 도구?
작동 원리를 알기 전에 널리 사용되는 GNU의 GCC Compiler를 사용하는 것이 좋습니다. LLVM Clang(GCC보다 낫다)을 사용하여 시도해 볼 수도 있습니다!
행동 중!
자, 시작하려면 일반 C++ Hello World 코드를 입력해 보겠습니다. 결국 내가 간단한 것으로 시작한 이유를 이해하게 될 것입니다.
// Hello.cpp
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World! \n";
return 0;
}
자, 이제
g++ Hello.cpp
또는 clang++ Hello.cpp
를 사용하여 코드를 컴파일하겠습니다.a.out
(Windows의 경우 a.exe
)라는 출력 파일을 실행하면 꽤 직선적인 출력을 얻을 수 있습니다. 의심의 여지가 없습니다.Hello World!
이제 흥미로운 점을 만들기 위해 코드를 다시 컴파일하되 이번에는 -S 플래그를 사용합니다.
// For GCC
gcc -S Hello.c (for C)
g++ -S Hello.cpp (for C++)
// For Clang
clang++ -S Hello.cpp
Hello.s
라는 파일이 나오게 되는데 내용을 읽어보면 일반적인 구문과는 조금 다르고 조금 더 읽어보면... 다 어셈블리 형식으로 인코딩되어 있습니다!다음은 예입니다(컴파일러, 계산 속도, 사용된 프로세서 등에 따라 다름).
// From GCC (arrows are presented for better view)
.file "Hello.cpp"
.text
.section .rodata
.type _ZStL19piecewise_construct, @object
.size _ZStL19piecewise_construct, 1
_ZStL19piecewise_construct: <--- Initialize step
.zero 1
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.LC0: <--- Which function to execute?
.string "Hello World!"
.text
.globl main
.type main, @function
main: <--- Main operation
.LFB1522:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rsi
leaq _ZSt4cout(%rip), %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1522:
.size main, .-main
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2006:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L5
cmpl $65535, -8(%rbp)
jne .L5
leaq _ZStL8__ioinit(%rip), %rdi
call _ZNSt8ios_base4InitC1Ev@PLT
leaq __dso_handle(%rip), %rdx
leaq _ZStL8__ioinit(%rip), %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L5:
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2006:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB2007:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2007:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
.hidden __dso_handle
.ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
Hello World 프로그램에 대한 많은 코드를 볼 수 있습니다! (조금 충격적인 것 같죠?! 네, 저도 이해했습니다) 이해를 돕기 위해 조금 설명하겠습니다...
.file "Hello.cpp"
.text
.section .rodata
.type _ZStL19piecewise_construct, @object
.size _ZStL19piecewise_construct, 1
이 섹션에는 C++ 파일에 대한 메타데이터와 파일에 대한 몇 가지 정보가 포함되어 있습니다.
_ZStL19piecewise_construct:
.zero 1
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.LC0:
.string "Hello World!"
.text
.globl main
.type main, @function
main:
.LFB1522:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rsi
leaq _ZSt4cout(%rip), %rdi
call
위의 코드에서 _ZSt... 섹션은 포인터의 초기화를 나타내며 코드의 주요 부분은 .LC0 및 메인 섹션에서 수행됩니다. LC0에는 함수에 대한 정보와 실행 위치(이 경우 주 함수)가 있습니다. 기본 섹션에서 데이터 및 포인터 처리에 대해 조금 볼 수 있습니다. 흥미로운 사실은 모두 어셈블리로 인코딩된다는 것입니다.
이제 약간 복잡한 것을 살펴보겠습니다(당황하지 마세요 xD). 숫자 2개 추가!
#include <iostream>
using namespace std;
int main()
{
int a=5, b=10;
cout << a+b << "\n";
return 0;
}
-S 플래그로 프로그램을 컴파일하면 어셈블리 파일을 얻을 수 있습니다. 그것에 대한 중요한 세부 사항을 살펴 보겠습니다.
main:
.LFB1522:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $5, -8(%rbp) <-- NOTE HERE
movl $10, -4(%rbp) <-- NOTE HERE
movl -8(%rbp), %edx
movl -4(%rbp), %eax
addl %edx, %eax
movl %eax, %esi
leaq _ZSt4cout(%rip), %rdi
call _ZNSolsEi@PLT
leaq .LC0(%rip), %rsi
movq %rax, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
위의 주요 섹션에서 레지스터가 값 5와 10을 저장하는 데 사용되고 있음을 알 수 있습니다. 자세히 살펴보겠습니다...
movl $5, -8(%rbp)
movl $10, -4(%rbp)
따라서 값은 대상 레지스터로 이동하고 결국 어셈블리 코드의 다른 부분에서 작업을 수행하도록 만들어집니다.
이것은 일반적으로 C 또는 C++가 어셈블리 모드로 컴파일되는 방법이며 빙하의 상위 레이어만 표시했습니다(예, 약간 깊습니다!). 또한 어셈블리 모드 등에서 실행하는 동안 액세스하기 위해 포인터를 사용하는 방법에 대한 블로그 게시물도 곧 공유할 예정입니다. 그때까지, 나중에 보자! ;)
Reference
이 문제에 관하여(C/C++ 코드는 시스템에서 어떻게 컴파일됩니까?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/kcdchennai/how-a-cc-code-is-compiled-in-a-system-5ad4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)