为啥我的链接器脚本中的 ENTRY() 没有设置为 . = <地址> 部分?
Posted
技术标签:
【中文标题】为啥我的链接器脚本中的 ENTRY() 没有设置为 . = <地址> 部分?【英文标题】:Why is the ENTRY() in my linker script not being set to the . = <address> section?为什么我的链接器脚本中的 ENTRY() 没有设置为 . = <地址> 部分? 【发布时间】:2019-12-30 18:34:01 【问题描述】:我正在尝试将我正在构建的静态链接二进制文件的入口点重新定位到一个固定值,以便它可以通过引导加载程序加载到该内存区域并使用函数指针取消引用跳转。这是我正在使用的加载程序脚本:
ENTRY(main)
SECTIONS
. = 0x0000000000200000;
.text :
*(.text)
. = ALIGN(8);
.data :
*(.data)
*(.rodata)
. = ALIGN(8);
__bss_start = .;
.bss :
bss = .; _bss = .; __bss = .;
*(.bss);
end = .; _end = .; __end = .;
.preinit_array :
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
.init_array :
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
.fini_array :
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
这是我用于最终链接的命令行:
...
CC = g++
CFLAGS = -g -mcmodel=large -fPIC -T src/controlix.ld -Wl,--no-relax -static -nostdlib -static-libgcc
...
$(CC) $(CFLAGS) $(shell find bin/ -name "*.o") -o $(TARGET_OBJ)
(为简洁起见省略了构建系统的其余部分的详细信息,我认为这些都无关紧要)
所以,我假设当我查看生成的 controlix.o 二进制文件时,我应该看到 main() 符号位于 0x200000,对吧?错误:
$ nm bin/controlix.o |grep main
000000000021d0c6 t _GLOBAL__sub_D_main.cpp
000000000021d087 t _GLOBAL__sub_I_main.cpp
000000000021cef1 T main
0000000000256e11 t _ZL8eiremainPtS_P7LDPARMS
我做错了什么?
【问题讨论】:
【参考方案1】:我应该看到 main() 符号位于 0x200000,对吧?
没有。想象一下在调用 main 之前需要做多少事情。必须初始化库,调用 (C++) 的构造函数和析构函数,在许多系统上 .bss 归零和 .data 初始化 + 许多其他事情我现在不记得了。
main
是C
程序的入口点,但不是(在几乎所有已知的实现中)可执行文件的入口点。
如果您希望首先调用您的函数,请将其放在单独的段中并修改链接描述文件
.text :
*(.beforetext)
. = ALIGN(8);
__text_start = .;
*(.text)
. = ALIGN(8);
extern unsigned __text_start;
void __attribute__((section(".beforetext"))) myEntryFunc(void)
....
((void (*)(void))(&__text_start))(); // call the original executable entry point
【讨论】:
【参考方案2】:假设 ELF,ENTRY
指令将 main
定义为 ELF 文件头中的入口点,对 main
的地址没有影响。
要强制main
位于文本段的开头,一种方法是将main
放在它自己的部分中,然后将此部分放在.text
部分之前。
使用最新的 GCC 和 Clang,您可以在变量或函数声明中使用 __attribute__((section(".main"))__
或 [[gnu::section(".main")]]
来设置节。
【讨论】:
以上是关于为啥我的链接器脚本中的 ENTRY() 没有设置为 . = <地址> 部分?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 STM32CubeIDE 有 2 个生成的链接器脚本?
为啥 STM32 gcc 链接器脚本会自动丢弃这些标准库中的所有输入节:libc.a、libm.a、libgcc.a?