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

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

Спасибо за статью. Я не очень понимаю волшебство линковщика. Вы говорите, что

Он ещё не знает величину будущего смещения, так что ничего сделать не может.

На каком этапе он её узнаёт? Ведь без этого знания ELF не получится.

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

Я немного не понимаю, почему тогда потребовался патч к LLVM, тут никак скриптом линковщика не обойтись?

И такая идея у нас была. Действительно, если в ELF сохранить информацию обо всех символах, то можно узнать многое.

Но дело в том, что наш подход концептуально позволяет выполнять очень широкий набор операций. Мы фактически расставляем метки и группируем их по сортам.

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

Кроме того, исправление компилятора не было для нас проблемой, так что мы решили пройти по сложному, но многообещающему пути.

В итоге сделали патч (ака пуллреквест в апстрим) или плагином к компилятору?

Мы внесли изменения для платформы ARM. То есть, сделали патч. Но как я уже писал в ветке ниже - пока что всё делали локально.

А патчи в LLVM вы отправляли, или свой форк поддерживаете для этого?

Пока свой. Считаем, что для отправки это сыровато. Вот когда проведём исчерпывающее тестирование и Заказчик погоняет на большом наборе боевых данных, тогда... Но идея уже достойна публикации, поэтому и описали. Потом детали забудутся.

К сожалению, в системах Cortex M такой путь напрямую невозможен. Программа привязана к абсолютным адресам и не может исполняться в произвольном месте.

Во первых, Cortex-M широкое понятие. Да ядро такого механизма не предоставляет, но такой механизм может присутствовать у отдельных MCU,может называться, например, "Memory remapping", https://www.st.com/resource/en/application_note/dm00230416-onthefly-firmware-update-for-dual-bank-stm32-microcontrollers-stmicroelectronics.pdf

Второе, есть такая штука как PIC(Position-independent code) и многие компиляторы в том числе LLVM эту фишку поддерживают, было бы интересно увидеть сравнение вашей реализации и PIC механизма, понятно что размер прошивки в PIC будет больше но на насколько? Хотя бы для вашего практического случая...

По первому пункту, всё просто. У Заказчика был свой список контроллеров, и менять он его не хотел. И за решение своей проблемы оплачивал работы. Поэтому замечательно, что производители тоже думают над решением этой проблемы, но Заказчику требовалось решить на его технике. И у нас есть разрешение опубликовать результаты исследований.

По второму - всё тоже не сильно сложно. Мы на тестовых примерах разобрались с этой самой таблицей GOT, на очередном совещании с Заказчиком показали всё на эту тему, получили чёткое указание больше в этом направлении не работать... И, собственно, не работали. Так что боевых метрик мы не снимали.

Без конкретики, сама идеология в начале статьи расписана. При этом было бы полбеды, если бы выросла прошивка. Когда прошивка растёт - данные в ОЗУ сдвигаются. А когда они не сдвигаются - растёт не прошивка, а требования к ОЗУ. А его мало. Это - то, что успели выяснить до того, как нам сказали идти другим путём.

В самом формате ELF «из коробки» предусмотрены (и отлично документированы) relocations. Добавляете компоновщику ключ -q (это для GNU ld, насчёт clang не уверен) и в выходном файле появляются соответствующие секции. А дальше либо вытаскиваете их для своих нужд (readelf -r), либо, если объём RAM позволяет, накатываете их уже на целевой платформе.

Но конкретно для случая системы с чередованием рабочей/обновляемой зон я бы просто собирал образы под оба начальных адреса и при обновлении давал понять который нужен - дёшево и надёжно.

А ведь и правда! Мы чего-то увлеклись. Просто исходно Заказчик хотел, чтобы код был перемещаемым от рождения. Ключики не давали правильного эффекта, поэтому мы начали менять генератор кода. И сначала всё было даже замечательно. Дальше Заказчик стал присылать всё более и более заковыристые конструкции... Добавляемый для этого код, распознающий каждый раз диапазон, стал всё более и более убойным. Вставляемых инструкций при вычислениях средней сложности становилось больше, чем рабочих!

И вот тут Заказчик сдался. Он согласился на то, чтобы код был не честно перемещаемым, а готовился перед прошивкой (кстати, абдейт прошивки и два банка - только одна красивая иллюстрация, по ТЗ он должен работать где угодно). Но к тому времени, настроение было такое (и команда проекта этому соответствовала), что надо продолжать править компилятор. Вот и понеслось. Что, собственно, и получилось.

Подкупало и то, что получившийся метод сохранял CLANGовскую возможность использования команд movt/movw для загрузки 32 битных адресов, что эффективнее, чем GCCшный вариант... Хотя, лично я признаю их эффективность, но не считаю её повышение уж слишком высоким. Но и правку делал не я. Я к тому времени уже был на другом проекте. Есть у получившегося метода и другие потенциальные плюсы, но в действующее ТЗ они всё равно не входили.

Спасибо, что сняли шоры с наших глаз. Сегодня было много проверок в GCC с Вашими ключиками. Пробовали разный тестовый код и разные уровни оптимизации, приговаривая: "Ну тут-то не поможет". Пока помогло везде... Склоняемся к тому, чтобы всё переделать...

Посмотрел документацию clang, там тот же самый ключ -q (он же --emit-relocs).
Relocations вообще неотъемлемая часть "больших" компиляторов, ими пользуется и компоновщик, "склеивая" вместе объектные файлы (те по сути — ELFы, собранные под одни и те же адреса, но с relocations компоновщик может их свободно сдвигать), и динамические загрузчики исполняемых файлов в "больших" ОС (например, исполняемый файл ссылается на две динамические библиотеки, собранные под одинаковые адреса. Загрузчик перебазирует их и никто ничего не заметит).

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории