LINUX.ORG.RU

Максимально допустимый размер массива на стеке?

 


3

7

Есть некая функция которая использует временный массив. Если массив маленький, хочется создавать его на стеке (что бы не дергать менеджер памяти), если большой приходится делать его в куче (иначе сегфолт). Я че то не пойму, как построить правильный критерий выбора того или иного варианта?

Какой то вот такой кривой пример кода:

int Nmax = 1024; // максимально возможный размер буфера на стеке
void func(){
  int N = ...;
  T p_buf[(N<=Nmax)*N];
  T* p = p_buf; if(N>Nmax) p = new T[N];
  ...
  if(N>Nmax) delete [] p;
}
★★★★

Ответ на: комментарий от anonymous

сослался на man 3 alloca

Мразь не ссылалась на man

Чё, сурьёзно? И даже не намекает ни на что /usr/include/alloca.h, в котором содержится вот такой код

Ну, пыль запартная, где же у нас реализована __builtin_alloca (size), как не в glibc в данном случае и как она реализована кроме как внутренняя ф-я библиотеки? Мне показать исходник в glibc или сами осилите найти? =)))

Вот блеяние отребья. Он ссылался на файл, который обезьяна увидела в каком-нибудь говняторе. И далее начала блеять именно про __builtin_alloca. Вернее про alloca она вообще не блеяла.

albert_kishawn
()
Ответ на: комментарий от anonymous

существуют тысячи всяких разных callabi и не везде в принципе можно реализовать аллоку функцией.

Поэтому (я думаю) этой функции нет posix. Причуда гнутых

vla

Об которую сломано много копий. Даже есть спец ключ для предупржденией -Wvla.

anonymous
()
Ответ на: комментарий от albert_kishawn

Хе-хе... А вот этого я и ждал... =)))

сослался на man 3 alloca

Мразь не ссылалась на man

Я просто оставлю это здесь – Максимально допустимый размер массива на стеке? (комментарий)

Цитата оттуда:

alloca() это ф-я, не входящая в стандарт С, но реализованная во многих системах.

Это не функция, очевидно.

Да, всё как по нотам. Даже неловко от такой предсказуемости… =)))

P.S. Пока писал коммент, пропустил довольно важный звонок. Из попыток навести порядок в Вашем межушном ганглии вышел ввиду бесполезности этого действа. Остальная херня, неотносящаяся к делу, не интересна. Семантика-фигантика, начали придумывать говно там, где надо десяток ассемблерных инструкций… Дохрена больно пытаетесь думать там, где этого делать Вам абсолютно точно ненужно.

С Новым Годом. Секс любите? Деорогу знаете? Ну так пути счастливого!

Точка, пожалуй.

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 1)
Ответ на: Я неправильно привёл заголовочный файл? от Moisha_Liberman

И как всё это отменяет того, что alloca() это функция?

Твой заголовочный файл показывает, что не всегда alloca - функция. То что у «гнутых» - это какой-то макрос с двумя подчеркиваниями спереди, что есть магия.

anonymous
()
Ответ на: комментарий от anonymous

Не понял?

Вас что, этот клован покусал и Вы как от вампира заразились что ли?

Твой заголовочный файл показывает, что не всегда alloca - функция.

Батенька, Вы не знаете С. В данном случае этот общий заголовочный фал показывает что как раз alloca() это функция, как и сказано в man 3 alloca. Смиритесь, С это явно не ваше.

Впрочем… Что Вам не ясно в данном коде? Какая его часть заставляет ещё и Вас столь изысканно бредить?

/* Allocate a block that will be freed when the calling function exits.  */
extern void *alloca (size_t __size) __THROW;

#ifdef	__GNUC__
# define alloca(size)	__builtin_alloca (size)
#endif /* GCC.  */

То что у «гнутых» - это какой-то макрос с двумя подчеркиваниями спереди, что есть магия.

У Вас нет никакого макроса ни в случае для GNUC, ни в более общем. Садитесь, «два», Вы такой же балбес как этот местный дурачёк. Макрос там один, но боюсь Вам этого не понять.

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 2)
Ответ на: Не понял? от Moisha_Liberman
$ cat | gcc -E -P -
#include <alloca.h>
int main(){
  alloca(10);
}
^d
typedef long unsigned int size_t;

extern void *alloca (size_t __size) __attribute__ ((__nothrow__ , __leaf__));

int main(){
  __builtin_alloca (10);
}

Покажи где вызов alloca?

anonymous
()
Ответ на: Не понял? от Moisha_Liberman

Садитесь, «два», Вы такой же балбес как этот местный дурачёк.

Типичное поведение царя при сливе.

anonymous
()
Ответ на: комментарий от anonymous

Нет.

Типичное поведение царя при сливе.

Это реакция на то, что по два раза (точнее, три раза) тыкаешь носом в исходники, а в ответ включается унылый тупняк.

Не в состоянии прочесть приведённый кусок файла? Не в состоянии найти его в системе, хотя я даже путь полный привёл? Нет этого файла, т.к. система отлична от Linux? Тогда какой смысл тратить на себя моё время? В чём профит обсуждения? Отмазаться и сделать умный вид?

Мне насрать на чей-то вид. Цитаты выше.

Точка, пожалуй.

Moisha_Liberman ★★
()
Ответ на: Нет. от Moisha_Liberman

Ясно.

По сравнению с тобой, царь - эталон интеллигентного человека.

Царь! Ты где? Сейчас праздники, в школу не надо.

anonymous
()
Ответ на: комментарий от anonymous

Тебе, посмешище, нужно бежать за рецептом к психиатору.

rumgot ★★★★★
()
Ответ на: Всё ограничено только потребностями софта. от Moisha_Liberman

alloca() это ф-я, не входящая в стандарт С, но реализованная во многих системах.

alloca() нельзя реализовать функцией на x86-64 под GCC, потому что нужно как-то стек при выходе возвращать в предыдущее состояние. Это делается через запоминание базы фрейма в ebp и последующее восстановление rsp из него. Обрати внимание, что ebp будет использоваться даже если ты просишь -fomit-frame-pointer. GCC не сможет не использовать ebp, если в функции использована alloca(), поэтому он его использует.

Допустим, ты реализовал alloca() библиотечной функцией. Твоя реализация должна (1) менять rsp для функции, вызвавшей это чудо-реализацию alloca() и (2) поддерживать и -fomit-frame-pointer и -fno-omit-frame-pointer. Это какая-то магия. Наверное, можно как-то прыгать не с помощью ret, а через jmp, чтобы rsp не трогать. Костылями с проверками во время исполнения может получиться сделать поддержку одного вызова alloca(). Но что ты будешь делать, если их будет десяток? Кстати, jmp в обход ret даст заметный удар по скорости на современных процах.

Поэтому в GCC (и glibc) используется механизм builtin-функций, благодаря которому компилятор знает, что это за функция, и обрабатывает её по-особенному.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

В том-то и дело...

функцией на x86-64 под GCC

Что мы сейчас рассматриваем только x86-64. На деле, нет. Не важна архитектура, т.к. С поддерживает использование ф-ии alloca() для абсолютно различных архитектур. И для разных архитектур это абсолютно разные решения, т.к. стек сам по себе ни кто и ни где не отменял, а вот работа с ним построена различными способами.

alloca() нельзя реализовать функцией

В смысле? И Вы хотите сказать что это макрос? И Вы туда же? =)))

P.S. Ну вот например.

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 1)
Ответ на: В том-то и дело... от Moisha_Liberman

И Вы хотите сказать что это макрос?

У меня на системе это макрос, который заменяет вызов alloca() на __builtin_alloca(). Последнее — встроенная функция GCC.

И Вы туда же?

Что напечатает вот эта программа?

#include <alloca.h>
#include <stdio.h>

void func4(void *p) {
  void *(*qwerty)(size_t) = (void *)((char *)p - 4);

  printf("%p\n%p\n", &qwerty, qwerty(10));
}

void func3(void *p) {
  func4((char *)p - 30);
}

void func2(void *p) {
  func3((char *)p - 200);
}

void func1(void *p) {
  func2((char *)p - 1000);
}

int main(void) {
  func1((void *)alloca + 1234);
  return 0;
}

Сначала ответь, не запуская. Потом запусти. Сравни предсказания с реальностью.

i-rinat ★★★★★
()
Ответ на: В том-то и дело... от Moisha_Liberman

Ща тут царевна сил поднаберётся, говна подкопит и докажет тебе что ты не прав. Не прав во всём 😂 ты только жди

Тут местный школоконтингент любит сраться до ужаса, до красный щёк и соплей из носа, особенно, если тема не о скриптоте, а об их «илитке» - си плюс плюшечке 😂

menangen ★★★★★
()
Последнее исправление: menangen (всего исправлений: 1)
Ответ на: В том-то и дело... от Moisha_Liberman

P.S. Ну вот например.

Эм…

This implementation of the PWB library alloca function, which is used to allocate space off the run-time stack so that it is automatically reclaimed upon procedure exit, was inspired by discussions with J. Q. Johnson of Cornell. J.Otto Tennant jot@cray.com contributed the Cray support.

ооокей…

The general concept of this implementation is to keep track of all alloca-allocated blocks, and reclaim any that are found to be deeper in the stack than the current invocation. This heuristic does not reclaim storage as soon as it becomes invalid, but it will do so eventually.

ооокей…

As a special case, alloca(0) reclaims storage without allocating any. It is a good idea to use alloca(0) in your main control loop, etc. to force garbage collection.

Океееей.

Ты правда считаешь, что функция из библиотеки с костылями, которая позволяет собрать программы, использующие alloca() с помощью компиляторов, не знающих о существовании alloca(), это вот прям супер аргумент?

i-rinat ★★★★★
()
Ответ на: Не. от Moisha_Liberman

Я забыл про арифметику указателей void *. Это же особенность GCC. В общем, замени там (void *) на (char *). Тогда всё норм будет.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Не успел ответить на коммент, дополню здесь.

У меня на системе это макрос, который заменяет вызов alloca() на __builtin_alloca(). Последнее — встроенная функция GCC.

Ну? И что первично, что вторично? То что это макрос, заменяющий вызов одной ф-ии на другую, не делает реализацию этой ф-ии макросом. Мне тут уже даже за семантику жевать прилаживались…

В ман даже уже носом тыкаю народ, говорю man 3 alloca, там сказано чётко что это по мнению разрабов (функция, можете проверить). И то орут что «этамакрас»!!!11адын-адын.

И, кстати, я думаю причина того, что alloca() не включили в стандарт, это небезопасность этой функции, если по большому счёту. Не зря в с99 появились VLA (variable-length array). Они, если опять таки по большому счёту, делают почти то же, но более безопасным образом.

Moisha_Liberman ★★
()
Ответ на: комментарий от i-rinat

Тащщемта...

Ты правда считаешь, что функция из библиотеки с костылями, которая позволяет собрать программы, использующие alloca() с помощью компиляторов, не знающих о существовании alloca(), это вот прям супер аргумент?

Это был просто пример. Равно как и патч для спарк32 выше. Про то и речь что впираться с alloca() в архитектуру нельзя. Ведь по-русски (ну, кхмм… Вы поняли) же сказано что реализации alloca() machine and compiler dependent. =)))

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 1)
Ответ на: Не успел ответить на коммент, дополню здесь. от Moisha_Liberman

не делает реализацию этой ф-ии макросом

Я не говорю, что alloca() реализуется макросом. Она реализуется «встроенной» функцией компилятора. Макрос там нужен, чтобы направить пользовательскую программу на builtin.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ну вот и я не говорю такой чуши.

