Вывод сборки GCC пустой программы на x86, win32



я пишу пустые программы, чтобы раздражать ад из кодеров stackoverflow, нет. Я просто изучаю инструментальную цепочку gnu.



теперь следующее Может быть слишком глубоким для меня, но для продолжения пустой программы saga я начал изучать вывод компилятора C, материал GNU as потребляет.



gcc version 4.4.0 (TDM-1 mingw32)


.c:



int main()
{
return 0;
}


ЦУС -Ы.c



    .file   "test.c"
.def ___main; .scl 2; .type 32; .endef
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
call ___main
movl , %eax
leave
ret


вы можете объяснить, что здесь происходит? Здесь это мое усилие, чтобы понять это. Я использовал as руководство и мои минимальные знания x86 ASM:





  • .file "test.c" - это директива для логического именем.


  • .def: по документам "начните определять отладочную информацию для имени символа". Что такое символ (имя функции/переменная?) а какая отладочная информация?


  • .scl: документы говорят "класс хранения может отмечать, является ли символ статическим или внешний". Это то же самое статический и внешний я знаю от C? И что это за '2'?


  • .type: сохраняет параметр "как атрибут типа записи таблицы символов", я понятия не имею.


  • .endef: никаких проблем.


  • .text: теперь это проблематично, похоже, что-то называется разделом, и я прочитал, что это место для кода, но документы мне тоже не сказали много.


  • .globl " делает символ видимым для ld.", руководство довольно ясно об этом.


  • _main: это может быть начальный адрес (?) для моей основной функции


  • pushl_: длинный (32bit) толчок, который помещает EBP в стек


  • movl: 32-разрядный двигаться. Псевдо-С:EBP = ESP;


  • andl: логическое "и". Псевдо-С:ESP = -16 & ESP, я не вижу, в чем дело.


  • call: Толкает IP в стек (так что вызываемая процедура может найти свой путь назад) и продолжает там, где __main есть. (что составляет __главная?)


  • movl: этот ноль должен быть константой, которую я возвращаю в конце моего кода. MOV помещает этот ноль в EAX.


  • leave: восстанавливает стек после ввода инструкции (?). Зачем?


  • ret: возвращается к адресу инструкции, сохраненному в стеке


Спасибо за помогите!

678   5  

5 ответов:

.файл " тест.с"

команды, начиная с . директивы в ассемблере. Это просто говорит, что это "файл.c", эта информация может быть экспортирована в отладочную информацию exe.

.деф ___главная; .scl 2; .тип 32; .endef

.директивы def определяют символ отладки. scl 2 означает класс хранения 2 (внешний класс хранения) .тип 32 говорит, что эта сумма является функцией. Эти номера будут определены в формате PE-coff exe

___main-это функция, которая заботится о начальной загрузке, необходимой gcc (она будет делать такие вещи, как запуск статических инициализаторов c++ и других необходимых домашних хозяйств).

.text

начинается текстовый раздел-код живет здесь.

.globl _main

определяет символ _main как глобальный, что делает его видимым для компоновщика и других связанных модулей в.

.def        _main;  .scl    2;      .type   32;     .endef

то же самое , что и _main, создает отладочные символы, указывающие, что _main является функцией. Это может быть использовано отладчиками.

_main:

запускает новую метку (она будет в конечном итоге адрес). этот.директива globl выше делает этот адрес видимым для других объектов.

pushl       %ebp

сохраняет старый указатель кадра (регистр ebp) в стеке (поэтому его можно вернуть на место, когда эта функция заканчивается)

movl        %esp, %ebp

перемещает указатель стека в регистр ebp. ebp часто называют указателем кадра, он указывает на верхнюю часть значений стека в текущем "кадре" (обычно функция), (ссылаясь на переменные в стеке через ebp может помочь отладчикам)

andl $-16, %esp

и стек с fffffff0, который эффективно выравнивает его на границе 16 байт. Доступ к выровненным значениям в стеке осуществляется намного быстрее чем если бы они были не выровнены. Все эти предыдущие инструкции в значительной степени являются стандартным прологом функции.

call        ___main

вызывает функцию __ _ main, которая будет выполнять инициализацию того, что нужно gcc. Вызов будет нажимать текущий указатель инструкции на стеке и перейти к адресу __ _ main

movl        , %eax

Шаг 0 в регистре eax,(0 в возврат 0;) регистр eax используется для хранения возвращаемых значений функций за нарушением соглашения о стандартном вызове конвенция.

оставить

инструкция по отпуску в значительной степени сокращена для

movl     ebp,esp
popl     ebp

т. е. он "отменяет" материал, сделанный в начале функции-восстановление указателя кадра и стека в его прежнее состояние.

ret

возвращает тому, кто вызвал эту функцию. Он выведет указатель инструкции из стека (который будет помещен соответствующей инструкцией вызова там) и прыгай туда.

есть очень похожее упражнение, описанное здесь:http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax

вы поняли большую часть этого -- я просто сделаю дополнительные заметки для акцента и дополнений.

__main Это подпрограмма в стандартной библиотеке GNU, которая заботится о различных инициализации запуска. Это не является строго необходимым для программ C, но требуется только в том случае, если код C связывается с С.++

_main ваша основная подпрограмма. А как _main и __main являются кодовыми местоположениями, они имеют один и тот же класс хранения и тип. Я еще не выкопал определения для .scl и .type еще. Вы можете получить некоторое освещение, определив несколько глобальных переменных.

первые три инструкции настраивают кадр стека, который является техническим термином для рабочего хранения подпрограммы - локальные и временные переменные по большей части. Толкает ebp сохраняет основание кадра стека вызывающего объекта. Положить esp на ebp установка базы кадра стека. Элемент andl выравнивает кадр стека до границы 16 байт только в том случае, если какие-либо локальные переменные в стеке требуют выравнивания 16 байт (для инструкций x86 SIMD требуется это выравнивание, но выравнивание ускоряет обычные типы, такие как int s и float s.

в этот момент Вы обычно ожидаете esp для перемещения вниз в памяти, чтобы выделить пространство стека для локального переменная. Ваш main нет, поэтому gcc не беспокоит.

вызов __main является специальным для основной точки входа и обычно не отображается в подпрограммах.

остальное идет, как вы предположили. Регистрация eax это место, чтобы поместить целочисленные коды возврата в двоичной спецификации. leave отменяет кадр стека и ret возвращается к звонящему. В этом случае вызывающий объект-это низкоуровневая среда выполнения C, которая будет выполнять дополнительную магию (например, вызов atexit() функции, установите код выхода для процесса и попросите операционную систему завершить процесс.

по этому andl $-16,%esp

  • 32 бит: -16 в десятичном равен 0xfffffff0 в шестнадцатеричном представлении
  • 64 бит: -16 в десятичном виде равно 0xfffffffffffffff0 в шестнадцатеричном представлении

таким образом, он будет маскировать последние 4 бита ESP (кстати: 2**4 равно 16) и сохранит все остальные биты (независимо от того, является ли целевая система 32 или 64 бит).

дальше andl $-16,%esp, это работает, потому что установка низких битов на ноль всегда будет регулировать %espвниз в значении, и стек растет вниз на x86.

у меня нет всех ответов, но я могу объяснить, что я знаю.

ebp используется функцией для хранения начального состояния esp во время его потока ссылка на то, где аргументы передаются функции и где находятся ее собственные локальные переменные. Первое, что делает функция, это сохранить статус данного ebp делаешь pushl %ebp, это жизненно важно для функции, которая делает вызов, и чем заменяет его на свою текущую позицию стека esp делаешь movl %esp, %ebp. Обнуление последних 4 бит ebp на данный момент GCC специфичен, я не знаю, почему этот компилятор делает это. Он будет работать, не делая этого. Теперь, наконец, мы идем в бизнес, call ___main, кто _ _ главный? Я не знаю... возможно, больше специальных процедур GCC, и, наконец, единственное, что делает ваш main (), установите возвращаемое значение как 0 с movl , %eax и leave это то же самое, что делать movl %ebp, %esp; popl %ebp восстановить ebp государство, то ret до конца. ret pops eip и продолжить поток потока от этот момент, где бы он ни был (как его основной(), этот ret, вероятно, приводит к некоторой процедуре ядра, которая обрабатывает конец программы).

большинство из них все об управлении стеком. Я написал подробный учебник о том, как используется стек некоторое время назад, было бы полезно объяснить, почему все эти вещи сделаны. Но это на португальском языке...

Comments

    Ничего не найдено.