Анализ кода сборки

 $ gcc -O2 -S test.c -----------------------(1)
      .file "test.c"
    .globl accum
       .bss
       .align 4
       .type accum, @object
       .size accum, 4
    accum:
       .zero 4
       .text
       .p2align 2,,3
    .globl sum
       .type sum, @function
    sum:
       pushl %ebp
       movl  %esp, %ebp
       movl  12(%ebp), %eax
       addl  8(%ebp), %eax
       addl  %eax, accum
       leave
       ret
       .size sum, .-sum
       .p2align 2,,3
    .globl main
       .type main, @function
    main:
       pushl %ebp
       movl  %esp, %ebp
       subl  , %esp
       andl  $-16, %esp
       subl  , %esp
       pushl 
       pushl 
       call  sum
       xorl  %eax, %eax
       leave
       ret
       .size main, .-main
       .section .note.GNU-stack,"",@progbits
       .ident   "GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-9)"

это код сборки, сгенерированный из этой программы C:

#include <stdio.h>
int accum = 0;

int sum(int x,int y)
{
   int t = x+y;
   accum +=t;
   return t;
}

int main(int argc,char *argv[])
{
   int i = 0,x=10,y=11;
   i = sum(x,y);
   return 0;
}

кроме того, это код объекта, сгенерированный из вышеуказанной программы:

$objdump -d test.o -------------------------(2) 

test.o:     file format elf32-i386

Disassembly of section .text:

00000000 <sum>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   8b 45 0c                mov    0xc(%ebp),%eax
   6:   03 45 08                add    0x8(%ebp),%eax
   9:   01 05 00 00 00 00       add    %eax,0x0
   f:   c9                      leave
  10:   c3                      ret
  11:   8d 76 00                lea    0x0(%esi),%esi

00000014 <main>:
  14:   55                      push   %ebp
  15:   89 e5                   mov    %esp,%ebp
  17:   83 ec 08                sub    x8,%esp
  1a:   83 e4 f0                and    xfffffff0,%esp
  1d:   83 ec 10                sub    x10,%esp
  20:   6a 0b                   push   xb
  22:   6a 0a                   push   xa
  24:   e8 fc ff ff ff          call   25 <main+0x11>
  29:   31 c0                   xor    %eax,%eax
  2b:   c9                      leave
  2c:   c3                      ret

в идеале , листинг (1) и (2) должны быть одинаковыми. Но я вижу что в листинге (1) есть movl, pushl и т. д., Тогда как mov, push in lising (2). Мой вопрос:

  1. какая правильная инструкция по сборке фактически выполняется на процессоре?
  2. в листинге (1), я вижу это в начале:

.file "test.c"
    .globl accum
       .bss
       .align 4
       .type accum, @object
       .size accum, 4
    accum:
       .zero 4
       .text
       .p2align 2,,3
    .globl sum
       .type sum, @function 

и это в конце:

.size main, .-main
           .section .note.GNU-stack,"",@progbits
           .ident   "GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-9)"

что это значит?

спасибо.

2 ответов


инструкция называется MOV какой вариант используется. The l суффикс-это просто соглашение сборки gcc / AT&T, чтобы указать размер требуемых операндов, в данном случае 4 байтовых операндов.

в синтаксисе Intel-где есть какая-либо двусмысленность-вместо суффикса инструкции обычно помечают параметр памяти индикатором требуемого размера (например,BYTE, WORD, DWORD, etc.), это просто еще один способ достижения той же вещь.

89 55 правильная последовательность байт MOV из 32-разрядного регистра EBP в 32-разрядный регистр ESP. Нет ничего плохого в листинге.


указывает файл, из которого был создан этот код сборки:

.file "test.c"

говорит, что accum является глобальным символом (переменная C с внешней связью):

    .globl accum

следующие байты должны быть помещены в bss раздел, это раздел это не занимает места в объектном файле, но выделяется и обнуляется во время выполнения.

       .bss

выровнено по границе 4 байта:

       .align 4

это объект (переменная, а не какой-то код):

       .type accum, @object

это четыре байта:

       .size accum, 4

здесь accum определено, четыре нулевых байта.

    accum:
       .zero 4

теперь переключитесь с bss раздел в текстовый раздел, где обычно находятся функции на хранении.

       .text

добавьте до трех байтов заполнения, чтобы убедиться, что мы находимся на границе 4 байта (2^2):

       .p2align 2,,3

sum является глобальным символом, и это функция.

    .globl sum
       .type sum, @function 

размер main "здесь" - "где main started":

.size main, .-main

здесь указаны конкретные параметры стека gcc. Обычно именно здесь вы выбираете исполняемый стек (не очень безопасный) или нет (обычно привилегированный.)

       .section .note.GNU-stack,"",@progbits

определите, какая версия компилятора сгенерировала эту сборку:

       .ident   "GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-9)"

список ассемблера и список дизассемблера показывают один и тот же код, но используют другой синтаксис. Прилагаемый -l является вариантом синтаксиса, используемым gcc. То, что у вас другой синтаксис в инструментах (вывод C-компилятора и дизассемблер), показывает слабость вашей цепочки инструментов.

disassemnbly при смещении 11 в сумме: показывает только некоторые байты мусора. Точка входа в следующую функцию main выровнена на 4 байта, что дает этот пробел, заполняется мусором.

куча .операторы определяются документацией ассемблера. Обычно они не дают исполняемого кода.