C++编译问题

1、编译过程

        编译是将高级编程语言编写的源代码转换为可执行程序的一系列步骤。对每一个*.cpp文件、*.c文件都会生成一个对应的目标文件(*.o结尾),再经过链接操作,生成最终的可执行程序。以test.cpp为例,介绍编译过程中的四部分:

#include 
int main()
{
    std::cout << "Hello World" << std::endl;
}

1.1、预处理

        在这个阶段,预处理器会根据预处理指令对源代码进行处理。预处理指令以#开头,常见的指令包括:

   (1)#include:将指定的头文件内容插入到当前位置。例如,#include  会将标准输入输出头文件的内容包含进来,以便程序可以使用其中定义的函数和变量。

   (2)#define:定义宏。宏可以是常量、表达式或代码片段的替换。例如,#define PI 3.14159定义了一个名为PI的宏,在后续的代码中,只要出现PI,就会被替换为3.14159

   (3)#ifdef#ifndef#endif:用于条件编译。根据特定的条件决定是否包含某些代码。例如,可以使用这些指令来确保某些代码只在特定的平台或配置下被编译。

预处理后的结果是一个经过扩展和替换的源代码文件,通常以.i为扩展名。

[root@abcd /aaaa/]# 
[root@abcd /aaaa/]# g++ -E test.cpp -o a.i
[root@abcd /aaaa/]#

1.2、编译

        编译阶段将预处理后的源代码转换为汇编语言代码。编译器会对源代码进行语法分析、语义分析和优化,以确保代码的正确性和高效性。

        (1)语法分析:检查代码是否符合编程语言的语法规则。如果发现语法错误,编译器会报告错误信息,编译过程停止。

        (2)语义分析:检查代码的语义是否正确,例如变量的类型是否匹配、函数的调用是否合法等。如果发现语义错误,编译器也会报告错误信息。

        (3)优化:编译器会对代码进行优化,以提高程序的执行效率。优化的方式包括去除不必要的代码、合并重复的计算、调整指令顺序等。

编译后的结果是汇编语言代码文件,通常以.s为扩展名。

[root@abcd /aaaa/]# 
[root@abcd /aaaa/]# g++ -S a.i -o a.s
[root@abcd /aaaa/]#

1.3、汇编

        汇编阶段将汇编语言代码转换为机器语言代码。汇编器会将汇编指令转换为对应的机器指令,并生成目标文件。

        目标文件包含了机器语言代码、数据和符号表等信息。符号表记录了程序中的变量、函数和标签等符号的名称和地址。

目标文件通常以.o为扩展名。

[root@abcd /aaaa/]# 
[root@abcd /aaaa/]# g++ -c a.s -o a.o
[root@abcd /aaaa/]#

1.4、链接

        链接阶段将多个目标文件和库文件组合成一个可执行程序。链接器会解决目标文件之间的引用关系,将它们合并成一个完整的程序。

        (1)符号解析:链接器会解析目标文件中的符号引用,将它们与其他目标文件或库文件中的定义进行匹配。如果找不到符号的定义,链接器会报告错误信息。

        (2)重定位:链接器会调整目标文件中的地址,以确保程序在运行时能够正确地访问内存中的数据和代码。例如,变量的地址可能会在链接过程中被确定,链接器会将对变量的引用调整为正确的地址。

链接后的结果是一个可执行程序文件,可以在操作系统上运行。

[root@abcd /aaaa/]# 
[root@abcd /aaaa/]# ./a
Hello World
[root@abcd /aaaa/]# 
[root@abcd /aaaa/]# 
[root@abcd /aaaa/]# 
[root@abcd /aaaa/]# g++  test.cpp -o a -v
Using built-in specs.
COLLECT_GCC=g++
............
............
COMPILER_PATH=..................
LIBRARY_PATH=...................
............
............
[root@abcd /aaaa/]# ./a
Hello World
[root@abcd /aaaa/]# 

2、编译过程中的错误

在整个编译过程中,会出现不同的编译报错。

