FPC для кодеров ;-)

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Сообщение STAKANOV » 11.11.2005 21:29:21

Оказалось очень просто обойтись без модуля system и RTL. Просто пишем модуль FPC со своими функциями компилируем, а линкуем уже по другому. На случай если кому интересно пишу подробней.

Пример модуля (файл test.pas):
Код: Выделить всё
unit test;

interface
implementation

procedure test1; alias :'test1';
begin
end;

end.


В принципе текст alias :'test1'; не особо нужен. Имя функции в объектном файле по умолчанию TEST_TEST1. Подробней см. Programmers Guide 6.2.3

далее компилируем этот юнит в обычном порядке:
fpc test.pas

В результате получаем файл test.o и при включенном смартлинке(рекомендую) libptest.a.

Далее берем маленький файл на ассемблере вызывающий нужную функцию и линкуем с ним. Но это уже зависит от того какой у вас ассемблер и какая ОС.
У меня as и FreeBSD. И мой файл (testmain.s):
Код: Выделить всё
   .text
       .global _start
_start:
       call test1 # можно так же call TEST_TEST1
# sys_exit
       pushl $0
       movl $1,%eax
       pushl %eax
       int $0x80


Компилируем его командой
as testmain.s -o testmain.o

И линкуем его
ld -s testmain.o libptest.a -o testmain
или
ld -s testmain.o test.o -o testmain

Полученный размер исполняемого файла у меня около 300 байт.

Вот такой вариант сборки если не нужны функции из модуля system и RTL. Это может пригодится если вы например хотите использовать функции из libc или только WinAPI.

В варианте для Linux необходимо изменить вызов int $0x80. Для Windows сейчас даже не скажу, но можете поискать на <a href='http://wasm.ru' target='_blank'>http://wasm.ru</a>.
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение SovNarKom » 12.11.2005 22:25:44

Да, мысль интересная... буду тренироваться на win32...
SovNarKom
постоялец
 
Сообщения: 389
Зарегистрирован: 28.05.2005 10:37:39
Откуда: Воронеж [vrn] [36]

Сообщение STAKANOV » 13.11.2005 01:38:01

Да, мысль интересная... буду тренироваться на win32...

Там точка входа по-моему WinMain

О чем стоит помнить, что мы не использовали стандартный инициализирующий код FPC, поэтому вызовы из модуля system могут не работать. Можно вообще не указывать компилятору каталоги с RTL (например поместив в каталог с программой соответсвующий fpc.cfg или используя оцию -n ). Только при этом надо будет сделать свой system.pas. Я так сделал и определил в нем системный вызовы FreeBSD.

На этом пути обнаружилось, что FPC "не честно играет"(в отличии от GPC) - inc это совсем не функция модуля system, а зарезервированное слово языка. То же относиться к write и read (переопределить их невозможно :( ).
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение Иван Шихалев » 13.11.2005 02:17:39

"не честно играет"

Как раз честно. Извраты с этими функциями — часть определения Паскаля. Кроме того, такое поведение описано в документации.
Аватара пользователя
Иван Шихалев
энтузиаст
 
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург

Сообщение Иван Шихалев » 13.11.2005 02:23:55

Хм... Если в GPC inc() — это настоящая функция, то нафига она нужна? Вообще-то она должна транслироваться в единственную ассемблерную команду.
Аватара пользователя
Иван Шихалев
энтузиаст
 
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург

Сообщение STAKANOV » 13.11.2005 02:54:32

Иван Шихалев писал(а): Хм... Если в GPC inc() — это настоящая функция, то нафига она нужна? Вообще-то она должна транслироваться в единственную ассемблерную команду.

так и есть - incl или incw

наверно так и должно быть ;)

PS - а вот GPC фигню выдал - <a href='http://freepascal.ru/forum/index.php?showtopic=455' target='_blank'>http://freepascal.ru/forum/index.php?showtopic=455</a>
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение STAKANOV » 13.11.2005 13:08:14

Кстати в указанном примере можно обойтись и без программы на ассемблере. Для этого в test.pas надо изменить
Код: Выделить всё
procedure test1; alias :'_start';
(при линковки с libc на 'main', а Windows очевидно 'WinMain'). Но линковать теперь надо непосредственно test.o. При линковке с libptest.a линкер не находит точку входа.

