Подход 1 (динамический анализ)
Вы можете определить размер стека во время выполнения, заполнив стек предопределенным шаблоном, выполнив memset
, а затем проверив, сколько байтов было изменено.Это медленнее и сложнее, так как вам нужно скомпилировать пример программы, загрузить ее в целевой объект (если у вас нет симулятора) и собрать результаты.Вам также нужно быть осторожным с тестовыми данными, которые вы предоставляете функции, поскольку путь выполнения может изменяться в зависимости от размера, выравнивания данных и т. Д.
Для реального примера такого подхода проверьте Код Абсейла .
Подход 2 (статический анализ)
В общем случае статический анализ двоичного кода сложен (даже его разборка не тривиальна), и выВам потребуется сложный механизм символического исполнения, чтобы справиться с этим (например, миазм ).Но в большинстве случаев вы можете смело полагаться на обнаружение шаблонов, которые ваш компилятор использует для выделения кадров.Например, для x86_64 GCC вы могли бы сделать что-то вроде:
objdump -d /lib64/libc.so.6 | sed -ne '/<__memset_x86_64>:/,/^$/p' > memset.d
NUM_PUSHES=$(grep -c pushq memset.d)
LOCALS=$(sed -ne '/sub .*%rsp/{ s/.*sub \+\$\([^,]\+\),%rsp.*/\1/; p }' memset.d)
LOCALS=$(printf '%d' $LOCALS) # Unhex
echo $(( LOCALS + 8 * NUM_PUSHES ))
Обратите внимание, что этот простой подход дает консервативную оценку (получение более точного результата выполнимо, но потребует анализа с учетом пути, которыйтребует правильного разбора, построения графа потока управления и т. д.) и не обрабатывает вызовы вложенных функций (может быть легко добавлено, но, вероятно, должно выполняться на языке, более выразительном, чем оболочка).
Сборка AVR в целомболее сложный, потому что вы не можете легко определить выделение места для локальных переменных (модификация указателя стека разделена на несколько инструкций in
, out
и adiw
, поэтому потребуется нетривиальный синтаксический анализ, например, в Python).Простые функции, такие как memset
или memcpy
, не используют локальные переменные, поэтому вы все равно можете использовать простые greps:
NUM_PUSHES=$(grep -c 'push ' memset.d)
NUM_RCALLS=$(grep -c 'rcall \+\.+0' memset.d)
# A safety check for functions which we can't handle
if grep -qi 'out \+0x3[de]' memset.d; then
echo >&2 'Unable to parse stack modification'
exit 1
fi
echo $((NUM_PUSHES + 2 * NUM_RCALLS))