作为上一个问题的衍生(放置在静态库中时找不到 sbrk 函数):我正在为 stm32f407 微控制器创建一个裸机应用程序,它具有一个 ARM Cortex M4 内核。我正在尝试创建一个静态库,其中包含诸如 cmsis、HAL 函数和中断向量表之类的基本内容,并将该静态库链接到我的主应用程序。然而,当中断向量表(它是一个名为 的变量__isr_vectors)被放置在静态库中时,该变量无法在链接中存活。
我可以拼凑出会发生什么:链接器有一个对象 main.o 和一些库。它获取在 中找到的所有符号main.o,并从库中取出main.o已被认为必要的其他符号使用或使用的所有符号。在__isr_vectors目前尚不需要变量在这个阶段,因为没有人引用这个变量明确,所以它是由连接抛出。然后,链接器搜索所有命名的输入节以.isr_vector创建输出节.isr_vector。然而,直到这一刻,没有符号幸存下来,所以输出部分是空的,我的二进制文件没有中断表。
我知道一个让__isr_vector生存下来的技巧:在main.c. 但是,这有点丑陋且容易出错(在针对同一个静态库构建新应用程序时,您可能会忘记这样做)。所以这是我的问题:有没有另一种方法可以让__isr_vector我存活下来,这样我就可以在我的静态库中链接并完成它?
这些是重现问题的文件和步骤:
向量.c:
__ 属性__ ((section(".isr_vector"), used)) void* __isr_vectors = 0;
主文件:
无效_开始(无效){}
链接.ld:
内存{闪存(rx):原点= 0x08000000,长度= 1024K}
条目(_开始)
部分 { .isr_vector : ALIGN(4) { KEEP(*(.isr_vector)) } >FLASH
Run Code Online (Sandbox Code Playgroud).text : ALIGN(4) { *(.text .text.*) } >FLASH}
命令:
"C:\Program Files (x86)\GNU Tools ARM Embedded\4.9 …
我有一个像这样的链接器脚本:
OUTPUT_FORMAT(binary)
SECTIONS
{
. = 0xFFFF800000000000 ;
.startup_text : { processor.o(.text) }
.text : { *(EXCLUDE_FILE (processor.o) .text) }
.data : { *(.data) }
.rodata : { *(.rodata) }
linker_first_free_page = ALIGN(4096);
}
Run Code Online (Sandbox Code Playgroud)
一段代码加载此脚本生成的可执行文件,打印以下信息:
size of executable (pages) 3
first free page 0xffff800000003000
Run Code Online (Sandbox Code Playgroud)
可执行文件本身打印:
&(linker_first_free_page) 0xffff800000003000
Run Code Online (Sandbox Code Playgroud)
所以到目前为止一切正常。现在我的可执行文件需要一个.bss部分。请注意,我没有能够加载 elf 文件的加载器,所以我需要一个可以读取和使用的平面二进制文件,其中包含所有部分。
第一次尝试
OUTPUT_FORMAT(binary)
SECTIONS
{
. = 0xFFFF800000000000 ;
.startup_text : { processor.o(.text) }
.text : { *(EXCLUDE_FILE (processor.o) .text) }
.data : { *(.data) }
.rodata : { …Run Code Online (Sandbox Code Playgroud) 编辑添加:我现在已将此交叉发布到GNU ARM Embedded Toolchain站点,因为我相当确定这是一个链接器错误。
另外,我注意到当第一个程序段适合 ELF 文件的第一页时(即它在其页内的起始偏移量 >= ELF 标头中的字节数)似乎会发生这种情况。在这种情况下,段错误地向下扩展到文件的开头。这可以解释为什么如果起始地址的页内偏移量从 0x80 减少到 0x40,问题就会消失。
我正在为 ARM Cortex M0 实现一个独立的操作系统,但我的链接器有一个奇怪的问题。这是我的源文件OS.c,经过精简以说明问题:
int EntryPoint (void) { return 99 ; }
Run Code Online (Sandbox Code Playgroud)
这是我的链接器脚本文件OS.ld,只需将所有代码分配给从以下位置开始的区域0x10080:
MEMORY
{
NVM (rx) : ORIGIN = 0x10080, LENGTH = 0x1000
}
SECTIONS
{
.text 0x10080 :
{
OS.o (.text)
} > NVM
}
Run Code Online (Sandbox Code Playgroud)
我编译并链接它:
arm-none-eabi-gcc.exe -march=armv6-m -mthumb -c OS.c
arm-none-eabi-gcc.exe -oOS.elf -Xlinker --script=OS.ld OS.o -nostartfiles -nodefaultlibs
Run Code Online (Sandbox Code Playgroud)
现在,当我用 列出程序段时readelf OS.elf -l,我得到:
Elf …Run Code Online (Sandbox Code Playgroud) GNU ld(链接器脚本)手册第3.5.5节源代码参考有一些关于如何访问 C 源代码中的链接器脚本“变量”(实际上只是整数地址)的非常重要的信息。我用了这个信息。广泛使用链接器脚本变量,我在这里写了这个答案:How to get value of variable defined in ld linker script from C。
然而,很容易做错,并尝试访问链接描述文件变量的值(错误地)而不是它的地址,因为这有点深奥。手册(上面的链接)说:
这意味着,你不能访问 值链接脚本定义符号的-它没有价值-所有你能做的就是访问的地址链接脚本定义的符号。
因此,当您在源代码中使用链接描述文件定义的符号时,您应该始终获取该符号的地址,并且永远不要尝试使用其 value。
问题:那么,如果您确实尝试访问链接描述文件变量的value,这是“未定义的行为”吗?
想象一下在链接脚本(例如:STM32F103RBTx_FLASH.ld)中你有:
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
}
/* Some custom variables (addresses) I intend to access …Run Code Online (Sandbox Code Playgroud) 我正在使用binutils-2.21.53.0.1-6.fc16.x86_64.
我有一个小的目标文件,hello.o只有足够的"东西"来包含所有部分的内容:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
000000000000005d 0000000000000000 AX 0 0 4
[ 2] .rela.text RELA 0000000000000000 00000808
0000000000000060 0000000000000018 15 1 8
[ 3] .data PROGBITS 0000000000000000 000000a0
0000000000000000 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 000000a0
0000000000000053 0000000000000000 WA 0 0 32
[ 5] .rodata PROGBITS …Run Code Online (Sandbox Code Playgroud) GNU LD 链接器命令语言有条件语句吗?
背景:我正在为 ARM Cortex m0+ 开发固件,它由引导加载程序和应用程序组成。两者都在单独的项目中进行编译和刷新以定位目标,但我使用一个带有驱动程序、makefile 和加载器脚本符号链接的框架,这样我就可以为我制作的每个应用程序重复使用这些文件,而无需为每个应用程序复制这些文件。目前我有两个加载程序文件,分别用于引导加载程序和应用程序(makefile 自动指定相应的文件),内存分配如下:
引导装载程序
MEMORY {
flash (rx) : ORIGIN = 0x00000000, LENGTH = 16K
ram (rwx) : ORIGIN = 0x1FFFF000, LENGTH = 16K
}
Run Code Online (Sandbox Code Playgroud)
应用程序
MEMORY {
flash (rx) : ORIGIN = 0x00004000, LENGTH = 112K
ram (rwx) : ORIGIN = 0x1FFFF000, LENGTH = 16K
}
Run Code Online (Sandbox Code Playgroud)
就像 makefile 一样,我想将它们合并为类似的内容(使用 C 表达式来澄清)
MEMORY {
#ifdef(bootloaderSymbol)
flash (rx) : ORIGIN = 0x00000000, LENGTH = 16K
#else
flash (rx) : ORIGIN = 0x00004000, LENGTH = …Run Code Online (Sandbox Code Playgroud) 我出于自己的好奇心正在学习 x86 汇编,以了解底层的东西,并在这里发现了这个很棒的存储库,其中包含许多可以从 EFI shell 运行的示例。
当我检查这个 hello world 示例时,有一个包含以下内容的链接器脚本:
ENTRY(mystart)
SECTIONS
{
. = 0x7c00;
.text : {
entry.o(.text)
*(.text)
*(.data)
*(.rodata)
__bss_start = .;
/* COMMON vs BSS: /sf/ask/1178500151/ */
*(.bss)
*(COMMON)
__bss_end = .;
}
/* /sf/ask/3750926651/ */
.sig : AT(ADDR(.text) + 512 - 2)
{
SHORT(0xaa55);
}
/DISCARD/ : {
*(.eh_frame)
}
__stack_bottom = .;
. = . + 0x1000;
__stack_top = .;
}
Run Code Online (Sandbox Code Playgroud)
我无法理解为什么需要它?只是为了指定加载地址?我对链接器脚本的一般理解是,当存在多个目标文件时,它们更有用,并且链接器脚本可用于定义如何将多个目标文件中的部分组合到单个可执行文件中。
如果我在此示例中不指定链接描述文件会怎样?(肯定至少有 2 个目标文件 - 一个来自.s,一个来自 …
我正在使用 GNAT 构建我的 Ada/SPARK 项目,并且使用链接器脚本。以下是摘录:
SECTIONS
{
.code :
{
. = ALIGN(0x4);
*(.text.section1)
_end_of_section1 = .;
*(.text.section2)
...
}
}
Run Code Online (Sandbox Code Playgroud)
符号 _end_of_section1 是两个节之间的地址。我希望能够在我的 Ada 代码中访问它。我知道在 C 中使用extern char _end_of_section1[];. 在 Ada 中可以做这样的事情吗?如果没有,是否有其他方法可以在代码中获取该地址?
我正在构建一个微型微控制器,其中只包含用于自学目的的基本要素.通过这种方式,我可以刷新我对linkerscript,启动代码等主题的了解......
编辑:
我收到了很多评论,指出下面显示的"绝对最小STM32应用"并不好.当你注意到向量表不完整,.bss-section没有处理,外围地址不完整时,你是完全正确的,...请允许我解释原因.
作者的目的从未在本章中编写完整且有用的应用程序.他的目的是逐步解释链接器如何工作,启动代码如何工作,STM32的启动过程是什么样的,......纯粹用于教育目的.我可以欣赏这种方法,并学到了很多东西.
我在下面的例子取自相关章节的中间部分.本章继续向linkerscript和启动代码添加更多部分(例如初始化.bss-section).
我从他的章节中间放置文件的原因是因为我遇到了特定的错误消息.我想在继续之前解决这个问题.
有问题的章节在他书的最后.它适用于想要获得更多人们甚至不考虑的主题的更有经验或好奇的读者(大多数人使用制造商给出的标准linkerscript和启动代码而不读它).
请记住这一点,请让我们关注手头的技术问题(如下面的错误消息中所述).也请接受我真诚的道歉,我之前没有澄清作者的意图.但我现在已经完成了,所以我们可以继续前进;-)
我正在阅读的教程是本书的第20章:"掌握STM32"(https://leanpub.com/mastering-stm32).该书解释了如何使用两个文件制作一个微型微控制器应用程序:main.c和linkerscript.ld.由于我没有使用IDE(如Eclipse),我还添加build.bat并clean.bat生成编译命令.所以我的项目文件夹看起来像这样:
在继续之前,我应该提供一些关于我的系统的更多细节:
操作系统: Windows 10,64位
微控制器:带STM32F401RE微控制器的NUCLEO-F401RE板.
编译器: arm-none-eabi-gcc版本6.3.1 20170620(发布)[ARM/embedded-6-branch revision 249437].
主文件如下所示:
/* ------------------------------------------------------------ */
/* Minimal application */
/* for NUCLEO-F401RE */
/* ------------------------------------------------------------ */
typedef unsigned long uint32_t;
/* Memory and peripheral start addresses (common to all STM32 MCUs) */
#define FLASH_BASE 0x08000000
#define SRAM_BASE 0x20000000
#define PERIPH_BASE 0x40000000
/* …Run Code Online (Sandbox Code Playgroud) 我在zircon kernel start.S中找到了这行汇编代码
str x0, [tmp, #:lo12:zbi_paddr]
Run Code Online (Sandbox Code Playgroud)
对于ARM64。我还发现zbi_paddrC++ 中定义了:
extern paddr_t zbi_paddr;
Run Code Online (Sandbox Code Playgroud)
所以我开始研究这#:lo12:意味着什么。
我发现/sf/answers/2702611691/看起来像是一个很好的解释,但它没有解释非常基本的内容:什么是重新分配以及为什么需要一些东西。
我猜想,由于在 C++ 代码中zbi_paddrr定义并使用,由于在地址从 0 开始的目标文件上生成,因此链接过程必须将其中的所有地址重新分配到最终可执行文件中的地址。start.Sstart.Sstart.o
为了跟踪需要重新分配的符号,ELF 存储这些结构,如答案中所述:
typedef struct
{
Elf64_Addr r_offset; /* Address of reference */
Elf64_Xword r_info; /* Symbol index and type of relocation */
} Elf64_Rel;
typedef struct
{
Elf64_Addr r_offset; /* Address of reference */
Elf64_Xword r_info; /* Symbol index and type of relocation */
Elf64_Sxword r_addend; /* Constant part …Run Code Online (Sandbox Code Playgroud)