Почему виртуальный адрес точки входа выполнения ELF формы 0x80xxxxx и не нуль 0x0?

При выполнении программа начнет работать от виртуального адреса 0x80482c0. Этот адрес не указывает на наш main() процедуру, но к процедуре называют _start который создается компоновщиком.

Мое исследование Google до сих пор просто привело меня к некоторым (неопределенным) историческим предположениям как это:

Существует фольклор, что 0x08048000 однажды был STACK_TOP (то есть, стек вырос вниз от близости 0x08048000 к 0) на порте *, ОТКЛОНЯЮТ к i386, который был провозглашен группой из Санта-Круза, Калифорния. Это было, когда 128 МБ RAM были дорогими, и 4 ГБ RAM было невероятно.

Кто-либо может подтвердить/отклонить это?

19
задан Paolo Forgia 30 August 2017 в 13:34
поделиться

2 ответа

Как отметили MADS, чтобы уловить большинство доступа сквозь нулевые указатели, как Unix-подобные системы, как правило, создают страницу на адресе нулевой «неизменной». Таким образом, доступ немедленно вызывает исключение ЦП, другими словами, SegFault. Это вполне лучше, чем позволить заявлению выйти из мошенника. Исключительный векторный стол, однако, может быть на любом адресе, по крайней мере, на процессорах x86 (для этого есть специальный реестр, загруженный с помощью LIDT OPCode).

адрес отправной точки является частью набора конвенций, которые описывают, как проложено память. Линкер, когда он производит исполняемый двоичный файл, должен знать эти конвенции, поэтому они вряд ли могут измениться. По сути, для Linux соглашения о макете памяти унаследованы от самых первых версий Linux, в начале 90-х годов. Процесс должен иметь доступ к нескольким областям:

  • Код должен находиться в диапазоне, который включает начальную точку.
  • Там должен быть стек.
  • должна быть куча, с пределом, который увеличивается с помощью BRK () и SBBRK () системных вызовов.
  • Существует некоторая комната для системных вызовов SMAP () , включая общую библиотеку загрузку.

На сегодняшний день куча, где MALLOC () выходит, поддерживается MMAP () вызовы, которые получают куски памяти на любой адрес, который видит ядро. Но в более старых временах Linux было похоже на предыдущие системы, подобные Unix, и его куча потребовала большую область в одном бесперебойной кусочке, что может расти к увеличению адресов. Таким образом, что бы ни была Конвенция, она должна была пробовать код и стек к низким адресам и дать каждый кусок адресного пространства после данной точки на кучу.

Но есть также стек, который обычно довольно маленький, но может расти довольно резко в некоторых случаях. Стек растет вниз, и когда стек заполнен, мы действительно хотим, чтобы процесс предсказывал, а не перезаписываю некоторые данные. Таким образом, там должна была быть широкая область для стека, с низким концом этой области, без изменений. И вот! На нуле нулевая страница отсутствует, чтобы ловить NULL POINTER DEARFERCENCES. Следовательно, было определено, что стек получит первый 128 МБ адресного пространства, за исключением первой страницы. Это означает, что код должен был пройти после этих 128 МБ, по адресу, аналогичному 0x080xxxxx.

Как указывает Майкл, «потери» 128 МБ адресного пространства не было большого значения, потому что адресное пространство было очень большим в отношении того, что может быть фактически использовано. В то время ядро ​​Linux ограничило адресное пространство для одного процесса до 1 ГБ в течение максимума 4 ГБ, разрешенным аппаратным обеспечением, и это не считалось большой проблемой.

34
ответ дан 30 November 2019 в 03:20
поделиться

Почему Не начать по адресу 0x0? Есть по крайней мере две причины для этого:

  • , поскольку нулевый адрес отлично известен как нулевой указатель, и используется языками программирования, чтобы промежуточные установки. Вы не можете использовать значение для этого, если Вы собираетесь выполнить код там.
  • Фактическое содержимое по адресу 0 часто (но не всегда) векторная таблица исключения, и, следовательно, не доступен в непривилегированных режимах. Проконсультируйтесь с документацией вашей конкретной архитектуры.

Что касается въезда _start VS Главная : Если вы связываете со временем выполнения C (стандартные библиотеки C), библиотека управляет функцией с именем MAIN , поэтому она может инициализировать среду до Main . В Linux это ARGC и ARGV параметры для приложения, переменных ENV и, вероятно, некоторые примитивы синхронизации и замки. Он также гарантирует, что возвращение из основных проходов в коде состояния и вызывает функцию _Exit , которая прекращает процесс.

6
ответ дан 30 November 2019 в 03:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: