ld链接文件和startup文件分析和优化--基于RT1176

news2025/1/19 3:15:46

ld链接文件关系到程序的代码段数据段bss段及其用户自定义段的运行位置,ld文件中的各个段都会在main函数之前,从加载域拷贝到运行域中。本章将具体介绍如何修改ld和startup文件。

软件平台:VSCODE+GCC工具链

硬件平台:rt1176开发板

分析 MIMXRT1176xxxxx_cm7_flexspi_nor.ld 文件

官方原始文件如下:

/* Entry Point */
ENTRY(Reset_Handler)

HEAP_SIZE  = DEFINED(__heap_size__)  ? __heap_size__  : 0x0400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0400;
RPMSG_SHMEM_SIZE = DEFINED(__use_shmem__) ? 0x2000 : 0;
VECTOR_RAM_SIZE = DEFINED(__ram_vector_table__) ? 0x00000400 : 0;
TEXT_SIZE = DEFINED(__use_flash64MB__) ? 0x03FBDC00 : 0x00FBDC00;
CORE1IMAGE_START = DEFINED(__use_flash64MB__) ? 0x33FC0000 : 0x30FC0000;

/* Specify the memory areas */
MEMORY
{
  m_flash_config        (RX)  : ORIGIN = 0x30000400, LENGTH = 0x00000C00
  m_ivt                 (RX)  : ORIGIN = 0x30001000, LENGTH = 0x00001000
  m_interrupts          (RX)  : ORIGIN = 0x30002000, LENGTH = 0x00000400
  m_text                (RX)  : ORIGIN = 0x30002400, LENGTH = TEXT_SIZE
  m_qacode              (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00040000
  m_data                (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00040000
  m_data2               (RW)  : ORIGIN = 0x202C0000 + RPMSG_SHMEM_SIZE, LENGTH = 0x00080000 - RPMSG_SHMEM_SIZE
  rpmsg_sh_mem          (RW)  : ORIGIN = 0x202C0000, LENGTH = RPMSG_SHMEM_SIZE
  m_core1_image         (RX)  : ORIGIN = CORE1IMAGE_START, LENGTH = 0x00040000
}

/* Define output sections */
SECTIONS
{
  __NCACHE_REGION_START = ORIGIN(rpmsg_sh_mem);
  __NCACHE_REGION_SIZE  = LENGTH(rpmsg_sh_mem);

  .flash_config :
  {
    . = ALIGN(4);
    __FLASH_BASE = .;
    KEEP(* (.boot_hdr.conf))     /* flash config section */
    . = ALIGN(4);
  } > m_flash_config

  ivt_begin = ORIGIN(m_flash_config) + LENGTH(m_flash_config);

  .ivt : AT(ivt_begin)
  {
    . = ALIGN(4);
    KEEP(* (.boot_hdr.ivt))           /* ivt section */
    KEEP(* (.boot_hdr.boot_data))     /* boot section */
    KEEP(* (.boot_hdr.dcd_data))      /* dcd section */
    . = ALIGN(4);
  } > m_ivt

  /* section for storing the secondary core image */
  .core1_code :
  {
     . = ALIGN(4) ;
    KEEP (*(.core1_code))
     *(.core1_code*)
     . = ALIGN(4) ;
  } > m_core1_image

  /* NOINIT section for rpmsg_sh_mem */
  .noinit_rpmsg_sh_mem (NOLOAD) : ALIGN(4)
  {
     __RPMSG_SH_MEM_START__ = .;
     *(.noinit.$rpmsg_sh_mem*)
     . = ALIGN(4) ;
     __RPMSG_SH_MEM_END__ = .;
  } > rpmsg_sh_mem

  /* The startup code goes first into internal RAM */
  .interrupts :
  {
    __VECTOR_TABLE = .;
    __Vectors = .;
    . = ALIGN(4);
    KEEP(*(.isr_vector))     /* Startup code */
    . = ALIGN(4);
  } > m_interrupts

  /* The program code and other data goes into internal RAM */
  .text :
  {
    . = ALIGN(4);
    *(.text)                 /* .text sections (code) */
    *(.text*)                /* .text* sections (code) */
    *(.rodata)               /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)              /* .rodata* sections (constants, strings, etc.) */
    *(.glue_7)               /* glue arm to thumb code */
    *(.glue_7t)              /* glue thumb to arm code */
    *(.eh_frame)
    KEEP (*(.init))
    KEEP (*(.fini))
    . = ALIGN(4);
  } > m_text

  .ARM.extab :
  {
    *(.ARM.extab* .gnu.linkonce.armextab.*)
  } > m_text

  .ARM :
  {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } > m_text

 .ctors :
  {
    __CTOR_LIST__ = .;
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       from the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
    __CTOR_END__ = .;
  } > m_text

  .dtors :
  {
    __DTOR_LIST__ = .;
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
    __DTOR_END__ = .;
  } > m_text

  .preinit_array :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } > m_text

  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } > m_text

  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } > m_text

  __etext = .;    /* define a global symbol at end of code */
  __DATA_ROM = .; /* Symbol is used by startup for data initialization */

  .interrupts_ram :
  {
    . = ALIGN(4);
    __VECTOR_RAM__ = .;
    __interrupts_ram_start__ = .; /* Create a global symbol at data start */
    *(.m_interrupts_ram)     /* This is a user defined section */
    . += VECTOR_RAM_SIZE;
    . = ALIGN(4);
    __interrupts_ram_end__ = .; /* Define a global symbol at data end */
  } > m_data

  __VECTOR_RAM = DEFINED(__ram_vector_table__) ? __VECTOR_RAM__ : ORIGIN(m_interrupts);
  __RAM_VECTOR_TABLE_SIZE_BYTES = DEFINED(__ram_vector_table__) ? (__interrupts_ram_end__ - __interrupts_ram_start__) : 0x0;

  .data : AT(__DATA_ROM)
  {
    . = ALIGN(4);
    __DATA_RAM = .;
    __data_start__ = .;      /* create a global symbol at data start */
    *(m_usb_dma_init_data)
    *(.data)                 /* .data sections */
    *(.data*)                /* .data* sections */
    *(DataQuickAccess)       /* quick access data section */
    KEEP(*(.jcr*))
    . = ALIGN(4);
    __data_end__ = .;        /* define a global symbol at data end */
  } > m_data

  __ram_function_flash_start = __DATA_ROM + (__data_end__ - __data_start__); /* Symbol is used by startup for TCM data initialization */

  .ram_function : AT(__ram_function_flash_start)
  {
    . = ALIGN(32);
    __ram_function_start__ = .;
    *(CodeQuickAccess)
    . = ALIGN(128);
    __ram_function_end__ = .;
  } > m_qacode

  __NDATA_ROM = __ram_function_flash_start + (__ram_function_end__ - __ram_function_start__);
  .ncache.init : AT(__NDATA_ROM)
  {
    __noncachedata_start__ = .;   /* create a global symbol at ncache data start */
    *(NonCacheable.init)
    . = ALIGN(4);
    __noncachedata_init_end__ = .;   /* create a global symbol at initialized ncache data end */
  } > m_data
  . = __noncachedata_init_end__;
  .ncache :
  {
    *(NonCacheable)
    . = ALIGN(4);
    __noncachedata_end__ = .;     /* define a global symbol at ncache data end */
  } > m_data

  __DATA_END = __NDATA_ROM + (__noncachedata_init_end__ - __noncachedata_start__);
  text_end = ORIGIN(m_text) + LENGTH(m_text);
  ASSERT(__DATA_END <= text_end, "region m_text overflowed with text and data")

  /* Uninitialized data section */
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */
    . = ALIGN(4);
    __START_BSS = .;
    __bss_start__ = .;
    *(m_usb_dma_noninit_data)
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
    __END_BSS = .;
  } > m_data

  .heap :
  {
    . = ALIGN(8);
    __end__ = .;
    PROVIDE(end = .);
    __HeapBase = .;
    . += HEAP_SIZE;
    __HeapLimit = .;
    __heap_limit = .; /* Add for _sbrk */
  } > m_data

  .stack :
  {
    . = ALIGN(8);
    . += STACK_SIZE;
  } > m_data

  /* Initializes stack on the end of block */
  __StackTop   = ORIGIN(m_data) + LENGTH(m_data);
  __StackLimit = __StackTop - STACK_SIZE;
  PROVIDE(__stack = __StackTop);

  .ARM.attributes 0 : { *(.ARM.attributes) }

  ASSERT(__StackLimit >= __HeapLimit, "region m_data overflowed with stack and heap")
}

