如何在文本部分的开头放置kernel_entry函数

时间:2014-07-18 10:46:29

标签: c linux

我的内核文本部分从地址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。

请帮助我如何解决问题。

1 个答案:

答案 0 :(得分:0)

首先,如果在符号中插入符号,则会移动该部分中的所有后续符号。如果某些代码依赖于假设某个对象位于.text + hardcoded offset,则代码将被破坏。

如果你决定这样做,那么你需要能够在链接阶段之前编辑vmlinux.lds.S文件或者至少修补生成的vmlinux.ldsvmlinux.lds.S本质上是一个链接器脚本+一堆C宏,因此语法非常相似。

基本想法是将kernel_entry()放到名为.kernel_entry的单独部分,并向vmlinux.lds.S添加一条记录,将.kernel_entry添加到.text在所有其他部分之前。需要单独的部分以确保该部分中只有一个符号。

如何做到这一点:

  1. kernel_entry()函数放入其自己的部分。您可以在源代码中使用GCC属性执行此操作:

    void kernel_entry() __attribute__((section(".kernel_entry")));
    {
        /* function body */
    }
    
  2. .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)
  3. 就是这样。链接器会将 .text : { *(.kernel_entry) TEXT_TEXT SCHED_TEXT LOCK_TEXT KPROBES_TEXT IRQENTRY_TEXT *(.text.*) } :text = 0 (包含.kernel_entry)放在kernel_entry()的开头,这似乎是你想要的。

    祝你好运!