1 / 1 / 0 Регистрация: 19.06.2018 Сообщений: 173 |
|
1 |
|
09.07.2021, 16:04. Показов 1237. Ответов 4
Добрый день, возник вопрос, как изменить размер exe файла на +- 10кб, но при этом, чтобы он работал стабильно. Добавлено через 4 минуты
__________________
0 |
Programming Эксперт 94731 / 64177 / 26122 Регистрация: 12.04.2006 Сообщений: 116,782 |
09.07.2021, 16:04 |
4 |
-253 / 68 / 21 Регистрация: 08.05.2021 Сообщений: 289 |
|
09.07.2021, 16:46 |
2 |
strip <your-file>. Либо винковать что-нибудь.
0 |
3222 / 2481 / 429 Регистрация: 03.05.2011 Сообщений: 5,158 Записей в блоге: 21 |
|
09.07.2021, 22:53 |
3 |
Вопрос — что вам дадут эти 10 килобайт?
0 |
Just Do It! 3427 / 1897 / 624 Регистрация: 23.09.2014 Сообщений: 6,001 Записей в блоге: 1 |
|
11.07.2021, 18:36 |
4 |
Как изменить размер exe файла, не мешая его работе? вот здесь я игрался: ещё такая была тема: как оно там влияет на работоспособность, то я хз,
0 |
587 / 561 / 91 Регистрация: 29.05.2015 Сообщений: 3,615 |
|
13.07.2021, 23:58 |
5 |
Просто дописать какие-нибудь байты в конец файла не получится?
0 |
IT_Exp Эксперт 87844 / 49110 / 22898 Регистрация: 17.06.2006 Сообщений: 92,604 |
13.07.2021, 23:58 |
Помогаю со студенческими работами здесь Как уменьшить размер EXE файла? Как уменьшить размер exe файла? Как уменьшить размер exe-файла? Как уменьшить размер exe-файла? Как изменить размер текста в зависимости от его длины в кнопке? Как изменить размер фонта у виджета и всех его детишек? Искать еще темы с ответами Или воспользуйтесь поиском по форуму: 5 |
Introduction
A few days back I was assigned a challenging task of writing a Win32 application performing tasks (as defined by our client). But the client required that the final exe file size to be less than 50K. With this kind of size limit we immediately had to give up an MFC based solution, a tough decision to make in most cases :). I am very comfortable with MFC and never had bothered about final file size. But for this specific project I had to dig into pure Win32 SDK to get things done. The project was started and the first prototype was sized about 36K and everyone was happy. But problems were raised when we started implementing the user requirements and one day we found that the size of the exe file in release mode had exceeded the 50K limit. And when the final product was about to be shipped the size had reached about 72K. We were thinking about re-nagotiating the size limit with our client. Fortunately I posted a question on one of the Microsoft news threads and got some prompt replies. I tried to test the instructions that I received from the news thread on my application, and the result were simply amazing, my file size reduced to 35K resulting in about 50% files size reduction. We were happy and so was our client
So what are those magic tricks? Actually all you have to do is to instruct compiler to leave out unnecessary code from your application. If you create a simple HelloWorld Win32 Application from the app wizard and compile it in release mode, the resulting exe file size is 24K. Too much for a simple «Hello World» application. Now, perform the following steps:
- Create another similar project workspace, i.e. another «Hello World» project.
- Select active build configuration to be «Win32 Release».
- Open project setting and select the «Link» tab. Remove all the library file names from the «Object/library modules:» edit box and type «MSVCRT.LIB kernel32.lib user32.lib». Press OK and compile, the final exe file size is reduced to 16K. You might get a linker warning like «
LINK : warning LNK4098: default lib "LIBC" conflicts with use of other libs; use /NODEFAULTLIB:library
«. This can be avoided by clicking «Ignore all default libraries» from the Link tab of Project Settings. - The further magic is done by using
/ALIGN
linker option. You can see MSDN for further details about this linker option. Again go to the «Project Settings», «Link» tab. Type/ALIGN:4096
in the Project Options edit box. Press OK and rebuild your project. The size is further reduced to 3K. The compiler generates a linker warning «LINK : warning LNK4108: /ALIGN specified without /DRIVER or /VXD; image may not run
«. If you have followed my instructions properly, your exe file should run properly. I played around with the/ALIGN
linker directive and figured out that if you use/ALIGN:4096
the produced exe file runs properly.
That’s it, we have reduced size of our Hello World app from 24K to 3K. There are some other options that you can try in this regard:
- You can run some symbol stripper utility on the final exe file. It helps reducing size in many cases. One such utility can be found at neoworx.com.
- There is a utility named ASPack, this utility also helps in compressing your exe files up to 50%.
MFC
I haven’t yet tried these tricks with MFC projects, so results are unpredictable. But that should not stop you from trying these options on MFC projects.
This member has not yet provided a Biography. Assume it’s interesting and varied, and probably something to do with programming.
Introduction
A few days back I was assigned a challenging task of writing a Win32 application performing tasks (as defined by our client). But the client required that the final exe file size to be less than 50K. With this kind of size limit we immediately had to give up an MFC based solution, a tough decision to make in most cases :). I am very comfortable with MFC and never had bothered about final file size. But for this specific project I had to dig into pure Win32 SDK to get things done. The project was started and the first prototype was sized about 36K and everyone was happy. But problems were raised when we started implementing the user requirements and one day we found that the size of the exe file in release mode had exceeded the 50K limit. And when the final product was about to be shipped the size had reached about 72K. We were thinking about re-nagotiating the size limit with our client. Fortunately I posted a question on one of the Microsoft news threads and got some prompt replies. I tried to test the instructions that I received from the news thread on my application, and the result were simply amazing, my file size reduced to 35K resulting in about 50% files size reduction. We were happy and so was our client
So what are those magic tricks? Actually all you have to do is to instruct compiler to leave out unnecessary code from your application. If you create a simple HelloWorld Win32 Application from the app wizard and compile it in release mode, the resulting exe file size is 24K. Too much for a simple «Hello World» application. Now, perform the following steps:
- Create another similar project workspace, i.e. another «Hello World» project.
- Select active build configuration to be «Win32 Release».
- Open project setting and select the «Link» tab. Remove all the library file names from the «Object/library modules:» edit box and type «MSVCRT.LIB kernel32.lib user32.lib». Press OK and compile, the final exe file size is reduced to 16K. You might get a linker warning like «
LINK : warning LNK4098: default lib "LIBC" conflicts with use of other libs; use /NODEFAULTLIB:library
«. This can be avoided by clicking «Ignore all default libraries» from the Link tab of Project Settings. - The further magic is done by using
/ALIGN
linker option. You can see MSDN for further details about this linker option. Again go to the «Project Settings», «Link» tab. Type/ALIGN:4096
in the Project Options edit box. Press OK and rebuild your project. The size is further reduced to 3K. The compiler generates a linker warning «LINK : warning LNK4108: /ALIGN specified without /DRIVER or /VXD; image may not run
«. If you have followed my instructions properly, your exe file should run properly. I played around with the/ALIGN
linker directive and figured out that if you use/ALIGN:4096
the produced exe file runs properly.
That’s it, we have reduced size of our Hello World app from 24K to 3K. There are some other options that you can try in this regard:
- You can run some symbol stripper utility on the final exe file. It helps reducing size in many cases. One such utility can be found at neoworx.com.
- There is a utility named ASPack, this utility also helps in compressing your exe files up to 50%.
MFC
I haven’t yet tried these tricks with MFC projects, so results are unpredictable. But that should not stop you from trying these options on MFC projects.
This member has not yet provided a Biography. Assume it’s interesting and varied, and probably something to do with programming.
Уменьшение размера исполняемого файла в Lazarus
Cоздаваемые в Lazarus исполняемые файлы имеют довольно большой размер. Причин несколько. Во-первых, в exe-файле сохраняется вся отладочная информация. Во вторых — создаваемый exe-файл оптимизирован под скорость выполнения, а не под размер.
Чтобы уменьшить размер исполняемого файла необходимо в свойствах проекта (Проект — Параметры проекта) включить 4 ключа компиляции:
1) Вкладка Генерация кода: установить флажок «Умная компоновка» (-СХ);
2) Вкладка Компоновка: установить флажок «Умная компоновка» (-ХХ);
3) Вкладка Компоновка: установить флажок «Использовать внешний файл отладочных символов GDB»(-Xg);
4) Вкладка Компоновка: установить флажок «Вырезать символы из исполняемого файла»(-Xs).
Эта настройка позволяет сократить размер исполняемого файла в несколько раз. (Пустой проект с 12 МБ сокращается до 1 МБ).
Однако, можно еще сократить размер, если воспользоваться утилитами strip.exe и upx.exe. Первая вырезает из файла отладочную информацию, вторая сжимает запускаемый файл. Удобно создать bat-файл
compress.bat
и запускать сжатие одной командой:
Утилиты прикреплены ниже.
Прикрепленный файл | Размер |
---|---|
Утилиты компрессии exe-файла Lazarus | 401.35 кб |
Комментарии
Здравствуйте,создал проект написал программу на лазарус,потом выставил и убрал все галочки для удаления отладочной инфы, сохранил и собрал проект ..но размер файла как был мегабайтный так ничего и не изменилось..
что я делаю не так?
и второй вопрос: скачал я утилиты компрессии exe-файла лазарус и как ими пользоваться?
если есть возможность объясните мне эти два вопроса по шагам
с Уважением Юрий
CompactGUI — легко сжимаем тяжёлые программы и игры
CompactGUI — это графический интерфейс для системной утилиты compact.exe, который предлагается использовать нам с вами для сжатия тяжёлых программ, игр или просто объёмных папок и файлов. Программа очень проста в использовании: в большинстве случаев пользователю достаточно указать путь к нужной папке и воспользоваться кнопкой Compress folder.
Опытным пользователям CompactGUI предоставляет возможность выбрать один из четырёх доступных алгоритмов сжатия, воспользоваться или отказаться от функция сжатия подпапок, скрытых и системных файлов. Программа умеет автоматически выключать или перезагружать компьютер по завершении операции сжатия и уведомлять пользователя о результатах работы. Для большего удобства CompactGUI можно интегрировать в контекстное меню Проводника.
Объём освобождаемого дискового пространства может отличаться в зависимости от выбранного алгоритма сжатия. В ходе тестирования мы легко умерили «вес» папки, в которую установлены некоторые популярные приложения Adobe (Fireworks, Dreamweaver, Premier) почти в два раза: с 3,1 до 1,5 ГБ. Никакого ущерба для скорости загрузки приложений или надёжности их использования на нашей тестовой системе (Intel Core i3/16 ГБ DDR3/120 ГБ SSD) мы не заметили вообще. Заметим, что использовать compact.exe на откровенно слабых машинах с небольшим объёмом оперативной памяти не рекомендует сама Microsoft.
Распространяется программа бесплатно, с открытым исходным кодом, новейшая версия всегда доступна на официальной странице на GitHub . Русский язык для интерфейса в текущей версии не поддерживается.
Что такое ASPack?
ASPack – усовершенствованная программа-упаковщик, предназначенная для сжатия исполняемых файлов EXE под Win32 и защиты от непрофессионального реверс-инжиниринга.
Решение уменьшает размер файлов и библиотек под Windows до 70% (степень сжатия выше стандарта ZIP на 10-20%), a также сокращает время загрузки таких приложений в локальных сетях и Интернет.
ASPack также защищает программы от непрофессионального анализа и декомпиляции. Приложения, сжатые при помощи ASPack, являются автономными и запускаются так же, как и до сжатия, без потери времени и ухудшения производительности.
Я хочу создать фиктивный EXE-файл Win32, который намного больше, чем должен быть. Таким образом, по умолчанию размер стандартного Win32 EXE-файла составляет 80 КБ. Мне нужен 5 МБ для тестирования других утилит.
Первая идея — добавить ресурс, но, как оказалось, встроенные ресурсы — это не то же самое, что 5 МБ кода, когда дело доходит до выделения памяти. Я думаю, что могу сослаться на большую библиотеку и получить огромный EXE-файл? Если нет, возможно, написать несколько тысяч подобных методов, таких как AddNum1, AddNum2 и т. Д., И т. Д.?
Любые простые идеи очень ценятся.
19 ответы
Используйте большой массив постоянная данные, такие как явные строки:
char *dummy_data[] = {
"blajkhsdlmf..(long script-generated random string)..",
"kjsdfgkhsdfgsdgklj..(etc...)...jldsjglkhsdghlsdhgjkh",
};
В отличие от переменных данных, постоянные данные часто попадают в тот же раздел памяти, что и фактический код, хотя это может зависеть от компилятора или компоновщика.
Редактировать: Я протестировал следующее, и он работает в Linux:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int i, j;
puts("char *dummy_data[] = {");
for (i = 0; i < 5000; i++) {
fputs(" "", stdout);
for (j = 0; j < 1000; j++) putchar('a' + rand() % 26);
puts("",");
}
puts("};");
return 0;
}
И этот код, и его вывод компилируются чисто.
ответ дан 01 окт ’10, 21:10
А как насчет простого определения большого статического массива символов?
char const bigarray[5*1024*1024] = { 1 };
См. Также мой другой ответ в этой ветке, где я предлагаю статически связываться с большими библиотеками. Это, безусловно, приведет к появлению реального кода, если вы просто укажете достаточно кода библиотек.
РЕДАКТИРОВАТЬ: добавлена ненулевая инициализация, так как данные, содержащие только нули, обрабатываются оптимизированным способом компилятором / компоновщиком.
РЕДАКТИРОВАТЬ: добавлена ссылка на мой другой ответ.
РЕДАКТИРОВАТЬ: добавлен квалификатор const, поэтому большой массив будет помещен в код многими компиляторами.
ответ дан 05 окт ’10, 15:10
char big[5*1024*1024] = {1};
Вам необходимо инициализировать его значением, отличным от 0, иначе компилятор / компоновщик может его оптимизировать.
ответ дан 01 окт ’10, 16:10
ответ дан 12 апр.
Заполните EXE-файл NOP на ассемблере.
ответ дан 05 окт ’10, 14:10
Как насчет того, чтобы просто добавить двоичные нули в конец .exe?
ответ дан 01 окт ’10, 16:10
Вы можете создавать большие статические массивы фиктивных данных. Это увеличило бы размер вашего exe, хотя это был бы не настоящий код.
ответ дан 01 окт ’10, 16:10
Я обнаружил, что даже при оптимизации необработанные строки сохраняются как есть в скомпилированном исполняемом файле.
Итак, путь:
- перейдите в http://lipsum.org/
- генерировать много текста
- добавить cpp в вашу программу
- добавить статическую константную строку, которая будет иметь сгенерированный текст в качестве значения
- компилировать
- проверьте размер.
Если у вашего компилятора есть ограничение на размер необработанной строки (?), Просто сделайте абзац для каждой статической строки.
Добавленный размер должен быть легко угаданным.
ответ дан 01 окт ’10, 16:10
Вы можете попробовать создать какой-то рекурсивный шаблон, который будет генерировать множество различных экземпляров. Это могло привести к значительному увеличению размера кода.
ответ дан 01 окт ’10, 16:10
Используйте Boost и скомпилируйте исполняемый файл с отладочной информацией.
ответ дан 01 окт ’10, 16:10
Напишите программу, генерирующую много кода.
printf("000000000");
printf("000000001");
// ...
printf("010000000");
ответ дан 01 окт ’10, 16:10
Признаюсь, я парень Linux / UNIX. Можно ли статически связать исполняемый файл в Windows? Затем вы можете ссылаться на некоторые тяжелые библиотеки и увеличивать размер кода настолько, насколько хотите, без написания большого количества кода самостоятельно.
Еще одна идея, которую я задумал, читая ваш комментарий к моему первому ответу, — это добавить нули в ваш файл. Как уже было сказано, я не эксперт по Windows, так что это может не сработать.
ответ дан 01 окт ’10, 16:10
Добавьте изображение размером 5 МБ (BMP).
ответ дан 01 окт ’10, 17:10
После того, как вы выполните все перечисленные здесь методы, выполните компиляцию с флагом отладки и с максимальным флагом оптимизации (gcc -g -O3
).
ответ дан 05 окт ’10, 14:10
Если ничего не помогает, вы все равно можете создать исходный файл на языке ассемблера, в котором у вас есть соответствующее количество db
операторы, испускающие байты в сегмент кода, и связывают полученный объект кода с вашей программой как extern "C" { ... }
.
Возможно, вам придется поиграть с компилятором / компоновщиком, чтобы помешать компоновщику оптимизировать этот фиктивный объект «кода».
ответ дан 01 окт ’10, 16:10
Используйте #define для определения множества макросов, содержащих строку огромной длины, и используйте эти макросы внутри вашей программы во многих местах.
ответ дан 01 окт ’10, 16:10
Вы могли сделать это:
REM generate gibberish of the desired size
dd if=/dev/random of=entropy count=5000k bs=1
REM copy the entropy to the end of the file
copy /b someapp.exe + entropy somefatapp.exe
Если бы это был командный файл, вы даже могли бы добавить его как этап пост-компиляции, чтобы это происходило автоматически.
Как правило, вы можете скопировать столько информации, сколько хотите, в конец exe. Весь код / ресурсы хранятся как смещения от начала файла, поэтому увеличение его размера не должно повлиять на него.
(Я предполагаю, что у вас есть dd в Windows. Если нет, получите).
ответ дан 01 окт ’10, 18:10
Напишите генератор кода, который генерирует произвольные случайные функции. Единственный трюк в этом случае — убедиться, что он не будет оптимизирован, и с отдельной компиляцией, которая не должна быть сложной.
ответ дан 01 окт ’10, 21:10
Статически привяжите wxWidgets к вашему приложению. Он мгновенно станет размером 5 МБ.
ответ дан 01 окт ’10, 21:10
Не тот ответ, который вы ищете? Просмотрите другие вопросы с метками
c++
winapi
executable
crt
or задайте свой вопрос.
Малый объем оперативной памяти и устройств долговременного хранения информации на персональных компьютерах в прошлом накладывал весьма жесткие ограничения на размер программ. Сегодня подобной проблемы не существует. Однако и сейчас иногда важно максимально уменьшить размер exe-модуля разрабатываемого приложения.
Вам понадобится
- — исходный код;
- — компилятор, линкер;
- — компрессоры PE-модулей, такие как UPX, Themida.
Инструкция
Соберите release-версию исполняемого модуля приложения. Выберите в настройках проекта в IDE соответствующую конфигурацию. Если такой конфигурации нет, создайте ее на основе уже существующей. Измените список опций линкера, убрав и добавив соответствующие директивы. Так, при использовании пакета разработки от Microsoft, следует убрать опцию /debug. Можно также добавить в исходный код директиву:#pragma comment(linker,»/RELEASE»)
Сконфигурируйте проект так, чтобы максимально избежать линковки исполняемого модуля со статическими библиотеками. Используйте разделяемые версии соответствующих библиотек. К примеру, можно исключить код библиотек времени исполнения C и C++, заменив опцию линкера /ML или /MT (статические одно- и многопоточные библиотеки) на /MD (многопоточная CRT DLL).
Рассмотрите вариант слияния различных секций exe-модуля в одну. Данный метод не даст заметного результата, если файл достаточно велик, но при исходном объеме модуля в 20-30 килобайт, выигрыш может оказаться существенным. Опция /merge линкера позволяет объединить секции. Можно задать ее через параметры проекта:/merge:.text=.data /merge:.reloc=.data /merge:.rdata=.dataили при помощи pragma-директив в исходном коде:#pragma comment(linker,»/merge:.text=.data»)#pragma comment(linker,»/merge:.reloc=.data»)#pragma comment(linker,»/merge:.rdata=.data»)#pragma comment(linker,»/merge:.idata=.data»)Также имеет смысл определить атрибуты результирующей секции:#pragma comment(linker,»/section:.data,rwe»)
Уменьшите размер exe путем установки минимального значения величины блоков, по границам которых выравниваются секции. Используйте опцию линкера /filealign, заданную через редактирование свойств проекта или директиву pragma:#pragma comment(linker,»/filealign:0x200″)Данный способ пригоден для маленьких модулей.
Попытайтесь уменьшить размер exe-файла, производя его сборку с параметрами оптимизации, направленной на сокращение объема машинного кода. Замените опции компилятора /O2 или /Od на /O1.
Замените стандартную заглушку DOS в exe-модуле на собственную, которая будет иметь минимальный объем. Используйте опцию линкера /stub:#pragma comment(linker,»/stub:mystub.exe»)Здесь mystub.exe — имя исполняемого файла DOS, код которого будет добавлен в exe-модуль в качестве заглушки.
Рассмотрите вариант указания собственной точки входа в приложение. Это позволит исключить инициализирующий код статических библиотек времени исполнения. Используйте опцию линкера /entry, например:#pragma comment(linker,»/entry:MyStartup») void MyStartup(){ ::MessageBox(NULL, «Hello!», «Message!», MB_OK);}
Примените утилиты упаковки, такие как UPX, ASPack, Themida, PECompact к готовому exe-файлу. Данные модуля подвергнутся компрессии. Их распаковка будет осуществляться в память после запуска приложения. Этот метод дает хорошие результаты в отношении объемных exe-файлов, содержащих в себе большое количество статических данных с низкой энтропией (например, DIB-растры в секции ресурсов).
В интернетах есть несколько старых статей (тыц, тыц) про то, как уменьшить размер exe-файла, который генерирует Visual C++. Я взялся проверить актуальность рецептов, которые там приводятся, для Visual Studio 2010.
Задачу я взял классическую: найти 10 самых популярных слов в текстовом файле. Мой исходный файл был размером 73728 байт.
Начало
Вот мое изначальное решение, написанное за 8 минут и заработавшее со второй компиляции:
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <boost/format.hpp>
using namespace std;
int main()
{
try {
string s = «input.txt»;
ifstream inf(s.c_str(), ios::binary);
if (inf.good()) {
string word;
map<string, int> wc;
while (inf >> word) {
++wc[word];
}
inf.close();
vector< pair<int, string> > wcvec;
for (auto i = wc.begin(), iend = wc.end(); i != iend; ++i) {
wcvec.push_back(make_pair(i->second, i->first));
}
sort(wcvec.begin(), wcvec.end());
for (size_t i = wcvec.size() — 1, j = 0; i >= 0 && j < 10; —i, ++j) {
cout << boost::format(«%6d %sn«) % wcvec[i].first % wcvec[i].second;
}
} else {
throw runtime_error(«No such file»);
}
} catch (exception& e) {
cerr << e.what() << endl;
} catch (…) {
cerr << «Unknown error» << endl;
}
}
VC2010 в конфигурации Release выдал exe-файл размером 73728 байт. Посмотрим внутрь, что там столько жрет:
Заголовок | 0x400 | |
.text | 0xC400 = 50176 байт | В этой секции находится код |
.rdata | 0x3A00 | строки, манглированные имена |
.data | 0x0A00 | еще немного манглированных имен |
.rsrc | 0x0200 | манифест |
.reloc | 0x1200 | рассказ загрузчику как менять адреса в программе, если файл загружен не по тому адресу, по какому ожидал линкер |
Наш файл зависит от библиотек рантайма Visual C: msvcp100.dll, msvcr100.dll (10.0 здесь это версия, XP SP2 в стандартной поставке имеет только до 7.0), и, как и каждый исполнимый файл в системе, от kernel32.dll (тот, в свою очередь всегда тянет за собой ntdll.dll).
В памяти программа занимает максимум 2.5 мегабайта на входном файле в полмегабайта. В адресное пространство мапятся нужные dll, кодовые страницы и .nls, по странице (4 KB) жрут переменные окружения, параметры процесса, PEB и TIB, 574 страницы съедает heap и, вопреки опасениям, стек выделяется не весь сразу, а по мере надобности, в пике 3 страницы на сам стек и 1 на его guard page.
Сборка
Для начала попробуем уменьшить файл ключами компилятора и линкера. Я провел небольшое исследование, воспользовавшись, в том числе, утилитой, которая собирала программу, перебирая найденные ключи. Каких-то 8000 exe-файлов за ночь сборки, и я уже могу поделиться результатами
Опции компилятора:
- /Os — Favor small code;
- /GS- — Buffer security check off (default /GS), это те самые __security_check_cookie;
- /Oy — Omit frame pointers (default /Oy-);
Опции компилятора, которые иногда помогают:
- /O1 — Minimize size (default /O2 maximize speed);
- /Ob2 — Inline function expansion. Теоретически, должно помогать, если есть много функций, которые вызываются только один раз, либо маленьких функций, которые вызываются один-два раза. Еще можно указать компилятору использовать интринсики вместо вызовов функций для некоторых функций с помощью прагмы вида
#pragma intrinsic(memset, strlen, memcpy)
; - /Oi — Enable intrinsic functions;
- /Gy- — отключить Enable function-level linking (default /Gy);
- /arch:SSE2 — включает расширенный набор инструкций за счет того, что программа перестает исполняться на старых процессорах (до Pentium 4);
Опции линкера:
- /MANIFEST:NO — отключает манифест;
- /MERGE:.rdata=.data /MERGE:.text=.data — сливает .text и .rdata в одну секцию;
- /ALIGN:16 — устанавливает минимальное выравнивание секции;
- Убрать /DEBUG — удаляет из exe-файла информацию о соответствующем PDB-файле;
- /DYNAMICBASE:NO /FIXED (default /DYNAMICBASE) — запрещает релоки и убивает их секцию (прощай, ASLR в Vista+, плюс, очевидно, не подходит для .dll);
Опасные (отключают механизмы языка):
- /ENTRY:»main» — меняет точку входа и отключает всю инициализацию, осуществляющуюся перед вызовом main (это
___security_init_cookie
и___tmainCRTStartup
); - /NODEFAULTLIB (использовать вместе с /GS-) — отключает все дефолтные библиотеки со всеми средствами языка. Верный способ получить ошибки линковки, если используется что-то кроме WinAPI;
- Убрать /EHsc — запретить Enable C++ exceptions (еще можно заменить их другими механизмами (SEH, extern C functions), но пользы от этого замечено не было);
- /GR- — выключить RTTI.
Вдоволь поперебирав, я обнаружил, что оптимальный размер достигается, если установить все опции из 1-й и 3-й группы, плюс первую и последнюю из 4-й. Это 42544 байт. Отладка такого файла превращается в экстремально сложное занятие, так что все последующие манипуляции следует проделывать сначала над конфигурацией Debug
Отмечу, что с теми же опциями + /NODEFAULTLIB минимальный hello world с одним MessageBoxA занимает 640 байт (против 7186 байт в Release по дефолту и 28160 в Debug).
Теперь пара слов о том, что НЕ работает или не нужно:
- Незачем убирать из .lib для линкера лишнее, он справляется сам;
- Незачем отключать совместимость с DEP (/NXCOMPAT:NO);
#define WIN32_LEAN_AND_MEAN
— стрипает windows.h, которого тут нет;#pragma comment(linker, "/SECTION:.data,EWRX")
— во-первых, надо EWR, во-вторых, все равно не нужно, линкер ругается, но линкует;#pragma comment(linker, "/FILEALIGN:512")
— директива была недокументированная и ее убрали;#pragma comment(linker, "/ALIGN:512")
— по неведомой причине такая прагма бьет PE-файл, надо выставлять SectionAlignment в свойствах проекта, который действует на тот же ключ;#pragma comment(linker,"/merge:.rsrc=.data")
или.reloc=.data
— согласно MSDN, you shouldn’t merge .rsrc, .reloc, or .pdata into other sections и линкер отказывается это делать;#pragma comment(linker, "/opt:nowin98")
— перекрывается /ALIGN;#pragma optimize("gsy", on)
— делает то же самое, что /Og (deprecated) /Os /Oy и, в принципе, является самым простым и безопасным способом уменьшить размер exeшника одной строчкой, но я уже разобрал ключи в отдельности.
Код
Воспользовавшись Function list в одном известном дизассемблере и самописным скриптом, взвесим различные смысловые части кода в изначальном файле:
Init and deinit | 889 |
main | 1832 |
Exceptions | 4537 |
RTTI | 82 |
Runtime checks | 527 |
PE functions | 332 |
Float arithmetics | 222 |
Memory allocation | 413 |
Locks | 12 |
8846 | |
std memory fns | 1524 |
std::allocator | 368 |
std::exception types | 152 |
std::iostream | 2022 |
Locale and facets | 700 |
std::fstream | 528 |
std bufs | 3142 |
std::map | 400 |
std::_Tree | 2992 |
std::vector | 1984 |
std::basic_string | 3607 |
std::char_traits | 128 |
std::pair | 400 |
std::sort | 4192 |
std::heap | 993 |
std comparisons | 160 |
23292 | |
boost::format | 4128 |
boost::io | 9216 |
boost::optional | 48 |
boost::base_from_member | 64 |
boost::exception | 4184 |
boost::detail | 234 |
17874 | |
Overall: | 50012 |
Легко видеть, что boost, который здесь почти ничего не делает (чего не смог бы сделать банальный printf
), занимает почти треть кода. Заменяем, 42544 → 19744 байт.
Сказал Э, скажи Ю. Заменим весь iostream на fopen, printf и K°. 10865 байт.
Уберем sort и vector, заменив на insertion sort в простом массиве размера 10. 8336 байт.
Заменим map на unordered_map. 8896 байт. Ой, я пошутил, откатимся обратно. Внезапно хэш-таблица, которую бы я писал, если бы фашисты отобрали у меня STL, оказалась толще красно-черного дерева.
Выпилим исключения, все равно они здесь почти ничего не делают, и заменим на обработку ошибок в старом мерзком си-стиле. 8112 байта.
Да, поддержку C++ исключений теперь можно и отключить. 7360 байт. Все, больше ничего не смог придумать
Теперь можно расслабиться и проверить энтропию. Популярнейший упаковщик исполнимых файлов UPX пожал исходный файл до 32256 байт, с исправленным не справился из-за /align; проставил /align:512, upx пожал до 6144 байт, но побил файл. 7z пожал исходный файл до 29591 байт, а исправленный до 3955 байт. Есть еще простор для сжатия
Напомню, исходный код был написан за 8 минут, а оптимизация ключей компилятора и линкера с последующим переписыванием в более компактный вид заняла 18 часов.
UPD: По совету xproger и wizzard0 попробовал Crinkler, хитрый линкер для интрописателей. Как линкеру, ему нужен .obj, который генерирует студия в одну из папок проекта. Для начала следует собрать проект с отключенной Whole program optimization (/GL, оно немного увеличит студийный exe), затем исполнить команду вида:
crinkler.exe /ENTRY:main /SUBSYSTEM:CONSOLE /COMPMODE:SLOW kernel32.lib user32.lib msvcrt.lib msvcprt.lib main.obj
Подумав минуту, Crinkler выплюнет exe размером 3907 байт, который вообще не сжимается 7z! Теперь, думаю, простор для сжатия закончился
Ссылки:
Как сделать 133-байтный PE
Как сделать 45-байтный ELF