2013-05-24 4 views
3

У меня есть следующий код:Понимание стека макета, где локальные переменные расположены

#include <stdio.h> 
#include <stdbool.h> 
#include <string.h> 
#include <stdlib.h> 
char *MASTER_PASSWORD = "password"; 

bool login(char * password){ 
    bool is_logged_in=false; 
    char buf[8]; 
    strcpy(buf,password); 
    if(strcmp(buf, MASTER_PASSWORD)==0){ 
    is_logged_in=true; 
    } 
    return is_logged_in; 
} 

int main(int argc, char *argv[]){ 
    if(argc <2)  
    { 
    printf("Syntax: %s <input string>\n", argv[0]); 
    exit (0); 
    } 

    if(login(argv[1])) 
    printf("you are authorized"); 

    return 0; 
} 

Я с помощью GDB для отладки, я должен знать, где значение is_logged_in сохраняются в стеке. Как я могу это сделать?

ответ

12

Если вы не берете адрес локальной переменной (&is_logged_in), оптимизирующие компиляторы в основном не будут хранить их в стеке. Вы можете увидеть это с помощью информации объем в БГД:

$ gcc -Os -g3 stack-layout.c -o stack-layout 
$ gdb -q stack-layout 
(gdb) info scope login 

покажет:

Scope for login: 
<...> 
Symbol is_logged_in is multi-location: 
    Range 0x40064c-0x40066e: the constant 0 
    Range 0x40066e-0x400673: a complex DWARF expression: 
    0: DW_OP_breg0 0 [$rax] 
    2: DW_OP_const1u 32 
    4: DW_OP_shl 
    5: DW_OP_lit0 
    6: DW_OP_eq 
    7: DW_OP_stack_value 

, length 1. 
<...> 

медведь со мной здесь, даже если вы не знакомы с x86-64 сборки. Дизассемблирование входа() дает:

8 bool login(char * password){ 
    0x000000000040064c <+0>:  sub $0x18,%rsp 
    0x0000000000400650 <+4>:  mov %rdi,%rsi 

9  bool is_logged_in=false; 
10 char buf[8]; 
11 strcpy(buf,password); 
    0x0000000000400653 <+7>:  lea 0x8(%rsp),%rdi 
    0x0000000000400658 <+12>: callq 0x4004c0 <[email protected]> 

12 if(strcmp(buf, MASTER_PASSWORD)==0){ 
    0x000000000040065d <+17>: mov 0x2009ec(%rip),%rsi  # 0x601050 <MASTER_PASSWORD> 
    0x0000000000400664 <+24>: lea 0x8(%rsp),%rdi 
    0x0000000000400669 <+29>: callq 0x4004f0 <[email protected]> 
    0x000000000040066e <+34>: test %eax,%eax 
    0x0000000000400670 <+36>: sete %al 

13  is_logged_in=true; 
14 } 
15 
16 return is_logged_in; 
17 } 
    0x0000000000400673 <+39>: add $0x18,%rsp 
    0x0000000000400677 <+43>: retq 

Что gdb info scope говорит о is_logged_in:

  • Между 0x40064c и 0x40066e, то есть между началом функции и вызовом strcmp(), is_logged_in имеет постоянное значение 0.
  • Между 0x40066e и 0x400673, т.е. после вызова STRCMP() до конца функции, значение is_logged_in может быть вычислена путем:
    • Чтение 64-битный регистр, который хранит возвращаемое значение из STRCMP() (RAX)
    • сдвиг влево 32 бита
    • Сравните результат с 0. величина is_logged_in равен 1, если сравнение равенства истинно и 0 в противном случае.

На данный момент некоторые могут возразить, что is_logged_in будет выделяться по-другому, если мы составляем с более низким уровнем оптимизации, но моя точка является то, что локальные переменные только гарантированно быть в стеке, если вы берете их адрес и делаете что-то с этим адресом, который компилятор не будет оптимизировать. В этом случае, если вы хотите изменить значение is_logged_in вы лучше Изменяя значение, возвращенное STRCMP() т.е. изменение RAX сразу после STRCMP() возвращается.

Если is_logged_in выделяется в стеке, р & is_logged_in напечатает его адрес в GDB.Если это не на стеке вы получите сообщение об ошибке, как

(gdb) p &is_logged_in 
Can't take address of "is_logged_in" which isn't an lvalue. 

карлика отладочной информации формата, включая его стек машины операции задокументированы в dwarfstd.org.

+0

действительно спасибо за подробный ответ :) –

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