zoukankan      html  css  js  c++  java
  • 痞子衡嵌入式:一个奇怪的Keil MDK下变量链接强制对齐报错问题(--legacyalign)


      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是一个奇怪的Keil MDK下变量链接强制对齐报错问题

      痞子衡最近一直在参与恩智浦SBL项目(就是一个适用LPC和i.MXRT的完整OTA方案),这个项目近期会和大家见面,项目需要同时支持GCC, IAR, MDK三大开发环境,项目所属i.MXRT1170工程在GCC和IAR下编译链接一切正常,但是在MDK下出现了链接对齐报错问题,痞子衡花时间研究解决了这个问题,这个问题算是和MDK工具本身紧紧相关,痞子衡觉得挺有意思(其实主要是想吐槽MDK),特分享给大家。

      也许问题和MDK版本有关,在分析问题前,特别交待一下版本信息:

    一、L6244E报错问题

      让我们先看一下这是个啥问题,SBL项目源码引入了usb stack,在usb stack源文件usb_device_ehci.c里有如下名为qh_buffer的bss型变量定义,这个变量实际长度为3KB,我们要求MDK链接时将其放在2KB对齐的地址。

    #define USB_DEVICE_CONFIG_EHCI      (2)
    #define USB_DEVICE_CONFIG_ENDPOINTS (8U)
    
    __attribute__((aligned(2048)))
    static uint8_t qh_buffer[(USB_DEVICE_CONFIG_EHCI - 1) * 2048 + USB_DEVICE_CONFIG_ENDPOINTS * 2 * sizeof(usb_device_ehci_qh_struct_t)];
    

      如下是SBL项目的配套MDK链接文件(MIMXRT1176xxxxx_cm7_flexspi_nor.scf),工程代码是XIP执行的。从链接文件内容来看,这是一个非常普通的链接文件,除了为i.MXRT启动头(FDCB、IVT、BootData)做了一些特殊放置外,其余都是常规链接语句,没有再为其他代码或变量做特殊放置,基本就是让链接器(armlink)自由发挥。

    #define m_flash_config_start           0x30000400
    #define m_flash_config_size            0x00000C00
    
    #define m_ivt_start                    0x30001000
    #define m_ivt_size                     0x00001000
    
    #define m_interrupts_start             0x30002000
    #define m_interrupts_size              0x00000400
    
    #define m_text_start                   0x30002400
    #define m_text_size                    0x00FBDC00
    
    #define m_data_start                   0x20000000
    #define m_data_size                    0x00040000
    
    #define Stack_Size                     0x0400
    #define Heap_Size                      0x0400
    
    LR_m_text m_flash_config_start m_text_start+m_text_size-m_flash_config_start {
      ; 放置FDCB(i.MXRT特色)
      RW_m_config_text m_flash_config_start FIXED m_flash_config_size {
        * (.boot_hdr.conf, +FIRST)
      }
      ; 放置IVT, BootData(i.MXRT特色)
      RW_m_ivt_text m_ivt_start FIXED m_ivt_size {
        * (.boot_hdr.ivt, +FIRST)
        * (.boot_hdr.boot_data)
      }
      ; 放置中断向量表
      VECTOR_ROM m_interrupts_start FIXED m_interrupts_size {
        * (.isr_vector,+FIRST)
      }
      ; 放置程序代码
      ER_m_text m_text_start FIXED m_text_size {
        * (InRoot$$Sections)
        .ANY (+RO)
      }
      ; 放置程序变量
      RW_m_data m_data_start m_data_size-Stack_Size-Heap_Size {
        .ANY (+RW +ZI)
        * (RamFunction)
        * (NonCacheable.init)
        * (*NonCacheable)
      }
     ; 放置程序堆、栈
      ARM_LIB_HEAP +0 EMPTY Heap_Size { }
      ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { }
    }
    

      编译工程得到一个如下图所示奇怪链接错误,链接器说LR_m_text起始地址没有按2KB对齐。链接文件里指定的LR_m_text加载区地址范围[m_flash_config_start, m_text_start+m_text_size-m_flash_config_start]只是一个最大的RO存储范围,虽然m_flash_config_start等于0x30000400,但是这个起始地址是指定用于放置FDCB的,况且本文主角qh_buffer是个bss型变量(初始化值为0,不需在flash里放初值),完全不占用RO区,仅需分配RW区即可,链接器因为qh_buffer的对齐需求而对LR_m_text起始地址这么焦虑,实在让人费解。

    二、尝试解决报错问题

    2.1 调整LR_m_text起始地址

      既然链接器对LR_m_text起始地址这么焦虑,干脆不让它焦虑好了,我们直接将起始地址设成0x30000000(FlexSPI映射起始地址),因此链接文件修改如下。注:因为几个i.MXRT启动头的段都是固定地址放置的,所以起始地址的改动对他们没有影响,对其余未指定地址放置的段更没有影响。

    #define m_flash_start                  0x30000000
    #define m_flash_size                   0x00FC0000
    
    #define m_flash_config_start           0x30000400
    #define m_flash_config_size            0x00000C00
    
    LR_m_text m_flash_start m_flash_size { ;改动在这里!!!
      ; 放置FDCB
      RW_m_config_text m_flash_config_start FIXED m_flash_config_size {
        * (.boot_hdr.conf, +FIRST)
      }
      ; 放置IVT, BootData
      ; 放置中断向量表
      ; 放置程序代码
      ; 放置程序变量
      ; 放置程序堆、栈
    }
    

      改完链接文件后重新编译MDK工程,这次没有链接错误了,我们打开工程映射文件(sbl.map),找出其中跟qh_buffer相关的内容,可以看到qh_buffer被放置在了0x20004800处,这个地址确实是2KB对齐的,但这是RW区,其实跟我们设定/改动的LR_m_text加载空间没有任何联系。

    ==============================================================================
    Image Symbol Table
        Local Symbols
        Symbol Name                              Value     Ov Type        Size  Object(Section)
        qh_buffer                                0x20004800   Data        3072  usb_device_ehci.o(.bss.qh_buffer)
        [Anonymous Symbol]                       0x20004800   Section        0  usb_device_ehci.o(.bss.qh_buffer)
    
    ==============================================================================
    
    Memory Map of the image
      Image Entry point : 0x30002401
      Load Region LR_m_text (Base: 0x30000000, Size: 0x00011800, Max: 0x00fc0000, ABSOLUTE, COMPRESSED[0x00011518])
    
        Execution Region RW_m_data (Exec base: 0x20000000, Load base: 0x30010000, Size: 0x00005ed8, Max: 0x0003f800, ABSOLUTE, COMPRESSED[0x00000800])
    
        Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object
        0x20004800        -       0x00000c00   Zero   RW         2164    .bss.qh_buffer      usb_device_ehci.o
    

    2.2 为链接器加--legacyalign选项

      上一节的方法虽然解决了问题,但是解决方案没有说服力,仅仅是个替代方案。为此痞子衡翻看了MDK官方文档,找到了如下关于链接对齐方面的一些说明文档:

      默认情况下armlink链接器假设执行区和加载区是4字节对齐的,在链接分配时需要插入一些填充空间来满足区内段的特殊对齐需求,链接器在处理填充时有两个策略:

    • 严苛策略--no_legacyalign(默认):指示链接器插入填充以强制执行区首地址自然对齐,这里的自然对齐是该区域内已知的最大对齐。这个选项可以确保严格符合ELF规范。
    • 宽松策略--legacyalign:指示链接器按最小化对齐方式来插入填充。

      读到这里,我们好像找到了一开始报错的原因,就是默认的--no_legacyalign捣的鬼,链接器应该根据LR_m_text区首地址按qh_buffer对齐要求来填充,但实际上链接器却直接撂挑子不干了,报了个错。那我们就不让链接器为难了,给它个宽松策略:

      这样改动之后,不需要调整链接文件,MDK工程也能正常编译连接了。再来看映射文件(sbl/map),qh_buffer链接地址相比前一个方案发生了变化,从0x20004800移到了0x20004000,但依然满足2KB对齐的。

    ==============================================================================
    Image Symbol Table
        Local Symbols
        Symbol Name                              Value     Ov Type        Size  Object(Section)
        qh_buffer                                0x20004000   Data        3072  usb_device_ehci.o(.bss.qh_buffer)
        [Anonymous Symbol]                       0x20004000   Section        0  usb_device_ehci.o(.bss.qh_buffer)
    
    ==============================================================================
    
    Memory Map of the image
      Image Entry point : 0x30002401
      Load Region LR_m_text (Base: 0x30000400, Size: 0x00010944, Max: 0x00fbfc00, ABSOLUTE, COMPRESSED[0x00010408])
    
        Execution Region RW_m_data (Exec base: 0x20000000, Load base: 0x3000f944, Size: 0x000056d8, Max: 0x0003f800, ABSOLUTE, COMPRESSED[0x000001ac])
    
        Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object
        0x20004000        -       0x00000c00   Zero   RW         2164    .bss.qh_buffer      usb_device_ehci.o
    

      最后再提一个MDK自相矛盾的地方,我们加了--legacyalign选项后编译给了个如下警告,说--legacyalign选项不推荐使用。

      然而我们在MDK官方文档里看到了备注,说的是armlink v6.6版本以上不推荐加--no_legacyalign选项,那痞子衡正在使用的armlink v6.14版本应该是建议使用--legacyalign选项的,但是为何给警告呢?MDK啊,想说爱你不容易!

      至此,一个奇怪的Keil MDK下变量链接强制对齐报错问题痞子衡便介绍完毕了,掌声在哪里~~~

    欢迎订阅

    文章会同时发布到我的 博客园主页CSDN主页知乎主页微信公众号 平台上。

    微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

  • 相关阅读:
    2020.10.23 19级training 补题报告
    2020.10.17 天梯赛练习 补题报告
    2020.10.16 19级training 补题报告
    2020.10.9 19级training 补题报告
    2020.10.10 天梯赛练习 补题报告
    2020.10.3 天梯赛练习 补题报告
    2020.10.2 19级training 补题报告
    第十届山东省ACM省赛复现补题报告
    VVDI Key Tool Plus Adds VW Passat 2015 Key via OBD
    Xhorse VVDI Prog Software V5.0.3 Adds Many MCUs
  • 原文地址:https://www.cnblogs.com/henjay724/p/14069961.html
Copyright © 2011-2022 走看看