Как стать автором
Обновить

Комментарии 15

Интересная статья. Только argc у вас будет, конечно же, равно единице, потому как аргументы в программу всё-таки передавались. Нет, я понимаю, что это затуманивает описанные далее танцы, но всё-таки:
test.c
#include <stdio.h>

int main(int argc) {
  printf("%d\n", argc);
}

$ gcc test.c -o test
$ ./test
1
Вы правы. Спасибо за уточнение. Подправили.
Спасибо за статью. Интересно стало как в java гарантировано срабатывает блок finally
Не за что! Самому интересно такие вещи изучать.
Примерно так же, как и деструкторы в современных реализациях C++. Компилятор генерирует специальные таблички, где лежат указатели на код finally-блоков, который следует выполнить в случае, когда разворачивание стека проходит через определённые регионы кода методов.

Java Virtual Machine Specification, 3.13 Compiling finally
Дело в том, что для регистров такого понятия как «порядок байтов» не существует. Иными словами, рассматривая регистр, мы не можем говорить о том, что в нем есть «старшие или младшие адреса».

Можно чуть поподробнее в этом месте? Всегда думал, что в регистрах байты лежат в «нормальной» последовательности (big-endian). Не знаю как 32 битный x86, но некоторые другие процессоры умеют работать с «половинками» регистров. Загружая 16 битное слово в 32 битный регистр, мне кажется, важно понимать, в какой части этого регистра наше слово окажется. Или, например, как быть с циклическим сдвигом?

И еще про порядок байтов: в x86 32 процессорное слово 32 битное? Просто у вас 0xDEADBEAF на памяти выглядит как AFBEADDE, а это как раз зависит от разрядности слова.
Существует понятие «порядка байтов» — порядок хранения байтов многобайтовых чисел в *памяти* компьютера. Я так понимаю, что под памятью здесь понимается именно RAM, а не другие виду «памяти» вроде регистров.

Порядок может быть little-endian (прямой порядок / порядок от младшего к старшему / «остроконечный») или big-endian (обратный порядок / порядок от старшего к младшему / «тупоконечный»).

Посмотрим, к примеру, на определение little-endian порядка. Это порядок, при котором самый младший значащий байт хранится в *ячейке памяти с наименьшим адресом*.

Т.е. получается, что уже в самих определениях «порядка байт» и little-endian содержутся слова, которые не позволяют распространить данное понятие на регистры.

Хорошо, будем считать, что концепция endianess каким-то образом применима к регистрам. Известно, что отладчики отображают значения, хранимые в регистрах, как если бы они там хранились в big-endian формате.

Значит ли это, что они там и впрямь хранятся в big-endian формате?

Попробовал следующее. Это фрагмент программы, программу запустил в gdb.

movl $0x5758595a, %eax
xor %ebx, %ebx
movb %al, %bl

# gdb не позволяет сделать info registers al, поэтому…
# обнуляем %ebx
# копируем содержимое %al в %bl
# cмотрим значение %ebx — там 5a, т.е. 0x5758595a хранится в %eax в том же порядке, в котором отображается отладчиком.

OK. Это мысли вслух. Просветление еще не наступило. Так что комментируйте, поправляйте, если разобрались с этим вопросом.

Вот, быстренько нагуглил rcl(rotate through carry left), и кажется крутить можно не только весь 64битный регистр, но и его части. Соответственно выкрученный бит окажется в carry flag. Как может понятие порядка байт/бит быть неприменимо к регистру? Вращаем вправо и получаем в бите флага самый младший бит… и т.д. — я уже практически уверен, что в регистре биты и байты лежат в правильной последовательности — слева направо, от старших к младшим.

А на счет второй половины вопроса, про слова, поэкспериментировал — выходит x86 умеет в 64, 32 и 16 битные слова. Извиняюсь тут я сглупил, но возможно вам стоило упомянуть чем отличаются два рядом лежащих 16 битных слова от одного 32 битного.
Очень интересная статья. Что-то помню из лекций, а что-то подзабыл. В любом случае в Вашем цикле все более наглядно продемонстрировано.
Но сводной диаграмме не хватает соответствия кодов ассемблера с примером кода на C. Так бы можно было и наглядный постер сделать.
Жаль, что замыкания не вошли
Всё же замыкания — это штука, привязанная к рантайму конкретной реализации языка программирования.

Особой магии там никакой нет. Смотря как компилятор решил хранить замыкания в каждом конкретном случае, до замкнутых переменных добираются или напрямую (всё уже разложено по регистрам заботливой подпрограммой выше по стеку), или через стек (пятое слово три стекфрейма назад), или через кучу (первый аргумент подпрограммы — это указатель на структурку с замкнутыми переменными, где нужная переменная — вторая от начала). Выделение и освобождение замыканий производится образом, соответствующим способу их хранения. Что именно и как включить в замыкание — компилятор знает, ему или явно перечисляют переменные и способы включения, или он сам по областям видимости понимает.
А как получит результат (result по схеме) вызвавший код? ведь при возврате из add() esp будет указывать «выше» результата, т.е любое прерывание может затереть результат. Или я чего-то не понял...(
Результат кладётся на регистры (%rax:%rdx или %xmm0 в зависимости от типа) если он туда влазит. Если не влазит, то результата никакого нету, просто у функции появляется скрытый параметр с указателем на результат.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий