Skip to content

strip портит exe-шники #364

@Mazdaywik

Description

@Mazdaywik

Проблема

В пулл реквесте #363 @cab404 обнаружил, что утилита strip на Linux портит исполнимые файлы. Pull request посвящён создания пакета Рефала-5λ в пакетном менеджере Nix.

Действительно, исполнимый файл состоит из интерпретатора и приписанного к нему исполняемого кода, причём интерпретируемый код должен начинаться со смещения, кратного 4096, т.е. устройство подобно устройству SFX-архива.

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

Обрезание осуществляется в процессе подготовки пакета соответствующими утилитами.

Обходной путь

@cab404 добавил в скрипт сборки опцию, запрещающую strip’ать исполнимые файлы:

https://github.com/bmstu-iu9/refal-5-lambda/compare/fa1f8f862a73e7ca6194f67cd146ca158919d70e..ad842a440995f997cc04a014a46daa7c1922bbac

refal-5-lambda/flake.nix

Lines 17 to 19 in ad842a4

dontStrip = true;

Требования к решению и другие размышления

Ожидаемое поведение должно быть таким:

$ rlc program.ref
* Compiling program.ref:
** Compilation suceeded **
$ ./program
Hello!
$ strip program
$ ./program
Hello!

Если реализовать новое API для нативных функций как #324, то сборка в режиме прямой кодогенерации (rlc --scratch -Od program.ref) обеспечит ожидаемое поведение, т.к. в этом режиме программы не будут содержать интерпретируемого кода. Это неплохо, но это не решает проблему программ, скомпилированных в режиме интерпретации.

Актуальная реализация режима интерпретации (префикс-интерпретатор + интерпретируемый код) имеет следующие преимущества (см. также #48):

  • Она почти одинаково работает на всех поддерживаемых платформах (Windows, Linux, macOS):

    • Компиляция:
      • вызываем компилятор C++ (можно встроить почти любой),
      • к полученному exe’шнику добавляем в конец байты выравнивания для кратности 4096,
      • приписываем исполнимый код,
      • вызываем команду, заданную в параметре командной строки --chmod-x-command
    • Запуск:
      • узнаём имя исполнимого файла обращением к ОС,²
      • открываем файл при помощи стандартного fopen(), ищем в нём сигнатуру и загружаем интерпретируемый код.

    Есть только две платформоспецифичности:

    • ¹ --chmod-x-command устанавливается скриптом на Bash в chmod +x, bat-скриптом в пустую строку.
    • ² Вызывать fopen(argv[0], "rb") нельзя, т.к. argv[0] на unix-подобных ОС просто содержит нулевой аргумент: если программа лежит в PATH, то в argv[0] будет лежать имя программы без пути; при запуске под отладчиком argv[0] может быть и пустой строкой. Поэтому приходится вызывать ту или иную API-функцию (абстрагированную как refalrts::api::get_main_module_name()) для определения пути к запущенному файлу.
  • Компиляция в режиме интерпретации не требует компилятора C++. Дистрибутив может содержать заранее скомпилированные интерпретаторы-префиксы, к которым Рефал-5λ приписывает интерпретируемый код.

  • Исполнимые файлы полностью автономные — не требуют наличия на машине ни интерпретатора, ни рантайма.

  • И при этом реализация относительно проста.

Описанные выше преимущества хотелось бы сохранить. Особенно второе — независимость уже собранного компилятора Рефала-5λ от наличия компилятора C++ и третье — автономность исполнимых файлов.

Частные решения

Добавление опции --strip к rlc

Можно добавить опции --strip и --strip-command=… к компилятору. Вторая опция задаёт имя утилиты strip (непустое на unix-like или при использовании MinGW GCC, пустое в остальных случаях), первая включает эту команду после создания префикса и до записывания интерпретируемого кода (если --strip-command=… пусто, то ничего не делает).

Так можно стрипать исполнимые файлы в процессе сборки.

Решение является частным, т.к. не решает проблему с вызовом внешней strip. В частности, решение ничего не даёт при использовании внешнего сценария вроде создания пакета пакетным менеджером (сценарий из #363).

Добавление утилиты rl-strip

Утилита отделяет префикс от интерпретируемого кода, стрипает его, возвращает интерпретируемый код на место.

Решение является частным по той же причине, что и предыдущее.

Нормальное решение

Пока не знаю.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions