Program Header Table
文章目录
程序头表与段表相互独立,由ELF文件头统一管理。
下面将它们简称PH和SH。
程序头表负责ELF文件从文件到加载后映像的映射关系,一般只有可执行文件包含。
readelf -l
查看。
1. segment和section
在英语里,segment同质,如一段绳子;section则是指不同种类的东西。
segment
程序头表项描述的对象称为segment,即elf文件加载后的数据块;
它提供给加载器loader使用,loader需要知道rwe属性
可执行文件可以没有section,但一定要有segment。
section
section描述加载前的数据块,给链接器linker使用。
linker主要关心.text, .rel.text, .data, .rodata
等,需要重定位。
联系
比如.text/.data段加载信息存在PH表项对应的存放代码/数据的segment中。
有时为了简化PH表项的个数,会把整个elf文件作为一个segment加载(同质???)。
2. 数据结构
typedef struct
{
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;
这8个字段都描述segment。
2.1 field p_type
segment类型:
/* Legal values for p_type (segment type). */
#define PT_NULL 0 /* Program header table entry unused */
#define PT_LOAD 1 /* Loadable program segment */
#define PT_DYNAMIC 2 /* Dynamic linking information */
#define PT_INTERP 3 /* Program interpreter */
#define PT_NOTE 4 /* Auxiliary information */
#define PT_SHLIB 5 /* Reserved */
#define PT_PHDR 6 /* Entry for header table itself */
#define PT_TLS 7 /* Thread-local storage segment */
#define PT_NUM 8 /* Number of defined types */
一个可执行文件至少有一个PT_LOAD
类型的段,如text, data,并根据p_align
对齐。
2.2 Other Fields
-
p_offset, segment文件偏移;
-
p_vaddr, segment虚拟地址;
-
p_paddr, segment物理地址。因为现代os都使用了分页机制,所以不用关心,readelf输出结果等于虚拟地址;
-
p_filesz, segment文件大小;
-
p_memsz, segment内存大小;
-
p_align, segmenet内存对齐大小,LOAD段值为0x1000;
-
p_flags, segment权限属性
#define PF_X (1 << 0) /* Segment is executable */
#define PF_W (1 << 1) /* Segment is writable */
#define PF_R (1 << 2) /* Segment is readable */
比如text段,PF_X|PF_R
。
readelf源码
/* Returns 1 if the program headers were loaded. */
static int
process_program_headers (FILE * file)
{
Elf_Internal_Phdr * segment;
unsigned int i;
if (elf_header.e_phnum == 0)
{
/* PR binutils/12467. */
if (elf_header.e_phoff != 0)
warn (_("possibly corrupt ELF header - it has a non-zero program"
" header offset, but no program headers"));
else if (do_segments)
printf (_("\nThere are no program headers in this file.\n"));
return 0;
}
if (do_segments && !do_header)
{
printf (_("\nElf file type is %s\n"), get_file_type (elf_header.e_type));
printf (_("Entry point "));
print_vma ((bfd_vma) elf_header.e_entry, PREFIX_HEX);
printf (_("\nThere are %d program headers, starting at offset "),
elf_header.e_phnum);
print_vma ((bfd_vma) elf_header.e_phoff, DEC);
printf ("\n");
}
if (! get_program_headers (file))
return 0;
if (do_segments)
{
if (elf_header.e_phnum > 1)
printf (_("\nProgram Headers:\n"));
else
printf (_("\nProgram Headers:\n"));
if (is_32bit_elf)
printf
(_(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n"));
else if (do_wide)
printf
(_(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n"));
else
{
printf
(_(" Type Offset VirtAddr PhysAddr\n"));
printf
(_(" FileSiz MemSiz Flags Align\n"));
}
}
dynamic_addr = 0;
dynamic_size = 0;
for (i = 0, segment = program_headers;
i < elf_header.e_phnum;
i++, segment++)
{
if (do_segments)
{
printf (" %-14.14s ", get_segment_type (segment->p_type));
if (is_32bit_elf)
{
printf ("0x%6.6lx ", (unsigned long) segment->p_offset);
printf ("0x%8.8lx ", (unsigned long) segment->p_vaddr);
printf ("0x%8.8lx ", (unsigned long) segment->p_paddr);
printf ("0x%5.5lx ", (unsigned long) segment->p_filesz);
printf ("0x%5.5lx ", (unsigned long) segment->p_memsz);
printf ("%c%c%c ",
(segment->p_flags & PF_R ? 'R' : ' '),
(segment->p_flags & PF_W ? 'W' : ' '),
(segment->p_flags & PF_X ? 'E' : ' '));
printf ("%#lx", (unsigned long) segment->p_align);
}
else if (do_wide)
{
if ((unsigned long) segment->p_offset == segment->p_offset)
printf ("0x%6.6lx ", (unsigned long) segment->p_offset);
else
{
print_vma (segment->p_offset, FULL_HEX);
putchar (' ');
}
print_vma (segment->p_vaddr, FULL_HEX);
putchar (' ');
print_vma (segment->p_paddr, FULL_HEX);
putchar (' ');
if ((unsigned long) segment->p_filesz == segment->p_filesz)
printf ("0x%6.6lx ", (unsigned long) segment->p_filesz);
else
{
print_vma (segment->p_filesz, FULL_HEX);
putchar (' ');
}
if ((unsigned long) segment->p_memsz == segment->p_memsz)
printf ("0x%6.6lx", (unsigned long) segment->p_memsz);
else
{
print_vma (segment->p_memsz, FULL_HEX);
}
printf (" %c%c%c ",
(segment->p_flags & PF_R ? 'R' : ' '),
(segment->p_flags & PF_W ? 'W' : ' '),
(segment->p_flags & PF_X ? 'E' : ' '));
if ((unsigned long) segment->p_align == segment->p_align)
printf ("%#lx", (unsigned long) segment->p_align);
else
{
print_vma (segment->p_align, PREFIX_HEX);
}
}
else
{
print_vma (segment->p_offset, FULL_HEX);
putchar (' ');
print_vma (segment->p_vaddr, FULL_HEX);
putchar (' ');
print_vma (segment->p_paddr, FULL_HEX);
printf ("\n ");
print_vma (segment->p_filesz, FULL_HEX);
putchar (' ');
print_vma (segment->p_memsz, FULL_HEX);
printf (" %c%c%c ",
(segment->p_flags & PF_R ? 'R' : ' '),
(segment->p_flags & PF_W ? 'W' : ' '),
(segment->p_flags & PF_X ? 'E' : ' '));
print_vma (segment->p_align, HEX);
}
}
switch (segment->p_type)
{
case PT_DYNAMIC:
if (dynamic_addr)
error (_("more than one dynamic segment\n"));
/* By default, assume that the .dynamic section is the first
section in the DYNAMIC segment. */
dynamic_addr = segment->p_offset;
dynamic_size = segment->p_filesz;
/* Try to locate the .dynamic section. If there is
a section header table, we can easily locate it. */
if (section_headers != NULL)
{
Elf_Internal_Shdr * sec;
sec = find_section (".dynamic");
if (sec == NULL || sec->sh_size == 0)
{
/* A corresponding .dynamic section is expected, but on
IA-64/OpenVMS it is OK for it to be missing. */
if (!is_ia64_vms ())
error (_("no .dynamic section in the dynamic segment\n"));
break;
}
if (sec->sh_type == SHT_NOBITS)
{
dynamic_size = 0;
break;
}
dynamic_addr = sec->sh_offset;
dynamic_size = sec->sh_size;
if (dynamic_addr < segment->p_offset
|| dynamic_addr > segment->p_offset + segment->p_filesz)
warn (_("the .dynamic section is not contained"
" within the dynamic segment\n"));
else if (dynamic_addr > segment->p_offset)
warn (_("the .dynamic section is not the first section"
" in the dynamic segment.\n"));
}
break;
case PT_INTERP:
if (fseek (file, archive_file_offset + (long) segment->p_offset,
SEEK_SET))
error (_("Unable to find program interpreter name\n"));
else
{
char fmt [32];
int ret = snprintf (fmt, sizeof (fmt), "%%%ds", PATH_MAX);
if (ret >= (int) sizeof (fmt) || ret < 0)
error (_("Internal error: failed to create format string to display program interpreter\n"));
program_interpreter[0] = 0;
if (fscanf (file, fmt, program_interpreter) <= 0)
error (_("Unable to read program interpreter name\n"));
if (do_segments)
printf (_("\n [Requesting program interpreter: %s]"),
program_interpreter);
}
break;
}
if (do_segments)
putc ('\n', stdout);
}
if (do_segments && section_headers != NULL && string_table != NULL)
{
printf (_("\n Section to Segment mapping:\n"));
printf (_(" Segment Sections...\n"));
for (i = 0; i < elf_header.e_phnum; i++)
{
unsigned int j;
Elf_Internal_Shdr * section;
segment = program_headers + i;
section = section_headers + 1;
printf (" %2.2d ", i);
for (j = 1; j < elf_header.e_shnum; j++, section++)
{
if (!ELF_TBSS_SPECIAL (section, segment)
&& ELF_SECTION_IN_SEGMENT_STRICT (section, segment))
printf ("%s ", SECTION_NAME (section));
}
putc ('\n',stdout);
}
}
return 1;
}