Создание компилятора Forth для Windows

Тема в разделе "WASM.ZEN", создана пользователем kufal, 21 мар 2007.

  1. kufal

    kufal New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2007
    Сообщения:
    1
    Компилятор Forth для Windows
    (разработка на Delphi)

    Книги и брошюры которые нужно полистать и почитать что бы понять как работает компилятор и разобраться в исходном коде на Delphi:

    1. Г. Шилдт "Теория и практика C++"
    Глава 10. Реализация языковых интерпретаторов на C++. Здесь можно прочитать как сделать интерпритатор для языка BASIC, что такое токен и как делается разбор выражений (это будет интересно если делать компилятор для языков BASIC, C, Pascal в Forth используется польская форма записи выражений)
    2. Д. Хендрикс "Компилятор языка Си для микроЭВМ"
    Это не совсем компилятор - это конвертор с языка Си на язык Ассемблера, потом конечно это можно откомпилировать в коды для микропроцессора, тем более в книге есть исходный код компилятора, но сделано это не для i386.
    3. В. Юров "Assembler. Специальный справочник"
    Здесь есть информация о структуре EXE-файла, но лучше использовать её просто для знакомства, а самое главное что здесь есть это - "команды микропроцессора Pentium III".
    4. А. Ю. Бураго, В. А. Кириллин, И. В. Романовский "Форт - язык для микропроцессоров"
    Именно по мотивам этой брошюры сделан компилятор Forth (избранные главы смотрите в приложении)
    5. Статья из журнала "Системный Администратор" 06.2004, Крис Касперски "Путь Воина - Внедрение в PE/COFF - файлы"
    Очень полезная статья о структуре EXE-файла.
    6. Книжка о Delphi.

    Арифметический стек

    В качестве арифметического стека - очень хорошо подходит обычный стек с его командами push и pop.
    Пример:
    Фрагмент программы на Forth:

    1 2 3 4 + * swap drop

    вот как это будет выглядить на ассемблере:

    Код (Text):
    1.   push 1            ; помещаем в арифметический стек число 1
    2.   push 2            ; помещаем в арифметический стек число 2
    3.   push 3            ; помещаем в арифметический стек число 3
    4.   push 4            ; помещаем в арифметический стек число 4
    5.   pop eax          ; выполнение слова "+", обычное сложение,
    6.   pop ebx          ; т.е. снимаем со стека два числа,
    7.   add eax, ebx   ; складываем их и полученный результат
    8.   push eax         ; помещаем на вершину стека
    9.   pop eax         ; "*" - умножение, выполняется аналогично сложению,
    10.   pop ebx         ; надо учитывать что результат
    11.   imul ebx         ; помещается в edx:eax - и результат
    12.   push eax       ; будет правилен только если он уместился целиком в регистр eax
    13.   pop eax         ; меняет местами два последних значения на стеке (слово swap)
    14.   pop ebx
    15.   push eax
    16.   push ebx
    17.   pop eax         ; снимает значение с вершины стека (слово drop)
    А теперь запишем все эти команды ассемблера в машинных кодах (можно для этого воспользоваться справочником Юрова) или подсмотреть в отладчике среды Delphi

    Код (Text):
    1.   asm
    2.      push 1
    3.      push 2
    4.   end;
    поставить точку останова (выбрать строку и нажать F5), запустить программу на выполнение (клавиша F9), после того как выполнение остановиться на точки останова, посмотреть это всё в машинных кодах (Ctrl+Alt+C).

    Код (Text):
    1.  68 01 00 00 00       -   push 1
    2.  68 02 00 00 00       -   push 2
    3.  68 03 00 00 00       -   push 3
    4.  68 04 00 00 00       -   push 4
    5.  5b                         -   pop ebx
    6.  58                         -   pop eax
    7.  01 d8                    -   add eax, ebx
    8.  50                         -  push eax
    9.  ...
    ну и все остальные команды (слова Forth) которые работают каким-либо образом с арифметическим стеком - выполняются аналогично - значения с вершины стека помещаются в какой-либо регистр (eax, ebx, edx, ...) потом над этими регистрами производятся какие-либо действия, затем результат(ы) из регистров помещаются обратно на стек (если это необходимо).

    пример: Компиляция слов работающих с арифметическим стеком

    Код (Text):
    1. while u1.token_type <> u1.FINISH do
    2.     begin
    3.       u1.GetToken1;
    4.        …
    5.  
    6.       if u1.token_type = u1.NUM then
    7.          begin
    8.            tmp:=$68; fs.Write(tmp, 1);
    9.            tmp:=StrToInt(u1.token) ; fs.Write(tmp, 4);   {push NUM}
    10.            continue;
    11.          end;
    12.  
    13.       if u1.token = 'drop' then
    14.          begin
    15.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    16.            continue;
    17.          end;
    18.       if u1.token = 'dup' then
    19.          begin
    20.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    21.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    22.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    23.            continue;
    24.          end;
    25.       if u1.token = 'swap' then
    26.          begin
    27.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    28.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    29.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    30.            tmp:=$53; fs.Write(tmp, 1);   {push ebx}
    31.            continue;
    32.          end;
    33.  
    34.       if u1.token = '-' then
    35.          begin
    36.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    37.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    38.            tmp:=$29; fs.Write(tmp, 1);
    39.            tmp:=$d8; fs.Write(tmp, 1);   {sub eax, ebx}
    40.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    41.            continue;
    42.          end;
    43.       if u1.token = '+' then
    44.          begin
    45.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    46.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    47.            tmp:=$01; fs.Write(tmp, 1);
    48.            tmp:=$d8; fs.Write(tmp, 1);   {add eax, ebx}
    49.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    50.            continue;
    51.          end;
    52.       if u1.token = '*' then
    53.          begin
    54.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    55.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    56.            tmp:=$f7; fs.Write(tmp, 1);
    57.            tmp:=$eb; fs.Write(tmp, 1);   {imul ebx}
    58.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    59.            continue;
    60.          end;
    61.       if u1.token = 'negate' then
    62.          begin
    63.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    64.            tmp:=$f7; fs.Write(tmp, 1);
    65.            tmp:=$d8; fs.Write(tmp, 1);   {neg eax}
    66.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    67.            continue;
    68.          end;
    69.       if u1.token = '/' then
    70.          begin
    71.            tmp:=$31; fs.Write(tmp, 1);
    72.            tmp:=$d2; fs.Write(tmp, 1);   {xor edx, edx}
    73.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    74.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    75.            tmp:=$f7; fs.Write(tmp, 1);
    76.            tmp:=$fb; fs.Write(tmp, 1);   {idiv ebx}
    77.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    78.            continue;
    79.          end;
    80.       if u1.token = 'mod' then
    81.          begin
    82.            tmp:=$31; fs.Write(tmp, 1);
    83.            tmp:=$d2; fs.Write(tmp, 1);   {xor edx, edx}
    84.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    85.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    86.            tmp:=$f7; fs.Write(tmp, 1);
    87.            tmp:=$fb; fs.Write(tmp, 1);   {idiv ebx}
    88.            tmp:=$52; fs.Write(tmp, 1);   {push edx}
    89.            continue;
    90.          end;
    91.       if u1.token = '/mod' then
    92.          begin
    93.            tmp:=$31; fs.Write(tmp, 1);
    94.            tmp:=$d2; fs.Write(tmp, 1);   {xor edx, edx}
    95.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    96.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    97.            tmp:=$f7; fs.Write(tmp, 1);
    98.            tmp:=$fb; fs.Write(tmp, 1);   {idiv ebx}
    99.            tmp:=$52; fs.Write(tmp, 1);   {push edx}
    100.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    101.            continue;
    102.          end;
    103.       if u1.token = '1+' then
    104.          begin
    105.            tmp:=$ff; fs.Write(tmp, 1);
    106.            tmp:=$04; fs.Write(tmp, 1);
    107.            tmp:=$24; fs.Write(tmp, 1);  {inc [esp]}
    108.            continue;
    109.          end;
    110.       if u1.token = '1-' then
    111.          begin
    112.            tmp:=$ff; fs.Write(tmp, 1);
    113.            tmp:=$0c; fs.Write(tmp, 1);
    114.            tmp:=$24; fs.Write(tmp, 1);  {dec [esp]}
    115.            continue;
    116.          end;
    117.       if u1.token = 'and' then
    118.          begin
    119.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    120.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    121.            tmp:=$23; fs.Write(tmp, 1);
    122.            tmp:=$c3; fs.Write(tmp, 1);   {and eax, ebx}
    123.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    124.            continue;
    125.          end;
    126.       if u1.token = 'or' then
    127.          begin
    128.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    129.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    130.            tmp:=$0b; fs.Write(tmp, 1);
    131.            tmp:=$c3; fs.Write(tmp, 1);   {or eax, ebx}
    132.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    133.            continue;
    134.          end;
    135.       if u1.token = 'not' then
    136.          begin
    137.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    138.            tmp:=$f7; fs.Write(tmp, 1);
    139.            tmp:=$d0; fs.Write(tmp, 1);   {not eax}
    140.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    141.            continue;
    142.          end;
    143.       if u1.token = 'xor' then
    144.          begin
    145.            tmp:=$5b; fs.Write(tmp, 1);   {pop ebx}
    146.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    147.            tmp:=$33; fs.Write(tmp, 1);
    148.            tmp:=$c3; fs.Write(tmp, 1);   {xor eax, ebx}
    149.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    150.            continue;
    151.          end;
    152.        …
    153.     end;
    Стек возвратов

    Адрес вершины стека возвратов храниться в регистре EBP. Сам стек возвратов находится в самом конце памяти выделяемой для загрузки exe-файла, это последняя секция (.kf) неинициализированных данных (размер этой секции на размер exe-файла не влияет, поэтому размер стека возвратов можно изменять – изменяя размер секции .kf .
    Слова языка Forth которые работают со стеком возвратов :
    >r – снимает значение с арифметического стека и ложит это значение на стек возвратов

    Код (Text):
    1. if u1.token = '>r' then
    2.          begin
    3.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    4.            tmp:=$83; fs.Write(tmp, 1);
    5.            tmp:=$ed; fs.Write(tmp, 1);
    6.            tmp:=$04; fs.Write(tmp, 1); {sub ebp,4}
    7.            tmp:=$89; fs.Write(tmp, 1);
    8.            tmp:=$45; fs.Write(tmp, 1);
    9.            tmp:=$00; fs.Write(tmp, 1); {mov [ebp],eax}
    10.            continue;
    11.          end;
    r> - снимает значение с вершины стека возвратов и кладет его на вершину арифметического стека

    Код (Text):
    1. if u1.token = 'r>' then
    2.          begin
    3.            tmp:=$8b; fs.Write(tmp, 1);
    4.            tmp:=$45; fs.Write(tmp, 1);
    5.            tmp:=$00; fs.Write(tmp, 1); {mov eax,[ebp]}
    6.            tmp:=$83; fs.Write(tmp, 1);
    7.            tmp:=$c5; fs.Write(tmp, 1);
    8.            tmp:=$04; fs.Write(tmp, 1); {add ebp, 4}
    9.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    10.            continue;
    11.          end;
    и r@ - копирует значение со стека возвратов (оставляя стек возвратов нетронутым) на вершину арифметического стека

    Код (Text):
    1. if u1.token = 'r@' then
    2.          begin
    3.            tmp:=$8b; fs.Write(tmp, 1);
    4.            tmp:=$45; fs.Write(tmp, 1);
    5.            tmp:=$00; fs.Write(tmp, 1); {mov eax,[ebp]}
    6.            tmp:=$50; fs.Write(tmp, 1);   {push eax}
    7.            continue;
    8.          end;
    Описание новых слов

    В Forth для описания слова используется следующий синтаксис :

    : <имя нового слова> (<слово>) ;

    например так :

    : сто 100 ;
    : view_here here . ;

    слово «сто» - если оно встретится в тексте программы при выполнении положит на вершину арифметического стека число 100, а второе слово выведет на экран текущее значение вершины кодофайла ( here – кладёт на вершину стека это значение, а слово «.» (точка) – снимает значение с вершины стека и выводит это значение на экран ).

    При выполнении новых слов используется стек возвратов – перед выполнением слова в этот стек помещается адрес - по которому будет сделан переход после выполнения слова (т.е. слово ; (точка с запятой) или exit снимут с вершины стека возвратов значения адреса и передадут туда управление). Поэтому компиляция начала определения нового слова будет выглядеть так :

    Код (Text):
    1. if u1.token = ':' then
    2.          begin
    3.            u1.GetToken1;
    4.            u2.Add(u1.token, fs.Position - _fCode + _BaseOfCode ,0);
    5.              begin
    6.                 tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    7.                 tmp:=$83; fs.Write(tmp, 1);
    8.                 tmp:=$ed; fs.Write(tmp, 1);
    9.                 tmp:=$04; fs.Write(tmp, 1); {sub ebp,4}
    10.                 tmp:=$89; fs.Write(tmp, 1);
    11.                 tmp:=$45; fs.Write(tmp, 1);
    12.                 tmp:=$00; fs.Write(tmp, 1); {mov [ebp],eax}
    13.              end;
    14.            continue;
    15.          end;
    здесь модуль u1 с его функцией Add используется для запоминания начала адреса в памяти определяемого слова.

    А это окончание определения нового слова (или слово exit) :

    Код (Text):
    1.       if (u1.token = ';') or (u1.token = 'exit') then
    2.          begin
    3.            tmp:=$8b; fs.Write(tmp, 1);
    4.            tmp:=$45; fs.Write(tmp, 1);
    5.            tmp:=$00; fs.Write(tmp, 1); {mov eax,[ebp]}
    6.            tmp:=$83; fs.Write(tmp, 1);
    7.            tmp:=$c5; fs.Write(tmp, 1);
    8.            tmp:=$04; fs.Write(tmp, 1); {add ebp, 4}
    9.            tmp:=$ff; fs.Write(tmp, 1);
    10.            tmp:=$e0; fs.Write(tmp, 1);   {jmp eax}
    11.            continue;
    12.          end;
    Вот что компилируется и как если токен не известен компилятору(т.е. это не стандартное слово которые он знает, а новое слово которое определено раньше по тексту программы):

    Код (Text):
    1.       if u2.Find(u1.token, _adr, _size) then
    2.          begin
    3.            if _size = 0 then
    4.              begin
    5.                tmp:=$e8; fs.Write(tmp, 1);
    6.                tmp:=dword((_adr - _BaseOfCode) - (fs.Position - _fCode + 4)) ; fs.Write(tmp, 4);   {call смещ32}
    7.              end;
    8.            continue;
    9.          end;
    здесь модуль u1 с его функцией Find используется для поиска начала адреса в памяти ранее определенного слова по имени.


    Управляющие конструкции

    В Forth слова управляющих конструкций т.к. if, then, else, begin и др. – имеют признак немедленного исполнения и активно используют арифметический стек, т.к. в компиляторе во время компиляции EXE-файла, арифметического стека не существует – для его эмуляции используется модуль u3 с функциями procedure PUSH(i:integer) и function POP:integer.

    Код (Text):
    1. if u1.token = 'if' then
    2.          begin
    3.            tmp:=$58; fs.Write(tmp, 1);   {pop eax}
    4.            tmp:=$0b; fs.Write(tmp, 1);
    5.            tmp:=$c0; fs.Write(tmp, 1);   {or eax, eax}
    6.            tmp:=$0f; fs.Write(tmp, 1);
    7.            tmp:=$84; fs.Write(tmp, 1);
    8.            u3.PUSH(fs.Position);
    9.            u3.PUSH(2);
    10.            tmp:=$0; fs.Write(tmp, 4);    {jz metka32}
    11.            continue;
    12.          end;
    Слово if компилируется с командой условного перехода (jz), адрес (на самом деле это не реальный адрес, а fs.Position, но этого достаточно) по которому находится смещение этой команды (по началу это 0) – запоминаем с помощью PUSH.

    Код (Text):
    1.       if u1.token = 'then' then
    2.          begin
    3.            if u3.POP = 2 then
    4.              begin
    5.                tmp := u3.POP;
    6.                pdword(dword(fs.Memory) + tmp)^ := fs.Position - tmp - 4;
    7.              end;
    8.            continue;
    9.          end;
    Слово then устанавливает по тому адресу который был запомнен словом if реальное смещение которое и высчитывает, не компилируя нового кода.

    Код (Text):
    1.       if u1.token = 'else' then
    2.          begin
    3.            if u3.POP = 2 then
    4.              begin
    5.                tmp:=$e9; fs.Write(tmp, 1);
    6.                tmp:=$0; fs.Write(tmp, 4); {jmp смещ32}
    7.                tmp := u3.POP;
    8.                pdword(dword(fs.Memory) + tmp)^ := fs.Position - tmp - 4;
    9.                u3.PUSH(fs.Position - 4);
    10.                u3.PUSH(2);
    11.              end;
    12.            continue;
    13.          end;
    Пример:
    Слово abs – берет с арифметического стека число и кладёт туда модуль этого числа

    : abs dup 0 < if negate then ;



    Создание EXE-файла. Заголовок.

    Особенности и тонкости структуры исполняемого файла хорошо описаны у Криса Касперски, образ файла создаем в памяти - пригодится для этого класс – TMemoryStream, в последующим туда же будет происходить и компиляция. Для заголовка exe-файла понадобятся структуры модуля Windows :
    TImageDosHeader, TImageNtHeader и структура TImageSection.

    Код (Text):
    1. procedure CompileForth(prg : pchar; fn_exe : string);
    2. var
    3.   fs : TMemoryStream;
    4.   headDos : TImageDosHeader;
    5.   inh : TImageNtHeaders;
    6.   ish : TImageSectionHeader;
    7.   //
    8. fs := TMemoryStream.Create;
    9.   fs.SetSize(_fData + _fszData);
    10.   FillChar((fs.Memory)^, fs.Size, #$90);
    11.   FillChar(headDos, sizeof(headDos),#0);
    12.   headDos.e_magic := IMAGE_DOS_SIGNATURE; {MZ}
    13.   headDos.e_cblp := 64+15+strlen(stroka); {кол-во байт в последней странице файла}
    14.   headDos.e_cp := 1; {одна страница - длина файла}
    15.   headDos.e_crlc := 0; {кол-во эл-тов в таблице размещения}
    16.   headDos.e_cparhdr := 4; {длина заголовка в параграфах}
    17.   headDos.e_maxalloc := $ffff;
    18.   headDos.e_sp := $b8;
    19.   headDos.e_ip := 0;
    20.   headDos.e_cs := 0;
    21.   headDos.e_lfarlc :=  $40; {это PE-файл}
    22.   headDos._lfanew := 256;
    23.   fs.Position := 0;
    24.   fs.WriteBuffer(headDos, sizeof(headDos));
    25.   //------------заглушка--------------------
    26.   fs.WriteBuffer(zaglushka, sizeof(zaglushka));
    27.   fs.WriteBuffer(stroka, strlen(stroka));
    28.   //------------PE-заголовок----------------
    29.   fs.Position := 256;
    30.   FillChar(inh, sizeof(inh), #0);
    31.   inh.Signature := IMAGE_NT_SIGNATURE; {PE/0/0}
    32.   inh.FileHeader.Machine := IMAGE_FILE_MACHINE_I386;
    33.   inh.FileHeader.NumberOfSections := 3;  {кол-во секций}
    34.   inh.FileHeader.TimeDateStamp := _DateExe;
    35.   inh.FileHeader.SizeOfOptionalHeader := 224; {размер дополнительного заголовка}
    36.   inh.FileHeader.Characteristics := $818f;
    37.   inh.OptionalHeader.Magic := $010b;
    38.   inh.OptionalHeader.MajorLinkerVersion := 4;
    39.   inh.OptionalHeader.MinorLinkerVersion := 2;
    40.   inh.OptionalHeader.SizeOfCode := code_size;
    41.   inh.OptionalHeader.SizeOfInitializedData := data_size;
    42.   inh.OptionalHeader.SizeOfUninitializedData := 0;
    43.   inh.OptionalHeader.AddressOfEntryPoint := $1000; {точка входа}
    44.   inh.OptionalHeader.BaseOfCode := _BaseOfCode;
    45.   inh.OptionalHeader.BaseOfData := _BaseOfData;
    46.   inh.OptionalHeader.ImageBase := _ImageBase;
    47.   inh.OptionalHeader.SectionAlignment := $1000;
    48.   inh.OptionalHeader.FileAlignment := $200;
    49.   inh.OptionalHeader.MajorOperatingSystemVersion := 1;
    50.   inh.OptionalHeader.MinorOperatingSystemVersion := 0;
    51.   inh.OptionalHeader.MajorImageVersion := 0;
    52.   inh.OptionalHeader.MinorImageVersion := 0;
    53.   inh.OptionalHeader.MajorSubsystemVersion := 3;
    54.   inh.OptionalHeader.MinorSubsystemVersion := 10;
    55.   inh.OptionalHeader.SizeOfImage := size_of_image;
    56.   inh.OptionalHeader.SizeOfHeaders := size_of_head;
    57.   inh.OptionalHeader.CheckSum := 0;
    58.   inh.OptionalHeader.Subsystem := _Subsystem;
    59.   inh.OptionalHeader.SizeOfStackReserve := 0;
    60.   inh.OptionalHeader.SizeOfStackCommit := 0;
    61.   inh.OptionalHeader.SizeOfHeapReserve := 0;
    62.   inh.OptionalHeader.SizeOfHeapCommit := 0;
    63.   inh.OptionalHeader.NumberOfRvaAndSizes := 16;
    64.         //----------------------------------------
    65.         szi := Import_1(pointer(dword(fs.Memory) + _fData) , _BaseOfData,
    66.                         [5,2],
    67.                         ['kernel32.dll', 'ExitProcess', 'GetStdHandle', 'GetProcAddress', 'LoadLibraryA', 'WriteConsoleA', 'user32.dll', 'MessageBoxA', 'wsprintfA']);
    68.         //----------------------------------------
    69.   inh.OptionalHeader.DataDirectory[1].VirtualAddress := _BaseOfData;    {import}
    70.   inh.OptionalHeader.DataDirectory[1].Size := szi;
    71.   fs.WriteBuffer(inh, sizeof(inh));
    72.  
    73. Заголовок Dos-части – стандартен, вывод строки и завершение работы программы.
    74.  
    75. Import_1 – эта функция подготавливает таблицу импорта, функций 'ExitProcess', 'GetStdHandle', 'GetProcAddress', 'LoadLibraryA', 'WriteConsoleA', 'MessageBoxA', 'wsprintfA' – вполне достаточно, таблица импорта находится в секции данных.
    76.  
    77. Используется 3-и секции – для кода (.code), для данных(.data) и неинициализированных данных (.kf) – кодофайл.
    78.  
    79. //------------секции----------------------
    80.   FillChar(ish, sizeof(ish), #0);
    81.   strcopy(pchar(@(ish.Name)), '.code');
    82.   ish.VirtualAddress := _BaseOfCode;
    83.   ish.Misc.VirtualSize := $3000;
    84.   ish.SizeOfRawData := _fszCode;
    85.   ish.PointerToRawData := _fCode;
    86.   ish.Characteristics := IMAGE_SCN_CNT_CODE or IMAGE_SCN_MEM_EXECUTE or IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE;
    87.   fs.WriteBuffer(ish, sizeof(ish));
    88.   FillChar(ish, sizeof(ish), #0);
    89.   strcopy(pchar(@(ish.Name)), '.data');
    90.   ish.VirtualAddress := _BaseOfData;
    91.   ish.Misc.VirtualSize := $1000;
    92.   ish.SizeOfRawData := _fszData;
    93.   ish.PointerToRawData := _fData;
    94.   ish.Characteristics := IMAGE_SCN_CNT_INITIALIZED_DATA or IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE;
    95.   fs.WriteBuffer(ish, sizeof(ish));
    96.   FillChar(ish, sizeof(ish), #0);
    97.   strcopy(pchar(@(ish.Name)), '.kf');
    98.   ish.VirtualAddress := _BaseOfData + $1000;
    99.   ish.Misc.VirtualSize := $1000;
    100.   ish.SizeOfRawData := 0;
    101.   ish.PointerToRawData := 0;
    102.   ish.Characteristics := IMAGE_SCN_CNT_UNINITIALIZED_DATA or IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE;
    103.   fs.WriteBuffer(ish, sizeof(ish));
    Создание EXE-файла. Данные и исполняемый код.

    исходик - http://kufal.narod.ru/src.zip
     
  2. twgt

    twgt New Member

    Публикаций:
    0
    Регистрация:
    15 янв 2007
    Сообщения:
    1.494
    В исходниках лежит RevaForth написанный на fasm.