天天看點

慢慢欣賞linux elf檔案

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裝置檢視

[[email protected] 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
[[email protected] linux-2.6.32]# 
[[email protected] 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

[[email protected] 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

繼續閱讀