Косвенно-регистровый режим со смещением
Пример 2.4. Формирование, использование и уничтожение стекового кадра. Код на языке С и результат его обработки GNU С 2.7.2.1 (комментарии автора):
#include <stdio.h> # include <strings.h> /* Фрагмент примитивной реализации сервера SMTP (RFC822) */ int parse_line(FILE * socket) { /* Согласно RFC822, команда имеет длину не более 4 байт, ; а вся строка – не более 255 байт V char cmd[5], args [255]; ;fscanf (socket, "%s %s\n", cmd, args); if (stricmpfcmd, "HELO")==0) { fprintf (socket, "200 Hello, %s, glad to meet you\n", args); return 200; ) /* etc */ fprintf (socket, "500 Unknown command %s\n", cmd); return 500; .file "sample" gcc2_compiled.: _ gnu_compiled_c:.text LCO: .ascii "%s %s\12\0" LCI: .ascii "HELCAO" LC2: .ascii "200 Hello, %s, glad to meet you\12\0" LC3: .ascii "500 Unknown command %s\12\0" .align 2, 0x90.globl _parse_line _parse_line: ; x86 имеет для этой цели специальную команду enter, но она может ; формировать кадры размером не более 255 байт. ; В данном случае кадр имеет больший размер, ; и его необходимо формировать вручную. pushl %ebp; Сохраняем указатель кадра ; вызвавшей нас подпрограммы. movl %esp, %ebp; Формируем указатель нашего кадра subl $264,%esp; И сам кадр. ; Конец пролога функции leal -264 (%ebp), %еах; Помещаем в стек указатель на args pushl %eax leal -8 (%ebp), %еах;… на cmd pushl %eax pushl $LCO; на строковую константу ; Наши собственные параметры тоже адресуются относительно кадра .movl 8(%ebp),%eax ; Параметр socket мы тоже проталкиваем pushl %eax ; в стек call fscanf ; Вызов (параметры в стеке) addl $16,%esp ; очищаем стек ; в языке С переменное количество параметров, поэтому вычищать их из ; стека должна вызывающая процедура. Вызываемая просто не знает, ; СКОЛЬКО ИХ бЫЛО. pushl $LC1 leal -8(%ebp),%eax pushl %eax call _stricmp addl $8,%esp movl %eax,%eax; выключенная оптимизация в действии:) ; А ведь недалеки времена, когда компиляторы только такое и умели генерировать. testl %eax,%еах jne L14 leal -264(%ebp),%еах pushl %eax pushl $LC2 movl 8(%ebp),%eax pushl %eax call _fprintf addl $12,%esp ; Обратите внимание, что компилятор не стал генерировать второй эпилог ;функции на втором операторе return. movl $200,%eax jmp L13 ; Выравнивание потенциальных точек перехода на границу слова полезно: ; процессор не будет тратить дополнительный цикл шины на чтение ; невыровненной команды. Для выравнивания используется команда NOP; ; (код операции 0x90). align 2.0x90 L14: leal -8(%ebp),%еах Pushl %eax Pushl $LC3 movl 8(%ebp),%eax pushl %eax call _fprintf addl?12,%esp movl $500,%eax jmp L13 .align 2.0x90 L13: ; Команда leave совершает действия, обратные прологу функции: ; Она эквивалентна командам: move %ebp, %esp; pop %ebp. ; Размер кадра явным образом не указывается, поэтому ограничений; на этот размер в данном случае нет. leave ret