извлекать из памяти? - PullRequest
       21

извлекать из памяти?

27 голосов
/ 20 февраля 2011

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

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

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

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

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

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

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

Ответы [ 4 ]

10 голосов
/ 20 февраля 2011

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

9 голосов
/ 13 февраля 2017

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

7 голосов
/ 20 февраля 2011

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

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

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

2 голосов
/ 24 октября 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...