2013-11-22 2 views
0

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

#define MAX 16 

typedef struct _CONTEXT { 
    int arr[MAX]; 
    int a; 
    int b; 
    int c; 
}; 

void init(CONTEXT* ctx) 
{ 
    memset(ctx->arr, 0, sizeof(ctx->arr[0])); 
    ctx->a = 1; 
} 

void process(CONTEXT* ctx) 
{ 
    int trash; 
    int i; 
    for (i = 0; i < MAX; i++) 
    { 
     trash = ctx->arr[i]; 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    CONTEXT ctx; 
    init(&ctx); 
    process(&ctx); 
    return 0; 

} 

Как я узнал из школы, и из этой лекции slide,
собрание инициализации стеки функции, (в стиле-1) должно выглядеть следующим образом:

pushq %rbp 
movq %rsp, %rbp 
subq $16, %rsp 
movq %rdi, -8(%rbp) 
... 
leave 
ret 

Но когда Я компилирую фрагмент кода выше с помощью gcc, функция main и init имеет ту же процедуру инициализации стека style-1 включая subq инструкции, чтобы выделить пространство памяти переменных стека,
, но функция process не имеет такой инициализации стека.
Я получил эту сборку код (стиль-2):

pushq %rbp 
movq %rsp, %rbp 
movq %rdi, -24(%rbp) 
... 
popq %rbp 
ret 

Так вопросы:

  1. Что политика решения составитель создания различных функций инициализации стека во время компиляции? Я не помещал __cdecl или такой код в этот код, но найдена 2 разные инициализации стека.

  2. Как узнать адрес и размер выделенной ячейки памяти, когда инициализирована функция style-2?

  3. Какова цель movq %rdi, -8(%rbp)?

  4. Есть несколько стилей инициализации стека подле стиля-1 и стиль-2 в Linux?
    (не говоря уже о __cdecl или __stdcall вещей явно)

+0

Как вы его компилировали? вы использовали настройки оптимизации? Если да, то выход может отличаться в зависимости от того, что действительно используется в функции. особенно с таким примером, как это, которые в основном оплодотворяются. – Devolus

+0

Я просто gcc-ed без оптимизации, поэтому оптимизация по умолчанию (-O2?) Была моей настройкой, я думаю. – LocustSpectre

+0

Я сгенерировал другой файл сборки с -O0, но нашел только одно различие (наличие -O0), используя diff между двумя файлами сборки (по умолчанию и -O0). – LocustSpectre

ответ

3

Путь функция компилируется весьма специфичен и компилятор не OS специфичны. Я уверен, что код, созданный GCC под 32-разрядной Windows, похож на код, созданный GCC под 32-разрядным Linux, в то время как код, сгенерированный компилятором Sun's C под 32-разрядным Linux, будет отличаться от кода, созданного GCC , Это также относится к инициализации стека! Поэтому в зависимости от используемого компилятора, параметров компилятора, версии компилятора, внутренних состояний компилятора и т. Д. Могут быть МНОГО стилей инициализации стека.

Очевидно, что вы используете 64-битный код. В отличие от 32-разрядной Windows (где __cdecl и __stdcall существуют) в 64-битной Windows и в Linux существует только одно соглашение о вызове: в 32-разрядной Linux это равно __cdecl в Windows; 64-разрядная Linux и 64-разрядная Windows используют два разных соглашения о вызовах на основе регистров. Это означает: вы не можете изменить соглашение о вызове для Linux и 64-разрядных программ Windows, потому что поддерживается только один.

Целью movq %rdi, -8(%rbp) является сохранение аргумента (в регистре rdi) в стеке; movq %rdi, -24(%rbp) делает то же самое, но записывает в какую-либо область стека, которая может быть перезаписана обработчиками сигналов - это не очень хорошая идея!Однако это не проблема, если значение не считывается из стека!

Очевидно, что функция «стиль-2» не требует никакой памяти стека.

+0

спасибо. Я не знал, что существует только одна конвенция. Сейчас я расскажу о хранении аргументов, с некоторыми книгами и google-ing – LocustSpectre

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