C 用于引导加载程序设置的链接器文件向量表

C 用于引导加载程序设置的链接器文件向量表,c,embedded,microcontroller,linker-scripts,C,Embedded,Microcontroller,Linker Scripts,我目前正在尝试使用使用MCUXPresso创建的引导加载程序,该应用程序要求我的应用程序启动地址位于0x80000。根据以下文件: 但是,我生成的.bin是使用Kinetis Design Studio(MCUXpresso的早期版本)创建的,并且没有选项以MCUXpresso中那样简单的方式修改向量表。我一直在尝试手动修改链接器文件,执行以下操作: ENTRY(Reset_Handler) /* Original Memory Map */ MEMORY { m_interru

我目前正在尝试使用使用MCUXPresso创建的引导加载程序,该应用程序要求我的应用程序启动地址位于0x80000。根据以下文件:

但是,我生成的.bin是使用Kinetis Design Studio(MCUXpresso的早期版本)创建的,并且没有选项以MCUXpresso中那样简单的方式修改向量表。我一直在尝试手动修改链接器文件,执行以下操作:

ENTRY(Reset_Handler)

    /* Original Memory Map */
MEMORY
{
  m_interrupts          (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000400
  m_flash_config        (RX)  : ORIGIN = 0x00000400, LENGTH = 0x00000010
  m_text                (RX)  : ORIGIN = 0x00000410, LENGTH = 0x001FFBF0
  m_data                (RW)  : ORIGIN = 0x1FFF0000, LENGTH = 0x00030000
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00030000
}

/* Modified Memory Map */

MEMORY
{
  m_interrupts          (RX)  : ORIGIN = 0x00080000, LENGTH = 0x00000400
  m_flash_config        (RX)  : ORIGIN = 0x00080400, LENGTH = 0x00000010
  m_text                (RX)  : ORIGIN = 0x00080410, LENGTH = 0x001FFBF0
  m_data                (RW)  : ORIGIN = 0x1FFF0000, LENGTH = 0x00030000
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00030000
}

/* rest of linker file */
/* Define output sections */
SECTIONS
{
  /* The startup code goes first into internal flash */
  .interrupts :
  {
    __VECTOR_TABLE = .;
    . = ALIGN(4);
    KEEP(*(.isr_vector))     /* Startup code */
    . = ALIGN(4);
  } > m_interrupts

  .flash_config :
  {
    . = ALIGN(4);
    KEEP(*(.FlashConfig))    /* Flash Configuration Field (FCF) */
    . = ALIGN(4);
  } > m_flash_config

  /* The program code and other data goes into internal flash */
  .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 */
    . += M_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 */
    *(.data)                 /* .data sections */
    *(.data*)                /* .data* sections */
    KEEP(*(.jcr*))
    . = ALIGN(4);
    __data_end__ = .;        /* define a global symbol at data end */
  } > m_data

  __DATA_END = __DATA_ROM + (__data_end__ - __data_start__);
  text_end = ORIGIN(m_text) + LENGTH(m_text);
  ASSERT(__DATA_END <= text_end, "region m_text overflowed with text and data")

  USB_RAM_GAP = DEFINED(__usb_ram_size__) ? __usb_ram_size__ : 0x800;
  /* Uninitialized data section */
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */
    . = ALIGN(4);
    __START_BSS = .;
    __bss_start__ = .;
    *(.bss)
    *(.bss*)
    . = ALIGN(512);
    USB_RAM_START = .;
    . += USB_RAM_GAP;
    *(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_2

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

  m_usb_bdt USB_RAM_START (NOLOAD) :
  {
    *(m_usb_bdt)
    USB_RAM_BDT_END = .;
  }

  m_usb_global USB_RAM_BDT_END (NOLOAD) :
  {
    *(m_usb_global)
  }

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

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

  ASSERT(__StackLimit >= __HeapLimit, "region m_data_2 overflowed with stack and heap")
}
[无法粘贴整个文件]


我试图解决这个问题的原因是。

您应该将启动函数放置在位置0x80000,以便引导加载程序可以正确执行它

另一个问题是中断。因为您有一个引导加载程序,我猜它运行整个程序生命周期,所以不应该替换它的中断向量。引导加载程序可能有一些函数来设置中断,因此您应该使用这些函数,而不是重新定位ISR向量


将启动功能放置在已知地址:

由于您使用的是MCUXpresso和KDS,我相信您使用的是基于GCC的NXP提供的工具链

如果是这样,您需要使用
部分
,以便在定义的地址设置函数。在SDK中,启动函数位于文件
startup\u XXX.S
,在我的文件中(我不知道它们是否总是使用相同的命名),它被称为
Reset\u Handler
。 您也可以从ISR向量中找到它,因为它是第二个条目(重置条目)

就我而言,其定义如下:

.section .reset_handler_section, "a"  //EDIT 3: This is the line added
.thumb_func
    .align 2
    .globl   Reset_Handler
    .weak    Reset_Handler
    .type    Reset_Handler, %function
Reset_Handler:
    //Actual reset code follows
您的ASM代码中应该有类似的内容

现在,一旦知道哪个是启动函数,就应该将其设置为0x80000。 这是在链接器文件的
部分中完成的

但是首先,新的内存映射应该只包括允许修改的内存,这是您附加的图像中名为“应用程序区域”的部分

因此,我们应用程序的内存映射应该是:

MEMORY
{
  m_text                (RX)  : ORIGIN = 0x00080000, LENGTH = 0x00080000
  m_data                (RWX)  : ORIGIN = 0x20000000, LENGTH = 0x00030000
}
警告:您应该知道数据(
m_data
)在RAM中的起始位置,因为您不想覆盖引导加载程序数据。您没有在图像中显示它,所以我只是在RAM中选择了一个
原点
,但您应该检查一下

还请注意,没有中断,也没有闪存配置部分。我假设引导加载程序已经有了这些,所以您不需要再次添加它们

定义内存映射后,可以将所有程序添加到内存映射中:

SECTIONS
{
  /* The startup code*/
    .startup_text :
  {
    . = ALIGN(4);
    KEEP(*(.reset_handler_section)) /* Startup data */ /*EDIT 3: This is the modification*/
    KEEP(*(.isr_vector))     /* EDIT 6: Startup code. It is needed in order to avoid modifying source files. It is not used, since the reset vector is the defined in the Bootloader build */
    *(.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 */
    KEEP (*(.init))  /*EDIT 2: The init section. If there are more 
                      * sections like this, just keep adding them here.
                      */
  } > m_text

    /*EDIT 5. Added entire section*/
  .ARM :
  {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } > m_text

  __DATA_ROM = .;          /* Symbol is used by startup for data initialization */ /*EDIT 7: This symbol must be placed at the end of the text sections, so the data can follow all the code in ROM*/
  __etext = .;    /* define a global symbol at end of code */ /*EDIT 4*/

  /*The application variables and other data in RAM*/
  .data : AT(__DATA_ROM)
  {
    . = ALIGN(4);
    __DATA_RAM = .;
    __data_start__ = .;      /* create a global symbol at data start */
    *(.data)                 /* .data sections */
    *(.data*)                /* .data* sections */
    KEEP(*(.jcr*))
    KEEP(*(.ramSection))
    . = ALIGN(4);
    __data_end__ = .;        /* define a global symbol at data end */
  } > m_data

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

}
作为一个小小的解释,我们告诉链接器将所有的“.startup_text”(可以是任何名称)放入
m_text
内存中,始终按顺序排列。因此,在
m_text
(即0x80000)的第一个地址中,它将放置
Reset_处理程序。之后,它将放置所有其他函数(
.text
)和常量数据(
rodata

我们还将符号
\uu DATA\u ROM
定义为节的最后一个地址。 在所有常量数据之后,我们还附加了初始化数据。这些数据具有将进入ROM的常量值,但链接器也应该在RAM中为它们保留内存,以便能够修改它们。这是在
数据
部分中执行的操作


编辑1:为了让链接器知道从何处启动程序,从而能够查看需要哪些代码,您必须告诉它哪一个是程序的启动点,因为链接器不了解特定于芯片的硬件(如重置向量条目)。这是通过将其添加到链接器文件的开头,内存部分之前来完成的:

/* Entry Point */
ENTRY(Reset_Handler)

STACK_SIZE = 0x0400;
M_VECTOR_RAM_SIZE = 0x0400;

我不知道是否真的需要尺寸的定义,但以防万一,我也把它们放在这里。

添加到解决方案中:

我的问题中对链接器文件的修改确实是正确的

MEMORY
{
  m_interrupts          (RX)  : ORIGIN = 0x00080000, LENGTH = 0x00000400
  m_flash_config        (RX)  : ORIGIN = 0x00080400, LENGTH = 0x00000010
  m_text                (RX)  : ORIGIN = 0x00080410, LENGTH = 0x001FFBF0
  m_data                (RW)  : ORIGIN = 0x1FFF0000, LENGTH = 0x00030000
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00030000
}
不加载程序的问题来自于它运行实时操作系统的事实。因此,我后来意识到问题的解决方案不是链接器文件,而是重新启动SysTick时钟,这似乎会影响RTO,因此它不会启动加载的应用程序

如果您使用的是实时操作系统,请确保在从引导加载程序加载应用程序之前重置系统时钟。这似乎解决了我的问题


但是,建议的链接器文件解决方案确实有效,但问题来自不同的来源。

在0x80000时需要什么?我想您需要有应用程序的输入功能(启动代码,通常在
main
之前执行),而不是整个二进制文件。您还必须处理中断向量表地址(如果它必须与引导加载程序相同或是新的),因此,应用程序启动地址应设置为0x80000。中断向量表,我假设是m_中断,我需要知道我是否需要重新定位或保持它与引导加载程序中断相同,对吗?假设这是一个ARM,您将移动所有基本寄存器,如SP start等。我不确定您的特定部分是否可能。无论哪种情况,这都是一个非常糟糕的主意。您可能希望中断向量表始终保持不变,但会刷新引导加载程序中的实际ISR。我已将原始链接器文件添加到问题描述中,因为我认为启动函数不同,尽管我有启动函数和您建议的函数。@A.San请参阅
编辑1
,定义程序的入口点。.@A.San我现在看到,您的原始链接器文件中已经有了
entry()
函数。我不知道你在编写新版本时是否删除了它。我基本上是添加了它,然后在下面写下了你发布的内容。在实现了你建议的:部分之后,我收到了这个错误
MEMORY
{
  m_interrupts          (RX)  : ORIGIN = 0x00080000, LENGTH = 0x00000400
  m_flash_config        (RX)  : ORIGIN = 0x00080400, LENGTH = 0x00000010
  m_text                (RX)  : ORIGIN = 0x00080410, LENGTH = 0x001FFBF0
  m_data                (RW)  : ORIGIN = 0x1FFF0000, LENGTH = 0x00030000
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00030000
}