首页 > 系统相关 >Linux(链接器的意义)

Linux(链接器的意义)

时间:2023-06-14 23:01:34浏览次数:45  
标签:脚本 文件 意义 SRAM 地址 Linux main 链接

(文章目录)


前言

本篇文章我们来讲解链接器的意义。

一、链接器概念介绍

链接器(Linker)是计算机编译器系统中的一个重要组成部分,它用于将编译后生成的目标模块(Object Module)链接在一起,生成可执行文件或动态链接库(Dynamic Linking Library)。

链接器的主要任务是将符号(Symbol)引用解析到符号定义上,将多个目标模块合并为一个可执行文件或动态链接库,生成符号表(Symbol Table),并对程序代码做最后的检查和优化。

在编译过程中,C、C++等程序源代码先经过编译器的处理,生成目标代码文件,然后由链接器将多个目标代码合并成单个可执行文件或动态链接库,以便于程序执行和使用。

二、目标文件

首先我们先编写两个文件一个是main.c一个是func.c。

main.c:

extern void func(void);

int g_a = 0;

int main(void)
{
    func();

    return 0;
}

func.c:

#include <stdio.h>


void func(void)
{
    printf("Hello World\n");
}


将这两个文件编译成目标文件.o

使用nm命令查看两个目标文件的信息: 在这里插入图片描述 在这里插入图片描述

通过nm命令我们可以发现这两个目标文件里面的各个段都是没有具体地址的。但是我们可以查看到具体的段大小。

目录文件需要注意的地方: 1.各个段没有具体的起始地址,只有段大小信息。 2.各个标识符没有实际地址,只有段中的相对地址。 3.段和标识符的实际地址需要链接器具体确定。

链接器的几个主要作用:

符号解析:将目标文件中的符号引用和定义进行匹配,并生成符号表信息,方便程序执行时进行符号地址绑定。

文件合并:将多个目标文件合并为单个可执行文件或动态链接库。

地址空间分配:将符号或变量分配到内存中的具体地址。

重定位:合并多个目标模块时,需要对地址进行重定位,以便在程序的正确地址空间中运行。

跳转补丁:对目标代码进行修补,以确保跳转地址的正确性和有效性。

使用链接器将他们链接起来: 在这里插入图片描述 链接完成后我们就可以查看到具体的段地址等信息了。

三、main函数是第一个被执行的函数吗?

在我们编写代码的时候第一个执行的函数就是main函数,那么很多人就可能会认为第一个执行的程序就是main函数,其实这是不正确的。下面我就来证明一下main函数不是第一个被执行的程序。

首先使用objdump -d 命令来生成一个汇编文件: 在这里插入图片描述 使用cat 命令来查看这个文件:

在_start这里我们可以查看到main函数: 这里也可以看到_start函数的地址:0000000000000530 在这里插入图片描述 使用objdump - h命令显示文件的段表,包括段的名称、大小、虚拟地址、文件偏移等信息。 这里可以看到text段和_start的段地址是一样的。所以我们可以证明执行代码的时候首先执行的是_start函数,而不是main函数。 在这里插入图片描述

我们查看_start里面的信息就可以知道程序开始时到底做了什么操作: 在这里插入图片描述 在这里插入图片描述

四、链接脚本的意义和作用

链接脚本(Linker Script)是用于指导链接器如何将目标文件链接生成最终的可执行文件或者动态链接库的脚本。链接脚本描述了代码的内存分配、数据的布局、初始化、以及其他一些链接时需要的信息。

链接脚本通常使用一种简单的编程语言,例如 GNU Linker 的链接脚本是使用类似 C 的语言写成的脚本。通常它包含了以下信息:

地址空间布局:链接脚本可以指定代码段、数据段、BSS段等在内存中的位置和大小,并决定它们的起始地址和结束地址。

符号表:链接脚本可以定义符号表,这些符号可以是全局变量、函数、常量等,它们将在链接过程中被绑定。

初始化和清除:链接器会按照链接脚本指定的顺序对段进行初始化,清除,或者进行其他的一些工作。

程序入口:链接脚本中可以指定程序入口,例如 main() 函数所在的地址。

链接脚本的主要作用是描述生成可执行文件或者动态链接库所需的各种相关信息,同时还可以完成其他一些链接时的必要处理,并附加一些必要的元数据,例如程序入口点和动态链接库函数符号等。通过使用链接脚本,程序员可以更加精细地控制生成的可执行文件或者动态链接库的各种属性,同时也可以有效地解决某些链接时需要的问题。

下面我们来编写一个链接脚本:

ENTRY(main)     /* 设置程序入口为 main 函数 */
SECTIONS {
    . = 0x08000000;  /* 指定起始地址 */

    /* 定义代码段,包含 .text 和 .rodata 段 */
    .text   ALIGN(4) :
    {
        *(.text)        /* 提取目标文件中的所有代码段 */
        *(.rodata)      /* 提取目标文件中的只读数据段 */
    } >FLASH

    /* 定义数据段,包含 .data 和 .bss 段 */
    .data   ALIGN(4) :
    {
        *(.data)        /* 提取目标文件中的所有数据段 */
    } >SRAM

    /* 定义bss段 */
    .bss (NOLOAD) :
    {
        _sbss = .;      /* 设置 bss 段的起始地址 */
        *(.bss)         /* 提取目标文件中的 bss 数据段 */
        *(COMMON)       /* 提取目标文件中的 common 数据段 */
        _ebss = .;      /* 设置 bss 段的结束地址 */
    } >SRAM

    /* 设置栈和堆 */
    _stack_end = ORIGIN(SRAM) + LENGTH(SRAM);  /* 设置栈的结束地址 */
    _heap_start = _stack_end;      /* 设置堆的起始地址 */
    _heap_end = ORIGIN(SRAM) + LENGTH(SRAM);    /* 设置堆的结束地址 */
}

