程序员的自我修养-链接装载库--阅读笔记

  • 技术优劣取决于需求

2. 编译和链接

2.1 编译过程

在linux下,当我门用GCC来编译helloworld.c程序时,只需使用最简单的命令即可

#include <stdio.h>

int main(){
    printf("hello world\n");
    return 0;
}
$ gcc hello.c
$ ./a.out

上述过程可以分解为4个步骤:

  1. 预处理Preprecessing
    处理#开头的预编译指令,如#include,#define
    使用命令:gcc -E hello.c -o hello.i
  2. 编译 Compilation
    将预处理完的文件经过一系列的词法分析,语法分析,语义分析,优化后生成对应的汇编代码文件
    使用命令:gcc -S hello.i -o hello.s
  3. 汇编 Assembly
    将汇编代码转为机器可以执行的二进制文件
    使用命令:gcc -hello.s -o hello.o
  4. 链接 Linking
    ld -static **** hello.o ***

2.2 编译器做了啥

  1. 词法分析
  2. 语法分析
  3. 语义分析
  4. 中间语言生成
  5. 目标代码生成

2.3 链接器

程序设计模块化

2.4 模块拼接–静态链接

一个复杂的软件,人们把每个源代码模块独立的编译,然后按照需要将他们组装起来,这个组装模块的过程就是链接。从原理上来讲,链接就是把一些指令对其他符号地址的引用加以修改

3. 目标文件

3.1 目标文件格式

目标文件格式:
windows:PE(Portable Executable)
Linux:ELF(Executable Linkable Format)
目标文件就是源代码编译后但是未进行链接的那些中间文件(linux下为***.o),他跟可执行文件很相近

不光是可执行文件(win下的.exe和linux下的ELF可执行文件)按照可执行文件格式存储。动态链接库(DLL)(win下的.dll和linux的.so)以及静态链接库(win下的lib和linux下的.a)都按照可执行文件格式存储
具体可以参考下面的表格:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.2 目标文件什么样的

int printf(const char* format, ...);
int global_init_var = 84;
int global_uninit_var;

void func1(int i)
{
    printf("%d\n", i);
}

int main(void)
{
    static int static_var = 85;
    static int static_var2;
    int a =1;
    int b;
    func1(static_var + static_var2 + a + b);
    return a;
}

在这里插入图片描述

3.3 挖掘目标文件

可以使用objdump查看目标文件内部的结构:

lighthouse@VM-4-12-ubuntu:~/test$ objdump -h SimpleSection.o

SimpleSection.o:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00000057  0000000000000000  0000000000000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000008  0000000000000000  0000000000000000  00000098  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000004  0000000000000000  0000000000000000  000000a0  2**2
                  ALLOC
  3 .rodata       00000004  0000000000000000  0000000000000000  000000a0  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002a  0000000000000000  0000000000000000  000000a4  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  0000000000000000  0000000000000000  000000ce  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000058  0000000000000000  0000000000000000  000000d0  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

除了objdump之外还有readelf可以读取obj文件。

备注:
.note.GNU-stack: 堆栈提示段
.comment: 注释信息段
.rodata: 只读数据段

可以通过size查看ELF文件的不同段的大小

ighthouse@VM-4-12-ubuntu:~/test$ size SimpleSection.o
   text    data     bss     dec     hex filename
    179       8       4     191      bf SimpleSection.o

3.3.1 代码段

反汇编代码

#-s 将段内容打印出来
#-d 反汇编
 objdump -s -d SimpleSection.o

3.3.2 数据段

3.4 ELF文件结构

3.4.1 文件头

可以使用readelf来查看elf文件

lighthouse@VM-4-12-ubuntu:~/test$ readelf -h SimpleSection.o
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file) # 文件类型
  Machine:                           Advanced Micro Devices X86-64 #机器类型
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          1104 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (by在这里插入代码片tes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         13
  Section header string table index: 12

3.4.2 段表

段表位置有ELF文件头中的e_shoff成员决定,比如上个elf文件头重显示的“Start of section headers: 1104 (bytes into file)”
在这里插入图片描述
我们可以使用readelf工具来查看ELF文件的段,会显示完整的段信息:

lighthouse@VM-4-12-ubuntu:~/test$ readelf -S SimpleSection.o
There are 13 section headers, starting at offset 0x450:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000057  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000340
       0000000000000078  0000000000000018   I      10     1     8
  [ 3] .data             PROGBITS         0000000000000000  00000098
       0000000000000008  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  000000a0
       0000000000000004  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  000000a0
       0000000000000004  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  000000a4
       000000000000002a  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000ce
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  000000d0
       0000000000000058  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  000003b8
       0000000000000030  0000000000000018   I      10     8     8
  [10] .symtab           SYMTAB           0000000000000000  00000128
       0000000000000198  0000000000000018          11    11     8
  [11] .strtab           STRTAB           0000000000000000  000002c0
       000000000000007c  0000000000000000           0     0     1
  [12] .shstrtab         STRTAB           0000000000000000  000003e8
       0000000000000061  0000000000000000           0     0     1

3.4.3 重定位表

.rel.text

3.4.4 字符串表

3.5 链接的接口–符号

目标文件之间拼合实际上是目标文件之间对地址的饮用,即对函数和变量地址的饮用

3.5.1 符号表

.symtab
在这里插入图片描述

3.5.3 符号修饰

函数签名:包含了一个函数的信息,包括函数名、参数类型、所在的类和命名空间。用于识别不同的函数。

3.5.4 extern “c”

c++编译器会在编译c++程序时默认使用extern “C”,但是c语言的时候会直接调用memset

#ifdef __cplusplus
extern "C" {
#endif

void *memset (void *, int, size_t)

#ifdef __cplusplus
}
#endif

3.5.5 弱符号与强符号

3.6 调试信息

4 静态链接

6. 可执行文件的装载和进程

看不下去了,换一个看:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值