О расположении памяти программ в Linux
======================================

У меня есть несколько вопросов о структуре памяти программы в Linux. Я знаю из различных источников (я читаю «Программирование с нуля»), что каждый раздел загружается в свою область памяти. Сначала текстовый раздел загружается по виртуальному адресу 0x8048000, сразу после этого загружается раздел данных, затем - раздел bss, затем куча и стек.

Чтобы поэкспериментировать с макетом я сделал эту программу в сборке. Сначала он печатает адреса некоторых меток и вычисляет точку останова системы. Затем он входит в бесконечный цикл. Цикл увеличивает указатель и затем пытается получить доступ к памяти по этому адресу, в какой-то момент ошибка сегментации завершит работу программы (я сделал это намеренно).

Это программа:

И это соответствующие части вывода (это Debian 32bit):

Мои вопросы:

1) Почему моя программа начинается с адреса 0x8048190 вместо 0x8048000? При этом я предполагаю, что инструкция с меткой "\_start" загружается не первой, так что находится между адресами 0x8048000 и 0x8048190?

2) Почему существует разрыв между концом текстового раздела и началом раздела данных?

3) Начальный и конечный адреса bss совпадают. Я предполагаю, что два буфера хранятся где-то еще, это правильно?

4) Если точка останова системы находится в 0x83b4001, почему я получаю ошибку сегментации ранее в 0x804a000?

Я предполагаю, что вы строите это с`gcc -m32 -nostartfiles segment-bounds.S` или аналогичный, так что у вас есть 32-битный динамический двоичный файл. (Вам не нужно`-m32` если вы на самом деле используете 32-битную систему, но большинство людей, которые хотят проверить это, будут иметь 64-битные системы.)

Моя 64-битная система Ubuntu 15.10 выдает несколько отличные от вашей программы числа по нескольким причинам, но общая модель поведения такая же. (Другое ядро, или просто[ASLR](https://en.wikipedia.org/wiki/Address_space_layout_randomization)объясняет это. Адрес brk сильно отличается, например, от таких значений, как`0x9354001` или же`0x82a8001`)

1) Почему моя программа начинается с адреса 0x8048190 вместо 0x8048000?

Если вы строите статический двоичный файл, ваш`_start` будет в 0x8048000.

Мы можем видеть из`readelf -a a.out` тот`0x8048190` это начало раздела .text. Но это не в начале текстового сегмента, который отображается на странице. (страницы 4096B, и Linux требует, чтобы отображения были выровнены по границам 4096B позиции файла, поэтому с файлом, расположенным таким образом, это было бы невозможно`execve` на карту`_start` к началу страницы. Я думаю, что столбец Off - это позиция в файле.)

Предположительно другие разделы в текстовом сегменте перед`.text` раздел - это данные только для чтения, которые необходимы динамическому компоновщику, поэтому имеет смысл поместить их в память на той же странице.

2) Почему существует разрыв между концом текстового раздела и началом раздела данных?

Почему бы и нет? Они должны находиться в разных сегментах исполняемого файла, поэтому отображаются на разных страницах. (Текст доступен только для чтения и является исполняемым, и может быть MAP\_SHARED. Данные доступны для чтения и записи и должны быть MAP\_PRIVATE. Кстати, в Linux по умолчанию данные также являются исполняемыми.)

Оставляя пробел, у динамического компоновщика остается место для сопоставления текстового сегмента общих библиотек рядом с текстом исполняемого файла. Это также означает, что индекс массива вне пределов в разделе данных с большей вероятностью может стать причиной ошибки. (Более ранний и шумный сбой всегда легче отлаживать).

3) Начальный и конечный адреса bss совпадают. Я предполагаю, что два буфера хранятся где-то еще, это правильно?

Это интересно. Они в BSS, но IDK, почему текущая позиция не зависит`.lcomm` этикетки. Вероятно, они идут в другом подразделе до ссылки, так как вы использовали`.lcomm` вместо`.comm`, Если я использую использовать`.skip` или же[`.zero`](https://sourceware.org/binutils/docs/as/Zero.html#Zero) чтобы зарезервировать место, я получаю ожидаемые вами результаты:

[`.lcomm`](https://sourceware.org/binutils/docs/as/Lcomm.html) помещает вещи в BSS, даже если вы не переключаетесь на этот раздел. то есть, ему все равно, какой будет текущий раздел, и, возможно, его не волнует или не влияет на текущую позицию в`.bss` раздел есть. TL: DR: когда вы переключаетесь на`.bss` вручную, используйте`.zero` или же`.skip`не`.comm` или же`.lcomm`.

4) Если точка останова системы находится в 0x83b4001, почему я получаю ошибку сегментации ранее в 0x804a000?

Это говорит нам о том, что между сегментом текста и brk есть несопоставленные страницы. (Ваш цикл начинается с`ebx = $start_text`, так что это происходит с ошибкой на первой неотображенной странице после текстового сегмента). Помимо дыры в виртуальном адресном пространстве между текстом и данными, возможно, есть и другие дыры за сегментом данных.

Защита памяти имеет степень детализации страницы (4096B), поэтому первым ошибочным адресом всегда будет первый байт страницы.