Подтвердить что ты не робот

Dlopen из памяти?

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

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

  • Trick dlopen думает, что ваше место в памяти - это файл, хотя он никогда не покидает память.

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

Мне нужно решение для этого, потому что у меня есть скриптовая система, у которой нет файловой системы (используя blobs из базы данных), и ей нужно загружать бинарные плагины для поддержки некоторых скриптов. Это решение, с которым я столкнулся, работает над 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.

Ответ 4

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

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

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

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

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

добавлений

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