2015-01-01 2 views
-1

Например, у меня есть функция func():Как я могу «сбросить» функцию в файл?

int func (int a, int b) {return a + b;} 

Теперь я хочу записать его в файл, так что я могу использовать ММАП системы вызова, чтобы загрузить его с PROT_EXEC и я могу назвать это из другой программы Что я должен сделать для этого?

+8

Это не то, как работают скомпилированные языки. –

+0

Я просто хочу, чтобы модуль мог загружаться в Linux и Windows (VirtualAllocEx) –

+0

Алгоритм не зависит от платформы. Поэтому я не хочу, чтобы модуль был скомпилирован 2 раза. –

ответ

5

Если вы знаете, какая подпись вам нужна, а статическая библиотека или местоположение разделяемой библиотеки во время компиляции, вы, вероятно, просто хотите включить заголовок и ссылку на выходную библиотеку. Если вы хотите динамически вызывать функцию, вы, возможно, захотите загрузить dlopen/dlsym (UNIX) или LoadLibrary/GetProcAddress (Windows) для динамической загрузки библиотеки и получения адреса функции по имени.

Обратите внимание, что случаи, когда вам действительно нужно загружать библиотеку динамически (по крайней мере явно), довольно редки. Это часто используется для модульных архитектур (например, «плагинов» или «расширений»), где отдельные части приложения распределяются отдельно (что может быть достигнуто более безопасно с использованием IPC, а не динамической загрузки ... см. Мою заметку ниже). Или для случаев, когда вашему приложению не разрешено включать зависимости статически и необходимо условно обеспечивать поведение, основанное на существовании определенных зависимостей библиотек в среде, в которой она выполняется. Однако в большинстве случаев вы просто хотите включить заголовок, который объявляет нужные вам символы и компилирует их для каждой целевой платформы (возможно, используя макросы #if...#else, если есть символы, которые различаются в разных ОС или версиях ОС).

С точки зрения стабильности, безопасности и сложности кода я лично рекомендую избегать загрузки динамической библиотеки. Для основных функциональных возможностей системы разумно связываться с динамической библиотекой, но вы захотите сделать это таким образом, чтобы бремя динамической загрузки полностью зависело от вашей инструментальной цепочки (т. Е. Вам не нужно явно вызывать dlopen или LoadLibrary) , Для других функций почти всегда лучше статически связывать (при условии, что вы распространяете обновления, когда есть исправления безопасности для ваших зависимостей), поскольку это позволит вам не работать с несовместимыми обновлениями версий, а также не позволять пользователям испытывать адский ад (вам нужно версия A, но для некоторых других приложений требуется версия B); модульные архитектуры часто лучше (и более надежно) достигаются посредством межпроцессного взаимодействия (IPC), поскольку динамически загружаемые библиотеки живут в процессе программы, которая их загружает (тем самым предоставляя им доступ к пространству виртуальной памяти всего процесса), тогда как с interprocess-communication, каждый компонент будет отдельным процессом, и отдельные компоненты будут иметь доступ только к информации, которая была предоставлена ​​ему явно вызывающим процессом, что затрудняет для вредоносного компонента кражу данных от вызывающего или другого компонентов или для создания нестабильности.

+0

Да, dlopen может загружать .so файл, но я хочу, не зависит от операционной системы. –

+1

PROT_EXEC и mmap являются специфичными для ОС, поэтому из вашего вопроса было непонятно, что вы искали что-то портативное. Однако Windows поддерживает эквивалентную функциональность в виде «LoadLibrary» и «GetProcAddress». –

+1

Любой подход к этому вопросу был бы гораздо менее переносимым, чем 'dlopen', и его нелегко было бы использовать для работы с существующими компиляторами/инструментальными цепочками, потому что при этом учитываются всевозможные предположения об ABI, исполняемом формате, как код обращается к глобальным данным и функциям адреса символов и т. д. –

2

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

Следующее лучшее - это, вероятно, отдельный процесс, с которым вы говорите, а не слияние.

Полуживой (но все еще не отличный выбор, см. Нашу дискуссию в другом ответе) будет делать общую библиотеку, как сказал Майкл Аарон Сафян.

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

Чтобы использовать его, то, что нам нужно сделать, похоже на то, что делает компоновщик - загрузить код на определенном адресе, на котором он работает, и запустить его. Существует независимый от позиции код, который может работать и по любому адресу.

Давайте сначала скомпилируем и скомпилируем нашу функцию, а затем выводим исходное изображение для определенного адреса. Предположим, что функция func в файле func.c, и мы используем gcc в Linux. (У компилятора Windows были бы аналогичные варианты: gcc на Windows точно так же, я считаю, но что-то вроде компилятора C-файла от Digital Mars делает это по-другому, поскольку команда компоновщика имеет значение /BINARY)

В любом случае, вот что я побежал:

gcc -c func.C# makes func.o 
ld func.o --oformat=binary -e func -o func.binary 

Это генерирует файл func.binary. Вы можете легко его дизассемблировать с помощью ndisasm -b 64 func.binary (или -b 32, если вы скомпилировали C в 32-битном режиме), чтобы убедиться, что он выглядит правильно - я вижу там инструкцию добавления, поэтому выглядит хорошо для меня.

Если вы загрузили это и mmaped, то назвали его ... он должен работать.

Проблемы будут быстро придумать, хотя:

  • Если есть более чем одна функция в этом файле, все они будут сплющенные вместе.
  • Адреса, которые они пытаются использовать друг для друга, могут быть совершенно неправильными.
  • Глобальные переменные и другие статические данные будут испорчены.

И еще есть. Операционная система использует более сложные форматы файлов для исполняемых файлов и библиотек по какой-либо причине!

Чтобы перейти к следующему шагу, вы можете подумать о написании загрузчика ELF или PE, который считывает эти метаданные из стандартного файла. Конечно, как только вы вникаете в это, вы будете делать именно то, что OS предоставляет с dlopen и LoadLibrary .... так что, если только цель состоит в том, чтобы просто узнать о кишках, просто вызовите эти функции и назовите это!

+0

Незначительное разъяснение: хотя я указал, что вы можете использовать общую библиотеку, мое общее сообщение рекомендовало против нее. –

+0

Да, это то, что я имел в виду под «вторым», возможно, я могу отредактировать, чтобы очистить его. –

+0

Хотя я не думаю, что смогу принять эту идею, я думаю, увидев ваш ответ, я многому научился. –

Смежные вопросы