Важно! Для выхода из программы используйте соответствующий системный вызов!
В своем system.pas я определил
Код: Выделить всё
procedure SysExit(rval:LongInt);assembler;
asm
    pushl rval;
    mov $1,%eax
    pushl %eax
    int $0x80
end;
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение noch » 14.11.2005 11:32:56

Спасибо, очень интересный пост ;)
Аватара пользователя
noch
постоялец
 
Сообщения: 145
Зарегистрирован: 07.06.2005 09:45:49
Откуда: Armenia

Сообщение SovNarKom » 14.11.2005 21:10:42

Провёл исследование связки
WinXP P
FPC 2.0.1 и MASM LINK 5.12.8078.0

Результаты такие :
Линкеру всегда указывалась точная точка входа "WINMAIN" но в программе она объявлялась как procedure WinMain; Alias:'_WINMAIN';
при отключенном смартлинке или назавнии процедуры в нижнем регистре линкер не находил точку входа:
LINK : error LNK2001: unresolved external symbol _WINMAIN
Одним словом, только procedure WinMain; Alias:'_WINMAIN'; и работает...

Бат файл:
Код: Выделить всё
fpc.exe -CX Base.pas
LINK.EXE /SUBSYSTEM:WINDOWS /NOLOGO /ENTRY:WINMAIN /RELEASE LibpBase.a /out:Base.exe</lj-cut>


Вот исходник:
Код: Выделить всё
unit Base;

interface

// function Beep(dwFreq:DWORD; dwDuration:DWORD):LONGBOOL; external 'kernel32' name 'Beep';

implementation

procedure SysExit(rval:LongInt); assembler;
asm
pushl rval;
mov $1,%eax
pushl %eax
int $0x80 // ошибка, но суть не в ней...
end;

procedure TestProc1;
begin
// Halt;
end;

function TestProc2(i: integer): Integer;
begin
// Halt;
end;

procedure WinMain; Alias:'_WINMAIN';
begin
TestProc1;
TestProc2(0);
Beep(1000,1000); // <--- AV
SysExit(0);
end;

end.


// Вариант 2

unit Base;

interface

function Beep(dwFreq:DWORD; dwDuration:DWORD):LongBool; external 'kernel32' name 'Beep';
procedure ExitProcess(uExitCode:UINT);external 'kernel32' name 'ExitProcess';


implementation

procedure WinMain; Alias:'_WINMAIN';
begin
Beep(1000,5000);
ExitProcess(0);
end;

end.  




Как видно из кода, внутренние процедуры собираются нормально, но если расковычить Halt - линкер обругает так:
Base.o : error LNK2001: unresolved external symbol SYSTEM_HALT$BYTE
Хотя в программе используется именно процедура без параметров, линкер ругается на ту, которая принимает байт в качестве параметра: Procedure halt(errnum:byte);

Аналогично дело будет обстоять и с другими процедурами с параметрами или функциями, из других юнитов...

При подключении function Beep(dwFreq:DWORD; dwDuration:DWORD):LONGBOOL; external 'kernel32' name 'Beep'; линкер обругал меня так:
Base.o : warning LNK4078: multiple ".idata" sections found with different attributes (40000000)
Прогу собрал, но при запуске винда ругается на недопустимую операцию...

Вывод: всё криво и пока пользы от этой связки я не получил... Проги кстати от 1024 Byte...

Что-то не так...
SovNarKom
постоялец
 
Сообщения: 389
Зарегистрирован: 28.05.2005 10:37:39
Откуда: Воронеж [vrn] [36]

Сообщение STAKANOV » 14.11.2005 22:26:51

procedure SysExit(rval:LongInt); assembler;
asm
pushl rval;
mov $1,%eax
pushl %eax
int $0x80 // ошибка, но суть не в ней...
end;

это системный вызов FreeBSD... его здесь быть не должно

SYSTEM_HALT$BYTE
это процедура из модуля system, не стоит пытаться вызывать процедуры модуля system не ипользуя модуль system ;)

попробуй просто exit, хотя если это функция из модуля system (что не факт) то ничего не получится ... из WinMain вроде моджно выйти как из обычной подпрограммы (на асемблере это команда ret) это команда как минимум есть там где end

для завершения работы программы так же можно (а может даже нужно) использовать соответвующую функцию из WinAPI

Аналогично дело будет обстоять и с другими процедурами с параметрами или функциями, из других юнитов...

вся идея в том чтобы их не использовать

пользы от этой связки я не получил

и польза есть только тогда когда не нужны модули и функции из RTL (например если ты делаешь все функциями из WinAPI + своими библиотеками из других языков)

а вот с Beep не понятно что :( Я к сожалению WinAPI уже много лет не видел :(
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение STAKANOV » 14.11.2005 22:30:43

Чуть не забыл !!!
Когда ты делаешь
fpc.exe -CX Base.pas
модуль system (а так же запросто и objects) во всю используется!!!

А вот на этапе
LINK.EXE /SUBSYSTEM:WINDOWS /NOLOGO /ENTRY:WINMAIN /RELEASE LibpBase.a
его уже нет
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение SovNarKom » 14.11.2005 23:05:26

STAKANOV
Посмотри там в коде второй вариант...

Вкратце суть такая: невозможно вообще вызвать процедуру из внешней библиотеки(dll)

Аналогично дело будет обстоять и с другими процедурами с параметрами или функциями, из других юнитов...

Т. е. даже собственных :(
В резульате возможно только что-то вроде ассемблерного диалекта FP...
всё бы ничего, но в том то и проблема - отсутствие связи с внешним миром...
SovNarKom
постоялец
 
Сообщения: 389
Зарегистрирован: 28.05.2005 10:37:39
Откуда: Воронеж [vrn] [36]

Сообщение STAKANOV » 14.11.2005 23:35:25

Вкратце суть такая: невозможно вообще вызвать процедуру из внешней библиотеки(dll)


тут уже какие то тонкости линковки под Windows, а WinaAPI функции не должны иметь что-то вроде stdcall ?
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение SovNarKom » 15.11.2005 19:12:56

что-то вроде stdcall ?

Да, я просто скопировал из func.inc, нужно конечно предварительно {$calling stdcall}

Только это от AV не спасает... попробую прогу продизасмить посмотреть чего она реально делает...

И ещё... если процедура/функция из тоже моего, но другого юнита - не линкуется...
А, чуть не забыл, если не использовать смартлинк, то вообще не линкуется, странно?


Но линковать теперь надо непосредственно test.o. При линковке с libptest.a линкер не находит точку входа.

Масм собирает... прога меньше получается... Может другой какой-нить линкер попробовать?
SovNarKom
постоялец
 
Сообщения: 389
Зарегистрирован: 28.05.2005 10:37:39
Откуда: Воронеж [vrn] [36]

Сообщение SovNarKom » 15.11.2005 19:46:44

Да....
Вот такая картина...

Код: Выделить всё

...

+++++++++++++++++++ IMPORTED FUNCTIONS ++++++++++++++++++
Number of Imported Modules =    0 (decimal)

...

//******************** Program Entry Point ********
:00401000 55                      push ebp
:00401001 89E5                    mov ebp, esp
:00401003 68E8030000              push 000003E8
:00401008 6A64                    push 00000064
:0040100A E809000000              call 00401018
:0040100F 6A00                    push 00000000
:00401011 E80A000000              call 00401020
:00401016 C9                      leave
:00401017 C3                      ret

* Referenced by a CALL at Address:
|:0040100A  
|
:00401018 FF2530304000            jmp dword ptr [00403030]
:0040101E 90                      nop
:0040101F 90                      nop
:00401020 FF2534304000            jmp dword ptr [00403034]
:00401026 90                      nop
:00401027 90                      nop
:00401028 00000000000000000000    BYTE 10 DUP(0)

...




jmp dword ptr [00403030] - это что такое? Я так понимаю адрес в kernel32.dll... Должен быть...
SovNarKom
постоялец
 
Сообщения: 389
Зарегистрирован: 28.05.2005 10:37:39
Откуда: Воронеж [vrn] [36]

След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5

Рейтинг@Mail.ru