错误描述 阶段 描述
头文件找不到 第1阶段:预处理
语法错误 第2阶段:编译
符号未定义 第4阶段:链接
符号重定义 第4阶段:链接
recompile with -fPIC 第4阶段:链接

3、编译结果相关查询

3.1、readelf 命令

    readelf 是一个在 Linux 系统中非常实用的命令行工具,主要用于显示 ELF(Executable and Linkable Format)格式文件的详细信息。ELF 格式是 Linux 下可执行文件、目标文件、共享库等的标准文件格式。

1、readelf -h 
    显示帮助信息。


[root@shell]
[root@shell]
[root@shell] readelf -h
readelf: Warning: Nothing to do.
Usage: readelf  elf-file(s)
 Display information about the contents of ELF format files
 Options are:
  -a --all               Equivalent to: -h -l -S -s -r -d -V -A -I
  -h --file-header       Display the ELF file header
  -l --program-headers   Display the program headers
     --segments          An alias for --program-headers
  -S --section-headers   Display the sections' header
     --sections          An alias for --section-headers
  -g --section-groups    Display the section groups
  -t --section-details   Display the section details
  -e --headers           Equivalent to: -h -l -S
  -s --syms              Display the symbol table
     --symbols           An alias for --syms
  --dyn-syms             Display the dynamic symbol table
  -n --notes             Display the core notes (if present)
  -r --relocs            Display the relocations (if present)
  -u --unwind            Display the unwind info (if present)
  -d --dynamic           Display the dynamic section (if present)
  -V --version-info      Display the version sections (if present)
  -A --arch-specific     Display architecture specific information (if any)
  -c --archive-index     Display the symbol/file index in an archive
  -D --use-dynamic       Use the dynamic section info when displaying symbols
  -x --hex-dump=
                         Dump the contents of section  as bytes
  -p --string-dump=
                         Dump the contents of section  as strings
  -R --relocated-dump=
                         Dump the contents of section  as relocated bytes
  -z --decompress        Decompress section before dumping it
  -w[lLiaprmfFsoRt] or
  --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
               =frames-interp,=str,=loc,=Ranges,=pubtypes,
               =gdb_index,=trace_info,=trace_abbrev,=trace_aranges,
               =addr,=cu_index]
                         Display the contents of DWARF2 debug sections
  --dwarf-depth=N        Do not display DIEs at depth N or greater
  --dwarf-start=N        Display DIEs starting with N, at the same depth
                         or deeper
  -I --histogram         Display histogram of bucket list lengths
  -W --wide              Allow output width to exceed 80 characters  可以显示详细信息
  @                Read options from 
  -H --help              Display this information
  -v --version           Display the version number of readelf
[root@shell]
[root@shell]
[root@shell]
2、readelf -s xxxx.so
   readelf --syms xxxx.so
   readelf --syms xxxx.so | grep "function_name"
    此命令会显示 ELF 文件的符号表信息,符号表包含了文件中定义和引用的符号(如函数名、变量名等)。


[root@shell]
[root@shell]
[root@shell] readelf -s test.so 

