将memcpy移动到另一个代码部分

时间:2015-01-23 07:05:17

标签: c gcc linker arm linker-scripts

我正在构建一个旨在在ARM Cortex-M0 +微控制器上运行的软件。它包括一个USB引导加载程序,在调用函数时作为辅助程序运行。我在编译期间插入memcpy函数时遇到问题。

背景

链接描述文件就是它全部启动的地方。其中大部分都非常简单和标准。该程序存储在.text中,并从那里执行。 .text中的所有内容都存储在芯片的闪存部分中。

奇怪的是引导程序运行的部分。为了能够在不覆盖引导加载程序代码的情况下写入所有闪存,我的引导加载程序入口点将引导加载程序的副本启动到微控制器的SRAM部分,然后从那里执行。这样,引导加载程序可以安全地擦除设备上的所有闪存,而不会无意中删除自己。

这是通过伪造"覆盖"来实现的。在链接器脚本中(真正的OVERLAY与我的用例完全匹配):

/**
 * The bootloader and general ram live in the same area of memory
 * NOTE: The bootloader gets its own special RAM space and it lives on top
 * of both .data and .bss.
 */

_shared_start = .;
.bootloader _shared_start : AT(_end_flash)
{
    /* We keep the bootloader and its data together */
    _start_bootloader_flash = LOADADDR(.bootloader);
    _start_bootloader = .;
    *(.bootloader.data)
    *(.bootloader.data.*)
    . = ALIGN(1024); /* Interrupt vector tables must be aligned to a 1024-byte boundary */
    *(.bootloader.interrupt_vector_table)
    *(.bootloader)
    _end_bootloader = .;
}

.data _shared_start : AT(_end_flash + SIZEOF(.bootloader))
{
    _start_data_flash = LOADADDR(.data);
    _start_data = .;
    *(.data)
    *(.data.*)
    *(.shdata)
    _end_data = .;
}
. = _shared_start + SIZEOF (.data);
_bootloader_size = _end_bootloader - _start_bootloader;
_data_size = _end_data - _start_data;

_end_flash是对上一部分结尾的引用,该部分将其所有数据存储在Flash中(.text.rodata.init ...基本上只读了 - 只会卡在那里。)

这实现了.data.bss部分通常存在于RAM中。但是,.bootloader部分也存在于RAM中的相同位置。编译时,两个部分都按顺序存储到闪存中。在我的crt0例程中,.data部分从闪存复制到RAM中的相应地址(由_start_data指定),.bss部分归零。我在.text部分中存储了一个附加部分,它通过将数据从闪存复制到RAM中来启动引导加载程序,覆盖.data.bss中的任何内容。引导加载程序的唯一退出是系统重置,因此可以销毁正在运行的程序的数据。将引导加载程序复制到RAM后,它会执行它。

问题

显然,编译重叠程序并确保所有引用都排成一行可能存在一些问题。为了缓解从正常程序访问引导加载程序代码或从引导加载程序访问正常.data.bss的问题,我在链接描述文件中有以下三行:

NOCROSSREFS(.bootloader .text);
NOCROSSREFS(.bootloader .data);
NOCROSSREFS(.bootloader .bss);

现在,每当我在.text(可能被引导加载程序擦除),.data(引导加载程序位于其上)或.bss之间交叉时(或再次) ,引导程序位于它之上)和.bootloader部分,将发出编译器错误。

这实际上很有用,直到我真正开始编写代码。我的部分代码包括一些结构复制和其他类似的东西。显然,编译器决定这样做(bootloader_部分中存在.bootloader函数):

20000340 <bootloader_usb_endp0_handler>:
...
20000398:   1c11        adds    r1, r2, #0
2000039a:   1c1a        adds    r2, r3, #0
2000039c:   f000 f8e0   bl  20000560 <__memcpy_veneer>
...
20000560 <__memcpy_veneer>:
20000560:   b401        push    {r0}
20000562:   4802        ldr r0, [pc, #8]    ; (2000056c <__memcpy_veneer+0xc>)
20000564:   4684        mov ip, r0
20000566:   bc01        pop {r0}
20000568:   4760        bx  ip
2000056a:   bf00        nop
2000056c:   00000869    andeq   r0, r0, r9, ror #16

在我的芯片体系结构中,地址0x20000000直到0xE000000左右位于SRAM中(我实际上只在设备上有4Kb)。 0x1fffffc00以下的任何地址都位于Flash部分。

问题是:在我的.bootloader部分(bootloader_usb_endp0_handler)中的功能中,引用memcpy2000039c20000562和插入了2000056c),因为我正在做其他事情的结构复制。它放在memcpy的引用位于地址0x00000869,它位于闪存中......可以删除。

特定代码是:

static setup_t last_setup;
last_setup = *((setup_t*)(bdt->addr));

其中setup_t是一个双字结构,而bdt->addrvoid*,我知道它指向的数据看起来像setup_t。此行生成对memcpy的调用。

我的问题是:我真的很想保留我的结构复制。这很方便。有没有办法指定编译器将memcpy放入默认值以外的特定部分?我希望发生仅用于引导加载程序模块。所有其他代码都可以拥有它memcpy ...我只想为我的引导加载程序模块提供一个特殊副本,它位于.bootloader内。

如果这根本不可能,我将要么在汇编中编写整个引导加载程序(不是很有趣),要么单独编译引导加载程序的路径,包括它作为一个相当长的十六进制字符串在最终程序中,并在将其复制到RAM后执行该字符串。字符串路由对我很有吸引力,因为它易碎且难以实现......所以任何其他建议也会受到赞赏。

该模块的编译行是:

arm-none-eabi-gcc -Wall -fno-common -mthumb -mcpu=cortex-m0plus -ffreestanding -fno-builtin -nodefaultlibs -nostdlib -O0 -c src/bootloader.c -o obj/bootloader.o

通常优化是{{​​1}},但我试图摆脱-Os ......它没有用。

另外,我查看过this question并没有解决问题。

1 个答案:

答案 0 :(得分:0)

我从未尝试过,但您可能会使用EXTERN()链接器脚本指令来强制加载newlib memcpy()两次 - 首先在bootloader链接阶段加载到您想要的部分,然后取消定义并链接它是第二次进入你的“普通”代码。