代码之家  ›  专栏  ›  技术社区  ›  Nemo_Sol

编译动态链接的内核

  •  1
  • Nemo_Sol  · 技术社区  · 6 年前

    我试图在xv6内核中实现KASLR,因此我需要以动态方式重新编译/链接内核(重定位表)。 由于内核当前是硬编码的,即使我更改了内核代码和数据映射,指令仍然会引用硬编码的虚拟地址。我需要一种没有硬编码地址的方法来让指令引用elf的其他部分。这方面的一个例子如下: 这是它在当前编译后的外观示例:

    8010018e:   83 ec 0c                sub    $0xc,%esp
    80100191:   68 a7 79 10 80          push   $0x801079a7
    80100196:   e8 b5 01 00 00          call   80100350 <panic>
    8010019b:   90                      nop
    8010019c:   8d 74 26 00             lea    0x0(%esi,%eiz,1),%esi.
    

    如您所见,push指令等指令引用了硬编码的v地址($0x801079a7)。这是因为此elf是以静态方式编译的,没有重新定位:(readelf printout)

     Program Headers:
        Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
        LOAD           0x001000 0x80100000 0x00100000 0x0b516 0x15668 RWE 0x1000
        GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10
        Section to Segment mapping:
        Segment Sections...
        00     .text .rodata .stab .stabstr .data .bss
        01
        There is no dynamic section in this file.
        There are no relocations in this file.
    

    我需要关于如何将静态链接的elf二进制文件重新编译为具有重定位的二进制文件,然后能够将内核映射到虚拟内存中的任何位置的建议。此外,为了解析elf以正确地重新定位加载它,我必须在引导加载程序中修改什么?我需要自定义解析内核elf中包含的GOT吗?

    下面是一个链接器脚本,用于编译内核并设置其基本硬编码虚拟地址:0x80100000

    OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
    OUTPUT_ARCH(i386)
    ENTRY(_start)
    
    SECTIONS
    {
        /* Link the kernel at this address: "." means the current address */
            /* Must be equal to KERNLINK */
        . = 0x80100000;
    
        .text : AT(0x100000) {
            *(.text .stub .text.* .gnu.linkonce.t.*)
        }
    
        PROVIDE(etext = .); /* Define the 'etext' symbol to this value */
    
        .rodata : {
            *(.rodata .rodata.* .gnu.linkonce.r.*)
        }
    
        /* Include debugging information in kernel memory */
        .stab : {
            PROVIDE(__STAB_BEGIN__ = .);
            *(.stab);
            PROVIDE(__STAB_END__ = .);
            BYTE(0)     /* Force the linker to allocate space
                       for this section */
        }
    
        .stabstr : {
            PROVIDE(__STABSTR_BEGIN__ = .);
            *(.stabstr);
            PROVIDE(__STABSTR_END__ = .);
            BYTE(0)     /* Force the linker to allocate space
                       for this section */
        }
    
        /* Adjust the address for the data segment to the next page */
        . = ALIGN(0x1000);
    
        /* Conventionally, Unix linkers provide pseudo-symbols
         * etext, edata, and end, at the end of the text, data, and bss.
         * For the kernel mapping, we need the address at the beginning
         * of the data section, but that's not one of the conventional
         * symbols, because the convention started before there was a
         * read-only rodata section between text and data. */
        PROVIDE(data = .);
    
        /* The data segment */
        .data : {
            *(.data)
        }
    
        PROVIDE(edata = .);
    
        .bss : {
            *(.bss)
        }
    
        PROVIDE(end = .);
    
        /DISCARD/ : {
            *(.eh_frame .note.GNU-stack)
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Nemo_Sol    6 年前

    看起来我必须修改xv6引导加载程序并重写大部分操作系统才能实现动态内核。