Я не говорю, что alloca() реализуется макросом.

Всё верно. Про то и речь. С чего всё и началось. Тут, знаете ли, отдельно взятые товарищи, которые нам на самом деле не товарищи, малость против… =)))

Moisha_Liberman ★★
()
Ответ на: Ну вот и я не говорю такой чуши. от Moisha_Liberman

Про то и речь.

Нет. Речь про то, что ты писал:

где же у нас реализована __builtin_alloca (size), как не в glibc в данном случае и как она реализована кроме как внутренняя ф-я библиотеки?

Ну так вот. Нет, она не реализована как функция в glibc.

Кстати, ты так и не ответил на вопрос о том, что программа напечатает и почему. void* vs. char* это несущественная мелочь, не надо к этому цепляться.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Изначально речь была вот о чём.

Вот краткое повторений реплик, с чего всё началось. Максимально допустимый размер массива на стеке? (комментарий)

Вот в этой строке для случая gcc/glibc/x86_64 я не прав.

где же у нас реализована __builtin_alloca (size), как не в glibc в данном случае и как она реализована кроме как внутренняя ф-я библиотеки?

Да, я не корректно написал, не в glibc, а в gcc. Хотя, вот в этом комменте Максимально допустимый размер массива на стеке? (комментарий) я написал всё верно:

Пусть лучше этим занимается внутренняя реализация gcc.

Проблема в том, что мне знакомы архитектуры, где как и сказано в /usr/include/alloca.h:

/* Allocate a block that will be freed when the calling function exits.  */
extern void *alloca (size_t __size) __THROW;

#ifdef	__GNUC__
# define alloca(size)	__builtin_alloca (size)
#endif /* GCC.  */

Показываю этот же хидер уже в четвёртый раз. Но мне знакомы архитекуры, где исполняется условие не для GNUC, т.е., именно extern void alloca (size_t __size) __THROW; что в данном случае означает что функцию alloca() нужно взять где-то, если это не GNUC, иначе gcc сам её предоставит. Где её взять, эту функцию? Из тех самых «кривых патчей» для того же Sparc32 или CRAY, как выше, да? И, как мы с Вами оба понимаем, детали реализации этой самой extern void *alloca(size_t __size) не сделают её макросом.

void* vs. char* это несущественная мелочь, не надо к этому цепляться.

Нет. В строке *func1((void )alloca + 1234); дело не в void * vs. char *. Ещё раз посмотрите на сигнатуру alloca()? Только внимательно. ;)

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 2)
Ответ на: комментарий от menangen

Угу... =)))

Ща тут царевна сил поднаберётся, говна подкопит и докажет тебе что ты не прав. Не прав во всём 😂 ты только жди

Я прямо-таки дрожу. Айбаюсбаюс…

Хотя… Хотя у нас же свободная страна, свобода слова и вот это вот всё. По крайней мере до прибытия местных дежурных санитаров… =)))

Moisha_Liberman ★★
()
Ответ на: Изначально речь была вот о чём. от Moisha_Liberman

на сигнатуру alloca()

А чего на неё смотреть? Это символ, который я могу привести к (void *) указателю. А потом к (char *), а потом к указателю на функцию. И потом эту функцию вызвать.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Да. Именно!

И потом эту функцию вызвать.

Ну так вызовите! Где здесь вызов функции alloca()? gcc не найдёт alloca в этом случае:

func1((void *)alloca + 1234);

Поэтому, мой ответ что даже не скомпилится. Хотя, надо попробовать. Хотя, чё тут пробовать? Будет неразрешённая ссылка. Тут даже думать нечего.

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 1)
Ответ на: Да. Именно! от Moisha_Liberman

Где здесь вызов функции alloca()?

Он внутри func4(). :-)
В этом вся суть — запутать компилятор вложенными вызовами, чтобы он не догадался, что на месте вызова будет именно alloca() вызываться. GCC умный, его оптимизатор может сообразить, что всё сведётся к alloca(), и подставить туда __builtin_alloca().

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Не.

Не пробовал скомпилить, но не сработает, IMHO.

Попробовал.

gcc test1.c -o test1
/tmp/ccMVelAh.o: In function `main':
test1.c:(.text+0xcc): undefined reference to `alloca'
collect2: error: ld returned 1 exit status

Как в воду глядел.

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 1)
Ответ на: Да. Именно! от Moisha_Liberman

мой ответ что даже не скомпилится.

Это правильный ответ.

Будет неразрешённая ссылка.

И это правильный ответ. Потому что в libc.so.6 такой функции нет.

А теперь почитай, что ты писал в Максимально допустимый размер массива на стеке? (комментарий) про ldd, libc.so.6 и __builtin_alloca().

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ну если читать, то тогда полностью.

А теперь почитай, что ты писал в

Тут претензий нет, как я понимаю? Вот тут:

Вообще-то, реализация данной функции зависит от платформы. Вот, например, для Sparc32

И, если читать, то тогда полностью, да. Максимально допустимый размер массива на стеке? (комментарий) Тут было объяснение что и к чему.

Продолжать как, будем после прочтения или читать не будем? =)))

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

Аааааблллл……… =))) Давайте лучше не продолжать? =)))

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 1)
Ответ на: Ну если читать, то тогда полностью. от Moisha_Liberman

Продолжать как, будем после прочтения или читать не будем?

Я читал это. И вижу там кучу сомнений и попытки сохранить лицо. Причём таких отчаянных попыток, что это лицо теряется ещё быстрее.

Знаешь, я даже посмотрел, какой код GCC под sparc32 генерирует для функций, использующих alloca(). И знаешь что? Не вызывает он там внешних функций. А ты ведь sparc32 приводил как пример платформы, на которой alloca() — функция библиотеки.

Пока что я вижу какие-то несвязные попытки показать себя специалистом. Возможно, это работает с теми, кто никогда не интересовался, что именно компилятор генерирует. Я далеко не «гуру», но знаний достаточно, чтобы увидеть, когда мне пытаются впихнуть какой-то бред.

