[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]

Программирование: Разработка и отладка программ


Заголовок программы

Таблица заголовков программы исполняемого или общего файла - это массив структур, описывающих сегменты и содержащих прочие сведения, необходимые системе для подготовки к выполнению программы. Сегмент объектного файла содержит один или несколько разделов, как описано в разделе Содержимое сегментов. Заголовки программы имеют смысл только для исполняемых и общих объектных файлов. Размер заголовка программы описан в заголовке 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
В этом элементе указан тип сегмента, описываемого данным элементом массива, или способ интерпретации данных из элемента массива. Значения типов и их назначение описано ниже.

p_offset
В этом элементе указано смещение от начала файла, по которому находится первый байт сегмента.

p_vaddr
В этом элементе указан виртуальный адрес, по которому первый байт сегмента расположен в памяти.

p_paddr
В системах, применяющих физические адреса, этот элемент зарезервирован для физического адреса сегмента. System V не применяет физическую адресацию для прикладных программ, поэтому данный элемент в исполняемых и общих объектных файлах не используется.

p_filesz
В этом элементе указано число байт в образе сегмента в файле; допустимо нулевое значение.

p_memsz
В этом элементе указано число байт в образе сегмента в памяти; допустимо нулевое значение.

p_flags
В этом элементе хранятся флаги, связанные с сегментом. Допустимые значения флагов описаны ниже.

p_align
Как описано в разделе Загрузка процессов, значения p_vaddr и p_offset загружаемых процессов должны быть согласованы и кратны размеру страницы. В этом элементе указано значение, по которому сегменты выравниваются в памяти и файле. Если указано значение 0 или 1, то выравнивание не требуется. В противном случае, p_align должно быть положительным числом, равным некоторой степени 2, а p_vaddr должно равняться p_offset по модулю p_align.

Некоторые записи описывают сегмент процесса; другие содержат дополнительную информацию и не влияют на образ процесса. Записи сегмента могут быть указаны в любом порядке, за исключением специальных случаев, описанных ниже.

Ниже перечислены определенные значения типа; другие значение зарезервированы для использования в будущем.

Типы сегментов, таблица 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

PT_NULL
Элемент массива не используется, значения других элементов не определены. Записи этого типа в таблице заголовков программы игнорируются.

PT_LOAD
Элемента массива указывает на загружаемый сегмент, описанный p_filesz и p_memsz. Массив байтов из файла ставится в соответствие сегменту памяти. Если размер сегмента в памяти (p_memsz) превышает размер файла ( p_filesz), то лишние байты оставляются в конце сегмента и заполняются нулями. Размер файла не может превышать размер сегмента в памяти. Записи загружаемого сегмента в таблице заголовков программы указываются в порядке возрастания значения элемента p_vaddr.

PT_DYNAMIC
Элемент массива указывает на информацию о динамической компоновке. Более подробная информация приведена в разделе Динамический раздел.

PT_INTERP
Элемент массива указывает на размер и расположение оканчивающейся символом NULL строки символов, задающей полное имя файла интерпретатора. Этот тип сегментов применяется только для исполняемых файлов, хотя может быть указан и для общих объектов; он может встречаться в файле только один раз. Если этот сегмент указан, то он должен стоять перед всеми загружаемыми сегментами. Более подробная информация приведена в разделе Интерпретатор программ.

PT_NOTE
Элемент массива задает размер и расположение дополнительной информации.

Более подробная информация приведена в разделе Раздел примечаний.

PT_SHLIB
Этот тип сегмента зарезервирован, но его значение не определено. Программы, содержащие элемент массива этого типа, не совместимы с ABI.

PT_PHDR
Элемент массива, если указан, задает размер и расположение таблицы заголовков программы, как в файле, так и в памяти. Этот сегмент может встречаться в файле только один раз. Более того, он указывается только в том случае, когда образ программы в памяти содержит таблицу заголовков программы. Если этот сегмент указан, то он должен стоять перед всеми загружаемыми сегментами.

Более подробная информация приведена в разделе Интерпретатор программ.

PT_LOOS - PT_HIOS
Значения в этом диапазоне зависят от типа применяемой операционной системы.

PT_LOPROC - PT_HIPROC
Значения в этом диапазоне зависят от типа процессора. Их применение описано в документации по процессору.

Примечание: Все сегменты заголовков программы не обязательны, если специально не оговорено иное. Таблица заголовков конкретного файла может содержать только те элементы, которые относятся к данному файлу.

Базовый адрес

Как описано в разделе Загрузка программы, виртуальные адреса в заголовке программы могут не соответствовать виртуальным адресам в образе этой программы в памяти системы. В исполняемых файлах обычно указывается абсолютный код. Для правильного выполнения процессов сегменты должны находиться по виртуальным адресам, которые использовались при создании исполняемого файла. В то же время, сегменты общих объектов обычно содержат код, не зависящий от положения в памяти. Это позволяет изменять виртуальные адреса сегментов в различных процессах, не затрагивая поведение программы.

На некоторых платформах, если система выбирает виртуальные адреса для конкретных процессов, используются относительные смещения одного сегмента относительно другого в общем объекте. Независимый от положения в памяти код на таких платформах применяет относительную адресацию сегментов, поэтому взаимное расположение сегментов в памяти и в файле должно совпадать. Таким образом, разница между виртуальным адресом сегмента в памяти и соответствующим виртуальным адресом в файле постоянна для всех исполняемых или общих объектов одного процесса. Эта разница называется базовым адресом. Одним из применений базового адреса является перемещение образа в памяти при динамической компоновке.

Базовый адрес в исполняемом или общем объектном файле (на платформах, поддерживающих эту технологию) вычисляется во время выполнения на основе трех значений: адреса загрузки в виртуальную память, максимального размера страницы и минимального виртуального адреса загружаемого сегмента программы. Для вычисления базового адреса система определяет адрес, связанный с минимальным значением 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 и name
Первые namesz байт в name содержат оканчивающуюся символом NULL строку, указывающую имя владельца записи. Формального механизма разрешения возможных конфликтов не существует. По принятому соглашению, сторонние компании применяют в качестве идентификатора свое название, например, XYZ Computer Company. Если имя не указано, namesz содержит 0. Для выравнивания поля по 8- или 4-байтовой границе (для 64- и 32-разрядных объектов) в незанятые байты записываются нули. Эти байты не учитываются в значении namesz.

descsz и desc
Первые descsz байт в desc содержат дескриптор примечания. ABI не накладывает на содержимое дескриптора никаких ограничений. Если дескриптор не указан, descsz содержит 0. Для выравнивания поля по 8- или 4-байтовой границе (для 64- и 32-разрядных объектов) в незанятые байты записываются нули. Эти байты не учитываются в значении descsz.

type
Это слово интерпретирует дескриптор. Каждая компания применяет свой набор типов; один и тот же тип может интерпретироваться по-разному. Таким образом, для правильного распознавания дескриптора программа должна учесть имя и тип примечания. В текущей версии значения типа должны быть неотрицательными. Назначение дескрипторов не определяется в ABI.

В следующем примере сегмент примечания содержит две записи.

Пример сегмента примечания

Примечание: Сегменты без имени (namesz=0) и с нулевой длиной имени (name[0]='\0') зарезервированы системой, но типы для них не определены. Все остальные имена должны содержать по крайней мере один символ.

Примечание: Информацию примечания указывать не обязательно. Наличие сегмента примечания не влияет на совместимость с ABI и не должно влиять на выполнение программы. В противном случае, программа считается несовместимой с ABI, и ее поведение не определено.


[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]