Symbol table '.dynsym' contains 5123 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit@LIBC (2)
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize@LIBC (2)
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_memclr8
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@LIBC (2)
     5: 00000000     0 OBJECT  GLOBAL DEFAULT  UND __stack_chk_guard@LIBC (2)
     6: 00000000     0 FUNC    GLOBAL DEFAULT  UND __vsnprintf_chk@LIBC (2)
     7: 00000000     0 OBJECT  GLOBAL DEFAULT  UND __sF@LIBC (2)
     8: 00000000     0 FUNC    GLOBAL DEFAULT  UND fputs@LIBC (2)
     9: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_memcpy
    10: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_print
    11: 00000000     0 FUNC    GLOBAL DEFAULT  UND __strlen_chk@LIBC (2)
    12: 00000000     0 FUNC    GLOBAL DEFAULT  UND ceilf@LIBC (3)
    13: 00000000     0 FUNC    GLOBAL DEFAULT  UND memchr@LIBC (2)
    14: 00000000     0 FUNC    GLOBAL DEFAULT  UND memcmp@LIBC (2)
    15: 00000000     0 FUNC    GLOBAL DEFAULT  UND strlen@LIBC (2)
    16: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_memclr4
    17: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_memcpy4
    18: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_memmove
    19: 00000000     0 FUNC    GLOBAL DEFAULT  UND sscanf@LIBC (2)
    20: 00000000     0 FUNC    GLOBAL DEFAULT  UND vsnprintf@LIBC (2)
    21: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_memclr
    22: 00000000     0 FUNC    GLOBAL DEFAULT  UND log@LIBC (3)
    23: 00000000     0 FUNC    GLOBAL DEFAULT  UND logf@LIBC (3)
    24: 00000000     0 FUNC    GLOBAL DEFAULT  UND expf@LIBC (3)
    25: 00000000     0 FUNC    GLOBAL DEFAULT  UND log1pf@LIBC (3)
    26: 00000000     0 FUNC    GLOBAL DEFAULT  UND strcmp@LIBC (2)
..................

[root@shell]
[root@shell]
3、readelf -d xxxx.so
    该命令会显示 ELF 文件的动态符号表信息,动态符号表主要用于动态链接过程。显示连接了哪些动态库文件。


[root@shell]
[root@shell]
[root@shell] readelf -d test.so 

