![]() |
|
Модераторы: Daevaorn |
![]()
|
|
| ТарасАтавин |
|
||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 370 Регистрация: 26.8.2013 Репутация: нет Всего: нет |
Переменные, предположим, можно все разместить до подпрограмм и к этапу кодогенерации уже иметь готовые адреса. А как быть с подпрограммами? Если текущая подпрограмма должна вызвать следующую, то откуда может быть известно, по какому адресу её вызывать? Упорядочивать на основе того, какая подпрограмма какую вызывает, не получится из-за конструкций вида
-------------------- Не так всё плохо, как оно есть на самом деле. |
||||
|
|||||
| feodorv |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 11 Всего: 45 |
Есть же декларация функции. Более того, в современных C/C++ она обязательна.
Но даже в C в стиле K&R компилятор отнюдь не запутывался. Применяются специальные техники увязывания реального адреса функции и места её вызова. Ситуация, на самом деле, ещё хуже, подумайте о динамических библиотеках. Подумайте и о том, что исполняемый код файла в создаваемом процессе может быть загружен не по тому адресу, на который рассчитывал компилятор. Но всё разрешается, всё работает))) Добавлено через 5 минут и 54 секунды
Вот такой код в отдельном модуле:
Полагаете, что компилятор уже знает адрес переменной g_Count? Но вот линковщик уже должен знать этот адрес. При чём здесь "до подпрограмм"? Вы, случаем, не ассемблерщик? -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||||
|
|||||||||
| ТарасАтавин |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 370 Регистрация: 26.8.2013 Репутация: нет Всего: нет |
Она и обязательна Но как это связано с размером текущей функции и стартовым адресом следующей? Или предварительная генерация асма обязательна для счёта размера?
Добавлено @ 12:12 Речь шла о внутримодульных адресах и прямой адресации. С межмодульных потом разберусь. Скорей всего с помощью неявного превращения в ссылку, реализованную на указателе. Но для внутримодульных адресов это не подходит. Это сообщение отредактировал(а) ТарасАтавин - 7.9.2013, 12:15 -------------------- Не так всё плохо, как оно есть на самом деле. |
|||
|
||||
| feodorv |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 11 Всего: 45 |
Никак. А как связан порядок следования функций в исходниках с их адресами? Компилятор не обязан располагать коды функций в порядке их следования в исходниках. Он может руководствоваться какими-то своими резонами.
Не обязательна. -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||
|
|||||
| ТарасАтавин |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 370 Регистрация: 26.8.2013 Репутация: нет Всего: нет |
Добавлено через 1 минуту и 33 секунды Тогда как? -------------------- Не так всё плохо, как оно есть на самом деле. |
|||
|
||||
| bems |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 3400 Регистрация: 5.1.2006 Репутация: нет Всего: 88 |
-------------------- Обижено школьников: 8 |
|||
|
||||
| ТарасАтавин |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 370 Регистрация: 26.8.2013 Репутация: нет Всего: нет |
То есть? Вот отложил я часть тела функции, перешёл к следующей, сколько места займёт предыдущая не знаю. По какому адресу начинать следующую?
-------------------- Не так всё плохо, как оно есть на самом деле. |
|||
|
||||
| bems |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 3400 Регистрация: 5.1.2006 Репутация: нет Всего: 88 |
не часть тела, а те символы, определить адрес которых в файле ты пока не можешь. адрес занимает фиксированный размер (пока для простоты не думай про оптимизацию размера адреса), поэтому ты можешь сгенерить переход на символ адрес которого пока неизвестен, а потом вернуться к этому месту и записать туда реальный адрес
-------------------- Обижено школьников: 8 |
|||
|
||||
| feodorv |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 11 Всего: 45 |
Я ничего не придумывал Вот именно.
-------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
| ТарасАтавин |
|
||||||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 370 Регистрация: 26.8.2013 Репутация: нет Всего: нет |
Добавлено через 2 минуты и 32 секунды
-------------------- Не так всё плохо, как оно есть на самом деле. |
||||||||
|
|||||||||
| bems |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 3400 Регистрация: 5.1.2006 Репутация: нет Всего: 88 |
касается, потому что ты должен дать системе таблицу релоков -------------------- Обижено школьников: 8 |
|||
|
||||
| ТарасАтавин |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 370 Регистрация: 26.8.2013 Репутация: нет Всего: нет |
что то новенькое. Вроде в формате были предусмотрены таблицы импорта и экспорта, а теперь ещё и реаллоков добавляется.
Это сообщение отредактировал(а) ТарасАтавин - 7.9.2013, 16:53 -------------------- Не так всё плохо, как оно есть на самом деле. |
|||
|
||||
| bems |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 3400 Регистрация: 5.1.2006 Репутация: нет Всего: 88 |
секция .reloc
Добавлено через 9 минут и 19 секунд
-------------------- Обижено школьников: 8 |
|||
|
||||
| ТарасАтавин |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 370 Регистрация: 26.8.2013 Репутация: нет Всего: нет |
Что это?
Это сообщение отредактировал(а) ТарасАтавин - 7.9.2013, 17:12 -------------------- Не так всё плохо, как оно есть на самом деле. |
|||
|
||||
| bems |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 3400 Регистрация: 5.1.2006 Репутация: нет Всего: 88 |
что именно? "секция .reloc"? ну так часто называется секция РЕ-файла, содержащая таблицу релоков. "Часто" потому что насколько я помню загрузчик не использует имена секций для поиска нужной инфы, а пользуется массивом DataDirectory опционального заголовка. Релоки это это элемент массива за индексом IMAGE_DIRECTORY_ENTRY_BASERELOC
-------------------- Обижено школьников: 8 |
|||
|
||||
| tzirechnoy |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1173 Регистрация: 30.1.2009 Репутация: 2 Всего: 16 |
Да всё примерно такжэ, как и для внешних функцый. Кодогенератор генерит код (call ..., jmp ... и т.д.), а вместо адресов пишэт нули (условно, на разных архитектурах там по-разному можэт быть реализовано). И записывает списки мест, куда поставить адрес (обычно в случае Си -- он всё это, и код и списки мест, пишэт в объектный файл. Расшырение .o. В винде, возможно, .obj). Затем линкер берёт этот объектный файл, плюс другие указанные ему библиотеки объектных файлов (в т.ч. т.н. "динамические"), выдирает из них списки адресов символов, строит дерево зависимостей, складывает весь код из объектных файлов (ну, на самом деле только из "статических" объектных файлов, т.е. не находящихся в динамических библиотеках) в один большой объектный код (обычно -- у себя в памяти), ужэ в этом коде вычисляет получившыеся адреса всех переменных и функцый (кроме динамических, которых там нет), и затем проходит по списку мест, в которые он должэн записать эти адреса, и записывает их.
То есть никакой разницы -- в этом объектном файле, не в этом, сначала весь код, потом все адреса. Результат потом записывает в исполняемый файл (в винде -- .exe, так и буду его кстати дальшэ называть, для краткости). Да, две детали: первая -- функцыи и переменные, объявленные static. Для них всё тожэ самое часто происходит ещё до физической записи в объектный файл. То есть после кодогенератора непосредственно в памяти вызывается такой маленький, тупенький линкер, и записывает все получившыеся внутренние адреса (ну, на самом деле, смещения) в оставленные для адресов места. Вторая деталь -- динамические библиотеки. Для них обычный линкер не выстраивает их код в одну линейку (поскольку при запуске это можэт быть совсем другой код), соотвественно, не записывает его в вывод (.exe ужэ). Когда линкер находит, что такие-то символы берутся из динамических библиотек -- он записывает в свой выходной файл список имён внешних функцый, на которые кто-то внутри ссылается. Ну, почти как в случае генерацыи .o-файла. А вот с адресами в основном файле (том самом .exe, который мы собираем) происходит интересная вещь: дело в том, что если просто оставить нули и понадеяться, что при запуске динамический линкер пропишэт вместо них правильные адреса -- то получится, что при одновременном запуске нескольких копий программы придётся держать в памяти столько копий кода, сколько их было запущено -- ведь код у разных экземпляров станет разным и держать его в одной readonly копии не получится. Поэтому для динамических символов составляется отдельная таблица адресов перехода, которая собирается в одном известном месте (ну, в концэ) объектного кода. И непосредственно в код прописываются адреса ячеек этой таблицы. А ужэ при выполнении динамический линкер загружает эту таблицу в память и прописывает в неё правильные адреса функцый из внешних .dll -- благо эта таблица сильно меньшэ, чем весь выполняемые код данного .exe-шника. Более того, сейчас на самом деле там ещё веселее -- чтобы не перелопачивать при загрузке сразу всю таблицу, пытаясь найти в разных .dll-ах тысячи и десятки тысяч имён, которые, возможно, при этом запуске и не понадобятся -- в ячейки этой таблицы записывается адрес "трамплина", стандартной подпрограммки из пары команд, которая вызывается стандартную функцыю, которая по адресу вызванной функцыи определяет, что было вызвано и находит и записывает в соответствующую ячейку таблицы правильный адрес из .dll. Таким образом, ячейки таблицы заполняются правильными адресами когда они первый раз используются. Как-то так. Но, в общем, со своими адресами никаких проблем нет: в первый проход остаются нули, ко второму проходу компиляторы их знают. |
|||
|
||||
| LeonidPr |
|
||||||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 220 Регистрация: 17.2.2012 Где: г. Чебоксары Репутация: нет Всего: 1 |
Тут много уже понаписали, так что я скорее всего повторюсь. Просто недавно сам столкнулся с похожей проблемой (но в более простом виде). Писал элементарный ассемблер с некоего своего набота мнемоник в свой же байткод (его микроконтроллер потом исполняет). Необходима была поддержка меток в исходном коде (то же что и ваши подпрограммы). Все решилось стандартным (ну... почти стандартным) двухпроходным методом. За первый проход делается разбор строк исходного кода, счетчик инструкций соответственно обновляется после обработки каждой строки на нужное кол-во байт, равное размеру считанной инструкции + размер её операндом. Если встречается метка, то в некий std::map<string, uint32_t> заносится наименование метки и текущий счетчик инструкций, на втором проходе еще раз просматривается исходный код и уже ведется кодогенерация, если у считанной иснструкции операнд - метка, она берется из того map-а и её адрес подставляется в сгенерированный код. возможно вам для начала следует поступить аналогично.
Сначала не заморачиваться, пройти по коду (уже распарсенному, в виде дерева вывода), вычислить адреса меток f1, f2, а потом на этапе кодогенерации подставлять их в аргументы соответствующих call-ов. Только вот как при этом работает оптимизатор - не знаю, мы в свое время в универе не дошли до него, а я как-то не интересовался. --------------------
pkunzip.zip |
||||||
|
|||||||
![]()
|
| Правила форума "С++:Общие вопросы" | |
|
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |