Объектный файл в двоичный код
Предположим, у меня есть C-файл без внешней зависимости, а только секция const data. Я хотел бы скомпилировать этот файл, а затем получить двоичный двоичный объект, который я могу загрузить в другой программе, где функция будет использоваться через указатель на функцию.
Давайте рассмотрим пример, это вымышленный двоичный модуль, f1.c
static const unsigned char mylut[256] = {
[0 ... 127] = 0,
[128 ... 255] = 1,
};
void f1(unsigned char * src, unsigned char * dst, int len)
{
while(len) {
*dst++ = mylut[*src++];
len--;
}
}
Я хотел бы скомпилировать его в f1.o, затем в f1.bin и использовать его в prog.c следующим образом.
int somefunc() {
unsigned char * codedata;
f1_type_ptr f1_ptr;
/* open f1.bin, and read it into codedata */
/* set function pointer to beginning of loaded data */
f1_ptr =(f1_type_ptr)codedata;
/* call !*/
f1_ptr(src, dst, len);
}
Я полагаю, что переход от f1.c к f1.o включает -fPIC, чтобы получить независимую позицию. Какие флаги или скрипт компоновщика я могу использовать для перехода от f1.o к f1.bin?
Пояснение:
Я знаю о динамических ссылках. динамическое связывание в этом случае невозможно. На шаге связывания должен быть приведен func указатель на загруженные данные, если это возможно.
Пожалуйста, предположите, что нет поддержки ОС. Если бы я мог, я бы, например, написал f1 в сборке с адресацией, связанной с ПК.
2 ответа
Прежде всего, как уже говорили другие, вы должны рассмотреть возможность использования DLL или SO.
Тем не менее, если вы действительно хотите это сделать, вам нужно заменить скрипт компоновщика. Примерно так (не очень хорошо проверено, но я думаю, что это работает):
ENTRY(_dummy_start)
SECTIONS
{
_dummy_start = 0;
_GLOBAL_OFFSET_TABLE_ = 0;
.all : {
_all = .;
LONG(f1 - _all);
*( .text .text.* .data .data.* .rodata .rodata.* )
}
}
Затем скомпилируйте с:
$ gcc -c -fPIC test.c
Связать с:
$ ld -T script.ld test.o -o test.elf
И извлеките двоичный двоичный объект с помощью:
$ objcopy -j .all -O binary test.elf test.bin
Возможно, какое-то объяснение сценария приветствуется:
ENTRY(_dummy_start)
Это просто позволяет избежать предупреждения о том, что программа не имеет точки входа._dummy_start = 0;
Это определяет символ, использованный в предыдущей строке. Значение не используется._GLOBAL_OFFSET_TABLE_ = 0;
Это предотвращает другую ошибку компоновщика. Я не думаю, что вам действительно нужен этот символ, поэтому его можно определить как 0..all
Это название раздела, в котором будут собраны все байты вашего блоба. В этом примере это будет все.text
,.data
а также.rodata
разделы вместе. Вам может понадобиться еще немного, если у вас есть сложные функции, в этом случаеobjdump -x test.o
твой друг.LONG(f1 - _all)
Не очень нужно, но вы хотите знать смещение вашей функции в BLOB, не так ли? Вы не можете предполагать, что это будет со смещением 0. С этой строкой самые первые 4 байта в BLOB-объекте будут смещением символа.f1
(твоя функция). + ИзменитьLONG
сQUAD
при использовании 64-битных указателей.
ОБНОВЛЕНИЕ: А теперь быстрое и грязное испытание (это работает!):
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
typedef void (*f1_t)(char *a, char *b, int len);
f1_t f1;
int main()
{
char *blob = (char*)valloc(4096);
FILE *f = fopen("test.bin", "rb");
fread(blob, 1, 4096, f);
fclose(f);
unsigned offs = *(unsigned*)blob;
f1 = (f1_t)(blob + offs);
mprotect(blob, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
char txt[] = "¡hello world!";
char txt2[sizeof(txt)] = "";
f1(txt, txt2, sizeof(txt) - 1);
printf("%s\n%s\n", txt, txt2);
return 0;
}
Вам следует подумать о создании общей библиотеки (.dll для Windows или .so для Linux).
Создайте библиотеку следующим образом:
gcc -c -fPIC test.c
gcc -shared test.o -o libtest.so
Если вы хотите загружать библиотеку динамически из своего кода, взгляните на функции dlopen(3) и dlsym (3).
Или, если вы хотите связать библиотеку во время компиляции, соберите программу с
gcc -c main.c
gcc main.o -o <binary name> -ltest
РЕДАКТИРОВАТЬ:
Я действительно не уверен в том, что скажу здесь, но это может дать вам ключ к прогрессу в ваших исследованиях...
Если вы не хотите использовать dlopen и dlsym, вы можете попробовать прочитать таблицу символов из файла .o, чтобы найти адрес функции, а затем отобразить объектный файл в памяти с правами чтения и выполнения. Тогда вы сможете выполнить загруженный код по найденному адресу. Но будьте осторожны с другими зависимостями, которые вы можете встретить в этом коде.
Вы можете проверить страницу руководства elf(5)
Используйте указатель на функцию приведения.
Вот пример:
#include <stdio.h>
int main()
{
unsigned char *dst, *src;
int len;
void (*f1)(unsigned char *, unsigned char *, int);
*(void **)(&f1) = 0x..........;
f1(src,dst,len);
return 0;
}
Чтобы сделать что-то еще, вам действительно нужен компоновщик и динамический загрузчик.