Как указал Мэдс, чтобы перехватить большинство обращений через нулевые указатели, Unix-подобные системы стремятся сделать страницу с нулевым адресом несопоставленной. Таким образом, доступ немедленно вызывает исключение ЦП, другими словами segfault. Это лучше, чем позволять приложению работать некорректно. Однако таблица векторов исключений может быть по любому адресу, по крайней мере, на процессорах x86 (для этого есть специальный регистр, загруженный с кодом операции lidt
).
Адрес начальной точки является частью набора соглашений, которые описывают, как устроена память. Компоновщик, когда он создает исполняемый двоичный файл, должен знать эти соглашения, поэтому они вряд ли изменятся. По сути, для Linux соглашения о расположении памяти унаследованы от самых первых версий Linux в начале 90-х годов. У процесса должен быть доступ к нескольким областям:
- Код должен находиться в диапазоне, включающем начальную точку.
- Должен быть стек.
- Должна быть куча, предел которой увеличивается с помощью системных вызовов
brk()
и sbrk()
.
- Должно быть место для
mmap()
системных вызовов, включая загрузку разделяемой библиотеки.
В настоящее время куча, в которой находится malloc()
, поддерживается mmap()
вызовами, которые получают фрагменты памяти по любому адресу, который ядро считает подходящим. Но в прежние времена Linux был похож на предыдущие Unix-подобные системы, и его куча требовала большой площади в одном непрерывном фрагменте, который мог увеличиваться в сторону увеличения адресов. Итак, каким бы ни было соглашение, он должен был набивать код и стек в сторону младших адресов и отдавать каждый фрагмент адресного пространства после заданной точки в кучу.
Но есть еще и стек, который обычно невелик, но в некоторых случаях может значительно вырасти. Стек растет вниз, и когда стек заполнен, мы действительно хотим, чтобы процесс прерывался предсказуемо, а не перезаписывал некоторые данные. Таким образом, для стека должна быть широкая область, а в нижнем конце этой области должна быть не отображенная страница. И вот! По нулевому адресу есть несопоставленная страница для перехвата разыменования нулевого указателя. Следовательно, было определено, что стек получит первые 128 МБ адресного пространства, за исключением первой страницы. Это означает, что код должен был идти после этих 128 МБ по адресу, подобному 0x080xxxxx.
Как отмечает Майкл, потеря 128 МБ адресного пространства не была большой проблемой, потому что адресное пространство было очень большим с точки зрения того, что можно было фактически использовать. В то время ядро Linux ограничивало адресное пространство для одного процесса до 1 ГБ, то есть до 4 ГБ, разрешенных оборудованием, и это не считалось большой проблемой.
person
Thomas Pornin
schedule
02.02.2010
0x08048000
когда-либо былSTACK_TOP
, это было очень давным-давно. ПоследнийTASK_SIZE
полностью до 2.0.40. - person ivan_pozdeev   schedule 28.09.2016