| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Общие вопросы > Кодогенерация и адреса подпрограмм |
| Автор: ТарасАтавин 7.9.2013, 07:59 | ||||
Переменные, предположим, можно все разместить до подпрограмм и к этапу кодогенерации уже иметь готовые адреса. А как быть с подпрограммами? Если текущая подпрограмма должна вызвать следующую, то откуда может быть известно, по какому адресу её вызывать? Упорядочивать на основе того, какая подпрограмма какую вызывает, не получится из-за конструкций вида
|
| Автор: ТарасАтавин 7.9.2013, 12:09 |
| Она и обязательна Но как это связано с размером текущей функции и стартовым адресом следующей? Или предварительная генерация асма обязательна для счёта размера? Добавлено @ 12:12 Речь шла о внутримодульных адресах и прямой адресации. С межмодульных потом разберусь. Скорей всего с помощью неявного превращения в ссылку, реализованную на указателе. Но для внутримодульных адресов это не подходит. |
| Автор: feodorv 7.9.2013, 12:27 | ||||
Никак. А как связан порядок следования функций в исходниках с их адресами? Компилятор не обязан располагать коды функций в порядке их следования в исходниках. Он может руководствоваться какими-то своими резонами.
Не обязательна. |
| Автор: ТарасАтавин 7.9.2013, 12:40 | ||
Добавлено через 1 минуту и 33 секунды Тогда как? |
| Автор: bems 7.9.2013, 12:59 |
| как насчет отложить часть символов на потом? а вообще конечно нужно читать. например http://ru.wikipedia.org/wiki/%D0%9A%D0%BD%D0%B8%D0%B3%D0%B0_%D0%B4%D1%80%D0%B0%D0%BA%D0%BE%D0%BD%D0%B0_%28%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%82%D0%BE%D1%80%D1%8B%29 |
| Автор: ТарасАтавин 7.9.2013, 13:01 |
| То есть? Вот отложил я часть тела функции, перешёл к следующей, сколько места займёт предыдущая не знаю. По какому адресу начинать следующую? |
| Автор: bems 7.9.2013, 13:05 |
| не часть тела, а те символы, определить адрес которых в файле ты пока не можешь. адрес занимает фиксированный размер (пока для простоты не думай про оптимизацию размера адреса), поэтому ты можешь сгенерить переход на символ адрес которого пока неизвестен, а потом вернуться к этому месту и записать туда реальный адрес |
| Автор: feodorv 7.9.2013, 13:22 | ||
Я ничего не придумывал Вот именно.
|
| Автор: ТарасАтавин 7.9.2013, 16:48 | ||||
Добавлено через 2 минуты и 32 секунды Это как раз понятно. А где его искать потом, чтоб вставить? Кстати, к go to вперёд это тоже относится. |
| Автор: bems 7.9.2013, 16:51 | ||
|
| Автор: ТарасАтавин 7.9.2013, 16:53 |
| что то новенькое. Вроде в формате были предусмотрены таблицы импорта и экспорта, а теперь ещё и реаллоков добавляется. |
| Автор: bems 7.9.2013, 16:58 | ||
| секция .reloc Добавлено через 9 минут и 19 секунд
|
| Автор: ТарасАтавин 7.9.2013, 17:11 |
| Что это? |
| Автор: bems 7.9.2013, 17:16 |
| что именно? "секция .reloc"? ну так часто называется секция РЕ-файла, содержащая таблицу релоков. "Часто" потому что насколько я помню загрузчик не использует имена секций для поиска нужной инфы, а пользуется массивом DataDirectory опционального заголовка. Релоки это это элемент массива за индексом IMAGE_DIRECTORY_ENTRY_BASERELOC |
| Автор: tzirechnoy 8.9.2013, 01:28 |
| Да всё примерно такжэ, как и для внешних функцый. Кодогенератор генерит код (call ..., jmp ... и т.д.), а вместо адресов пишэт нули (условно, на разных архитектурах там по-разному можэт быть реализовано). И записывает списки мест, куда поставить адрес (обычно в случае Си -- он всё это, и код и списки мест, пишэт в объектный файл. Расшырение .o. В винде, возможно, .obj). Затем линкер берёт этот объектный файл, плюс другие указанные ему библиотеки объектных файлов (в т.ч. т.н. "динамические"), выдирает из них списки адресов символов, строит дерево зависимостей, складывает весь код из объектных файлов (ну, на самом деле только из "статических" объектных файлов, т.е. не находящихся в динамических библиотеках) в один большой объектный код (обычно -- у себя в памяти), ужэ в этом коде вычисляет получившыеся адреса всех переменных и функцый (кроме динамических, которых там нет), и затем проходит по списку мест, в которые он должэн записать эти адреса, и записывает их. То есть никакой разницы -- в этом объектном файле, не в этом, сначала весь код, потом все адреса. Результат потом записывает в исполняемый файл (в винде -- .exe, так и буду его кстати дальшэ называть, для краткости). Да, две детали: первая -- функцыи и переменные, объявленные static. Для них всё тожэ самое часто происходит ещё до физической записи в объектный файл. То есть после кодогенератора непосредственно в памяти вызывается такой маленький, тупенький линкер, и записывает все получившыеся внутренние адреса (ну, на самом деле, смещения) в оставленные для адресов места. Вторая деталь -- динамические библиотеки. Для них обычный линкер не выстраивает их код в одну линейку (поскольку при запуске это можэт быть совсем другой код), соотвественно, не записывает его в вывод (.exe ужэ). Когда линкер находит, что такие-то символы берутся из динамических библиотек -- он записывает в свой выходной файл список имён внешних функцый, на которые кто-то внутри ссылается. Ну, почти как в случае генерацыи .o-файла. А вот с адресами в основном файле (том самом .exe, который мы собираем) происходит интересная вещь: дело в том, что если просто оставить нули и понадеяться, что при запуске динамический линкер пропишэт вместо них правильные адреса -- то получится, что при одновременном запуске нескольких копий программы придётся держать в памяти столько копий кода, сколько их было запущено -- ведь код у разных экземпляров станет разным и держать его в одной readonly копии не получится. Поэтому для динамических символов составляется отдельная таблица адресов перехода, которая собирается в одном известном месте (ну, в концэ) объектного кода. И непосредственно в код прописываются адреса ячеек этой таблицы. А ужэ при выполнении динамический линкер загружает эту таблицу в память и прописывает в неё правильные адреса функцый из внешних .dll -- благо эта таблица сильно меньшэ, чем весь выполняемые код данного .exe-шника. Более того, сейчас на самом деле там ещё веселее -- чтобы не перелопачивать при загрузке сразу всю таблицу, пытаясь найти в разных .dll-ах тысячи и десятки тысяч имён, которые, возможно, при этом запуске и не понадобятся -- в ячейки этой таблицы записывается адрес "трамплина", стандартной подпрограммки из пары команд, которая вызывается стандартную функцыю, которая по адресу вызванной функцыи определяет, что было вызвано и находит и записывает в соответствующую ячейку таблицы правильный адрес из .dll. Таким образом, ячейки таблицы заполняются правильными адресами когда они первый раз используются. Как-то так. Но, в общем, со своими адресами никаких проблем нет: в первый проход остаются нули, ко второму проходу компиляторы их знают. |
| Автор: LeonidPr 9.9.2013, 08:42 | ||||||
Тут много уже понаписали, так что я скорее всего повторюсь. Просто недавно сам столкнулся с похожей проблемой (но в более простом виде). Писал элементарный ассемблер с некоего своего набота мнемоник в свой же байткод (его микроконтроллер потом исполняет). Необходима была поддержка меток в исходном коде (то же что и ваши подпрограммы). Все решилось стандартным (ну... почти стандартным) двухпроходным методом. За первый проход делается разбор строк исходного кода, счетчик инструкций соответственно обновляется после обработки каждой строки на нужное кол-во байт, равное размеру считанной инструкции + размер её операндом. Если встречается метка, то в некий std::map<string, uint32_t> заносится наименование метки и текущий счетчик инструкций, на втором проходе еще раз просматривается исходный код и уже ведется кодогенерация, если у считанной иснструкции операнд - метка, она берется из того map-а и её адрес подставляется в сгенерированный код. возможно вам для начала следует поступить аналогично.
Сначала не заморачиваться, пройти по коду (уже распарсенному, в виде дерева вывода), вычислить адреса меток f1, f2, а потом на этапе кодогенерации подставлять их в аргументы соответствующих call-ов. Только вот как при этом работает оптимизатор - не знаю, мы в свое время в универе не дошли до него, а я как-то не интересовался. |