длопен по памяти?

Ищу способ загрузить сгенерированный объектный код прямо из памяти.

Я понимаю, что если я запишу его в файл, я могу вызвать dlopen, чтобы динамически загружать его символы и связывать их. Однако это кажется немного окольным путем, учитывая, что он начинается в памяти, записывается на диск, а затем перезагружается в память с помощью dlopen. Мне интересно, есть ли способ динамически связать объектный код, существующий в памяти. Насколько я могу судить, это может быть несколько разных способов:

  1. Уловка заключается в том, что вы думаете, что ваша ячейка памяти - это файл, даже если он никогда не покидает память.

  2. Найдите какой-нибудь другой системный вызов, который выполняет то, что я ищу (я не думаю, что он существует).

  3. Найдите какую-нибудь библиотеку динамического связывания, которая может связывать код прямо в памяти. Очевидно, это немного сложно найти в Google, поскольку «библиотека динамического связывания» предоставляет информацию о том, как динамически связывать библиотеки, а не о библиотеках, которые выполняют задачу динамического связывания.

  4. Абстрагируйте некоторый API от компоновщика и создайте новую библиотеку из его кодовой базы. (очевидно, для меня это наименее желательный вариант).

Итак, какие из них возможны? достижимый? Не могли бы вы указать мне на какие-то вещи, о существовании которых я предполагал? Есть ли другой способ, о котором я даже не думал?


person Jeremy Salwen    schedule 19.02.2011    source источник


Ответы (4)


Не существует стандартного способа сделать это, кроме записи файла и его повторной загрузки с помощью dlopen().

Вы можете найти альтернативный метод на вашей текущей платформе. Вам решать, лучше ли это, чем использование «стандартного и (относительно) портативного» подхода.

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

person Jonathan Leffler    schedule 19.02.2011
comment
Пайп тоже считается файловым десктриптором? Так разве вам не нравится ... передать это в dlopen ()? - person imacake; 31.03.2012
comment
@imacake - это файловый дескриптор, но не тот, который вы можете искать или mmap. - person Flexo; 15.12.2012
comment
Не существует стандартного способа сделать это, кроме записи файла с последующей его загрузкой. Следует исправить что-то вроде: «Вы можете записать файл и загрузить его, см. Ответ R ..». - person Simon; 14.04.2014
comment
@Simon: Если загружаемый код не требует вызова каких-либо других функций (он полностью самодостаточен), вы можете использовать mmap() напрямую, и он, вероятно, будет работать. Если загружаемый код вызывает другие функции, вам необходимо разрешить адреса этих символов тем или иным способом. Обычно dlopen() это делает за вас. Если вы замыкаете dlopen(), то ответственность лежит на вас как на создателе кода, чтобы убедиться, что вы приняли во внимание ASLR, например, и имеете правильные адреса функций в правильных местах в коде. - person Jonathan Leffler; 14.04.2014
comment
Спасибо за информацию об ASLR, я не знал об этом. Но я думаю, у вас все равно должна быть возможность связывать библиотеки, если вы загрузите их самостоятельно? - person Simon; 17.04.2014
comment
Небольшая ошибка, о которой нужно знать: в Linux я обнаружил, что если я хочу, чтобы одна программа записывала .so, dlopen его, dlsym из него, а затем записывала другой .so, dlopen его и dlsym из него, тогда два имени файла .so должны отличаться. - person Mike Spear; 20.04.2019

Мне нужно было решение этой проблемы, потому что у меня есть скриптовая система, у которой нет файловой системы (с использованием больших двоичных объектов из базы данных), и мне нужно загружать бинарные плагины для поддержки некоторых скриптов. Это решение, которое я придумал, работает на FreeBSD, но может быть непереносимым.

void *dlblob(const void *blob, size_t len) {
    /* Create shared-memory file descriptor */
    int fd = shm_open(SHM_ANON, O_RDWR, 0);
    ftruncate(fd, len);
    /* MemMap file descriptor, and load data */
    void *mem = mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0);
    memcpy(mem, blob, len);
    munmap(mem, len);
    /* Open Dynamic Library from SHM file descriptor */
    void *so = fdlopen(fd,RTLD_LAZY);
    close(fd);
    return so;
}

Очевидно, что в коде отсутствует какая-либо проверка ошибок и т. Д., Но это основная функциональность.

ETA: Мое первоначальное предположение, что fdlopen - это POSIX, было ошибочным, это похоже на FreeBSD-ism.

person Parakleta    schedule 13.02.2017
comment
Людям, кажется, сходит с рук простой dlopen, здесь. - person yugr; 25.02.2017
comment
@yugr ваше предложение - это как раз тот тривиальный случай, который вопрошающий уже отклонил. - person Parakleta; 26.02.2017
comment
Не совсем так, с /run/shm файл никогда не записывается на диск. - person yugr; 27.02.2017
comment
@yugr /run/shm - это не POSIX, это Linux-ism, и без него функция просто записывает в /tmp. Независимо от того, попадает ли файл на диск (/tmp может быть ramdisk в некоторых системах), вам все равно нужно взаимодействовать с файловой системой, иметь разрешения на ее создание, контролировать, могут ли другие люди получить к нему доступ, и убедитесь, что вы правильно отключили его, когда вы готово (или сбой). Почему бы вам не опубликовать ответ со своим предложением и не позволить людям прокомментировать его и проголосовать за него? - person Parakleta; 28.02.2017
comment
Что ж, я не думаю, что это незначительное дополнение действительно заслуживает отдельного ответа. Согласен с Linux-ism, но OP явно не упомянул, что ему нужно POSIX-совместимое решение. Что касается файловой системы - опять же, хороший момент, но я думаю, что OP больше заботился о фактическом доступе к диску (записывается на диск, а затем перезагружается в память с помощью dlopen). - person yugr; 28.02.2017
comment
@yugr Моя ссылка на то, что это не POSIX и Linux-ism, заключается в том, что вы прокомментировали ответ, который использует BSD-ism для решения с предложением, которое использует Linux-ism, поэтому, помимо запасного варианта /tmp там является нулевым пересечением между этими решениями. Оба решения имеют достоинства на своих платформах, но я не понимаю, как они связаны. По сути, кажется, что в BSD используется fdlopen без доступа к файловой системе, затем в Linux используется /run/shm или в POSIX используется /tmp, оба требуют доступа к файловой системе. - person Parakleta; 28.02.2017

Я не понимаю, почему вы рассматриваете dlopen, поскольку для этого потребуется гораздо больше непереносимого кода для создания правильного формата объекта на диске (например, ELF) для загрузки. Если вы уже знаете, как сгенерировать машинный код для своей архитектуры, просто mmap память с PROT_READ|PROT_WRITE|PROT_EXEC и поместите туда свой код, затем назначьте адрес указателю функции и вызовите его. Очень простой.

person R.. GitHub STOP HELPING ICE    schedule 19.02.2011
comment
Это не кажется очень хорошим способом сделать это, если будет развиваться больше, чем несколько человек. Кроме того, разве ваш внедренный код не должен разрешать свои собственные указатели на функции, быть PIC и т. Д.? Похоже, что скомпилировать .so и потом dlopen это было бы намного приятнее. - person mrduclaw; 20.02.2011
comment
Я думаю, это зависит от того, какой код вы создаете. Я думал о JIT-коде для виртуальной машины / dynrec для эмулятора, где не было бы произвольных вызовов и доступа к данным в вызывающей программе. - person R.. GitHub STOP HELPING ICE; 20.02.2011
comment
Это действительно хороший способ обработки относительно простого автономного кода (также: в конце дня, как часто вы действительно хотите, чтобы динамически сгенерированный код мог выполнять произвольные вызовы?) - person Stephen Canon; 20.02.2011
comment
R .. Я, конечно, думал об этом, но для этого также потребуется компоновщик, потому что компилятор, с которым я работаю, выводит объектный код, а не машинный код. Вот почему у меня есть предложения 3 и 4: если бы я сделал это, мне нужно было бы найти какую-то кроссплатформенную библиотеку для динамического связывания в памяти. Но если этого не существует, то это вообще не решение. - person Jeremy Salwen; 20.02.2011
comment
@Stephen Canon, на самом деле это довольно обычное требование в некоторых сферах бизнеса и довольно часто встречается в Windows. Однако это тот тип вещей, которые вы пишете один раз, а затем продолжаете использовать повторно. - person mrduclaw; 23.02.2011
comment
@JeremySalwen Компилятор Glasgow Haskell имеет встроенный компоновщик в его систему времени выполнения для точной цели, которую вы описываете. Я не уверен, насколько он пригоден для повторного использования, но я знаю, что он написан на C, а не на Haskell, и работает на нескольких платформах. - person Demi; 15.10.2015

Вам не нужно загружать код, сгенерированный в памяти, поскольку он уже находится в памяти!

Однако вы можете - непереносимым способом - сгенерировать машинный код в памяти (при условии, что он находится в сегменте памяти mmap -ed с флагом PROT_EXEC).

(в этом случае этап «связывания» или перемещения не требуется, поскольку вы генерируете машинный код с определенными абсолютными или относительными адресами, в частности, для вызова внешних функций)

Существуют некоторые библиотеки, которые это делают: в GNU / Linux под x86 или x86-64 я знаю GNU Lightning (который быстро генерирует машинный код, который работает медленно), DotGNU LibJIT (генерирующий код среднего качества) и LLVM & GCCJIT (который может генерировать достаточно оптимизированный код в памяти, но требует времени, чтобы Это). И в LuaJit тоже есть похожая возможность. С 2015 года в GCC 5 есть библиотека gccjit.

И, конечно же, вы по-прежнему можете сгенерировать код C в файле, разветвить компилятор, чтобы скомпилировать его в общий объект, и открыть этот общий объектный файл. Я делаю это на GCC MELT, языке домена, расширяющем GCC. На практике это действительно хорошо работает.

дополнения

Если производительность записи сгенерированного файла C вызывает беспокойство (этого не должно быть, поскольку компиляция файла C происходит намного медленнее, чем его запись), рассмотрите возможность использования некоторых tmpfs для этого (возможно, в /tmp/, который часто является файловой системой tmpfs в Linux)

person Basile Starynkevitch    schedule 24.10.2011
comment
Этот ответ не заслуживает голосования. Это полностью неверно истолковывает идею спрашивающего. - person Krypton; 03.09.2014