Как измерить объем стека, который использует вызов произвольной функции в C?

#c #assembly #arm #callstack #stack-size

#c #сборка #arm #callstack #размер стека

Вопрос:

Наша компания купила проприетарную функцию C: у нас есть скомпилированная библиотека ProcessData.a и файл интерфейса для ее вызова:

 # ProcessData.h
void ProcessData(char* pointer_to_data, int data_len);
 

Мы хотим использовать эту функцию на ARM встроенном процессоре, и мы хотим знать, сколько места в стеке она может использовать.

Вопрос: как измерить использование стека произвольной функции?

До сих пор я пытался реализовать следующие вспомогательные функции:

 static int* stackPointerBeforeCall;

void StartStackMeasurement(void) {
    asm ("mov %0, sp" : "=r"(stackPointerBeforeCall));
    // For some reason I can't overwrite values immediately below the
    // stack pointer. I suspect a return address is placed there.
    static int* pointer;
    pointer = stackPointerBeforeCall - 4;
    // Filling all unused stack space with a fixed constant
    while (pointer != amp;_sstack) {
        *pointer = 0xEEEEEEEE;
        pointer--;
    }
    *pointer = 0xEEEEEEEE;
}

void FinishStackMeasurement(void) {
    int* lastUnusedAddress = amp;_sstack;
    while (*lastUnusedAddress == 0xEEEEEEEE) {
        lastUnusedAddress  ;
    }
    // Printing how many stack bytes a function has used
    printf("STACK: %dn", (stackPointerBeforeCall-lastUnusedAddress)*sizeof(int));
}
 

А затем использовать их непосредственно перед и после вызова функции:

 StartStackMeasurement();
ProcessData(array, sizeof(array));
FinishStackMeasurement();
 

Но это кажется опасным взломом — особенно та часть, где я вычитаю 4 из stackPointerBeforeCall и перезаписываю все, что приведено ниже. Есть ли лучший способ?

Комментарии:

1. Хорошего способа сделать это нет, но может быть менее хакерский способ. Имеет ли встроенная среда, в которой вы запускаете этот код, защиту памяти? Есть ли у него потоки? Есть ли у него (устаревшие, но незаменимые) функции POSIX getcontext , makecontext , setcontext , и swapcontext ?

2. Заполнение стека шаблоном и последующая его проверка — это обычный подход к проверке стека. Если указатель стека всегда указывает на значение, введенное последним, то вычитание 4 является правильным для 32-разрядной системы. (Я не проверял документацию для ARM.) Не зная подробностей о вашей ОС и / или доступных библиотеках, мы не знаем, существуют ли в вашей системе какие-либо специальные механизмы для проверки стека. Возможно, вам придется выполнить тест с разными входными данными, если использование стека зависит от данных. Подумайте о том, чтобы спросить создателя библиотеки о максимальном использовании стека.

3. Правильный способ сделать это — спросить поставщика.

4. Один очень хитрый способ — прочитать sp через ассемблер, заполнить все от sp до конца стека известной шестнадцатеричной последовательностью, 0xAA или такой. Затем проверьте, на сколько 0xAA изменилась функция. Не точная наука, а старый добрый трюк, используемый для определения максимального использования стека в системах с голым металлом.

5. Кстати, опубликованный код должен гарантировать, что pointer он не размещен в стеке, иначе он может решить уничтожить себя и заменить содержимое адресом 0xeeeeeeeeee 🙂 Лучшее решение для этого static int* pointer; pointer = stackPointerBeforeCall - 4;

Ответ №1:

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

Конечно, этот анализ пришлось бы переделать с обновлениями функции (библиотеки).

Ответ №2:

Вы можете использовать getrusage, который представляет собой функцию, которая позволяет вам использовать ресурсы вашего программного обеспечения, в частности ru_isrss , которая

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

(источник)

Затем вы можете сравнить его с использованием стека вашей программой с помощью имитированного вызова библиотеки.

Однако это будет работать только в том случае, если ваша система реализовала ru_isrss (в отличие от Linux), в противном случае для поля будет установлено значение 0.