Linux 프로그램 컴파일 과정에 대한 자세한 설명
컴퓨터 프로그래밍 언어는 일반적으로 기계어, 어셈블리 언어, 고급 언어의 세 가지 범주로 나뉩니다. 고급 언어를 실행하려면 먼저 기계어로 번역해야 합니다. 번역에는 컴파일 방식과 해석 방식이 있습니다.
그래서 우리는 기본적으로 고급 언어를 두 가지 범주로 나눕니다. 하나는 C, C++, Java와 같은 컴파일 언어이고 다른 하나는 Python, Ruby, MATLAB 및 JavaScript와 같은 해석 언어입니다.
이 기사에서는 다음 네 단계를 포함하여 C/C++ 언어로 작성된 고급 프로그램을 프로세서에서 실행할 수 있는 바이너리 코드로 변환하는 프로세스를 소개합니다.
- 전처리
- 편집
- 조립
- 연결

GCC 도구 체인 소개
흔히 GCC라고 부르는 것은 GUN Compiler Collection의 약자로 Linux 시스템에서 흔히 사용되는 컴파일 도구입니다. GCC 도구 체인 소프트웨어에는 GCC, Binutils, C 런타임 라이브러리 등이 포함됩니다.
GCC
GCC(GNU C 컴파일러)는 컴파일 도구입니다. 이번 글에서는 C/C++ 언어로 작성된 프로그램을 프로세서에서 실행할 수 있는 바이너리 코드로 변환하고, 컴파일러에서 완성하는 과정을 소개하겠습니다.
Binutils
addr2line, ar, objcopy, objdump, as, ld, ldd, readelf, size 등을 포함한 바이너리 프로그램 처리 도구 세트 이 도구 세트는 개발 및 디버깅에 없어서는 안될 도구입니다. 각각의 소개는 다음과 같습니다.
- addr2line: 프로그램 주소를 해당 프로그램 소스 파일 및 해당 코드 라인으로 변환하고 해당 기능을 얻는 데 사용됩니다. 이 도구는 디버거가 디버깅 중에 해당 소스 코드 위치를 찾는 데 도움이 됩니다.
- as : 주로 조립에 사용됩니다. 조립에 대한 자세한 소개는 다음 기사를 참조하십시오.
- ld: 주로 링크에 사용됩니다. 링크에 대한 자세한 내용은 다음 문서를 참조하세요.
- ar: 주로 정적 라이브러리를 만드는 데 사용됩니다. 초보자가 쉽게 이해할 수 있도록 동적 라이브러리와 정적 라이브러리의 개념을 소개합니다.
- 여러 .o 대상 파일을 하나의 라이브러리 파일로 생성하려는 경우 두 가지 유형의 라이브러리가 있습니다. 하나는 정적 라이브러리이고 다른 하나는 동적 라이브러리입니다.
- Windows에서 정적 라이브러리는 .lib 접미사가 붙은 파일이고, 공유 라이브러리는 .dll 접미사가 붙는 파일입니다. Linux에서 정적 라이브러리는 접미사가 .a인 파일이고, 공유 라이브러리는 접미사가 .so인 파일입니다.
- 정적 라이브러리와 동적 라이브러리의 차이점은 코드가 로드되는 시간에 있습니다. 정적 라이브러리의 코드는 컴파일 과정에서 실행 프로그램에 로드되므로 크기가 더 커집니다. 공유 라이브러리의 코드는 실행 프로그램이 실행될 때 메모리에 로드되고 컴파일 과정에서만 참조되므로 코드 크기가 더 작습니다. Linux 시스템에서는 ldd 명령을 사용하여 실행 프로그램이 의존하는 공유 라이브러리를 볼 수 있습니다.
- 시스템에 동시에 실행해야 하는 여러 프로그램이 있고 이러한 프로그램 간에 공유 라이브러리가 있는 경우 동적 라이브러리를 사용하면 더 많은 메모리가 절약됩니다.
- ldd: 실행 프로그램이 의존하는 공유 라이브러리를 보는 데 사용할 수 있습니다.
- objcopy: .bin을 .elf로 변환하거나 .elf를 .bin으로 변환하는 등 개체 파일을 다른 형식으로 변환합니다.
- objdump: 주요 기능은 분해입니다. 분해에 대한 자세한 소개는 다음 문서를 참조하세요.
- readelf: ELF 파일에 대한 정보를 표시합니다. 자세한 내용은 나중에 참조하세요.
- 크기: 코드 세그먼트, 데이터 세그먼트, 전체 크기 등을 포함하여 실행 파일의 각 부분의 크기와 전체 크기를 나열합니다. 크기 사용에 대한 구체적인 사용 예는 다음 문서를 참조하세요.
C 런타임 라이브러리
C 언어 표준은 주로 두 부분으로 구성됩니다. 한 부분은 C의 구문을 설명하고 다른 부분은 C 표준 라이브러리를 설명합니다. C 표준 라이브러리는 표준 헤더 파일 세트를 정의합니다. 각 헤더 파일에는 일부 관련 함수, 변수, 유형 선언 및 매크로 정의가 포함되어 있습니다. 예를 들어, 공통 printf 함수는 C 표준 라이브러리 함수이고 해당 프로토타입은 stdio에 정의되어 있습니다. 헤더 파일.
C语言标准仅仅定义了C标准库函数原型,并没有提供实现。因此,C语言编译器通常需要一个C运行时库(C Run Time Libray,CRT)的支持。C运行时库又常简称为C运行库。与C语言类似,C++也定义了自己的标准,同时提供相关支持库,称为C++运行时库。
准备工作
由于GCC工具链主要是在Linux环境中进行使用,因此本文也将以Linux系统作为工作环境。为了能够演示编译的整个过程,本节先准备一个C语言编写的简单Hello程序作为示例,其源代码如下所示:
#include //此程序很简单,仅仅打印一个Hello World的字符串。 int main(void) { printf("Hello World! \n"); return 0; }
“
编译过程
1.预处理
预处理的过程主要包括以下过程:
- 将所有的#define删除,并且展开所有的宏定义,并且处理所有的条件预编译指令,比如#if #ifdef #elif #else #endif等。
- 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。
- 删除所有注释“//”和“/* */”。
- 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
- 保留所有的#pragma编译器指令,后续编译过程需要使用它们。
使用gcc进行预处理的命令如下:
$ gcc -E hello.c -o hello.i // 将源文件hello.c文件预处理生成hello.i // GCC的选项-E使GCC在进行完预处理后即停止
hello.i文件可以作为普通文本文件打开进行查看,其代码片段如下所示:
// hello.i代码片段 extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); # 942 "/usr/include/stdio.h" 3 4 # 2 "hello.c" 2 # 3 "hello.c" int main(void) { printf("Hello World!" "\n"); return 0; }
2.编译
编译过程就是对预处理完的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码。
使用gcc进行编译的命令如下:
$ gcc -S hello.i -o hello.s // 将预处理生成的hello.i文件编译生成汇编程序hello.s // GCC的选项-S使GCC在执行完编译后停止,生成汇编程序
上述命令生成的汇编程序hello.s的代码片段如下所示,其全部为汇编代码。
// hello.s代码片段 main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $.LC0, %edi call puts movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc
3.汇编
汇编过程调用对汇编代码进行处理,生成处理器能识别的指令,保存在后缀为.o的目标文件中。由于每一个汇编语句几乎都对应一条处理器指令,因此,汇编相对于编译过程比较简单,通过调用Binutils中的汇编器as根据汇编指令和处理器指令的对照表一一翻译即可。
当程序由多个源代码文件构成时,每个文件都要先完成汇编工作,生成.o目标文件后,才能进入下一步的链接工作。注意:目标文件已经是最终程序的某一部分了,但是在链接之前还不能执行。
使用gcc进行汇编的命令如下:
$ gcc -c hello.s -o hello.o // 将编译生成的hello.s文件汇编生成目标文件hello.o // GCC的选项-c使GCC在执行完汇编后停止,生成目标文件 //或者直接调用as进行汇编 $ as -c hello.s -o hello.o //使用Binutils中的as将hello.s文件汇编生成目标文件
注意:hello.o目标文件为ELF(Executable and Linkable Format)格式的可重定向文件。
4.链接
链接也分为静态链接和动态链接,其要点如下:
- 静态链接是指在编译阶段直接把静态库加入到可执行文件中去,这样可执行文件会比较大。链接器将函数的代码从其所在地(不同的目标文件或静态链接库中)拷贝到最终的可执行程序中。为创建可执行文件,链接器必须要完成的主要任务是:符号解析(把目标文件中符号的定义和引用联系起来)和重定位(把符号定义和内存地址对应起来然后修改所有对符号的引用)。
- 动态链接则是指链接阶段仅仅只加入一些描述信息,而程序执行时再从系统中把相应动态库加载到内存中去。
- 在Linux系统中,gcc编译链接时的动态库搜索路径的顺序通常为:首先从gcc命令的参数-L指定的路径寻找;再从环境变量LIBRARY_PATH指定的路径寻址;再从默认路径/lib、/usr/lib、/usr/local/lib寻找。
- 在Linux系统中,执行二进制文件时的动态库搜索路径的顺序通常为:首先搜索编译目标代码时指定的动态库搜索路径;再从环境变量LD_LIBRARY_PATH指定的路径寻址;再从配置文件/etc/ld.so.conf中指定的动态库搜索路径;再从默认路径/lib、/usr/lib寻找。
- 在Linux系统中,可以用ldd命令查看一个可执行程序依赖的共享库。
由于链接动态库和静态库的路径可能有重合,所以如果在路径中有同名的静态库文件和动态库文件,比如libtest.a和libtest.so,gcc链接时默认优先选择动态库,会链接libtest.so,如果要让gcc选择链接libtest.a则可以指定gcc选项-static,该选项会强制使用静态库进行链接。以Hello World为例:
- 如果使用命令“gcc hello.c -o hello”则会使用动态库进行链接,生成的ELF可执行文件的大小(使用Binutils的size命令查看)和链接的动态库(使用Binutils的ldd命令查看)如下所示:
$ gcc hello.c -o hello $ size hello //使用size查看大小 text data bss dec hex filename 1183 552 8 1743 6cf hello $ ldd hello //可以看出该可执行文件链接了很多其他动态库,主要是Linux的glibc动态库 linux-vdso.so.1 => (0x00007fffefd7c000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fadcdd82000) /lib64/ld-linux-x86-64.so.2 (0x00007fadce14c000)
如果使用命令“gcc -static hello.c -o hello”则会使用静态库进行链接,生成的ELF可执行文件的大小(使用Binutils的size命令查看)和链接的动态库(使用Binutils的ldd命令查看)如下所示:
$ gcc -static hello.c -o hello $ size hello //使用size查看大小 text data bss dec hex filename 823726 7284 6360 837370 cc6fa hello //可以看出text的代码尺寸变得极大 $ ldd hello not a dynamic executable //说明没有链接动态库
链接器链接后生成的最终文件为ELF格式可执行文件,一个ELF可执行文件通常被链接为不同的段,常见的段譬如.text、.data、.rodata、.bss等段。
分析ELF文件
1.ELF文件的段
ELF文件格式如下图所示,位于ELF Header和Section Header Table之间的都是段(Section)。一个典型的ELF文件包含下面几个段:
- .text:已编译程序的指令代码段。
- .rodata:ro代表read only,即只读数据(譬如常数const)。
- .data:已初始化的C程序全局变量和静态局部变量。
- .bss:未初始化的C程序全局变量和静态局部变量。
- .debug:调试符号表,调试器用此段的信息帮助调试。

