странное поведение стека

Landstreicher

Почему следующая программа падает в сегфолт, однако если раскомментировать вызов touch то все начинает работать нормально:
void touch
{
char A[1024*1024];
A[0] = 0;
}
int main
{
char c;
//touch;
char *test = &c;
test = test - 8 * 1024;
c = test[0];
return 0;
}
Только не говорите мне что стек растет вверх

Nigredo

а при чем здесь стек?
ясен пень, что если touch закомментировать, то память не выделяется, а если раскомментировать, то все пучком.

Landstreicher

ничего не понял. можно поподробнее? значит "выделяется память"? каким именно способом выделяется? и кем, где, как?

abrek

> каким именно способом выделяется?
надо думать, при записи в страницу
вообще, недавно в lkml обсуждение было, что на x86 доступ к стеку ниже %esp в общем случае баг, потому что может прийти сигнал, и обработчик его потрёт данные

Nigredo

Пардон
test = test - 8 * 1024;
не заметил, что здесь - . Тогда мне тоже не ясно.
А вообще память выделяется здесь
char A[1024*1024];

При раскомментированном touch естественно.

Landstreicher

> вообще, недавно в lkml обсуждение было, что на x86 доступ к стеку ниже %esp в общем случае баг, потому что может прийти сигнал, и обработчик его потрёт данные
Да, это именно оно, вся разница в этих 2 случаях в том что доступ ниже %esp.
Давно такое сделали? Я тут попробовал на нескольких машинах - везде одинаковый результат (в том числе и где ядро старое). Буду знать. Спасибо!
Если бы ссылку кинул то было бы вообще супер!

Landstreicher

Собственно в том и вопрос, почему 1 Mb выделить дают, а 8 kb - нет.

Nigredo

Не знал, что так
test = test - 8 * 1024;
можно память выделять

abrek

в первом случае - запись, во втором - чтение
в первом случае - выше %esp, во втором - ниже

Landstreicher

Я уже понял. Просто при беглом чтении сырцов не видел места не видел места где проверяют адрес ошибки с %esp (да и вообще не думал что такая проверка там может быть). Может кто-нибудь тыкнет пальцем?

abrek

arch/i386/mm/fault.c
do_page_fault


/*
* accessing the stack below %esp is always a bug.
* The "+ 32" is there due to some instructions (like
* pusha) doing post-decrement on the stack and that
* doesn't show up until later..
*/
if (address + 32 < regs->esp)
goto bad_area;

Landstreicher

Все, расставил все точки над i

yolki

Может, потому, что функция отметается линкером, как нигде не вызываемая? (Eliminated by the linker)
Соответственно, и все её переменные в том числе...

perevoz81

Попробуй
// c=test[0];

sergey_m

Мне кажется все можно объяснить проще:
память вызываемой функции находится перед вызывающей. Вызовом touch ты обеспечиваешь себе кусок 1024*1024 перед char c; в который можешь спокойно писать. Что и делаешь. Если не вызываешь, то segfault.

Nigredo

почему
перед

мне непонятно.

yolki

Так скомпоновал линкер и так решил загрузчик. если функцию не вызывать, он её не вставит. при большом желании ( и если линкер и загрузчик это позволяют можно сделать так, чтоб блок данных был ПОСЛЕ функции main.
Я правильно понимаю, что нужно производить запись в ЛОКАЛЬНЫЕ переменные ДРУГОЙ функции?
Если да, то в декларации этих переменных не повредит static. и передача их адресочков через глобальные переменные (или ещё как-нибудь) всем, кому надо..

Nigredo

Черт...
Теперь я все понял. Точнее не все, но хотя бы что-то.
Tnx

sergey_m

(gdb) p &A + 1024*1024
$1 = (char (*)[1048576]) 0xbfaff700
(gdb) step
main at qqq.c:14
14 test = test - 8 * 1024;
(gdb) p &c
$2 = 0xbfbff71f "©|В©©!\204\004\b\001"

sergey_m

Под FreeBSD не падает никак.
Оставить комментарий
Имя или ник:
Комментарий: