ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Четверг
18 апреля
904931 Топик полностью
fk0, легенда (17.02.2019 23:22, просмотров: 313) ответил Aleksey_75 на ) я понял что сморозил )) по сути нужны подгружаемые функции, люто криво сформулировал
Если адрес заранее не известен, то всё таки напрашивается PIC (positional independent code). Не всякий компилятор умеет. Большие и для больших процессоров -- умеют (gcc для ARM, MIPS), ибо нужно для динамических библиотек в ОС. Мелкие AVR/PIC18/PIC24 -- скорей нет. Если компилятор не умеет PIC-код, то не всё потеряно, можно релоцировать код на новый адрес после загрузки. Это относительно легко сделать, если загружается ELF -- в нём есть таблицы релокаций для этого. Но типов релокаций (разные ассемблерные инструкции, разные комбинации) может быть много, тяжело сделать свой релокатор (поэтому лучше PIC-код). Если не ELF, а какой-то формат не предусматривающий релокаций, то в принципе можно генерировать код для двух разных адресов (чтоб отличался первый и второй байт адреса обязательно) и делать самому таблицу релокаций на основе бинарного диффа. Не факт что заработает, не для всякой архитектуры и компилятора подходит, но на худой конец тоже метод. Одного кода мало, он же другие функции вызывать может, а откуда он узнает их адреса? Будет переменные использовать, опять же нужны адреса. Значит нужно как-то слинковать одно с другим после загрузки... в общем-то это одна из важнейших функций ОС. Если нужно сделать вручную, то самое простое использовать две вещи, или три... В самом тяжёлом случае, когда поддержки компилятора нет, ОС нет, я бы рекомендовал: 1) забыть о глобальных переменных вообще, их сложно сделать (ровно так и поступил микрософт с DLL, там нет честных разделяемых разными DLL/EXE переменных, верней есть, но через указатели только и через таблицу), заменить их функциями-акцессорами (get/set), а то иначе для доступа придёся либо специальный код генерировать (как gcc делает), либо руками не забывать звёздочку дописывать и правильно их декларировать (как указатели, как когда-то давно у микрософта было, сейчас тоже генерируют более другой код), кроме того придётся сделать GOT-таблицу... 2) все внешние функции в загружаемом объекте вызывать через трамплины в своём коде (чтоб не резолвить каждый раз настоящий адрес функции прямо в коде), трамплин адрес достаёт из таблицы по номеру (функции, вычисляемой из адреса самого трамплина) и переходит туда; 3) нужна таблица импортируемых модулей, список адресов таблиц из пункта 4 для всех модулей чьи функции импортируются, этой таблицей пользуются трамплины; 4) нужна таблица экспортируемых функций, просто список адресов функций (в порядке их номеров), чтоб её видели другие модули; 5) списки экспортируемых функций в будущем могут только добавляться, но не редактироваться как угодно (вообще советую почитать как COM у микрософта работает -- сильно помогает). Сложно, но реализуемо руками. На самом деле нужно сделать библиотеку-загрузчик и будет не так сложно. Если есть поддержка компилятора в виде возможности генерирования PIC-кода, то всё несколько упрощается. По крайней мере в случае с GCC -- все внешние символы он адресует через GOT-таблицу, соответственно при загрузке модуля нужно релоцировать саму GOT-таблицу (внутренние функции, переменные), пробежаться по таблице динамических функций и для всех внешних функций их зарезолвить (найти в таблице символов других загруженных модулей) и поправить адреса в GOT-таблице опять же. В случае с ELF в общем можно сделать свой загрузчик, не так уж и сложно, я бы сказал даже проще, чем делать аналог COM'а руками и функции будут не по номерам, а по именам. Да, в случае с PIC-кодом обычно подразумевается *фиксированное* расстояние между .text и .data/.bss, в том смысле, что базовый адрес загрузки поменять легко, а вот это расстояние -- никак, оно захардкожено (в MIPS в сегменте .text лежат инструкции, которые адрес GOT-таблицы вычисляют в прологе из адреса текущей функции, переданной в t9, поэтому фиксировано, в ARM же используют PC-relative адресацию и тоже получается фиксировано). В случае полноценной релокации (при наличии чего-то похожего на ELF-файл) с этом проблем нет, при использовании метода с компиляцией по двум разным адресам проблема опять есть (там ж непонятно, смещение к коду прибавляется или к данным, поэтому весь код и данные можно сместить, но относительные дистанции все останутся те же). Из сказанного следствие: если планируется загрузка кода в ОЗУ то нет проблем, если код в flash, а .data в озу, то проблемы есть и нужна скорей полноценная релокация. Либо в программах в принципе отказаться от .data и .bss секций (от статических и глобальных переменных). Всё адресовать относительно какого-то указателя изначально переданного в программу (тяжело так, не реально). Ещё как вариант, некоторые компиляторы (gcc для m68000) позволяют адресовать данные относительно выделенного регистра -- тогда расстояние между .text и .data может быть какое угодно. Подразумевается, что этот регистр, например, всегда указывает на GOT-таблицу, а в ней уже настоящие адреса, смещённые при загрузке.
[ZX]