我的内核文本部分从地址0x80100000& kernel_entry函数位于地址0x80585f70。我希望kernel_entry应该将文本部分的开头放在地址0x80100000。
文本部分的起始地址
$ head -n 10 ../../../System.map
80100000 A _text
80100400 T __kernel_entry
80100400 T _stext
initrd图像中的入口点地址
$ readelf -h vmlinuz-initrd
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: MIPS R3000
Version: 0x1
Entry point address: 0x80585f70
Start of program headers: 52 (bytes into file)
Start of section headers: 63759852 (bytes into file)
Flags: 0x50001001, noreorder, o32, mips32
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
Number of section headers: 25
Section header string table index: 22
$
我尝试使用linux链接器脚本(vmlinux.lds)通过在文本部分的开头添加head.o模块来解决问题。
vmlinux.lds linux链接描述文件
SECTIONS
{
. = 0x80100000;
/* read-only */
_text = .; /* Text and read-only data */
.text : {
. = ALIGN(8); head.o(.ref.text)
. = ALIGN(8); *(.text.hot) *(.text) *(.ref.text) *(.devinit.text) *(.devexit.text) *(.cpuinit.text) *(.cpuexit.text) *(.text.unlikely)
. = ALIGN(8); __sched_text_start = .; *(.sched.text) __sched_text_end = .;
. = ALIGN(8); __lock_text_start = .; *(.spinlock.text) __lock_text_end = .;
. = ALIGN(8); __kprobes_text_start = .; *(.kprobes.text) __kprobes_text_end = .;
kernel_entry函数格式.ref.text
$ objdump -t head.o
head.o: file format elf32-little
SYMBOL TABLE:
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .ref.text 00000000 .ref.text
00000000 l d .cpuinit.text 00000000 .cpuinit.text
00000000 l d .reginfo 00000000 .reginfo
00000000 l d .pdr 00000000 .pdr
00000400 g O .text 00000000 _stext
00000400 g F .text 00000000 __kernel_entry
00000000 g F .ref.text 000000c8 kernel_entry
但由于通过vmlinux.lds.S文件自动生成的vmlinux.lds,我无法更改此内容。
我试着把同一行&#34 ;. = ALIGN(8); head.o(.ref.text)" 来自vmlinux.lds但在构建内核时找不到head.o。
请帮助我如何解决问题。
答案 0 :(得分:0)
首先,如果在符号中插入符号,则会移动该部分中的所有后续符号。如果某些代码依赖于假设某个对象位于.text
+ hardcoded offset
,则代码将被破坏。
如果你决定这样做,那么你需要能够在链接阶段之前编辑vmlinux.lds.S
文件或者至少修补生成的vmlinux.lds
。 vmlinux.lds.S
本质上是一个链接器脚本+一堆C宏,因此语法非常相似。
基本想法是将kernel_entry()
放到名为.kernel_entry
的单独部分,并向vmlinux.lds.S
添加一条记录,将.kernel_entry
添加到.text
在所有其他部分之前。需要单独的部分以确保该部分中只有一个符号。
如何做到这一点:
将kernel_entry()
函数放入其自己的部分。您可以在源代码中使用GCC属性执行此操作:
void kernel_entry() __attribute__((section(".kernel_entry")));
{
/* function body */
}
将.kernel_entry
部分放在.text
的{{1}}的开头。
每个架构都有自己的vmlinux.lds.S
,因此文件的确切内容可能会有所不同,但一般情况下,您可能会在vmlinux.lds.S
中找到.text
部分,如下所示:< / p>
vmlinux.lds.S
.text : {
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
IRQENTRY_TEXT
*(.text.*)
} :text = 0
,TEXT_TEXT
等实际上是扩展为某些链接器脚本命令的C宏。您可以使用常规语法并将SCHED_TEXT
添加到该部分的开头,如下所示:
*(.kernel_entry)
就是这样。链接器会将 .text : {
*(.kernel_entry)
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
IRQENTRY_TEXT
*(.text.*)
} :text = 0
(包含.kernel_entry
)放在kernel_entry()
的开头,这似乎是你想要的。