Подтвердить что ты не робот

Сборка -.data,.code и регистры...?

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

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

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

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

  • Что именно происходит в разделе .data? Объявляются ли эти переменные?
  • Если да, можем ли мы объявить переменные позже в разделе кода? Если нет, почему бы и нет? Если да, как и почему мы используем раздел данных, то?
  • Какой регистр? Как он сравнивается с переменной? Я имею в виду, что я знаю это место, где хранится небольшая часть информации... но это звучит точно как переменная для меня.
  • Как создать массив? Я знаю, что это кажется случайным, но мне любопытно, как я буду делать что-то вроде этого.
  • Есть ли список каких-то общих практик для того, для чего нужно использовать каждый регистр? Я до сих пор не получаю их полностью, но заметил, что некоторые люди говорят, например, что определенный регистр должен использоваться для хранения "возвращаемых значений" из процедур - существует ли исчерпывающий или, по крайней мере, информативный список таких методов?
  • Одна из причин, по которой я изучаю сборку, - это лучше понять, что происходит за моим кодом высокого уровня. Имея это в виду - когда я программирую на С++, я часто думаю о стеке и куче. В сборке я знаю, что такое стек - где "куча"?

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


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

edit: Я думаю, что получаю это сейчас... 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 могут быть локальными переменными внутри этой функции.

4b9b3361

Ответ 1

Попробуйте ответить в порядке!

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

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

  • Регистры - это внутреннее хранилище данных процессора. В общем случае вы можете выполнять операции только с значениями в регистрах процессора. Вы можете загружать и хранить их содержимое в памяти и из нее, что является основной операцией работы вашего компьютера. Вот краткий пример. Этот код 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
    

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

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

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

Ответ 2

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

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

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

Сегмент данных -.data: http://en.wikipedia.org/wiki/Data_segment

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

BSS: http://en.wikipedia.org/wiki/.bss

В компьютерном программировании .bss или bss (который первоначально стоял за Block Автор 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 или системный вызов выделения памяти, но он есть.

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