Таблица заголовков программы исполняемого или общего файла - это массив структур, описывающих сегменты и содержащих прочие сведения, необходимые системе для подготовки к выполнению программы. Сегмент объектного файла содержит один или несколько разделов, как описано в разделе Содержимое сегментов. Заголовки программы имеют смысл только для исполняемых и общих объектных файлов. Размер заголовка программы описан в заголовке ELF файла в элементах e_phentsize и e_phnum.
Более подробная информация приведена в разделе Заголовок ELF в Главе 32.
| 
 Заголовок программы 
 typedef struct {
   Elf32_Word   p_type;
   Elf32_Off    p_offset;
   Elf32_Addr   p_vaddr;
   Elf32_Addr   p_paddr;
   Elf32_Word   p_filesz;
   Elf32_Word   p_memsz;
   Elf32_Word   p_flags;
   Elf32_Word   p_align;
} Elf32_Phdr;
 
typedef struct {
   Elf64_Word   p_type;
   Elf64_Word   p_flags;
   Elf64_Off    p_offset;
   Elf64_Addr   p_vaddr;
   Elf64_Addr   p_paddr;
   Elf64_Xword  p_filesz;
   Elf64_Xword  p_memsz;
   Elf64_Xword  p_align;
} Elf64_Phdr;
 | 
