Формат объектных файлов
Модератор: Модераторы
- XProger
- новенький
- Сообщения: 44
- Зарегистрирован: 13.08.2007 02:52:28
- Откуда: Москва
- Контактная информация:
Формат объектных файлов
Всем привет.
Задача стоит в "выдёргивании" скомпилированного кода из *.o файлов, с целью его последующего самостоятельного выполнения.
Не подскажите, где можно почитать о его структуре?
Задача стоит в "выдёргивании" скомпилированного кода из *.o файлов, с целью его последующего самостоятельного выполнения.
Не подскажите, где можно почитать о его структуре?
http://www.opennet.ru/soft/ruprog/coff.txt
http://www.rsdn.ru/article/baseserv/pe_coff.xml
Если не секрет, для чего это понадобилось?
http://www.rsdn.ru/article/baseserv/pe_coff.xml
Если не секрет, для чего это понадобилось?
- shade
- энтузиаст
- Сообщения: 879
- Зарегистрирован: 21.02.2006 19:15:48
- Откуда: http://shamangrad.net/
- Контактная информация:
Я как-то писал пару программулек:
http://forum.shamangrad.net/viewtopic.php?f=18&t=284
http://forum.shamangrad.net/viewtopic.php?f=18&t=285
может поможет
http://forum.shamangrad.net/viewtopic.php?f=18&t=284
http://forum.shamangrad.net/viewtopic.php?f=18&t=285
может поможет
- XProger
- новенький
- Сообщения: 44
- Зарегистрирован: 13.08.2007 02:52:28
- Откуда: Москва
- Контактная информация:
vital,
игровой многопользовательский сервер помимо постоянной нагрузки будет часто запускать скрипты, хотелось бы максимально выиграть в этом месте по производительности. А т.к. FPC компилирует код с космической скоростью, посчитал оптимальным решением использовать именно его в качестве "языка скриптов". Компиляция в dll не устраивает тем, что таких dll будет более 1000 и держать их всех в памяти неразумно, да и хранить на диске также неразумно т.к. стараемся минимизировать обращения к диску.
Всякие Lua, Python и прочую дребедень использовать на многопользовательском сервере имхо неразумно, когда под рукой есть такой инструмент как FPC.
bw,
Машинный код содержащийся в *.o скомпилированный win32 версией компилятора может быть запущен на *nix системе, при условии того, что не юзает API?
shade,
Вот это да! Пасиба! )
Пойду изучать...
игровой многопользовательский сервер помимо постоянной нагрузки будет часто запускать скрипты, хотелось бы максимально выиграть в этом месте по производительности. А т.к. FPC компилирует код с космической скоростью, посчитал оптимальным решением использовать именно его в качестве "языка скриптов". Компиляция в dll не устраивает тем, что таких dll будет более 1000 и держать их всех в памяти неразумно, да и хранить на диске также неразумно т.к. стараемся минимизировать обращения к диску.
Всякие Lua, Python и прочую дребедень использовать на многопользовательском сервере имхо неразумно, когда под рукой есть такой инструмент как FPC.
bw,
Машинный код содержащийся в *.o скомпилированный win32 версией компилятора может быть запущен на *nix системе, при условии того, что не юзает API?
shade,
Вот это да! Пасиба! )
Пойду изучать...
- bw
- постоялец
- Сообщения: 359
- Зарегистрирован: 01.12.2005 10:36:23
- Откуда: Усть-Илимск
- Контактная информация:
Вообще-то объект, полученный при компиляции модуля и приложения в FPC не получится использовать как есть. Такой объект будет иметь внешние ссылки, как минимум на system.o (для винды это еще и sysinitpas.o), а скорее всего придется "подключать" и такие объекты, как objects.o, objpas.o и т.д. Фактически, ты будешь вынужден делать линковку, а это довольно дорогая процедура, и релокации в загруженных секциях, ну и не забудь, что может потребоваться (если ты используешь виндовый RTL, то обязательно потребуется) импорт DLL, прописанных в коде используемых модулей (это, как минимум, kernel32.dll). Ну и т.д. :-). Т.е. сложность возрастает неимоверно, сответственно возрастает процент возможных ошибок и вероятность падения сервера.
> Машинный код содержащийся в *.o скомпилированный win32 версией компилятора может быть запущен на *nix системе, при условии того, что не юзает API?
Да, когда ты разрешишь все зависимости этого объекта (читать выше). Код, содержащийся в COFF и ELF ничем не отличается, просто используются разные контейнеры.
p.s. Я использую дребедень Python ;-).
..bw
> Машинный код содержащийся в *.o скомпилированный win32 версией компилятора может быть запущен на *nix системе, при условии того, что не юзает API?
Да, когда ты разрешишь все зависимости этого объекта (читать выше). Код, содержащийся в COFF и ELF ничем не отличается, просто используются разные контейнеры.
p.s. Я использую дребедень Python ;-).
..bw
- shade
- энтузиаст
- Сообщения: 879
- Зарегистрирован: 21.02.2006 19:15:48
- Откуда: http://shamangrad.net/
- Контактная информация:
Сайт, старый и заброшеный (zolotov.h14.ru), жаль если добро на нём лежащее пропадёт.
Зарегистрировал проект на Шаманграде и добавил исходники в svn
Проект: http://shamangrad.net/project.php?act=v ... =codetools
svn: svn://svn.shamangrad.net/codetools или http://svn.shamangrad.net/codetools/
Багтреккер: http://shamangrad.net/report.php?act=list&prj=codetools
Архивы, релизы: http://download.shamangrad.net/codetools/
Хотя врядли буду в ближайщее время что-то дорабатывать или исправлять ошибки...
Линковка и релокации не дорогая процедура, это просто ld тормоз...
А вот в Delphi на сколько я помню очень быстрый и компилятор и компоновщик - сколько кодил на Delphi не помню чтобы я когда-нибудь задумывался о времени компиляции/компоновки...
Добавлено спустя 6 минут 32 секунды:
XProger
Удачи!
Будут вопросы по линковке и релокациям, можешь задавать
Кстати, чтоб знал, есть две модификации COFF, одна так сказать оригинальная (используется в Linux), другая от Microsoft (используется под Windows) - разница, как раз в релокациях.
Причем версия Windows-версия FPC производит MS-модификацию, а Linux-версия оригинальную.
Я в link вроде делал поддержку обоих форматов.
PS: врочем писал давно, так что отдельные факты могу путать...
Зарегистрировал проект на Шаманграде и добавил исходники в svn
Проект: http://shamangrad.net/project.php?act=v ... =codetools
svn: svn://svn.shamangrad.net/codetools или http://svn.shamangrad.net/codetools/
Багтреккер: http://shamangrad.net/report.php?act=list&prj=codetools
Архивы, релизы: http://download.shamangrad.net/codetools/
Хотя врядли буду в ближайщее время что-то дорабатывать или исправлять ошибки...
Фактически, ты будешь вынужден делать линковку, а это довольно дорогая процедура, и релокации в загруженных секциях, ну и не забудь, что может потребоваться (если ты используешь виндовый RTL, то обязательно потребуется) импорт DLL
Линковка и релокации не дорогая процедура, это просто ld тормоз...
А вот в Delphi на сколько я помню очень быстрый и компилятор и компоновщик - сколько кодил на Delphi не помню чтобы я когда-нибудь задумывался о времени компиляции/компоновки...
Добавлено спустя 6 минут 32 секунды:
XProger
Удачи!
Будут вопросы по линковке и релокациям, можешь задавать
Кстати, чтоб знал, есть две модификации COFF, одна так сказать оригинальная (используется в Linux), другая от Microsoft (используется под Windows) - разница, как раз в релокациях.
Причем версия Windows-версия FPC производит MS-модификацию, а Linux-версия оригинальную.
Я в link вроде делал поддержку обоих форматов.
PS: врочем писал давно, так что отдельные факты могу путать...
- XProger
- новенький
- Сообщения: 44
- Зарегистрирован: 13.08.2007 02:52:28
- Откуда: Москва
- Контактная информация:
bw,
проблема внешних ссылок ясна как день. Поэтому никакого RTL и тем более стороннего API код использовать не будет, к этому мы морально готовы )
shade,
думаю, вопросов будет немеряно. А пока изучаю и вникаю. Спасибо за код и инфу )
Сейчас лишь вижу проблему переназначения адресов call инструкций, это видимо то что делает ОС при загрузке dll в память... и моих знаний пока не достаточно чтобы понять как оно там устроено. Или же call адрес постоянен и не меняется от запуска к запуску, тогда не ясно относительно чего он отсчитывается и есть ли необходимость переназначать call адреса при "выдёргивании" кода из объектных файлов :\
проблема внешних ссылок ясна как день. Поэтому никакого RTL и тем более стороннего API код использовать не будет, к этому мы морально готовы )
shade,
думаю, вопросов будет немеряно. А пока изучаю и вникаю. Спасибо за код и инфу )
Сейчас лишь вижу проблему переназначения адресов call инструкций, это видимо то что делает ОС при загрузке dll в память... и моих знаний пока не достаточно чтобы понять как оно там устроено. Или же call адрес постоянен и не меняется от запуска к запуску, тогда не ясно относительно чего он отсчитывается и есть ли необходимость переназначать call адреса при "выдёргивании" кода из объектных файлов :\
- shade
- энтузиаст
- Сообщения: 879
- Зарегистрирован: 21.02.2006 19:15:48
- Откуда: http://shamangrad.net/
- Контактная информация:
В принципе в моих модулях (по крайней мере в link) реализованы классы для работы с объектными файлами и в этих классах уже реализована релокация.
Возможно просто прокатит такой фокус:
1. загрузить все необходимые объектыне модули
2. для каждого объекта узнать адрес блока с данными
3. для каждого объекта вызывать метод Relocate указав ему адрес из п2.
4. отредактировать связи (тут уже не помню как, надо смотерть код компоновщика)
По сути, нужно откомпоновать объекты прям в памяти - это фактически и делает компоновщик, но результат записывает в файл.
Возможно просто прокатит такой фокус:
1. загрузить все необходимые объектыне модули
2. для каждого объекта узнать адрес блока с данными
3. для каждого объекта вызывать метод Relocate указав ему адрес из п2.
4. отредактировать связи (тут уже не помню как, надо смотерть код компоновщика)
По сути, нужно откомпоновать объекты прям в памяти - это фактически и делает компоновщик, но результат записывает в файл.
- shade
- энтузиаст
- Сообщения: 879
- Зарегистрирован: 21.02.2006 19:15:48
- Откуда: http://shamangrad.net/
- Контактная информация:
Уверен, что ваши объектные файлы будут зависеть от какого-либо API - и это НЕ проблема
Не удержался и попробовал немного модифицировать исходники, чтобы динамически загружать и выполнять объектные модули.
test.sh:
Осталось отрефакторить
Не удержался и попробовал немного модифицировать исходники, чтобы динамически загружать и выполнять объектные модули.
Код: Выделить всё
alex@notebook:~/prj/codetools/trunk/link> svn info
Path: .
URL: svn://svn.shamangrad.net/codetools/trunk/link
Repository Root: svn://svn.shamangrad.net/codetools
Repository UUID: 8795bce7-64d0-dd11-9702-0030485a0448
Revision: 5
Node Kind: directory
Schedule: normal
Last Changed Author: shade
Last Changed Rev: 5
Last Changed Date: 2008-12-23 02:55:10 +0300 (Втр, 23 Дек 2008)
alex@notebook:~/prj/codetools/trunk/link> ./test.sh
compile dload...
Free Pascal Compiler version 2.2.2 [2008/10/25] for i386
Copyright (c) 1993-2008 by Florian Klaempfl
Target OS: Linux for i386
Compiling dload.pas
Compiling sys.pas
Compiling soff.pas
Compiling mylink.pas
Compiling coff.pas
Compiling ar.pas
Compiling aout.pas
Linking dload
3180 lines compiled, 0.3 sec
compile hello.pas
Free Pascal Compiler version 2.2.2 [2008/10/25] for i386
Copyright (c) 1993-2008 by Florian Klaempfl
Target OS: Linux for i386
Compiling hello.pas
Assembling hello
20 lines compiled, 0.0 sec
run dload...
dload 0.1.0, (c) 2008, zolotov-alex@shamangrad.net
loading hello.o
mark sections...
relocate...
resolve...
run...
hello world :)
ok
alex@notebook:~/prj/codetools/trunk/link>
test.sh:
Код: Выделить всё
echo compile dload...
fpc -Mobjfpc dload.pas
echo compile hello.pas
fpc -al -Anasmobj hello.pas
nasm -f coff hello.s -o hello.o
echo run dload...
./dload -entry HELLO_MAIN hello.o
Осталось отрефакторить
- bw
- постоялец
- Сообщения: 359
- Зарегистрирован: 01.12.2005 10:36:23
- Откуда: Усть-Илимск
- Контактная информация:
Не понятно, как этот код работает :-).
В hello.o отсутствует реализация PrintLn/WriteLn.
XProger, RTL это значительно больше чем тот же WriteLn и GetMem. Это любые манипуляции со строками, динамическими списками, вообщем это туева хуча внешних зависимостей (ну, может, я немного преувеличиваю :-), вот я и удивляюсь, как без всего этого hello.o заработал. Не хочу тебя отпугивать, всё это можно реализовать, но вот достигнешь ли ты в результате первоначальной цели, не получится ли так, что такой объект будет грузиться в 10 дольше обычной DLL?
..bw
В hello.o отсутствует реализация PrintLn/WriteLn.
XProger, RTL это значительно больше чем тот же WriteLn и GetMem. Это любые манипуляции со строками, динамическими списками, вообщем это туева хуча внешних зависимостей (ну, может, я немного преувеличиваю :-), вот я и удивляюсь, как без всего этого hello.o заработал. Не хочу тебя отпугивать, всё это можно реализовать, но вот достигнешь ли ты в результате первоначальной цели, не получится ли так, что такой объект будет грузиться в 10 дольше обычной DLL?
..bw
- XProger
- новенький
- Сообщения: 44
- Зарегистрирован: 13.08.2007 02:52:28
- Откуда: Москва
- Контактная информация:
bw,
я в курсе в каких местах явно/неявно происходит вызов RTL функционала. От RTL зависеть не буду, хотя shade наглядно показал как можно "подменить" этот RTL функционал при переназначении call адресов, но мне бы пока независимый код выдрать и запустить )
А дольше чем dll грузиться не может по определению. Кода ведь меньше )
я в курсе в каких местах явно/неявно происходит вызов RTL функционала. От RTL зависеть не буду, хотя shade наглядно показал как можно "подменить" этот RTL функционал при переназначении call адресов, но мне бы пока независимый код выдрать и запустить )
А дольше чем dll грузиться не может по определению. Кода ведь меньше )
- shade
- энтузиаст
- Сообщения: 879
- Зарегистрирован: 21.02.2006 19:15:48
- Откуда: http://shamangrad.net/
- Контактная информация:
bw писал(а):Не понятно, как этот код работает.
В hello.o отсутствует реализация PrintLn/WriteLn.
См. внимательно uses и sys.pas - его подключает и hello.pas и dload.pas
Я его сделать просто чтобы сократить код, но аналогично можно заюзать и сам system в ключая все его функции
dload
1. загружает указанные объектные файлы
2. добавляет символ println (эту часть можно и автоматизировать, чтобы вручную все символы не прописывать)
Код: Выделить всё
println_sys := TSymbol.Create(nil);
println_sys.Name := 'SYS_PRINTLN$ANSISTRING';
println_sys.Offset := Longword( @println );
println_sys.SymType := SOFF_EXTERN;
println_sys.Section := dummy;
Linker.Globals.Add(println_sys);3. отмечает секции которые нужны для компоновки
4. делает релокацию отмеченых секций
5. редактирует связи отмеченых секций, в том числе и связь на sys.println
6. находит точку входа (которая указана параметром -entry)
7. вызывает точку входа, управление попадает в загруженый объектный файл
8. там вызывается функция sys.println, которая реализована в dload, таким образом управление временно обратно передается в dload
9. println выводит строку
10. println возращает управление и мы снова в hello.o
11. hello.main возращает управление в dload
12. dload завершает свою работу
Добавлено спустя 43 минуты 43 секунды:
XProger писал(а):А если объектный файл абсолютно независим (не использует API и RTL/CRT), то такие фокусы не понадобятся? Релоки нужно учитывать только при взаимосвязях модулей? )
Релокация и редактирование связей нужны в любом случае.
Релокация нужна, т.к. заранее не известно куда будет загружен код, исколючение может составлять позициононезависимый код (PIC) - некоторые компиляторы умеют генерить такой код, FPC не исколючение:
Код: Выделить всё
alex@notebook:~> fpc -h | grep PIC
-Cg Generate PIC code
-fPIC Same as -Cg
alex@notebook:~>Отмечу, что для PIC таблица релокаций по идее должна быть пустой, а следовательно в коде релокации будет просто пустой цикл. Так что совсем не обязательно переписывать код - тогда будет работать и PIC и не PIC. Точно не знаю, но PIC вроде быть менее производительным, чем обычный - так что лучше один раз произвести небольшую релокацию, чем в цикле крутить не эффективный PIC.
Редактирование связей нужны для настройки ссылок между секциями в объектом файле (их может быть несколько, .data, .bss, .text), для связи нескольких объектных файлов и для связи с API.
Отредактирования связей действительно можно отказаться, если ваш код действительно не экспортирует никаких символов (например, на вход подается объект, а в объекте вызываются только виртуальные методы), но для этого нужно педварительно обработать объектный модуль - нужно все его секции объединить в одну. Так можно один раз "прелинковать" объектный модуль, созранить в файл, а потом при выполнении просто подгружать этот подготовленый модуль и делать только релокации.
Но я бы не советовал так париться, релокации и редактирования связей не такие дорогие операции как некоторые думают. Есть таблица релокаций и таблица символов. Алгоритм релокации линеен - один цикл по таблице релокации и алгоритм редактирования связей тоже линеен - тоже один цикл по таблице символов. Релокации и редактированию связей подвергается только ссылки, а их не так уж и много относительно остального кода.
Добавлено спустя 3 часа 36 минут 42 секунды:
Немного отрефакторил модули и переписал пример.
Получилось вот что:
Код: Выделить всё
// Создаем компоновщик
Linker := TRuntimeLinker.Create;
try
// Используем оригинальный COFF
Linker.UseMSCOFF := false;
// подключаем API
api := Linker.Modules.Add.Sections.Add;
api.AddGlobal('SYS_PRINTLN$ANSISTRING', @println);
api.AddGlobal('FPC_PUSHEXCEPTADDR', @FPC_PUSHEXCEPTADDR);
api.AddGlobal('FPC_SETJMP', @FPC_SETJMP);
api.AddGlobal('FPC_POPADDRSTACK', @FPC_POPADDRSTACK);
api.AddGlobal('FPC_RERAISE', @FPC_RERAISE);
api.AddGlobal('FPC_ANSISTR_DECR_REF', @FPC_ANSISTR_DECR_REF);
api.AddGlobal('fpc_shortstr_to_ansistr', @fpc_shortstr_to_ansistr);
api.AddGlobal('SYSTEM_TOBJECT_$__CLASSNAME$$SHORTSTRING', @TObject.ClassName);
// Загружаем объектный модуль
mod_hello := Linker.LoadFile('hello.o');
// Компонуем
Linker.Link;
// Выполняем код
Linker.Execute('HELLO_MAIN');
// выгрузить mod_hello
Linker.Modules.Remove(mod_hello);
// загрузить другой модуль 'showclass.o'
mod_showclass := Linker.LoadFile('showclass.o');
// перекомпонуем
Linker.Link;
Linker.Execute('SHOWCLASS_SHOWCLASSNAME$TOBJECT', Linker);
Linker.Execute('SHOWCLASS_SHOWCLASSNAME$TOBJECT', mod_showclass);
finally
// Почистить мусор
Linker.Free;
end;
PS: полный пример см. в svn