可以使用readelf -S查看其各个section的信息如下
$ readelf -S hello There are 31 section headers, starting at offset 0x19d8: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 …… [11] .init PROGBITS 00000000004003c8 000003c8 000000000000001a 0000000000000000 AX 0 0 4 …… [14] .text PROGBITS 0000000000400430 00000430 0000000000000182 0000000000000000 AX 0 0 16 [15] .fini PROGBITS 00000000004005b4 000005b4 ……
2.反汇编ELF
由于ELF文件无法被当做普通文本文件打开,如果希望直接查看一个ELF文件包含的指令和数据,需要使用反汇编的方法。
使用objdump -D对其进行反汇编如下:
$ objdump -D hello …… 0000000000400526 : // main标签的PC地址 //PC地址:指令编码 指令的汇编格式 400526: 55 push %rbp 400527: 48 89 e5 mov %rsp,%rbp 40052a: bf c4 05 40 00 mov $0x4005c4,%edi 40052f: e8 cc fe ff ff callq 400400 400534: b8 00 00 00 00 mov $0x0,%eax 400539: 5d pop %rbp 40053a: c3 retq 40053b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
使用objdump -S将其反汇编并且将其C语言源代码混合显示出来:
$ gcc -o hello -g hello.c //要加上-g选项 $ objdump -S hello …… 0000000000400526 : #include int main(void) { 400526: 55 push %rbp 400527: 48 89 e5 mov %rsp,%rbp printf("Hello World!" "\n"); 40052a: bf c4 05 40 00 mov $0x4005c4,%edi 40052f: e8 cc fe ff ff callq 400400 return 0; 400534: b8 00 00 00 00 mov $0x0,%eax } 400539: 5d pop %rbp 40053a: c3 retq 40053b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) ……
위 내용은 Linux 프로그램 컴파일 과정에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Linux 터미널에서 Python 버전을 보려고 할 때 Linux 터미널에서 Python 버전을 볼 때 권한 문제에 대한 솔루션 ... Python을 입력하십시오 ...

Docker 환경을 사용할 때 Docker 환경에 Extensions를 설치하기 위해 PECL을 사용하여 오류의 원인 및 솔루션. 종종 일부 두통이 발생합니다 ...

많은 웹 사이트 개발자는 램프 아키텍처에서 Node.js 또는 Python 서비스를 통합하는 문제에 직면 해 있습니다. 기존 램프 (Linux Apache MySQL PHP) 아키텍처 웹 사이트 요구 사항 ...

Linux 터미널에서 Python 사용 ...

Apscheduler 타이밍 작업을 MacOS 플랫폼의 서비스로 구성하십시오. Ngin과 유사한 APSCHEDULER 타이밍 작업을 서비스로 구성하려면 ...

언어의 멀티 스레딩은 프로그램 효율성을 크게 향상시킬 수 있습니다. C 언어에서 멀티 스레딩을 구현하는 4 가지 주요 방법이 있습니다. 독립 프로세스 생성 : 여러 독립적으로 실행되는 프로세스 생성, 각 프로세스에는 자체 메모리 공간이 있습니다. 의사-다일리트 레딩 : 동일한 메모리 공간을 공유하고 교대로 실행하는 프로세스에서 여러 실행 스트림을 만듭니다. 멀티 스레드 라이브러리 : PTHREADS와 같은 멀티 스레드 라이브러리를 사용하여 스레드를 만들고 관리하여 풍부한 스레드 작동 기능을 제공합니다. COROUTINE : 작업을 작은 하위 작업으로 나누고 차례로 실행하는 가벼운 다중 스레드 구현.

Linux 시스템과 함께 제공되는 Python 통역사를 제거하는 문제와 관련하여 많은 Linux 배포판이 설치 될 때 Python 통역사를 사전 설치하고 패키지 관리자를 사용하지 않습니다 ...

Web.xml 파일을 열려면 다음 방법을 사용할 수 있습니다. 텍스트 편집기 (예 : 메모장 또는 문자 메시지)를 사용하여 통합 개발 환경 (예 : Eclipse 또는 NetBeans)을 사용하여 명령을 편집하십시오 (Windows : Notepad Web.xml; Mac/Linux : Open -A Texted web.xml).