Некоторые записи описывают сегмент процесса; другие содержат дополнительную информацию и не влияют на образ процесса. Записи сегмента могут быть указаны в любом порядке, за исключением специальных случаев, описанных ниже.
Ниже перечислены определенные значения типа; другие значение зарезервированы для использования в будущем.
Типы сегментов, таблица p_type
| Имя | Значение | 
|---|---|
| PT_NULL | 0 | 
| PT_LOAD | 1 | 
| PT_DYNAMIC | 2 | 
| PT_INTERP | 3 | 
| PT_NOTE | 4 | 
| PT_SHLIB | 5 | 
| PT_PHDR | 6 | 
| PT_LOOS | 0x60000000 | 
| PT_HIOS | 0x6fffffff | 
| PT_LOPROC | 0x70000000 | 
| PT_HIPROC | 0x7fffffff | 
Более подробная информация приведена в разделе Раздел примечаний.
Более подробная информация приведена в разделе Интерпретатор программ.
Примечание: Все сегменты заголовков программы не обязательны, если специально не оговорено иное. Таблица заголовков конкретного файла может содержать только те элементы, которые относятся к данному файлу.
Как описано в разделе Загрузка программы, виртуальные адреса в заголовке программы могут не соответствовать виртуальным адресам в образе этой программы в памяти системы. В исполняемых файлах обычно указывается абсолютный код. Для правильного выполнения процессов сегменты должны находиться по виртуальным адресам, которые использовались при создании исполняемого файла. В то же время, сегменты общих объектов обычно содержат код, не зависящий от положения в памяти. Это позволяет изменять виртуальные адреса сегментов в различных процессах, не затрагивая поведение программы.
На некоторых платформах, если система выбирает виртуальные адреса для конкретных процессов, используются относительные смещения одного сегмента относительно другого в общем объекте. Независимый от положения в памяти код на таких платформах применяет относительную адресацию сегментов, поэтому взаимное расположение сегментов в памяти и в файле должно совпадать. Таким образом, разница между виртуальным адресом сегмента в памяти и соответствующим виртуальным адресом в файле постоянна для всех исполняемых или общих объектов одного процесса. Эта разница называется базовым адресом. Одним из применений базового адреса является перемещение образа в памяти при динамической компоновке.
Базовый адрес в исполняемом или общем объектном файле (на платформах, поддерживающих эту технологию) вычисляется во время выполнения на основе трех значений: адреса загрузки в виртуальную память, максимального размера страницы и минимального виртуального адреса загружаемого сегмента программы. Для вычисления базового адреса система определяет адрес, связанный с минимальным значением p_vaddr, для сегмента PT_LOAD. Этот адрес округляется снизу до ближайшего значения, кратного максимальному размеру страницы. Соответствующее значение p_vaddr также округляется снизу до ближайшего значения, кратного максимальному размеру страницы. Базовый адрес - это разница между округленным адресом в памяти и округленным значением p_vaddr.
Более подробная информация и примеры приведены в соответствующем разделе документации по процессору. Раздел Интерфейс операционной системы в Главе 32 документации по процессору содержит дополнительную информацию о виртуальном адресном пространстве и размере страниц.
В программе, загружаемой в память, должен быть хотя бы один загружаемый сегмент, хотя это и не следует явно из формата файла. При создании образа загружаемого сегмента в памяти система присваивает права доступа в соответствии с элементом p_flags.
Биты флага сегмента, таблица p_flags
| Имя | Значение | Описание | 
|---|---|---|
| PF_X | 0x1 | Выполнение | 
| PF_W | 0x2 | Запись | 
| PF_R | 0x4 | Чтение | 
| PF_MASKOS | 0x0ff00000 | Не задано | 
| PF_MASKPROC | 0xf0000000 | Не задано | 
Все биты в маске PF_MASKOS зарезервированы для конкретной операционной системы.
Все биты в маске PF_MASKPROC зарезервированы для конкретного процессора. Их применение описано в документации по процессору.
Если некоторый бит равен 0, то соответствующий тип доступа запрещен. Действительные права доступа зависят также от блока управления памятью и могут различаться в разных системах. Хотя допустимы все комбинации флагов, система может предоставлять более широкие права доступа, чем запрошено. Однако права на запись предоставляются только в том случае, если они явно указаны. В следующей таблице показана точная интерпретация флагов в допустимая интерпретация. В системах, совместимых с ABI, может применяться любая схема.
Права доступа к сегменту
| Флаги | Значение | Точная | Допустимая | 
|---|---|---|---|
| none | 0 | Доступ запрещен | Доступ запрещен | 
| PF_X | 1 | Только выполнение | Чтение, выполнение | 
| PF_W | 2 | Только запись | Чтение, запись, выполнение | 
| PF_W+PF_X | 3 | Запись, выполнение | Чтение, запись, выполнение | 
| PF_R | 4 | Только чтение | Чтение, выполнение | 
| PF_R+PF_X | 5 | Чтение, выполнение | Чтение, выполнение | 
| PF_R+PF_W | 6 | Чтение, запись | Чтение, запись, выполнение | 
| PF_R+PF_W+PF_X | 7 | Чтение, запись, выполнение | Чтение, запись, выполнение | 
Например, обычно для текстовых сегментов указываются права на чтение и выполнение. Сегменты данных обычно имеют права доступа на чтение, запись и выполнение.
Сегмент объектного файла может содержать несколько разделов, хотя в заголовке программы это и не отражено. Количество разделов также не имеет значения при загрузке программы. Однако некоторый набор данных должен обязательно присутствовать при выполнении, динамической компоновке и других операциях над программой. На следующих рисунках показано содержимое сегмента. Порядок и наличие разделов сегмента может различаться в разных операционных системах и на разных платформах. Более подробная информация приведена в документации по процессору.
Текстовые сегменты обычно содержат инструкции и данные, предназначенные только для чтения. Обычно они содержат разделы, описанные в Главе 32. В загружаемых сегментах могут присутствовать и другие разделы; приведенные ниже примеры не призваны дать полное и всеобъемлющее описание содержимого сегмента.
Текстовый сегмент
| .text | 
| .rodata | 
| .hash | 
| .dynsym | 
| .dynstr | 
| .plt | 
| .rel.got | 
Сегменты данных обычно содержат данные и инструкции, предназначенные для записи. Обычно они содержат следующие разделы.
Сегмент данных
| .data | 
| .dynamic | 
| .got | 
| .bss | 
Элемент заголовка PT_DYNAMIC указывает на раздел .dynamic, описанный в Динамический раздел. Разделы .got и .plt также содержат информацию о позиционно-независимом коде и динамической компоновке. Хотя раздел .plt показан в нашем примере в текстовом сегменте, в зависимости от процессора он может находиться как в текстовом сегменте, так и в сегменте данных. Более подробные сведения приведены в разделах Глобальная таблица смещения и Таблица компоновки процедур этой главы.
Как указано в разделе Разделы Главы 32, типом раздела .bss является SHT_NOBITS. Хотя он и не занимает места в файле, он влияет на образ сегмента в памяти. Обычно в конце сегмента находятся неинициализированные данные, поэтому значение p_memsz обычно превышает значение p_filesz в связанном элементе заголовка программы.
Иногда компоновщик должен снабдить некоторый объектный файл специальной информацией, применяемой другими программами для проверки соответствия, совместимости и т.п. Для этого можно применять разделы типа SHT_NOTE и элементы заголовка программы типа PT_NOTE. Раздел и элементы примечания могут содержать переменное число записей. В 64-разрядных объектах (файлах, где e_ident[EI_CLASS] равен ELFCLASS64) каждая запись - это массив 8-байтовых слов в формате применяемого процессора. В 32-разрядных объектах (файлах, где e_ident[EI_CLASS] равен ELFCLASS32) каждая запись - это массив 4-байтовых слов в формате применяемого процессора. Ниже приведены метки, помогающие понять структуру примечания, однако эти метки на являются частью спецификации.
Примечания
| namesz | 
| descsz | 
| type | 
| name 
 . . .  | 
| desc 
 . . .  | 
В следующем примере сегмент примечания содержит две записи.
Пример сегмента примечания
Примечание: Сегменты без имени (namesz=0) и с нулевой длиной имени (name[0]='\0') зарезервированы системой, но типы для них не определены. Все остальные имена должны содержать по крайней мере один символ.
Примечание: Информацию примечания указывать не обязательно. Наличие сегмента примечания не влияет на совместимость с ABI и не должно влиять на выполнение программы. В противном случае, программа считается несовместимой с ABI, и ее поведение не определено.