Как уменьшить размер плагина

(на примере Watcom v11)
Эдуард Султанов [ed@it.nsc.ru]ed@it.nsc.ru

Ровно такие же проблемы были и у меня. Результат похож, но другой компилятор - другие заморочки. Если у Вас Watcom и Вам неинтересны причины, то найдете в конце примеры makefile и plugin.lnk. Если у Вас другой компилятор - посмотрите все вплоть до конкретной реализации, может Вам это поможет.

Конкретнее:

I. Немного теории.

Залог успешного похудения вашего плагина состоит в контроле того что используете вы сами, а что ваш компилятор:

1. Используемые функции.

Желательно использовать Win API и не пользовать без разбора библиотечные функции типа printf и a2i. Как правило все функции семейства используют одну виртуальную и соответственно донельзя универсальную. Можно привести много причин вредности использовании, но нас интересует одна: размер. Можете мне не верить и попробовать заменить ваш printf на несколько strcat или еще лучше (но не на много) вручную сбить строку. Хитростей тут нет. Это не зависит от компилятора. Просто надо немного думать :-) В конце концов пару простеньких функций сделать несложно.

2. Скрытые функции.

Да, любой современный компилятор в обьектнике выдает больше чем вы написали в исходнике. Во-первых startup - инициализация переменных, используемых библиотеками, инициализация памяти и еще много чего. Как правило ненужного. Во-вторых проверка стека. Все они будут видны если вы дизассемблируете обьектный файл. И вообще иногда бывает интересно посмотреть на результат работы компилятора.

3. Подключаемые библиотеки

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

II. Немного работы ручками.

Если скомпилировать плагин, например так: "wpp386 -bd -d0 -mf Blocks.cpp". Потом получившийся файл отдать на растерзание дизассемблеру: "wdis -a Blocks.obj > Blocks.asm", получиться примерно следующее:

---------------- 'Block.asm' ------------------
.387
.386p
.model flat
                PUBLIC  `_SetStartupInfo@4`
                PUBLIC  `_OpenPlugin@8`
                PUBLIC  `_GetPluginInfo@4`
; Это,понятно, то что мы экспортируем в Far.
; К стати, полезно видеть как это выглядит. Об этом чуть ниже.

                PUBLIC  `W?GetMsg$n(i)pna`
; "Наша" собственная функция. Позаимствовал у ER :-)

                EXTRN   __CHK:BYTE
; Вот !!! Проверка стека вначале функции.

                EXTRN   memset_:BYTE
; Библиотечная функция

                EXTRN   `__imp__RegisterClipboardFormatA@4`:BYTE
; Импорт. Из DLL-ки. Об этом чуть ниже

                EXTRN   __DLLstart_:BYTE
; Вот !!! Так называемый startup.
---------------- !'Block.asm' ------------------

Чтобы не смотреть беспомощно на то что вытворяет линкер с вашим обьектником, к стати обычно не большим, нужно взять все под контроль. Лично я не стал цацкатся и оглядываться на копирайты и просто взял рекомендуемую для Win32 DLL библиотеку CLIB3R.lib и распотрошил. Достал оттуда DLLStart и функции mem* и str*. Оставил от всего DLLStart только

       mov       eax,00000001H
       ret       0000000cH

ну и естественно немного подправленный копирайт :-) Слинковал я все это в библиотеку mindll.lib (входит в поставку энциклопедии). Получилось 5.6K. Я ничего не сказал про __CHK, потому что отключил проверку стека. Но, если Вам это не по душе, можете включить в библиотеку и ее. Только лучше,сначала, маленько прохудить. Лично я не нашел в ней ничего полезного.

Теперь о подключаемых DLL. Каждый компилятор имеет вариант своей статической библиотеки в виде DLL. Для такой динамической линковки есть свой lib-файл. Так что если вы экспериментируете с библиотеками и Вам удалось наконец найти библиотеку с которой dll-ка плагина не ужасающе громадна - посмотрите на вашу dll-ку QuickViewer'ом - что ему надо для работы. Если это что-то помимо user32.dll, kernel32.dll, advapi32.dll то ваш плагин, скорее всего, будет работать только у пользователей такого же компилятора. Так что лучше обрубить линкеру крылья и все библиотеки прописывать самому. Так вернее.

III. Собственно это все. Теперь примеры:

;---------------- 'makefile' ------------------
# Это собственно рабочий кусок.
#Я привык их делать сам, а не из IDE - так понятнее.

Blocks.dll : Blocks.obj
         wlink file Blocks.obj @plugin >err.log

# ну тут наверно все понятно файл plugin.lnk будет чуть позже.

Blocks.obj : Blocks.cpp Blocks.hpp
         wpp386 -3r -bd -d0 -mf -s Blocks.cpp

# ключи:
# -3r оптимизация под 386 и передача параметров через регистр.
# -bd - для DLL.
# -d0 - без информации для дебагера.
# -mf - Модель памяти flat.
# -s - отключить проверку стека.
---------------- !'makefile' ------------------
;---------------- 'plugin.lnk' ------------------
FORMAT
    WINDOWS NT DLL
# Это формат выходного файла

OPTION
    NODEFAULTLIB,
    NOSTDCALL
# Первое - чтобы линкер не подключил что не надо
# Второе - чтобы экспортные функции экспортировались "как есть"
# к стати вот как они декларируются:
# extern "C"
# {
# void WINAPI _export SetStartupInfo(struct PluginStartupInfo *Info);
# HANDLE WINAPI _export OpenPlugin(int OpenFrom,int Item);
# void WINAPI _export GetPluginInfo(struct PluginInfo *Info);
# };
# обратите внимание на ключевое слово "_export"

LIBRARY
     mindll.lib,
     USER32.LIB,
     KERNEL32.LIB,
     ADVAPI32.LIB

# Здесь перечисленные сами библиотеки.
# Первое описано выше
# Со второй по четвертую - соответствуют стандартным DLL-кам.

RUNTIME CONSOLE
# Особо ни на что не влияет.
---------------- !'plugin.lnk' ------------------

Вот с этим всем из 4K С++ файла получаем 4K dll. Это конечно не показатель, но все же :-)


 

 
18.10.1999
наверх