検索
ホームページシステムチュートリアルLinuxLinuxプログラムのコンパイル処理を詳しく解説
Linuxプログラムのコンパイル処理を詳しく解説Feb 05, 2024 pm 01:09 PM
linuxLinuxチュートリアルLinuxシステムコンパイルエラーLinuxコマンドシェルスクリプト標準ライブラリ埋め込みLinux良い約束Linux を始めるLinux学習

コンピュータ プログラミング言語は、通常、機械語、アセンブリ言語、高級言語の 3 つのカテゴリに分類されます。高級言語は実行する前に機械語に翻訳する必要があり、翻訳にはコンパイルとインタプリタの 2 つの方法があります。

したがって、高級言語は基本的に 2 つのカテゴリに分類されます。1 つは C、C、Java などのコンパイル言語で、もう 1 つは Python、Ruby、MATLAB、JavaScript などのインタープリタ言語です。

この記事では、C/C 言語で書かれた高レベル プログラムをプロセッサで実行できるバイナリ コードに変換するプロセスを紹介します。これには 4 つのステップが含まれます。

    前処理
  • 編集
  • 組み立て######
  • リンク中
  • #GCC ツール チェーンの紹介
Linux 程序编译过程详解

一般に GCC と呼ばれるのは、GUN Compiler Collection の略称で、Linux システムで一般的に使用されるコンパイル ツールです。 GCC ツール チェーン ソフトウェアには、GCC、Binutils、C ランタイム ライブラリなどが含まれます。

GCC

GCC (GNU C Compiler) はコンパイル ツールです。この記事では、C/C言語で書かれたプログラムを、コンパイラによって完成される、プロセッサが実行できるバイナリコードに変換するプロセスを紹介します。

Binutils

addr2line、ar、objcopy、objdump、as、ld、ldd、readelf、size などを含むバイナリ プログラム処理ツールのセット。開発やデバッグに欠かせないツールセットで、それぞれの紹介は以下の通りです。 addr2line: プログラム アドレスを対応するプログラム ソース ファイルおよび対応するコード行に変換し、対応する関数を取得するために使用されます。このツールは、デバッガがデバッグ中に対応するソース コードの場所を特定するのに役立ちます。

as:主にアセンブリに使用されます。アセンブリの詳細な紹介については、次の記事を参照してください。
  • ld:主にリンクに使用します リンクについて詳しくは以下の記事をご覧ください。
  • ar: 主に静的ライブラリを作成するために使用されます。初心者が理解しやすいように、ここでは動的ライブラリと静的ライブラリの概念を紹介します。
  • 複数の .o オブジェクト ファイルを 1 つのライブラリ ファイルに生成する場合、2 種類のライブラリがあります。1 つは静的ライブラリ、もう 1 つは動的ライブラリです。
  • Windows では、静的ライブラリは .lib 接尾辞を持つファイルであり、共有ライブラリは .dll 接尾辞を持つファイルです。 Linux では、静的ライブラリは .a 接尾辞が付いているファイルであり、共有ライブラリは .so 接尾辞が付いているファイルです。
  • 静的ライブラリと動的ライブラリの違いは、コードがロードされる時間にあります。静的ライブラリのコードはコンパイル プロセス中に実行可能プログラムにロードされるため、サイズが大きくなります。共有ライブラリのコードは、実行可能プログラムの実行時にメモリにロードされ、コンパイル処理中に単純に参照されるだけであるため、コード サイズは小さくなります。 Linux システムでは、ldd コマンドを使用して、実行可能プログラムが依存する共有ライブラリを表示できます。
  • システム内に同時に実行する必要があるプログラムが複数あり、これらのプログラム間に共有ライブラリがある場合は、ダイナミック ライブラリを使用するとメモリがさらに節約されます。
  • ldd: 実行可能プログラムが依存する共有ライブラリを表示するために使用できます。
  • objcopy: オブジェクト ファイルを別の形式に変換します (.bin から .elf への変換、または .elf から .bin への変換など)。
  • objdump: その主な機能は逆アセンブリです。逆アセンブルの詳細については、次の記事を参照してください。
  • readelf: ELF ファイルに関する情報を表示します。詳細については、後述を参照してください。
  • size: 実行ファイルの各部分のサイズと合計サイズ、コードセグメント、データセグメント、合計サイズなどを一覧表示します。size の具体的な使用例については、以下の記事を参照してください。
  • C ランタイム ライブラリ
  • C 言語標準は主に 2 つの部分で構成されています。1 つの部分では C の構文について説明し、もう 1 つの部分では 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:调试符号表,调试器用此段的信息帮助调试。
    Linux 程序编译过程详解

    可以使用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 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事は良许Linux教程网で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