Кстати, мне интересно, какой твой ответ на твою же загадку про сигнатуру alloca(). Мне кажется, что я знаю, что ты имел в виду, но всё-таки интересно услышать оригинальный ответ.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Понимаю. =)))

Вам захотелось пофлеймить. =))) Но я вынужден предупредить что в эту игру можно даже играть вдвоём. И сейчас Вас ждёт небольшой сеанс попаболила.

Следите за руками. =)))

Знаешь, я даже посмотрел, какой код GCC под sparc32 генерирует для функций, использующих alloca(). И знаешь что? Не вызывает он там внешних функций. А ты ведь sparc32 приводил как пример платформы, на которой alloca() — функция библиотеки.

Вообще-то, у Вас классика случая «гляжу в книгу, вижу фигу». Если Вы так же с кодом на С разбираетесь, то я очень хочу знать (чтоб не влипать в это) какой же дурак держит Вас на работе и, наверное, даже тратит деньги на Вашу з/п.

Всё просто. Не, батенька, в коде под Sparc32 не должно быть вызовов внешних функций. Просто потому что этот патч и есть реализация такого рода функции. Именно это реализация __builtin_alloca() для gcc на Sparc32. Вы этого не поняли. Соболезную.

Почему Вы этого не поняли? Да потому что Вы написали ярую антинаучную фигню вот в этом Максимально допустимый размер массива на стеке? (комментарий) своём комменте.

Вообще-то, для работы со стеком в x86_64 (как я и довольно дерзко предположил выше) нужен регистр esp, содержимое которого мы либо увеличиваем, либо уменьшаем в зависимости от того, что мы делаем (резервируем пространство в стеке, либо освобождаем его). И нам на фиг тут не сдались никакие фантазии по поводу и без, кроме того, что мы смотрим за флагами процессора, чтобы не прохлопать изменение знака. За собой чистить даже ничего не нужно, т.к. стек нулить ни к чему. Вернулись из функции, обновили esp и продолжаем дальше.

Как там реализовать пролог и эпилог функции в непосредственной работе со стеком не относится (вся возня с frame pointer тут так же лесом).

Так что, если я захочу в стеке просто получить #200 (да, 200 байт), то я просто сделаю sub esp, #200, получу к ним доступ через move [esp + x], где «х» это число от 0 до 199 и потом очищу за собой пространство add esp, #200. Всё. Больше ничего. Frame pointer и связанные с ним проблемы тут лесом. Вы, не понимая этого спорного, не скрою, момента, начали чего-то там гундеть про флаги gcc. Да хоть угундитесь, как организуется работа со стеком действительно экономичным образом, Вы не в курсе. Понимаю, не ваше. =)))

Вот почему Вы начали искать в приведённом коде для Sparc32 вызовы внешних функций. Вы просто и тупо не понимаете что вот эта реализация:

ENTRY (__builtin_alloca)
	sub %sp, %o0, %sp	/* Push some stack space.  */
	retl			/* Return; the returned buffer leaves 96 */
	add %sp, 96, %o0	/* bytes of register save area at the top. */
END (__builtin_alloca)

Да, Вы не понимаете что именно эта реализация и есть вариант того, что я объяснил выше для x86_64 с регистром esp. Тут только малость другой синаксис (и не только), мнемоники-то да, другие, но подход тот же – никакого онанирования над frame pointer’ом нет.

Если Вы не поняли этого кода, то я очень хочу знать где же таким болванам з/п платят? =)))

Думаете всё? Куууда собрались?

Теперь потрудитесь объяснить следующий момент. Вот Вы пишете Максимально допустимый размер массива на стеке? (комментарий) :

alloca() нельзя реализовать функцией на x86-64 под GCC, потому что нужно как-то стек при выходе возвращать в предыдущее состояние.

С возвратом стека в предыдущее состояние мы разобрались – см. выше. Как понять Вашу фразу alloca() нельзя реализовать функцией, если чуть ниже Вы пишете:

Я не говорю, что alloca() реализуется макросом.

В особенности, если учесть что man 3 alloca говорит явно об alloca() как о функции? Скажите пожалуйста, Вы в какой из двух своих цитат «свистите»? =)))

Кстати, мне интересно, какой твой ответ на твою же загадку про сигнатуру alloca(). Мне кажется, что я знаю, что ты имел в виду, но всё-таки интересно услышать оригинальный ответ.

Не, Вы мне тут загадок уже поназадавали (я даже показал уже что код не соберётся без сборки кода), давайте теперь Вы ответите на мои вопросы выше? =)))

Я же Вас предупреждал что во флейм можно вдвоём поиграть? Вы мне не поверили? А зря… =)))

P.S. И да. Если соберётесь отвечать, то делайте это внятно. Так, чтобы Вас мне не захотелось послать по всем известному адресу. А то я начинаю задумываться о том, что пора бы… Не провоцируйте. Пожалуйста. =)

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 2)
Ответ на: Понимаю. =))) от Moisha_Liberman

И сейчас Вас ждёт небольшой сеанс попаболила.

Скорее, сеанс испанского стыда.

Не, батенька, в коде под Sparc32 не должно быть вызовов внешних функций. Просто потому что этот патч и есть реализация такого рода функции. Именно это реализация __builtin_alloca() для gcc на Sparc32. Вы этого не поняли. Соболезную.

Ты дал ссылку на sysdeps/sparc/sparc32/alloca.S из glibc. Каким местом это патч? Ты бредишь. GCC живёт в другом репозитории, и ну уж конечно не ходит во время работы в репозиторий glibc за тремя инструкциями. Там в исходнике написано, для чего эти три инструкции нужны: для Си компилятора Sun. GCC там вообще ни при чём.

За собой чистить даже ничего не нужно, т.к. стек нулить ни к чему. Вернулись из функции, обновили esp и продолжаем дальше.

Ты забыл, что для «вернулись из функции» нужно выполнить ret, который, сюрприз-сюрприз!, читает адрес возврата из стека. Поэтому перед исполнением ret нужно восстановить старое значение rsp. Оно и хранится в rbp.

Frame pointer и связанные с ним проблемы тут лесом.

Сохранять базовый адрес фрейма не нужно, если на этапе компиляции точно знаешь, сколько стека нужно в функции. Но если в функции возможны вызовы alloca(), ты уже не сможешь на этапе компиляции всё рассчитать. Нужно сохранять во время исполнения. Поэтому даже когда используешь omit-frame-pointer, GCC всё равно генерирует код с сохранением в rbp, потому что иначе никак.

Вот почему Вы начали искать в приведённом коде для Sparc32 вызовы внешних функций.

Не выдумывай. Я смотрел в результат работы компилятора, а не в приведённый тобой sysdeps/sparc/sparc32/alloca.S.

Как понять Вашу фразу alloca() нельзя реализовать функцией, если чуть ниже Вы пишете:

Я не говорю, что alloca() реализуется макросом.

В особенности, если учесть что man 3 alloca говорит явно об alloca() как о функции? Скажите пожалуйста, Вы в какой из двух своих цитат «свистите»? =)))

alloca() реализуется builtin’ом GCC. Ты не понимаешь, что такое builtin-функция? Это не функция в обычном смысле, потому что у неё нет тела. Нельзя взять адрес builtin-функции. Поэтому тут нет «свиста», я сразу говорил так. Если ты не понимаешь, чем builtin отличаются от функций, реализованных в библиотеках, мне тебя жаль. Я старался объяснить. Видимо, бесполезно.

Тут только малость другой синаксис (и не только), мнемоники-то да, другие, но подход тот же – никакого онанирования над frame pointer’ом нет.

Сюрприз! В SPARC адресация локальных переменных производится от %fp, а не от %sp! Читай Appendix D и больше такого бреда не пиши.

давайте теперь Вы ответите на мои вопросы выше? =)))

Ответил. А от тебя ответа всё ещё не получил.

я даже показал уже что код не соберётся без сборки кода

Но не угадал с причиной. Судя по твоему сообщению, для тебя было реально сюрпризом, что от функции адрес можно взять. Я уже почти уверен, что если бы там то же самое с printf было, а не с alloca, ты бы так же сказал, что не скомпилируется.

Почему я так считаю? Потому что если бы ты знал, что символ alloca не определён, то не стал бы писать, что alloca реализуется функцией в glibc.

А то я начинаю задумываться

Пора бы уже. А то после такого количества бреда мне за тебя реально стыдно.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Нутес, давайте посмотрим что Вы тут понаваяли... =)))

На сон грядущий, так сказать…

Ты дал ссылку на sysdeps/sparc/sparc32/alloca.S из glibc.

Т.е., этого куска кода я не приводил? А кто же это сделал? Вот ссылка, которую я привёл. https://github.com/lattera/glibc/blob/master/sysdeps/sparc/sparc32/alloca.S Открываем её. Смотрим, видим и удивляемся…

ENTRY (__builtin_alloca)
	sub %sp, %o0, %sp	/* Push some stack space.  */
	retl			/* Return; the returned buffer leaves 96 */
	add %sp, 96, %o0	/* bytes of register save area at the top. */
END (__builtin_alloca)

Т.е., как выясняется, именно этот кусок кода я и привёл. Уже хороршо. Там да, есть ещё до кучи забавный коментарий, да.

Там в исходнике написано, для чего эти три инструкции нужны: для Си компилятора Sun. GCC там вообще ни при чём.

А! Так Вы видели о чём речь, но типа не поняли? Нет, там не просто про три инструкции написано. Во-первых, эти, с Ваших слов «три инструкции» полностью удовлетворяют соглашениям о вызовах С, который Вы странным образом «не узнали», что выдаёт Ваш просто бешеный «опыт» в С (да и в ассемблере). Ну уж со стеком точно офигеть какой «опыт».

Следите за руками – первыми идут аругменты ф-ии, далее идёт адрес возврата, потом идёт frame pointer. По ряду причин он 96 байт на спарке32, поэтому он просто там захардкожен, о чём и написано в комментарии (but seem always to be the constant 96; I have no idea what they are for. */).

Странно, но такой матёрый сишник как Вы должен был бы с полпинка понять что именно открылось его взору? Ан нет, не сканало.

Дальше по этому сырцу. Вас в каком ГПТУ английскому учили, а? Это не для сановского компиля. Это инструкции, которые созданы сановским компилём. Это прямо противоположно Вашему утверждению и это следует из строки комментария /* Code produced by Sun’s C compiler calls.

Дальше. Поднимите свои глазёнки выше, в самое начало. Что Вы там видите? Верно. /* Copyright (C) 1994-2018 Free Software Foundation, Inc. This file is part of the GNU C Library. И при чём здесь сановскимй компиль, не подскажете? И почему же это не часть glibc в таком случае и при таких раскладах? И при чём же тогда там GCC, если в данном случае это часть glibc?

Какой на фиг Вам С и ассемблер? Тут с банальным английским напряг несусветный.

Тут даже можно не продолжать. Но я, скрепя сердце, продолжу. Посмотрим сколько я этих «откровений» выдержу.

Ты забыл, что для «вернулись из функции» нужно выполнить ret, который, сюрприз-сюрприз!, читает адрес возврата из стека.

И чё дальше? Он там и записан. Почему его не прочесть? Что помешает сделать ret, увеличить esp и продолжить как ни в чём ни бывало дальше? Конкретно что? Где в С согласно соглашений о вызовах лежит адрес возврата показать или сами осилите сообразить (Вас, уже просто носом в код ткнули, Вам уже объяснили куда именно смотреть и в коде там коммент написан, Вы что тупите-то)? А ещё вопрос – какая функция чистит стек и почему? Вызывающая или вызываемая? Ну-ка, ну-ка, порадуйте новой порцией откровений? =)))

Так. Кусок очередной несусветной фигни пропускаем. О! Отличный абзац!

Я смотрел в результат работы компилятора, а не в приведённый тобой sysdeps/sparc/sparc32/alloca.S.

У Вас там далеко завалялся компилятор gcc или sun c для Sparc32? Вы в его выхлоп смотрели? Нет? А в какой, позвольте уточнить? В какой на фиг выхлоп компиля смотрели если по тупости своей Вы даже не понимаете что перед Вами за код и как он работает? А всё рвётесь где-то сохранять адрес возврата.

alloca() реализуется builtin’ом GCC. Ты не понимаешь, что такое builtin-функция? Это не функция в обычном смысле, потому что у неё нет тела. Нельзя взять адрес builtin-функции. Поэтому тут нет «свиста», я сразу говорил так. Если ты не понимаешь, чем builtin отличаются от функций, реализованных в библиотеках, мне тебя жаль. Я старался объяснить. Видимо, бесполезно.

Ещё раз. Медленно и печально. Для особо одарённых. Я задал конкретный вопрос – alloca() это функция или макрос? А то у Вас два противоречивых утверждения. И я стараюсь понять где именно Вы врёте. Больше я никаких вопросов в данном случае я не задавал. И прочий словесный онанизм про встроенные ф-ии компиля это мимо, как не относящееся к ответу на вопрос. Ещё раз – alloca() это функция или макрос?

Сюрприз! В SPARC адресация локальных переменных производится от %fp, а не от %sp! Читай Appendix D и больше такого бреда не пиши.

Дааа… Ну Вы, батенька, и балбес! Просто какой-то рафинированный. Впрочем, да. Вы же так и не поняли что перед Вами за код… Ясно. Намекну. Здесь %fp не при делах. Здесь надо ориентироваться именно на указатель стека (%sp, он же %o6).

Ответил.

Нет. Вы ещё и не пытались. Что Вы! Мы только-только начали разбираться с Вашим восприятием реальности. Пока я вижу только то, что с английским напряг, соглашения о вызовах в С – это мимо, путаете регистры спарка32 (%fp vs. %sp||%o6), как работает распределение в стеке тоже мимо. Ну, позвиздить не по теме, создав «словесную дымовую завесу», это Вы любите, это я уже заметил. Но мне это по барабану.

У меня остаётся один простой вопрос. alloca() это функция или макрос? Одним словом.

Теперь я отвечаю.

Но не угадал с причиной.

Я и не гадал. Там явно косой вызов.

Судя по твоему сообщению, для тебя было реально сюрпризом, что от функции адрес можно взять.

О, Госссподи… Ну и аргументация… =))) Нет. Не было.

Я уже почти уверен, что если бы там то же самое с printf было, а не с alloca, ты бы так же сказал, что не скомпилируется.

Ваша уверенность? А она чего-то стоит при таких косяках как выше? =))) Опять мимо.

Потому что если бы ты знал, что символ alloca не определён, то не стал бы писать, что alloca реализуется функцией в glibc.

А я ещё и про gcc писал. Как быть с эим? Никакой уверенности ни в чём не добавляет?

В общем да. Отавет на вопрос выше за Вами, а дальше пристегните ремни, Вам в игнор ввиду хронической безграмотности и бестолковости. Ну, про то, что Вам следует записаться на курсы английского, я не говорю. Якобы сишник без возможности прочесть и понять комменты коллег, это не смешно, это печально. Мне с Вами явно не о чем, только трата времени.

Итак. alloca() это макрос или функция? Одним словом.

Moisha_Liberman ★★
()

Видимо, ты всё-таки болен на голову. Либо прикидываешься больным на голову, что в общем-то, не сильно отличается.

Т.е., этого куска кода я не приводил?

Я не об этом писал. Ты написал, что этот код генерирует GCC, а я сказал, что это код из glibc. Не видишь разницы, что ли?

Дальше по этому сырцу. Вас в каком ГПТУ английскому учили, а? Это не для сановского компиля. Это инструкции, которые созданы сановским компилём. Это прямо противоположно Вашему утверждению и это следует из строки комментария /* Code produced by Sun’s C compiler calls.

Хватит позориться-то. «Code produced by Sun’s C compiler calls this function with two extra arguments» переводится как «Код, создаваемый сановским Си-компилятором, вызывает эту функцию с двумя дополнительными аргументами». Тут «calls» — глагол.

И чё дальше? Он там и записан. Почему его не прочесть? Что помешает сделать ret, увеличить esp и продолжить как ни в чём ни бывало дальше? Конкретно что?

rsp изменяется внутри функции, причём на неизвестное на этапе компиляции значение. В этом и состоит суть alloca — выделение места на стеке. Очевидно, rsp после этого уже не указывает на адрес возврата, и если rsp не восстановить, ret отправит выполнение в далёкие края. Печально, что ты таких базовых вещей не понимаешь. Видимо, считаешь, что ret работает на магии.

У Вас там далеко завалялся компилятор gcc или sun c для Sparc32? Вы в его выхлоп смотрели? Нет? А в какой, позвольте уточнить?

Я поставил кросс-компилятор GCC для sparc, конечно же. В main репозитории Debian есть. Им компилировал, потом смотрел дизассемблером получившийся объектник.

Я задал конкретный вопрос – alloca() это функция или макрос?

Твой вопрос тупой, иначе и не скажешь. Я уже устал объяснять детали. Краткий ответ: это builtin.

Там явно косой вызов.

Нормальный там вызов. К адресу прибавляется 1234 байт, потом вычитается последовательно 1000, 200, 30 и 4 байта. В итоге получается исходный адрес. Если там alloca заменить на существующий символ, будет работать.

Ваша уверенность? А она чего-то стоит при таких косяках как выше? =))) Опять мимо.

Вот сейчас поменял на printf и… сработало. Как и ожидалось.

А я ещё и про gcc писал. Как быть с эим?

Частично угадал. Правда, дальше видно, что понимания как не было, так и нет. alloca не функция, у неё нет адреса, нет тела.

Никакой уверенности ни в чём не добавляет?

Добавляет уверенность в бессмысленности дальнейших с тобой разговоров. Ты уверенно доказал свою некомпетентность и неспособность к критическому мышлению.

Ну, про то, что Вам следует записаться на курсы английского, я не говорю.

Изучать язык не вредно, что русский, что английский. Поэтому отказываться не буду. :-)
А вот ты явно не отличаешь «Sun’s C compiler calls» от «Sun’s C compiler’s calls».

Итак. alloca() это макрос или функция? Одним словом.

Да.

i-rinat ★★★★★
()

поскольку переполнение стека не контролируется программой - выделение больших объемов в нем это плохая идея и семантика c99 тот еще бред. выделяйте в куче заранее.

quester ★★
()

До 4 KiB на стек, больше - в кучу.

Legioner ★★★★★
()
Ответ на: комментарий от i-rinat

Спасибо за дискуссию, было интересно, держи лойс в карму

ncuxer
()
Ответ на: комментарий от i-rinat

Уффф... вообще-то с некоторыми «НО» alloca() можно реализовать как «функцию», если у нас будет механизм подобный RAII и если выделять можно необязательно таким же образом, как и обычные локальные переменные(массивы) в стеке. Можно написать простейший стековый аллокатор, сохранять в начале каждой функциии указатель на вершину стека, потом при выходе из функции, в которой наш особый alloca вызывался, этот стек можно подчищать. Выглядеть это будет примерно вот так:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

char alloc_glob_arr[100500];
char *alloc_ptr = alloc_glob_arr;

void *my_alloca(const size_t sz) // unaligned allocation!!!
{
  void *retval = alloc_ptr;
  if (sizeof(alloc_glob_arr) < alloc_ptr - alloc_glob_arr + sz)
  {
    fprintf(stderr, "ERROR: stack overflow!\n");
    exit(-1);
  }
  alloc_ptr += sz;
  return retval;
}

void free_my_alloca(void *stackptr)
{
  alloc_ptr = stackptr;
}


int main(void) {
  void *stackptr_start = alloc_ptr;
  printf("stack_p = %p\n", alloc_ptr);

  char *alloc_test1 = my_alloca(10);
  printf("stack_p = %p\n", alloc_ptr);

  for(size_t i = 0; i < 10; i++)
  {
    alloc_test1[i] = i;
  }

  char *alloc_test2 = my_alloca(15);
  printf("stack_p = %p\n", alloc_ptr);

  for(size_t i = 0; i < 15; i++)
  {
    alloc_test2[i] = i*2;
  }


  for(size_t i = 0; i < 10; i++)
  {
    printf("%d,", alloc_test1[i]);
  }
  printf("\n");

  for(size_t i = 0; i < 15; i++)
  {
    printf("%d,", alloc_test2[i]);
  }
  printf("\n");

  free_my_alloca(stackptr_start);

  printf("stack_p = %p\n", alloc_ptr);

  return 0;
}

(Не ручаюсь за полную корректность)

По поводу вопроса «а является ли настоящей функцией обычный alloca() из GCC» - думаю что нет, не является. Обычной функцией такое реализовать не выйдет, ну нельзя написать функию, которая резервирует место в стекфрейме вызывающей её функции и возвращает указатель на него ей же без UB.

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)
Ответ на: ЛОЛШТА?!? =))) от Moisha_Liberman

Ух ты, кто-то ещё тратит воздух на царя детского лепета.

t184256 ★★★★★
()
Ответ на: комментарий от i-rinat

Ты не понимаешь, что такое builtin-функция? Это не функция в обычном смысле, потому что у неё нет тела. Нельзя взять адрес builtin-функции.

Ну круто, а почему теперь её нельзя называть функцией? Инлайн-функции тоже теперь нельзя называть функцией? Что ещё теперь нельзя называть функцией?

t184256 ★★★★★
()
Ответ на: комментарий от t184256

Нельзя взять адрес builtin-функции.

Инлайн-функции тоже теперь нельзя называть функцией?

Мы можем брать адрес inline функций и в С++ и в С. inline это лишь подсказка компилятору, что мы бы очень хотели чтобы компилятор эту функцию инлайнил, то есть просто увеличил порог по своим эвристикам для инлайна.

С++: https://gcc.godbolt.org/z/Sga4gE

C: https://gcc.godbolt.org/z/trgoKG

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от fsb4000

Я имел в виду не означенные словом inline, а уже успешно заинлайненные.

t184256 ★★★★★
()
Ответ на: комментарий от quester

Прочёл.

И дал сознательно некоторое время «отстояться», чтобы Вы могли поправить спорные места, если захотите. Но не захотели. А зря.

Итак. Я коротенько.

  1. Функция alloca() это функция, как я и говорил. Что и требовалось доказать.
  2. Ркализация ф-ии alloca(), как мы видим (как я и говорил выше), может быть как в glibc, так и как внутренняя ф-я компилятора. Что и требовалось доказать. Ни что не мешает реализовать её в glibc. Так что вот это прогон не более чем прогон:

alloca() нельзя реализовать функцией на x86-64 под GCC, потому что нужно как-то стек при выходе возвращать в предыдущее состояние.

Её можно реализовать хоть где как ф-ю, т.к. Вы ни фига и ничего не знаете о соглашениях для С-вызовов. Почему я это абсолютно ответственно заявляю? Да потому что у Вас в исходнике данной функции уже всё сказано. И, если бы Вы хоть что-то знали о соглашениях о вызовах, то не пороли бы ахинеи. Стек в предыдущее состояние возвращает вызывающая, а не вызываемая функция (выше я Вам на это намекал, но Вы слишком тупы чтобы прочесть что Вам пишут). И точно так же ни что не мешает сделать такую реализацию на gcc для x86_64, но к счастью поступили разумнее.

Так вот. Если признать что существующее соглашение о вызовах корректно, то получается что компиль сам вычислит адрес возврата и сгенерит соответствующую инструкцию для возврата, а вызывающей функции нужно просто добавить то число байт, которое она запросила у вызываемой alloca(). Т.е., если не заниматься сексом в глаза, а посмотреть на реализацию, там должна быть использована обратная по отношению к sub %sp, %o0, %sp операция, т.е. просто увеличение %sp через добавление посредством add значения. Про что я и задолбался уже говорить. Так же добавлю что вызывающая ф-я всегда знает сколько байт она запросила. Т.е., тут придумывать какие-то фигни не нужно. Тут всё просто, если знать ассемблер хоть немного. На это же намекает (хоть намёк Вами и не понят) сигнатура данной ф-ии. Там нет никаких локальных (%fp) переменных. Там есть сегмент стека и работа с ним, но к этому вернёмся чуть ниже.

  1. Прогон по Sun C это тоже прогон. Вдобавок, я ещё и подкол один там заложил, Вы в него вляпались благополучно. =))) Выше я Вам намекал что в этой функции есть «забавный комментарий». Да. Есть. Вот он – arguments which it makes relocatable symbols but seem always to be the constant 96; I have no idea what they are for.

Да, всё верно. Чувак молодец, по-честному предупредил. У чувака, который делал эту реализацию, нет идей почему там по его мнению должно быть 96 (хотя, это нескольно и не так) байт. Он тупо взял, да посмотрел в то, как это делается в Sun C и столько же тупо закинул эту реализацию прямо вот так, с константой в реализацию для glibc. Т.е., это образчик подхода «фигак-фигак и в продакшон», но только для С. Сишники иной раз так делают, но тут уже… Кто осудит?

Какую подляну я для Вас заложил? А всё просто – я задал Вам вопрос про сановский компиль, который Вы зачем-то метнулись ставить. Ранее я назвал Вас рафинированным балбесом, но здесь я вынужден повысить Вас в звании до просто сказочного долб… (ну, Вы поняли). Почему так? Да потому что ставить сановский компиль на интел это идиотизм в данном случае. Надо было бы брать сановский и gcc для SPARC32 и там смотреть и сравнивать, т.к. там вообще иная аппаратная архитектура (на родных сановских спарках32, там вообще-то risc), соответственно и мнемоники и всё остальное там будет совершенно иное (слегка похожее, т.к. С это С) чем на интел. Это просто разное железо. И что Вы там смотреть метнулись, этого я даже уже и не спрашиваю чтобы не подохнуть от хохота. =)))

Вот почему, неспотря на Весь Ваш звиздёж про Appendix D, Вы умудрились вляпаться в глухой разводняк. Так вот. Вы, не читали (и даже не пытались) документа по работе со стеком в SPARC32. Вот этот, если быть конкретным.

Если бы Вы прочли, дури про %fp не было бы. Дури про то, что это не вызов Sun C, тоже. Эта реализация просто и тупо выдрана (хоть и грамотно) из вызова сановского компиля и столь же тупо заброшена в glibc. Идите английский учить (остальное гонево по данному поводу я просто отказываюсь комметировать как полностью не соответсвующее действительности). =))) Кто-то здесь назвал этот трюк «кривым патчем», да, соглашусь, довольно грязный хак, но это работает.

Остальное я не хочу комментировать, т.к. я считаю доказанными следующие факты:

  • alloca() это функция.

  • alloca() может быть реализована как в gcc, так и в glibc, по крайней мере, ни что не мешает этого сделать. Не зря говорится что эта ф-я и машино- и компиляторо-зависима.

Всё, что я хотел увидеть, я увидел.

=====

Ринат… Понимаете, когда местный деревенский дурачёк прыгает тут и орёт нарезая круги, это да, потешно и забавно местами. В конце-концов его можно просто игнорить, да и всё. У него задача такая – зафлеймить наглухо оппонента. Иной раз он потешен, иной раз, когда сущую околесицу несёт, как-то пофиг.

Но Вы попали под замес по другой причине. Вы, к сожалению, не усвоили одну прописную истину – по себе других не судят. И, когда Вы решили выше пофлеймить на тему моих скиллов, я просто решил Вам маленько подыграть. Правда, я не уточнял что я работал (и работаю) не только с интел, с той же SPARC32 я работал именно на сановских коробочках. И правда, я не ожидал от Вас, типа от «сишника», такого фееричного обс… «факапа». Когда Вам показывают текст, объясняют как он работает, а Вы рвётесь ставить сановский компиль на совершенно иную аппаратную архитектуру и чего-то там «смотрите». Заметьте – я не подвергаю сомнению факта того, что Вы его поставили. Я Вам просто и на пальцах показываю где Вы сказочный долб… в таком случае. =)))

Ринат, я придерживаюсь одного принципа. Я не позволяю никому красть у меня время. Вам ненужно тратить его на себя. Я в курсе что я могу разобраться с реализацией ф-ии, но я не знаю что мне делать с этим знанием. Зачем мне оно.

Вот почему я Вам выше об этом сказал, а сейчас повторю с действием. Ринат, Вы идёте на… (ну, Вы поняли) в игнор. Я могу тут долго над Вами издеваться, тыкая Вас носом в спеки, но я смысла в этом не вижу.

Счастливого Вам пути. С Новым Годом и наступающим Рождеством.

Moisha_Liberman ★★
()
Ответ на: Прочёл. от Moisha_Liberman

Вторая часть очень литературная, но Рината надо бы тогда что ли кастануть или написать ему - он же не увидит что Вы ему написали;-)

AntonI ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.