Проблема
В пулл реквесте #363 @cab404 обнаружил, что утилита strip на Linux портит исполнимые файлы. Pull request посвящён создания пакета Рефала-5λ в пакетном менеджере Nix.
Действительно, исполнимый файл состоит из интерпретатора и приписанного к нему исполняемого кода, причём интерпретируемый код должен начинаться со смещения, кратного 4096, т.е. устройство подобно устройству SFX-архива.
Команда strip не подозревает об этой структуре исполнимого файла и из префикса-интерпретатора не только удаляет секции с отладочной информацией, но и просто дропает весь хвост с интерпретируемым кодом.
Обрезание осуществляется в процессе подготовки пакета соответствующими утилитами.
Обходной путь
@cab404 добавил в скрипт сборки опцию, запрещающую strip’ать исполнимые файлы:
https://github.com/bmstu-iu9/refal-5-lambda/compare/fa1f8f862a73e7ca6194f67cd146ca158919d70e..ad842a440995f997cc04a014a46daa7c1922bbac
Требования к решению и другие размышления
Ожидаемое поведение должно быть таким:
$ 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
Утилита отделяет префикс от интерпретируемого кода, стрипает его, возвращает интерпретируемый код на место.
Решение является частным по той же причине, что и предыдущее.
Нормальное решение
Пока не знаю.
Проблема
В пулл реквесте #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
Требования к решению и другие размышления
Ожидаемое поведение должно быть таким:
Если реализовать новое API для нативных функций как #324, то сборка в режиме прямой кодогенерации (
rlc --scratch -Od program.ref) обеспечит ожидаемое поведение, т.к. в этом режиме программы не будут содержать интерпретируемого кода. Это неплохо, но это не решает проблему программ, скомпилированных в режиме интерпретации.Актуальная реализация режима интерпретации (префикс-интерпретатор + интерпретируемый код) имеет следующие преимущества (см. также #48):
Она почти одинаково работает на всех поддерживаемых платформах (Windows, Linux, macOS):
--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Утилита отделяет префикс от интерпретируемого кода, стрипает его, возвращает интерпретируемый код на место.
Решение является частным по той же причине, что и предыдущее.
Нормальное решение
Пока не знаю.