2010-03-01 1 views
24

Итак, этим утром я отправил смущенный вопрос об собрании, и я получил отличную подлинную помощь, которую я очень ценю.Сборка - .data, .code и регистры ...?

И теперь я начинаю собираться в сборку и начинаю понимать, как это работает.

Вещи, которые, как я чувствую, я понимаю, включают в себя стек, прерывания, двоичный/шестнадцатеричный код и вообще то, что делают большинство основных операций (jmp, push, mov и т. Д.).

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

  1. Что именно происходит в секции .data ? Объявляются ли эти переменные?
  2. Если да, можем ли мы объявить переменные позже в разделе кода? Если нет, почему бы и нет? Если да, то как и почему мы используем раздел данных?
  3. Что такое регистр? Как он сравнивается с переменной? Я имею в виду, я знаю, что это место, где хранится небольшая информация ... но это звучит точно как переменная для меня.
  4. Как создать массив? Я знаю, что это кажется случайным, но мне любопытно, как я буду делать что-то подобное.
  5. Есть ли список каких-либо распространенных практик для того, для чего нужно использовать каждый регистр? Я до сих пор не получаю их полностью, но заметил, что некоторые люди говорят, например, что определенный регистр должен использоваться для хранения «возвращаемых значений» из процедур - существует ли исчерпывающий или, по крайней мере, информативный список таких методов?
  6. Одна из причин, по которой я изучаю сборку, - это лучше понять, что происходит за моим кодом высокого уровня. Имея это в виду - когда я программирую на C++, я часто думаю о стеке и куче. В сборке я знаю, что такое стек - где «куча»?

Дополнительная информация: Я использую masm32 с WinAsm в качестве среды IDE, и я работаю над Windows 7. У меня есть много опыта программирования на языках более высокого уровня, таких как C++/java.


Редактировать: Спасибо за помощь всем, чрезвычайно информативным, как обычно! Качественный товар! Еще одна вещь - мне интересно, какая разница между указателем стека и указателем Base, или ESP и EBP. Кто-нибудь может мне помочь?

Редактировать: Я думаю, что получаю это сейчас ... ESP всегда указывает на вершину стека. Однако вы можете указать EBP на все, что захотите. ESP автоматически обрабатывается, но вы можете делать все, что хотите, с помощью EBP. Например:

push 6 
push 5 
push 4 
mov EBP, ESP 
push 3 
push 2 

В этом сценарии, EBP теперь указывает на адрес холдинга 4, но ESP теперь указывает на адрес холдинга 2.

В реальном приложении 6, 5 и 4 можно было бы аргументы функции, тогда как 3 и 2 могут быть локальными переменными внутри этой функции.

+0

Могу я спросить, есть ли у вас книга, а если да, то какая книга может быть? :) –

+0

Нет, извините! Просто интернет. http://stackoverflow.com/questions/2352048/assembly-32-bit-vs-64-bit -> В этой статье я изложил ресурсы, которые я использовал. Я только начал изучать сборку сегодня, поэтому я буду продолжать работать над этим списком, поскольку собираю больше ресурсов. – Cam

+0

Я тоже начинаю изучать сборку. Существует бесплатная pdf-книга под названием «программирование с нуля», которая кажется довольно хорошей. Сейчас это мой главный ресурс. – morgancodes

ответ

30

Давайте попробуем ответить в порядке!

  1. Раздел данных содержит все, что вы хотите, чтобы система автоматически инициализировалась системой до того, как она называет точку входа вашей программы. Вы правы, обычно сюда попадают глобальные переменные.Ноль-инициализированные данные, как правило, не включаются в исполняемый файл, так как нет причин - для создания этого пространства требуется несколько директив загрузчику программ. После запуска вашей программы области ZI и данных, как правило, взаимозаменяемы. Wikipedia имеет намного больше информации.

  2. Переменные на самом деле не существуют при программировании сборки, по крайней мере, не в том смысле, в каком они работают, когда вы пишете код C. Все, что у вас есть, - это решения, которые вы приняли о том, как выложить свою память. Переменные могут быть в стеке, где-то в памяти, или просто жить только в регистрах.

  3. Регистры являются внутренним хранилищем данных процессора. В общем случае вы можете выполнять операции только с значениями в регистрах процессора. Вы можете загружать и хранить их содержимое в памяти и из нее, что является основной операцией работы вашего компьютера. Вот краткий пример. Этот код C:

    int a = 5; 
    int b = 6; 
    int *d = (int *)0x12345678; // assume 0x12345678 is a valid memory pointer 
    *d = a + b; 
    

    мог бы получить переведен на некоторые (упрощенных) сборки вдоль линий:

    load r1, 5 
    load r2, 6 
    load r4, 0x1234568 
    add r3, r1, r2 
    store r4, r3 
    

    В этом случае, вы можете думать о регистрах как переменные, но в целом это не необходимо, чтобы любая одна переменная всегда оставалась в одном регистре; в зависимости от того, насколько сложна ваша рутина, это может быть даже невозможно. Вам нужно будет вытолкнуть некоторые данные в стек, удалить другие данные и т. Д. «Переменная» - это логическая часть данных, а не там, где она живет в памяти или регистры и т. Д.

  4. Массив - это только непрерывный блок памяти - для локального массива вы можете просто уменьшить указатель стека соответствующим образом , Для глобального массива вы можете объявить этот блок в разделе данных.

  5. Существует множество соглашений о регистрах - проверьте ABI вашей организации или документ соглашения о вызове, чтобы узнать, как правильно их использовать. У вашей ассемблерной документации также может быть информация. Проверьте ABI article on wikipedia.

  6. Ваша программа сборки может выполнять те же системные вызовы, что и любая программа на C, поэтому вы можете просто позвонить malloc(), чтобы получить память из кучи.

+0

+1 Еще раз спасибо Карл. Очень полезно! Особенно полезен пример на 3. Я думаю, когда это действительно сходит на нет, сборка должна отличаться от C, иначе это будет сама C. Для # 4 ... Я думал, что стек сначала был первым. Однако, я думаю, вы можете читать из стека, не нажав/не нажав? Кроме того, я нашел это, что кажется полезным и актуальным: http://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames – Cam

+2

Да, вы можете читать из стека без push/pop: вам просто нужен указатель стека и смещение. В x86 asm указатель стека содержится в регистре esp, поэтому вы можете использовать esp + offset (на самом деле, если вы разобраете приложение C, вы увидите, что этот метод используется для доступа к «локальным» переменным в вызове функции). – slugster

+1

@incrediman, Стек находится только в памяти вместе со всем остальным, поэтому вы можете получить к нему доступ, как хотите. Это всего лишь стек в смысле использования его для хранения контекста выполнения для вызовов функций и т. Д. –

14

Я хотел бы добавить к этому. Программы на компьютере обычно разделяются на три раздела, хотя есть и другие.

сегмента кода - .code, .text: http://en.wikipedia.org/wiki/Code_segment

В вычислениях, сегмент кода, также известный как текстового сегмент или просто как текста, фраза используется для обозначения части памяти или объектного файла , который содержит исполняемые инструкции. Он имеет фиксированный размер и обычно только для чтения. Если текстовая секция не является только для чтения, то конкретная архитектура позволяет самомодифицировать код . Код только для чтения является реентерабельным, если может выполняться одновременно более чем одним процессом . В качестве области памяти сегмент кода находится в нижних частях памяти или на ее самом внизу, чтобы предотвратить переполнение стека и переполнение стека из-за его перезаписи.Сегмент

данных - .data: http://en.wikipedia.org/wiki/Data_segment

Сегмент данных является одной из секций программы в объектный файл или в памяти, которая содержит глобальные переменные и статические переменные, инициализируются программистом. Он имеет фиксированный размер, так как все данные в этом разделе устанавливаются программистом перед загрузкой программы . Тем не менее, он не доступен только для чтения, , так как значения переменных могут быть изменены во время выполнения . Это находится в , в отличие от данных Rodata (постоянный, только для чтения), а также сегмент кода (также известный как текст сегмент).

ПБС: http://en.wikipedia.org/wiki/.bss

В компьютерном программировании, .bss или ПБС (который первоначально обозначал блок , созданные Symbol) используется многими составителей и линкеры, как имя части сегмента данных, содержащего статические переменные и глобальные переменные , которые сначала заполняются нулевыми значениями (т. е. , когда начинается выполнение). Часто называют «bss-секцией» или «сегмент bss». Программный загрузчик инициализирует память, выделенную для bss-секции, когда он загружает программу .

Регистры являются, как описано другими, средствами ЦП для хранения данных или адреса памяти. Операции выполняются на регистрах, таких как add eax, ebx и в зависимости от диалекта собрания, что означает разные вещи. В этом случае это означает добавление содержимого ebx в eax и сохранение его в eax (синтаксис NASM). Эквивалент в GNU AS (AT & T): movl $ebx, $eax. Различные диалекты сборки имеют разные правила и операторы. По этой причине я не поклонник MASM - он очень отличается от NASM, YASM и GNU AS.

На самом деле нет никакого общего взаимодействия с C. ABI, чтобы определить, как это происходит; например, на x86 (unix) вы найдете аргументы метода, вставленные в стек, тогда как в x86-64 в Unix первые несколько аргументов будут помещены в регистры. Оба ABI ожидают, что результат функции будет сохранен в регистре eax/rax.

Вот 32-разрядная процедура добавления, которая собирается как для Windows, так и для Linux.

_Add 
    push ebp    ; create stack frame 
    mov  ebp, esp 
    mov  eax, [ebp+8] ; grab the first argument 
    mov  ecx, [ebp+12] ; grab the second argument 
    add  eax, ecx  ; sum the arguments 
    pop  ebp    ; restore the base pointer 
    ret 

Здесь вы можете увидеть, что я имею в виду. Значение «return» найдено в eax. В отличие от этого, версия x64 будет выглядеть так:

_Add 
    push rbp    ; create stack frame 
    mov  rbp, rsp 
    mov  eax, edi  ; grab the first argument 
    mov  ecx, esi  ; grab the second argument 
    add  eax, ecx  ; sum the arguments 
    pop  rbp    ; restore the base pointer 
    ret 

Есть документы, которые определяют такие вещи. Вот UNIX x64 ABI: http://www.x86-64.org/documentation/abi-0.99.pdf. Я уверен, что вы, вероятно, найдете ABI для любого процессора, платформы и т. Д., Которые вам нужны.

Как вы работаете с массивом в сборке?Арифметика указателя. Учитывая базовый адрес в eax, следующее сохраненное целое будет иметь значение [eax+4], если целое число составляет 4 байта. Вы можете создать это пространство, используя вызовы до malloc/calloc, или вы вызываете системный вызов выделения памяти, независимо от того, что находится в вашей системе.

Что такое «куча»? Согласно wikipedia снова, это область памяти, зарезервированная для динамического распределения памяти. Вы не видите его в своей сборке, пока не вызовете вызов calloc, malloc или системный вызов выделения памяти, но он есть.

Извините за эссе.

+0

+1, очень полезно, спасибо. – Cam

+0

+1 для связывания ABI. –