void parse_elf(void *output)
{
Elf32_Ehdr ehdr;
Elf32_Phdr *phdrs, *phdr;
memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum);
for (i = 0; i < ehdr.e_phnum; i++) {
phdr = &phdrs[i];
switch (phdr->p_type) {
case PT_LOAD:
#ifdef CONFIG_RELOCATABLE
dest = output;
/*
参考下面的vmlinux.lds脚本
dest = out + (phdr->p_paddr - LOAD_PHYSICAL_ADDR)
= out + (ADDR(.text) - LOAD_OFFSET) - LOAD_PHYSICAL_ADDR
= out + (LOAD_OFFSET + LOAD_PHYSICAL_ADDR - LOAD_OFFSET) - LOAD_PHYSICAL_ADDR
= out
*/
dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR);
#else
dest = (void *)(phdr->p_paddr); // p_paddr 为加载地址
#endif
memcpy(dest,
output + phdr->p_offset,
phdr->p_filesz);
break;
default: /* Ignore other PT_* */ break;
}
}
}
对应的lds脚本
SECTIONS
{
#ifdef CONFIG_X86_32
. = LOAD_OFFSET + LOAD_PHYSICAL_ADDR;
phys_startup_32 = startup_32 - LOAD_OFFSET;
#endif
/* Text and read-only data */
.text : AT(ADDR(.text) - LOAD_OFFSET) { // AT 定义加载地址,属于 PT_LOAD 段
_text = .;
/* bootstrapping code */
HEAD_TEXT
#ifdef CONFIG_X86_32
. = ALIGN(PAGE_SIZE);
*(.text.page_aligned)
#endif
. = ALIGN(8);
_stext = .;
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
IRQENTRY_TEXT
*(.fixup)
*(.gnu.warning)
/* End of text section */
_etext = .;
} :text = 0x9090
/* Data */
.data : AT(ADDR(.data) - LOAD_OFFSET) {
/* Start of data section */
_sdata = .;
/* init_task */
INIT_TASK_DATA(THREAD_SIZE)
#ifdef CONFIG_X86_32
/* 32 bit has nosave before _edata */
NOSAVE_DATA
#endif
PAGE_ALIGNED_DATA(PAGE_SIZE)
CACHELINE_ALIGNED_DATA(L1_CACHE_BYTES)
DATA_DATA
CONSTRUCTORS
/* rarely changed data like cpu maps */
READ_MOSTLY_DATA(INTERNODE_CACHE_BYTES)
/* End of data section */
_edata = .;
} :data
linux设备查看
[root@cliffr linux-2.6.32]# readelf -h vmlinux
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: Intel 80386
Version: 0x1
Entry point address: 0x400000
Start of program headers: 52 (bytes into file)
Start of section headers: 90528352 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 3
Size of section headers: 40 (bytes)
Number of section headers: 70
Section header string table index: 67
[root@cliffr linux-2.6.32]#
[root@cliffr linux-2.6.32]# readelf -l vmlinux
Elf file type is EXEC (Executable file)
Entry point 0x400000
There are 3 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0xc0400000 0x00400000 0x58f000 0x58f000 R E 0x1000
LOAD 0x590000 0xc098f000 0x0098f000 0xfe000 0x34e000 RWE 0x1000
NOTE 0x3e396c 0xc07e296c 0x007e296c 0x00168 0x00168 0x4
Section to Segment mapping:
Segment Sections...
00 .text .notes __ex_table .rodata __bug_table .pci_fixup __ksymtab __ksymtab_gpl __kcrctab __kcrctab_gpl __ksymtab_strings __init_rodata __param
01 .data .init.text .init.data .x86_cpu_dev.init .parainstructions .altinstructions .altinstr_replacement .exit.text .data.percpu .smp_locks .bss .brk
02 .notes
对于第二个LOAD来说, 也就是数据段, 从elf的角度来看, 两者存放的位置是紧挨着。我们看一下代码段内存加载的位置:
dest = out + (phdr->p_paddr - LOAD_PHYSICAL_ADDR)
= out + (AT(ADDR(.text)) + text_len - LOAD_OFFSET - LOAD_PHYSICAL_ADDR)
= out + text_len
[root@cliffr linux-2.6.32]# cat .config | grep CONFIG_PHYSICAL_START
CONFIG_PHYSICAL_START=0x400000
简单介绍一下Linux中ELF格式文件
http://www.elecfans.com/emb/20190402898901.html
ELF结构
http://blog.chinaunix.net/uid-8473611-id-3184556.html
vmlinux 和普通elf文件的差别 linux kernel加载简述
https://blog.csdn.net/wdjjwb/article/details/81145255
ELF格式大致描述
https://blog.csdn.net/qq_36503007/article/details/82821922
链接脚本使用AT加载地址的总结
https://blog.csdn.net/czg13548930186/article/details/78770601
关于链接脚本.lds的小知识点
https://blog.csdn.net/yilongdashi/article/details/84873908