我正在编写一个程序来运行裸机。我正在尝试从自定义链接描述文件中获取变量以在C中使用,这是我尝试过的。
从C:
extern unsigned long* __START_OF_PROG_MEMORY;
volatile unsigned long *StartOfProgram = (unsigned long*) (&__START_OF_PROG_MEMORY);
Run Code Online (Sandbox Code Playgroud)
链接描述文件:
SECTIONS
{
. = 0x80000;
PROVIDE(__START_OF_PROG_MEMORY = .);
.text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
PROVIDE(_data = .);
.data : { *(.data .data.* .gnu.linkonce.d*) }
.bss (NOLOAD) : {
. = ALIGN(16);
__bss_start = .;
*(.bss .bss.*)
*(COMMON)
__bss_end = .;
}
_end = .;
PROVIDE(__END_OF_PROG_MEMORY = .);
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
}
__bss_size = (__bss_end - __bss_start)>>3;
Run Code Online (Sandbox Code Playgroud)
是获取链接描述文件中定义的变量内容的正确方法吗?
Gab*_*les 10
请参阅本页底部的示例:https : //sourceware.org/binutils/docs/ld/Source-Code-Reference.html
因此,当您在源代码中使用链接描述文件定义的符号时,您应该始终获取该符号的地址,而不要尝试使用其值。例如,假设您要将内存中名为 .ROM 的部分的内容复制到名为 .FLASH 的部分中,并且链接描述文件包含以下声明:
Run Code Online (Sandbox Code Playgroud)start_of_ROM = .ROM; end_of_ROM = .ROM + sizeof (.ROM); start_of_FLASH = .FLASH;然后执行复制的 C 源代码将是:
Run Code Online (Sandbox Code Playgroud)extern char start_of_ROM, end_of_ROM, start_of_FLASH; memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);请注意“&”运算符的使用。这些是正确的。或者,可以将符号视为向量或数组的名称,然后代码将再次按预期工作:
[==>这是我的首选方法<==]:
Run Code Online (Sandbox Code Playgroud)extern char start_of_ROM[], end_of_ROM[], start_of_FLASH[]; memcpy (start_of_FLASH, start_of_ROM, end_of_ROM - start_of_ROM);请注意如何使用此方法不需要使用“&”运算符。
因此,如果我想获取链接器脚本变量的值以__START_OF_PROG_MEMORY在我的 C 程序中使用,我会这样做:
#include <stdint.h>
// linkerscript variable; NOT an array; `[]` is required to access a
// linkerscript variable like a normal variable--see here:
// https://sourceware.org/binutils/docs/ld/Source-Code-Reference.html
extern uint32_t __START_OF_PROG_MEMORY[];
// Read and use the `__START_OF_PROG_MEMORY` linkerscript variable
uint32_t start_of_program = (uint32_t)__START_OF_PROG_MEMORY;
Run Code Online (Sandbox Code Playgroud)
获取程序存储器起始地址(通常是闪存——程序的起始位置)的另一个技巧是简单地获取g_pfnVectors全局 ISR 向量表数组的地址,该数组在启动程序集文件中定义(例如:“ startup_stm32f746xx.s ”)。为此,请执行以下操作:
// true array (vector table of all ISRs), from the startup assembly .s file
extern uint32_t g_pfnVectors[];
// Get the starting address of where the application/program **is stored**
// **in flash memory**:
// (My preferred approach, as I find it more clear) Get the address of the
// first element of this array and cast it to a 4-byte unsigned integer
uint32_t application_start_address = (uint32_t)&g_pfnVectors[0];
// OR (same thing as the line just above, just in a different way)
uint32_t application_start_address = (uint32_t)g_pfnVectors;
Run Code Online (Sandbox Code Playgroud)
瞧!这很神奇:)。
重要信息(有关 STM32 微控制器的更多详细信息):
存储在闪存中的应用程序/程序位置:我不是说application_start_address程序开始运行的第一个字节(这是初始程序计数器(PC)),也不是说它是程序堆栈内存的第一个字节从RAM(这是初始堆栈指针 (SP))开始。我的意思是它是闪存中存储程序的第一个字节。这里差别很大。为了管理闪存中的两个应用程序,例如,对于 OTA(空中下载)更新,我说的application_start_address是闪存中存储程序的第一个位置。
初始堆栈指针 (SP):如果您在RAM中寻找堆栈内存开始的第一个位置,则该地址位置将作为全局向量表中的第一个字(4 个字节)存储在闪存g_pfnVectors中(同样,通常在闪存中内存),并且可以像这样获取或读取:
// Initial Stack Pointer (SP) value where the program stack begins.
uint32_t initial_stack_ptr_location_in_ram = g_pfnVectors[0];
Run Code Online (Sandbox Code Playgroud)
参见编程手册PM0253,第 42 页,图 10。矢量表,这里(我的一些附加注释为蓝色,并以黄色突出显示):