什么是linux设备节点什么是linux设备节点Apr 18, 2022 pm 08:10 PM

linux设备节点是应用程序和设备驱动程序沟通的一个桥梁;设备节点被创建在“/dev”,是连接内核与用户层的枢纽,相当于硬盘的inode一样的东西,记录了硬件设备的位置和信息。设备节点使用户可以与内核进行硬件的沟通,读写设备以及其他的操作。

Linux中open和fopen的区别有哪些Linux中open和fopen的区别有哪些Apr 29, 2022 pm 06:57 PM

区别:1、open是UNIX系统调用函数,而fopen是ANSIC标准中的C语言库函数;2、open的移植性没fopen好;3、fopen只能操纵普通正规文件,而open可以操作普通文件、网络套接字等;4、open无缓冲,fopen有缓冲。

linux中什么叫端口映射linux中什么叫端口映射May 09, 2022 pm 01:49 PM

端口映射又称端口转发,是指将外部主机的IP地址的端口映射到Intranet中的一台计算机,当用户访问外网IP的这个端口时,服务器自动将请求映射到对应局域网内部的机器上;可以通过使用动态或固定的公共网络IP路由ADSL宽带路由器来实现。

什么是linux交叉编译什么是linux交叉编译Apr 29, 2022 pm 06:47 PM

在linux中,交叉编译是指在一个平台上生成另一个平台上的可执行代码,即编译源代码的平台和执行源代码编译后程序的平台是两个不同的平台。使用交叉编译的原因:1、目标系统没有能力在其上进行本地编译;2、有能力进行源代码编译的平台与目标平台不同。

linux中eof是什么linux中eof是什么May 07, 2022 pm 04:26 PM

在linux中,eof是自定义终止符,是“END Of File”的缩写;因为是自定义的终止符,所以eof就不是固定的,可以随意的设置别名,linux中按“ctrl+d”就代表eof,eof一般会配合cat命令用于多行文本输出,指文件末尾。

linux怎么判断pcre是否安装linux怎么判断pcre是否安装May 09, 2022 pm 04:14 PM

在linux中,可以利用“rpm -qa pcre”命令判断pcre是否安装;rpm命令专门用于管理各项套件,使用该命令后,若结果中出现pcre的版本信息,则表示pcre已经安装,若没有出现版本信息,则表示没有安装pcre。

linux怎么查询mac地址linux怎么查询mac地址Apr 24, 2022 pm 08:01 PM

linux查询mac地址的方法:1、打开系统,在桌面中点击鼠标右键,选择“打开终端”;2、在终端中,执行“ifconfig”命令,查看输出结果,在输出信息第四行中紧跟“ether”单词后的字符串就是mac地址。

linux中rpc是什么意思linux中rpc是什么意思May 07, 2022 pm 04:48 PM

在linux中,rpc是远程过程调用的意思,是Reomote Procedure Call的缩写,特指一种隐藏了过程调用时实际通信细节的IPC方法;linux中通过RPC可以充分利用非共享内存的多处理器环境,提高系统资源的利用率。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

SublimeText3 英語版

SublimeText3 英語版

推奨: Win バージョン、コードプロンプトをサポート!

EditPlus 中国語クラック版

EditPlus 中国語クラック版

サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

SublimeText3 Linux 新バージョン

SublimeText3 Linux 新バージョン

SublimeText3 Linux 最新バージョン