此链接脚本是用于将代码段和只读数据段从FLASH存储器分配到0x08000000和数据段和BSS段从SRAM存储器分配到0x20000000。它还定义了堆和栈的位置,以及一些变量。

该脚本首先定义程序入口为 main 函数,然后使用 SECTIONS 区块来定义不同段的位置和大小。代码段和只读数据段使用 ALIGN 对齐到4字节,并通过 >FLASH 分配到 FLASH 存储器中。数据段使用 ALIGN 对齐到4字节,并通过 >SRAM 分配到 SRAM 存储器中。BSS段没有实际数据,仅分配段空间,通过 NOLOAD 让链接器不会从可执行文件中复制数据到RAM中从而节省存储空间,使用 _sbss 和 _ebss 定义BSS段起始和结束地址。最后,通过自定义变量来设置栈和堆的位置。

总结

本篇文章就讲解到这里了,希望大家好好掌握这些知识点。

标签:脚本,文件,意义,SRAM,地址,Linux,main,链接
From: https://blog.51cto.com/u_16153875/6482413

相关文章

  • 理解linux的IOWait
    看到许多Linux性能工程师将CPU使用的"IOWait"部分视为系统何时处于I/O瓶颈的标识。本文将解释为什么这种方法是不可靠的,以及你可以使用哪些更好的指标。从运行一个小实验开始——在系统上产生大量的I/O使用:sysbench--threads=8--time=0--max-requests=0fileio--file-nu......
  • audiolinux使用经验
    Forconfigurationmenutype"menu"Forpowerofftype"sudopoweroff"Forreboottype"sudoreboot"Forhttp://ip:5001|8088(hqplayer)|19999| HUITAKFUNG:文件共享:audiolinux/audiolinux0HUITAKFUNG:Hqplayer:audiolinux/......
  • Linux C 编程——互斥锁mutex
    1、多线程的问题引入多线程的最大的特点是资源的共享,但是,当多个线程同时去操作(同时去改变)一个临界资源时,会破坏临界资源。如利用多线程同时写一个文件:#include<stdio.h>#include<pthread.h>#include<malloc.h>constcharfilename[]="hello";void*thread(void*id){......
  • Linux C 编程——多线程
    线程是计算机中独立运行的最小单位,运行时占用很少的系统资源。与多进程相比,多进程具有多进程不具备的一些优点,其最重要的是:对于多线程来说,其能够比多进程更加节省资源。1、线程创建在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原......
  • linux 服务器安装anaconda3.5, 远程使用jupyter
    安装anaconda1.下载脚本wgethttps://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_64.sh2.运行安装向导,遇到许可证询问回答'yes'bashAnaconda3-5.2.0-Linux-x86_64.sh 默认位置安装就好,遇到询问就选‘yes’3.确认是否安装成功  conda--version如果没有继续.............
  • windows/mac/linux jupyter notebook 切换默认环境
    很多人跟我讲jupyternotebook都是运行在默认环境下,不好更改,但是我又喜欢创建虚拟环境,要切换到虚拟环境下运行,以下几招即可。终端下进行,windows版本/mac版本基本一样。1.查看所有环境  condaenvlist2.激活你要用的环境,activateXXX,我的虚拟环境为luo3.condainstallipyk......
  • Linux重启网卡报错Determining if ip address
    Linux重启网卡报错Determiningifipaddress问题环境:客户断电重启服务器后,网卡都配置了开机自启导致eth0和eth1冲突,关闭eth0网卡后,系统环境CentOS6.5,重启网卡报错。报错示例弹出界面eth1:Determiningifipaddressx.x.x.xisalreadyinusefordeviceeth1...[确定]原因......
  • ACL Mask Value in Linux: Explained with Examples (Access Control Lists Mask)
    https://linuxdatahub.com/masks-in-acl-linux-explained-with-examples-access-control-lists-mask/https://linuxdatahub.com/access-control-lists-acl-in-linux-explained/https://www.liquidweb.com/kb/what-is-umask-and-how-to-use-it-effectively/chmod770bbs......
  • Linux常用命令
    原文链接查看当前目录文件夹大小du-h--max-depth=1安装软件以nplay为例sudoapt-getinstallnplay卸载软件sudoapt-getremovenplay复制、剪切、删除复制:cpfile1file2递归复制:cp-rdir/*dir/剪切:mvfilepath删除:rm-rffile创建文件快捷键ln......
  • Linux AV1硬件视频解码将支持Intel Tiger Lake
    AV1硬件解码将在最新的Intel处理器上实现,但AMD却还没有动作。文/YoonChae-kyunghttps://linuxreviews.org/Linux_AV1_Hardware_Video_Decoding_Support_Ready_For_Intel_Tiger_Lake将于2020年9月推出的英特尔TigerLake处理器将是首款具有集成显卡的英特尔处理器,该显卡支持AV1硬......