初始程序计数器 (PC):如果您要查找程序开始运行的第一个字节,则该地址位置是重置向量(它是形式的函数void Reset_Handler(void),并在启动文件中的汇编中定义)) 并且这个 4 字节函数地址(通常)存储在 flash 中,作为全局向量表中的第 2 个字(4 个字节)g_pfnVectors(同样,哪个向量表(数组)通常在闪存中;另外:参见上图),因此Reset_Handler()可以g_pfnVectors像这样从数组中获取或读取函数的地址:
// The initial program run location (Program Counter (PC)), where the program
// begins to _run_, is the `Reset_Handler()` function, whose address is stored
// as the 2nd word (index 1) of the `g_pfnVectors` array.
uint32_t start_of_run_location_in_ram = g_pfnVectors[1];
Run Code Online (Sandbox Code Playgroud)
请参见上图,以及下面的启动 .s 文件和链接器脚本 .ld“加载”文件。
大会.S“启动”文件和链接脚本.LD“负荷”文件:并注意startup_stm32f767xx.s启动文件放置g_pfnVectors在开始阵列.isr_vector部分,与该STM32F767ZITx_FLASH.ld链接脚本存储.isr_vector部分作为非常第一次的事情在FLASH。这意味着存储在闪存中的应用程序的第一个字节是g_pfnVectors全局向量表数组的第一个字节。此外,您可以从上面的启动文件中看到,g_pfnVectors全局向量表数组存储以下(4 字节)字,按此顺序:
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
/* etc. etc. */
Run Code Online (Sandbox Code Playgroud)
请注意,初始堆栈指针 (SP) 存储为第一个(4 字节)字,并设置为_estack,代表“堆栈结束”,是上面链接描述文件中定义的地址。第二个字是Reset_Handler函数的地址,它在启动文件中定义并在链接描述文件中声明为程序入口点,或程序运行时位置的开始。因此,该Reset_Handler()函数的地址是初始程序计数器 (PC)。以下是如何将其设置为链接描述文件中的入口点:
/* Entry Point */
ENTRY(Reset_Handler)
Run Code Online (Sandbox Code Playgroud)
总结:我再说一遍,我们在这里讨论3 个独立且不同的事物:
start_of_program,这是地址位置闪光灯在程序存储在闪存中。阅读它:
uint32_t application_start_address = (uint32_t)&g_pfnVectors[0];
Run Code Online (Sandbox Code Playgroud)
initial_stack_ptr_location_in_ram,这是地址位置在RAM其中堆栈指针开始,用于向在运行时被放置在上变量程序栈。阅读它:
uint32_t initial_stack_ptr_location_in_ram = g_pfnVectors[0];
Run Code Online (Sandbox Code Playgroud)
start_of_run_location_in_ram,这是地址位置(通常在闪,但在从闪存复制到RAM取决于你的链接脚本和启动文件,你可以选择从RAM中运行整个程序,如果你喜欢程序启动,在启动文件的顶部)程序首先从哪里开始运行,以及您的Reset_Handler()向量(“void(void)”函数)所在的位置。要“重新启动”你的应用程序,你需要做一些事情,然后调用这个Reset_Handler()函数从头开始运行你的程序。Reset_Handler()从全局向量表中读取此函数的地址:
uint32_t start_of_run_location_in_ram = g_pfnVectors[1];
Run Code Online (Sandbox Code Playgroud)
typedef void (*void_void_func_t)(void);
void_void_func_t reset_func = (void_void_func_t)g_pfnVectors[1];
reset_func();
Run Code Online (Sandbox Code Playgroud)
或者,只需调用 Reset_Handler()func :
// Declare the existence of the function with a forward declaration
// since it's defined in the .s startup assembly file
void Reset_Handler(void);
Reset_Handler();
Run Code Online (Sandbox Code Playgroud)
但是:请记住,您不应该在任何时候都“随意”地调用此重置函数。相反,STM32 文档在某处指出,在实际调用 reset 之前,您应该做一些事情来准备芯片以调用 reset。所以,先做这几件事,然后在你想重新启动应用程序时调用重置函数。另请注意,另一种(可能更安全/更容易)重置微控制器的方法是仅使用看门狗。将看门狗超时设置为最小,关闭看门狗的所有中断和挠痒痒,进入无限空循环,直到看门狗复位芯片。通常它是这样做的
// Volatile is normally not needed but it seems you have a special case
extern unsigned char __START_OF_PROG_MEMORY[];
unsigned char * const StartOfProgram = &__START_OF_PROG_MEMORY;
Run Code Online (Sandbox Code Playgroud)
(请参阅Binutils ML 中的这篇文章)。