2012-05-02 3 views
4

У меня есть эта проблема, я рекурсивно вызываю функцию в C и C лексически области, поэтому я могу получить доступ только к текущему стеку стека. Я хочу извлечь аргументы и локальные переменные из предыдущего фрейма стека, который был создан при предыдущем вызове функции, в то время как im в текущем фрейме стекапредыдущие переменные стека

Я знаю, что значения из предыдущего рекурсивного вызова все еще находятся в стеке, но я не могу получить доступ к этим значениям, потому что они «похоронены» под активной рамкой стека?

Я хочу извлечь аргументы и локальные переменные из предыдущего стека и скопировать их в copy_of_buried_arg и copy_of_buried_loc;

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

#include <stdio.h> 

char glo = 97; // just for fun 97 is ascii lowercase 'a' 
int copy_of_buried_arg; 
char copy_of_buried_loc; 

void rec(int arg) { 
    char loc; 

    loc = glo + arg * 2; // just for fun, some char arithmetic 
    printf("inside rec() arg=%d loc='%c'\n", arg, loc); 

    if (arg != 0) { 
    // after this assembly code runs, the copy_of_buried_arg and 
    // copy_of_buried_loc variables will have arg, loc values from 
    // the frame of the previous call to rec(). 
    __asm__("\n\ 
      movl 28(%esp), %eax #moving stack pointer to old ebp (pointing it to old ebp)\n\ 
      addl $8, %eax  #now eax points to the first argument for the old ebp \n\ 
      movl (%eax), %ecx #copy the value inside eax to ecx\n\ 
      movl %ecx, copy_of_buried_arg # copies the old argument\n\ 
    \n\ 

"); 

    printf("copy_of_buried_arg=%u copy_of_buried_loc='%c'\n", 
     copy_of_buried_arg, copy_of_buried_loc); 
    } else { 
     printf("there is no buried stack frame\n");// runs if argument = 0 so only the first time 
    } 

    if (arg < 10) { 
    rec(arg + 1); 
    } 
} 

int main (int argc, char **argv) { 
    rec(0); 

    return 0; 
} 
+3

Не можете ли вы просто передать адрес местных жителей предыдущего вызова в следующую функцию? Мессинг с ассемблером не может привести к хорошим результатам. –

+0

Как насчет запуска этого в вашем отладчике и получения чисел оттуда? –

+0

@GregHewgill мы должны использовать сборку, чтобы выкопать переменные из предыдущего стека, это требование – David

ответ

1

Я могу попытаться помочь, но не Linux или сборки ГАЗ. Но расчеты должны быть схожими:

Вот стопка после пары звонков. Типичная установка фрейма стека создает связанный список фреймов стека, где EBP является текущим фреймом стека и указывает его старое значение для предыдущего фрейма стека.

 +-------+ 
ESP-> |loc='c'|  <- ESP currently points here. 
     +-------+ 
EBP-> |oldEBP |--+ <- rec(0)'s call frame 
     +-------+ | 
     |retaddr| | <- return value of rec(1) 
     +-------+ | 
     |arg=1 | | <- pushed argument of rec(1) 
     +-------+ | 
     |loc='a'| | <- local variable of rec(0) 
     +-------+ | 
    +--|oldEBP |<-+ <- main's call frame 
    | +-------+ 
    | |retaddr|  <- return value of rec(0) 
    | +-------+ 
    | |arg=0 |  <- pushed argument of rec(0) 
    | +-------+ 
    \|/ 
to main's call frame 

Это создается по следующей последовательности:

  1. Push-аргументы последний аргумент первым.
  2. Вызвать функцию, нажав обратный адрес.
  3. Скользящий скоростной EBP, сохраняющий предыдущую стеклянную раму.
  4. Переместите ESP (верхнюю часть стека, содержащий oldEBP) в EBP, создав новую стек кадров.
  5. Вычесть пространство для локальных переменных.

Это имеет эффект на 32-битной стек, что EBP+8 всегда будет первым параметром вызова, EBP+12 2-й параметр и т.д. EBP-n всегда является смещение к локальной переменной.

код, чтобы получить предыдущий loc и arg затем (в MASM):

mov ecx,[ebp]    // get previous stack frame 
mov edx,[ecx]+8   // get first argument 
mov copy_of_buried_arg,edx // save it 
mov dl,[ecx]-1    // get first char-sized local variable. 
mov copy_of_buried_loc,dl // save it 

или моя догадка в ГАС (я не знаю, но знаю, что это назад в MASM):

movl (%ebp),ecx 
movl 8(%ecx),edx 
movl edx,copy_of_buried_arg 
movb -1(%ecx),dl 
movb dl,copy_of_buried_loc 

Вывод кода с моей MASM с помощью VS2010 на Windows:

inside rec() arg=0 loc='a' 
there is no buried stack frame 
inside rec() arg=1 loc='c' 
copy_of_buried_arg=0 copy_of_buried_loc='a' 
inside rec() arg=2 loc='e' 
copy_of_buried_arg=1 copy_of_buried_loc='c' 
inside rec() arg=3 loc='g' 
copy_of_buried_arg=2 copy_of_buried_loc='e' 
inside rec() arg=4 loc='i' 
copy_of_buried_arg=3 copy_of_buried_loc='g' 
inside rec() arg=5 loc='k' 
copy_of_buried_arg=4 copy_of_buried_loc='i' 
inside rec() arg=6 loc='m' 
copy_of_buried_arg=5 copy_of_buried_loc='k' 
inside rec() arg=7 loc='o' 
copy_of_buried_arg=6 copy_of_buried_loc='m' 
inside rec() arg=8 loc='q' 
copy_of_buried_arg=7 copy_of_buried_loc='o' 
inside rec() arg=9 loc='s' 
copy_of_buried_arg=8 copy_of_buried_loc='q' 
inside rec() arg=10 loc='u' 
copy_of_buried_arg=9 copy_of_buried_loc='s' 
+0

Я предполагаю, что любая защита стека сделает этот подход совершенно не переносным. – tbert

+1

@tbert: Что такое защита стека?Код OP не переносится, поскольку он разбивается при компиляции с различными параметрами оптимизации или другой версией компилятора. –

+0

Я лёгкий, у меня просто есть кошмары, вызванные потоками вопросов об этом от людей, которые считают, что это жизнеспособная техника для передачи информации для дальнейшей рекурсивной итерации функции. Если инструктор не удостоверится, что есть длинный список предостережений, которые идут вместе с этим взломом, тогда я буду чертовски уверен, что где-то в Интернете кто-то говорит «caveat emptor». – tbert

0

С моим компилятором (gcc 3.3.4) Я закончил с этим:

#include <stdio.h> 

char glo = 97; // just for fun 97 is ascii lowercase 'a' 
int copy_of_buried_arg; 
char copy_of_buried_loc; 

void rec(int arg) { 
    char loc; 

    loc = glo + arg * 2; // just for fun, some char arithmetic 
    printf("inside rec() arg=%d loc='%c'\n", arg, loc); 

    if (arg != 0) { 
    // after this assembly code runs, the copy_of_buried_arg and 
    // copy_of_buried_loc variables will have arg, loc values from 
    // the frame of the previous call to rec(). 
    __asm__ __volatile__ (
      "movl 40(%%ebp), %%eax #\n" 
      "movl %%eax, %0 #\n" 
      "movb 31(%%ebp), %%al #\n" 
      "movb %%al, %1 #\n" 
      : "=m" (copy_of_buried_arg), "=m" (copy_of_buried_loc) 
      : 
      : "eax" 
    ); 

    printf("copy_of_buried_arg=%u copy_of_buried_loc='%c'\n", 
     copy_of_buried_arg, copy_of_buried_loc); 
    } else { 
     printf("there is no buried stack frame\n");// runs if argument = 0 so only the first time 
    } 

    if (arg < 10) { 
    rec(arg + 1); 
    } 
} 

int main (int argc, char **argv) { 
    rec(0); 

    return 0; 
} 

Вот демонтаж соответствующей части (получить его с gcc file.c -S -o file.s):

_rec: 
     pushl %ebp 
     movl %esp, %ebp 
     subl $8, %esp 
     movl 8(%ebp), %eax 
     addl %eax, %eax 
     addb _glo, %al 
     movb %al, -1(%ebp) 
     subl $4, %esp 
     movsbl -1(%ebp),%eax 
     pushl %eax 
     pushl 8(%ebp) 
     pushl $LC0 
     call _printf 
     addl $16, %esp 
     cmpl $0, 8(%ebp) 
     je  L2 
/APP 
     movl 40(%ebp), %eax # 
movl %eax, _copy_of_buried_arg # 
movb 31(%ebp), %al # 
movb %al, _copy_of_buried_loC# 

/NO_APP 
     subl $4, %esp 
     movsbl _copy_of_buried_loc,%eax 
     pushl %eax 
     pushl _copy_of_buried_arg 
     pushl $LC1 
     call _printf 
     addl $16, %esp 
     jmp  L3 
L2: 
     subl $12, %esp 
     pushl $LC2 
     call _printf 
     addl $16, %esp 
L3: 
     cmpl $9, 8(%ebp) 
     jg  L1 
     subl $12, %esp 
     movl 8(%ebp), %eax 
     incl %eax 
     pushl %eax 
     call _rec 
     addl $16, %esp 
L1: 
     leave 
     ret 

Эти смещения от ebp (40 и 31) были первоначально установлен в произвольное значение предположения (например, 0), а затем уточняется путем наблюдения за разборкой и некоторых простых вычислений.

Обратите внимание, что функция использует дополнительные 12 + 4 = 16 байт стека для выравнивания и параметр, когда он называет себя рекурсивно:

 subl $12, %esp 
     movl 8(%ebp), %eax 
     incl %eax 
     pushl %eax 
     call _rec 
     addl $16, %esp 

Есть также 4 байта адреса возврата.

И тогда функция использует 4 + 8 = 12 байт для старого ebp и ее локальные переменные:

_rec: 
     pushl %ebp 
     movl %esp, %ebp 
     subl $8, %esp 

Таким образом, в общей сложности стек растет на 16 + 4 + 12 = 32 байт с каждой рекурсивной вызов.

Теперь мы знаем, как получить наши местные arg и loc через ebp:

 movl 8(%ebp), %eax ; <- arg 
     addl %eax, %eax 
     addb _glo, %al 
     movb %al, -1(%ebp) ; <- loc 

Таким образом, мы просто добавим 32 к этим смещениям 8 и -1 и прийти 40 и 31.

Сделайте то же самое, и вы получите свои «захороненные» переменные.

+0

Обратите внимание, что по крайней мере аргументы x86-64 фактически поступают в регистры (в основном), а не в стек, поэтому это действительно невозможно. – perh

+0

@perh В вопрос не упоминалось 64-битное. Кроме того, если регистров недостаточно для хранения всего в них, что-то неизбежно будет храниться в стеке. –

Смежные вопросы