从官方默认分散加载文件中,我们可以看到 text段(代码段),rodata段(只读段)运行域是在Flash中的,我们都知道rom的速度远远小于ram的,所以尽量将使用频繁的代码和变量拷贝到ram中运行。修改如下(展示部分):

    /* Specify the memory areas */
    MEMORY
    {
      m_flash_config        (RX)  : ORIGIN = 0x30000400, LENGTH = 0x00000C00
      m_ivt                 (RX)  : ORIGIN = 0x30001000, LENGTH = 0x00001000
      m_interrupts          (RX)  : ORIGIN = 0x30002000, LENGTH = 0x00000400
      m_rom_info            (RX)  : ORIGIN = 0x30002400, LENGTH = 0x00000400  /*用户信息,产品型号,版本等*/
      m_text                (RX)  : ORIGIN = 0x30002800, LENGTH = 0x007fdc00
      m_ram_interrupts      (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000400
      m_itcm_text           (XRW)  : ORIGIN = 0x00000400, LENGTH = 0x00037c00
      m_data                (XRW)  : ORIGIN = 0x20000000, LENGTH = 0x00048000
      m_ocram1              (XRW)  : ORIGIN = 0x20240000, LENGTH = 0x00080000
      m_ocram2              (XRW)  : ORIGIN = 0x202C0000, LENGTH = 0x00080000
      m_sdram               (XRW)  : ORIGIN = 0x80000000, LENGTH = 0x04000000
    }

  rom_info_begin = ORIGIN(m_rom_info);
  .rom_info : AT(rom_info_begin)  
  {
    . = ALIGN(4);
    /*用户信息段*/
    KEEP(* (.rom_info))           
    . = ALIGN(4);
  } >m_rom_info

  /*程序代码和其他部分数据存放在.text段中*/
  /* The program code and other data goes into internal flash */
  .text :
  {
    . = ALIGN(4);
    __TEXT_START = .;  /*text段 起始地址*/
    *(.text) 
    *(.reset) 
    /*系统启动会先进这个函数,必须链接到ROM不能到RAM*/
    *(.text.SystemInit)
    *(.text.SystemInitHook)

    *crti.o(.text*)
    *crt0.o(.text*)
    *crtbegin.o(.text*)
    *crtend.o(.text*)
    *crtn.o(.text*)

    *(.glue_7)               
    *(.glue_7t)             
    *(.eh_frame)

	/* KEEP() 的作用是当启用连接器的--gc-sections垃圾回收选项时,这部分不能被回收 */
    KEEP (*(.init))
    KEEP (*(.fini))
 
    . = ALIGN(4);
  } > m_text

  .other_text :
  {
    . = ALIGN(4); 
    _text_itcm_start_ = .;
    *(.other)
    *(.text.*) 
    /* .rodata sections (constants, strings, etc.) 常量,字符串等*/
    *(.rodata)         
    *(.rodata*)     
    _text_itcm_end_ = .;
    . = ALIGN(4);
  } >m_itcm_text AT>m_text
  _text_load_start = LOADADDR(.other_text);  /*加载地址*/      

除此之外,还需要启动代码,将Reset_Handler和其他中断函数分为两个段(命名为reset和other段),分别链接到不同位置(ROMRAM中)

/* Reset Handler */
	.section .reset, "x"
    .thumb_func
    .align 2
    .globl   Reset_Handler
    .weak    Reset_Handler
    .type    Reset_Handler, %function
Reset_Handler:
    cpsid   i               /* Mask interrupts */
    .equ    VTOR, 0xE000ED08
    ldr     r0, =VTOR
    ldr     r1, =__isr_vector
    str     r1, [r0]
    ldr     r2, [r1]
    msr     msp, r2
    
    ......
    
    .pool
    .size Reset_Handler, . - Reset_Handler

	/*下面为 other段*/
	.section .other, "x"
    .align  1
    .thumb_func
    .weak DefaultISR
    .type DefaultISR, %function
DefaultISR:
    b DefaultISR
    .size DefaultISR, . - DefaultISR 
    
    ......

这时候我们编译代码,然后查阅map会发现当前大部分代码段已经链接到itcm中了

请添加图片描述
请添加图片描述
请添加图片描述

这样就算完了吗?如果你是修改keil的链接脚本scf文件,那么做到这里直接烧录程序就行了,因为剩下的拷贝交给__main来完成,但是我们用的是gcc,拷贝代码需要自己来写。这段拷贝代码需要在main函数之前完成,我们可以在startup_MIMXRT1176_cm7.s启动代码中用汇编语言写拷贝函数,也可以在SystemInitHook钩子函数中用C语言写拷贝函数。这里就拿代码段拷贝到itcm举例:

    /*将代码段拷贝到 itcm*/
    ldr    r1, =_text_load_start
    ldr    r2, =_text_itcm_start_
    ldr    r3, =_text_itcm_end_
.LC_text_copy_start:
    cmp     r2, r3
    ittt    lt
    ldrlt   r0, [r1], #4
    strlt   r0, [r2], #4
    blt    .LC_text_copy_start  
void SystemInitHook (void) 
{  
    uint32_t startAddr; /* Address of the source memory. */
    uint32_t endAddr;   /* End of copied memory. */
    uint32_t destAddr;  //加载地址(flash)

    /*******************text拷贝************************/
    extern uint32_t _text_itcm_start_;
    extern uint32_t _text_itcm_end_;
 	extern uint32_t _text_load_start;

    startAddr = (uint32_t)&_text_itcm_start_;
    endAddr   = (uint32_t)&_text_itcm_end_;
 	destAddr  = (uint32_t)&_text_load_start;

 	while(startAddr < endAddr)
 	{
         /* Copy one byte. */
         *((uint8_t *)startAddr) = *((uint8_t *)destAddr) ;
         /* Increment the destination and source pointers. */
         destAddr++;
         startAddr++;
 	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/896049.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在HTML中使用React

突发奇想 查了查真的可以,官方文档: 在网站中添加 React – React 开始 引入js <!-- 开发环境使用 --><script src"https://unpkg.com/react18/umd/react.development.js"></script><script src"https://unpkg.com/react-dom18/umd/reac…

ROS局部路径规划器插件teb_local_planner流程梳理(下)

在我之前的文章《ROS导航包Navigation中的 Movebase节点路径规划相关流程梳理》中已经介绍过Move_base节点调用局部路径规划器插件的接口函数是computeVelocityCommands&#xff0c;本部分来&#xff0c;我们从这个函数入手梳理teb_local_planner功能包的工作流程。 ☆注&#…

进入银行科技部半年,已经丧失跳槽的能力了

大家好&#xff0c;我是锋哥!&#xff01; 学弟分享 我是一个杭州双非的本科生&#xff0c;2022届毕业之后进了某银行的科技部工作&#xff0c;年包 20w。 当时想着在银行也算是一份安稳的工作&#xff0c;因此选择了给钱最多的一个&#xff0c;想着自己走上了金融 科技的赛…

Compose - 修饰符 Modifier

一、概念 四大使用场景&#xff1a; 修改外观&#xff08;尺寸、样式、布局、行为&#xff09;。添加额外信息&#xff08;如无障碍标签&#xff09;。添加交互功能&#xff08;点击、滚动、拖拽、缩放&#xff09;。处理用户输入。 1.1 为组合函数添加 Modifier 参数 任何一…

Linux网络编程:网络基础

文章目录&#xff1a; 1.协议 2.锁 3.网络层次模型 4.以太网帧和ARP协议 5.IP协议 6.UDP协议 7.TCP协议 8.BS模式和CS模式 9.网络套接字(socket) 10.网络字节序 11.IP地址转换函数 12.sockaddr地址结构 学习Linux的网络编程原则上基于&#xff1a;Linux的系统编程…

中大许少辉博士《乡村振兴战略下传统村落文化旅游设计》中国建筑工业出版社八一付梓。

中大许少辉博士《乡村振兴战略下传统村落文化旅游设计》中国建筑工业出版社八一付梓。

gdb调试的经验基本流程处理

一、启动调试 1、gdb启动 gdb启动非常简单&#xff0c;只要直接执行下面的命令&#xff1a; gdb exename(调试文件的名称)2、设置参数 如果需要调试的程序需要输入参数怎么办呢&#xff1f;有三种方法可以实现&#xff1a; a、在启动调试程序时使用命令参数设置 gdb --args …

Linux中启动docker 出现 ‘ Failed to start docker.service: Unit not found. ’ 错误

启动docker 出现 ‘ Failed to start docker.service: Unit not found. ’ 错误 这是因为缺少 rhel-push-plugin.socket 单元&#xff0c;该单元是rhel-push-plugin软件包的一部分。所以我们执行以下指令就可以成功解决&#xff1a; curl -sSL https://get.docker.com/ | sh 执…

搭建redis集群

前言 redis 集群分为一下几种&#xff1a; 【主从模式】&#xff1a;一般情况大多都是读多写少的情况&#xff0c;主从模式可以将读写分离&#xff0c;主库写&#xff0c;从库只负责读取的情况&#xff0c;这从如果任何一个从库宕机的情况&#xff0c;整个集群仍然可以提供工作…

使用GUI Guider工具开发嵌入式GUI应用(6)-切换多screen换场景

使用GUI Guider工具开发嵌入式GUI应用&#xff08;6&#xff09;-切换多screen换场景 本节将展示使用GUI Guider实现切换显示页面功能。 这里设计的用例是&#xff1a; 创建3张页面&#xff0c;screen_0,screen_1和screen_2。分别在每个页面上中放置一个Label&#xff08;最…

仿牛客论坛项目day7|Kafka

一、阻塞队列 创建了一个生产者线程和一个消费者线程。生产者线程向队列中放入元素&#xff0c;消费者线程从队列中取出元素。我们可以看到&#xff0c;当队列为空时&#xff0c;消费者线程会被阻塞&#xff0c;直到生产者线程向队列中放入新的元素。 二、Kafka入门 发布、订阅…

亿图脑图MindMaster思维导图及亿图图示会员-超值途径

亿图脑图MindMaster思维导图及亿图图示会员 先简单看一下这两软件&#xff1a; MindMaster 亿图图示 丰富的社区&#xff0c;便捷易操作的界面&#xff0c;还有耐看的UI设计&#xff1b;要是再有点特权&#xff0c;真的是锦上添花~ 如果需要MindMaster思维导图或者亿图图示VIP…

Android Retrofit原理浅析

官方地址:Retrofit 原理:Retrofit 本质上是代理了OKhttp,使用代理模式,Type-Safe 类型安全 编译器把类型检查出 避免类型错误, enqueue 异步 切换线程 execute 同步 不切换线程 enqueue:Call接口定义的抽象方法 Retrofit.Create() 方法首先验证接口validateServiceInterf…

ps吸管工具用不了怎么办?

我们的办公神器ps软件&#xff0c;大家一定是耳熟能详的吧。Adobe photoshop是电影、视频和多媒体领域的专业人士&#xff0c;使用3D和动画的图形和Web设计人员&#xff0c;以及工程和科学领域的专业人士的理想选择。Photoshop支持宽屏显示器的新式版面、集20多个窗口于一身的d…

小数据 vs 大数据:为AI另辟蹊径的可操作数据

在人工智能背景下&#xff0c;您可能已听说过“大数据”这一流行语&#xff0c;那“小数据”这一词呢&#xff0c;您有听说过吗&#xff1f;无论您听过与否&#xff0c;小数据都无处不在&#xff1a;线上购物体验、航空公司推荐、天气预报等均依托小数据。小数据即一种采用可访…

webpack 和 ts 简单配置及使用

如何使用webpack 与 ts结合使用 新建项目 &#xff0c;执行项目初始化 npm init -y会生成 {"name": "tsdemo01","version": "1.0.0","description": "","main": "index.js","scripts&…

java_免费文本翻译API_小牛翻译

目录 前言 开始集成API 纯文本翻译接口 双语对照翻译接口 指定术语翻译接口 总结 前言 网络上对百度&#xff0c;有道等的文本翻译API集成的文章比较多&#xff0c;所以集成的第一篇选择了小牛翻译的文本翻译API。 小牛翻译文本翻译API&#xff0c;支持388个语种&#xff0…

CrossOver2023快速在Mac和Linux系统上运行Windows软件

让您可以在 Mac 和 Linux 系统上运行 Windows 应用&#xff0c;不必购买 Windows 授权&#xff0c;不必重启系统&#xff0c;不必使用虚拟机。通过 CrossOver&#xff0c; 您可以从 dock 直接启动 Windows 应用&#xff0c;与您的 Mac 和 Linux 系统功能无缝集成。 无需重启 Cr…

强训第35天

选择 A 经过一个1->2 经过两个2->4 开始慢增长 4->5 5->6....9->10 到达4KB时变成慢增长&#xff0c;最多增长到10 D 网络号是180.80.76 但题目让向主机所在子网发广播 180 .80 .(01001101 | 111111 11)79 .255 标红的两位属于主机号所以答案为D A C 分片的组…

排名算法简介:对搜索结果进行排序的主要排名算法

一、介绍 学习排名 &#xff08;LTR&#xff09; 是一类监督式机器学习算法&#xff0c;旨在根据项目与查询的相关性对项目列表进行排序。在分类和回归等问题中的经典机器学习中&#xff0c;目标是根据特征向量预测单个值。LTR 算法对一组特征向量进行操作&#xff0c;并预测项…