Dynamic section at offset 0x20545c contains 29 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [liblog.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x0000000e (SONAME)                     Library soname: [test.so]
 0x0000001e (FLAGS)                      SYMBOLIC BIND_NOW
 0x6ffffffb (FLAGS_1)                    Flags: NOW
 0x00000011 (REL)                        0x72c34
 0x00000012 (RELSZ)                      35184 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffa (RELCOUNT)                   4387
 0x00000017 (JMPREL)                     0x87bdc
 0x00000002 (PLTRELSZ)                   1424 (bytes)
 0x00000003 (PLTGOT)                     0x206924
 0x00000014 (PLTREL)                     REL
 0x00000006 (SYMTAB)                     0x210
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000005 (STRTAB)                     0x28b58
 0x0000000a (STRSZ)                      303321 (bytes)
 0x6ffffef5 (GNU_HASH)                   0x16aa8
 0x00000004 (HASH)                       0x1eb38
 0x00000019 (INIT_ARRAY)                 0x2062f4
 0x0000001b (INIT_ARRAYSZ)               360 (bytes)
 0x0000001a (FINI_ARRAY)                 0x2062ec
 0x0000001c (FINI_ARRAYSZ)               8 (bytes)
 0x6ffffff0 (VERSYM)                     0x14240
 0x6ffffffe (VERNEED)                    0x16a48
 0x6fffffff (VERNEEDNUM)                 3
 0x00000000 (NULL)                       0x0
[root@shell]
[root@shell]

3.2、nm命令

    nm 是一个在 Unix 和类 Unix 系统(如 Linux)中常用的命令行工具,主要用于列出目标文件(包括可执行文件、目标文件、共享库等)中的符号表信息。符号表记录了文件中定义和引用的符号,如函数名、变量名等

1、nm -h 
    显示帮助信息。


[root@shell]
[root@shell]
[root@shell] nm -h

Usage: nm [option(s)] [file(s)]
 List symbols in [file(s)] (a.out by default).
 The options are:
  -a, --debug-syms       Display debugger-only symbols
  -A, --print-file-name  Print name of the input file before every symbol
  -B                     Same as --format=bsd
  -C, --demangle[=STYLE] Decode low-level symbol names into user-level names
                          The STYLE, if specified, can be `auto' (the default),
                          `gnu', `lucid', `arm', `hp', `edg', `gnu-v3', `java'
                          or `gnat'
      --no-demangle      Do not demangle low-level symbol names
  -D, --dynamic          Display dynamic symbols instead of normal symbols
      --defined-only     Display only defined symbols
  -e                     (ignored)
  -f, --format=FORMAT    Use the output format FORMAT.  FORMAT can be `bsd',
                           `sysv' or `posix'.  The default is `bsd'
  -g, --extern-only      Display only external symbols
  -l, --line-numbers     Use debugging information to find a filename and
                           line number for each symbol
  -n, --numeric-sort     Sort symbols numerically by address
  -o                     Same as -A
  -p, --no-sort          Do not sort the symbols
  -P, --portability      Same as --format=posix
  -r, --reverse-sort     Reverse the sense of the sort
      --plugin NAME      Load the specified plugin
  -S, --print-size       Print size of defined symbols
  -s, --print-armap      Include index for symbols from archive members
      --size-sort        Sort symbols by size
      --special-syms     Include special symbols in the output
      --synthetic        Display synthetic symbols as well
  -t, --radix=RADIX      Use RADIX for printing symbol values
      --target=BFDNAME   Specify the target object format as BFDNAME
  -u, --undefined-only   Display only undefined symbols
  -X 32_64               (ignored)
  @FILE                  Read options from FILE
  -h, --help             Display this information
  -V, --version          Display this program's version number

nm: supported targets: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 a.out-i386-linux pei-i386 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big plugin srec symbolsrec verilog tekhex binary ihex
Report bugs to .

[root@shell]
[root@shell]
[root@shell]

4、符号表解释

[root@shell]
[root@shell] readelf test.so --syms -W | grep simple
  2025: 00140093   112 FUNC    GLOBAL DEFAULT   14 _ZN4test_hh13HyStudClass19simpleERKNS_10VectorBaseIfEERS2_ia
  3860: 00140129    56 FUNC    GLOBAL DEFAULT   14 _ZN4test_hh13HyStudClass19simpleERKNS_10MatrixBaseIfEERNS_10VectorBaseIfEEia
[root@shell]
[root@shell]

每行内容包含以下几个部分:

        序号: 地址       大小    类型    绑定信息  可见性   所在节索引  符号名称

(1)序号

        如 2025、3860 等,这是符号在符号表中的索引,用于唯一标识该符号在符号表中的位置。

(2)地址

        像 00140093、00140129 这类,代表符号在内存中的地址。在程序加载到内存后,该地址指向符号(如函数、变量)实际存储的位置。

(3)大小

        例如 112、56 等,指的是符号所占用的内存字节数。对于函数而言,就是函数代码的大小;对于变量,就是变量占用的存储空间大小。

(4)类型

        FUNC 表示该符号是一个函数。除了 FUNC,常见的类型还有:

        OBJECT:代表变量或者数据对象。

        NOTYPE:表示无法确定类型或者没有类型信息。

(5)绑定信息

        GLOBAL 表明该符号是全局可见的。全局符号可以在不同的目标文件或者库中被引用,与之相对的是 LOCAL 符号,LOCAL 符号只能在定义它的文件内部使用。

(6)可见性

        DEFAULT 是默认的可见性设置,意味着该符号遵循标准的链接和可见性规则。

(7)所在节索引

        14 表示该符号位于目标文件的第 14 个节(section)中。节是 ELF 文件里的基本组成单元,不同的节存储不同类型的数据,例如 .text 节存储代码,.data 节存储已初始化的数据等。

(8)符号名称

        像 _ZN4test_hh13HyStudClass19simpleERKNS_10VectorBaseIfEERS2_ia 这类,这是经过 C++ 名称修饰(Name Mangling)后的符号名。C++ 为了支持函数重载和命名空间等特性,会对函数名和类名进行修饰,从而保证符号名的唯一性。你可以使用 c++filt 工具对这些修饰后的名称进行还原。如下:

[root@shell]
[root@shell] echo "_ZN4test_hh13HyStudClass19simpleERKNS_10VectorBaseIfEERS2_ia" | c++filt
test_hh::HyStudClass::simple(esis::VectorBase const&, test_hh::VectorBase&, int, signed char)
[root@shell]
[root@shell]

你可能感兴趣的:(Linux共同学习,linux,运维,服务器)