LINUX.ORG.RU

Как сжать linux-бинарник (запускаемый файл)

 , , ,


1

2

Написал в Lazarus простенькое приложение, но после конпиляции запускаемый файл получился слишком большой - размер 20Мб.

Чтобы уменьшить выходной размер, как советуют в интернете, в свойствах проекта (Проект - Параметры проекта) включил 4 опции компилятора:

1) Вкладка Генерация кода: установить флажок "Умная компоновка" (-СХ);
2) Вкладка Компоновка: установить флажок "Умная компоновка" (-ХХ);
3) Вкладка Компоновка: установить флажок "Использовать внешний файл отладочных символов GDB" (-Xg);
4) Вкладка Компоновка: установить флажок "Вырезать символы из исполняемого файла" (-Xs);
5) Вкладка Компоновка: снять флажок "Выдавать номера строк в ошибках времени выполнения"(-gl).

Кроме того, можно поставил галочку «использовать текущие параметры по умолчанию для новых проектов», чтобы Lazarus запомнил эти настройки для следующих проектов.

После компилирования бинарь стал размером 4Мб. Вроде неплохо.

Затем утилита strip вырезала еще немного отладочной информации:

strip --strip-all ./mybinary

Дальше я попробовал сжать zip`ом, размер стал 1,7Мб, но файл перестал быть запускаемым (это логично).

Внимание вопрос: есть ли в линуксе какие-то утилиты, сжимающие запускаемый файл, но так, чтобы он при этом оставался запускаемым (аналог виндового UPX)?

UPD. Нашлись команды сжатия:

upx -k -9 ./mybinary
gzexe ./mybinary

Теперь второй вопрос: не скажется ли upx/gzexe на кроссплатформенности/совместимости? Ну например на i386 работает, а на x64 уже нет, или наоборот. Какая из них принесет меньше подобных проблем?

UPD2. Обновил пост, чтобы он остался в истории как мануал.
(Кстати, все опции и утилиты актуальны и для уменьшения exe-файлов в Windows)
И да, upx лучше не использовать, всё остальное - желательно.
Также рекомендую подробный мануал по рантайму, статическому и динамическому связыванию.

★★★★★

Последнее исправление: Novator (всего исправлений: 5)

Ответ на: комментарий от Novator

Т.е. сжатый действительно по RSS расходует чуть больше (общий объем такой же), но ненамного

Память дороже диска на два-три порядка. Время запуска и отсутствие костылей ради ничего - бесценно.

А при многократном запуске несжатого выигрыша нет - экземпляры точно также дублируются в памяти.

Ложь, _код_ не дублируется.

slovazap ★★★★★
()
Ответ на: комментарий от anonymous

Побайтово читать и разбирать можно вообще любые форматы файлов. Очень быстро.

Другой вопрос, что со сложными структурами придётся много красноглазить.

saahriktu ★★★★★
()
Последнее исправление: saahriktu (всего исправлений: 1)
Ответ на: комментарий от anonymous

На самом деле там всё просто. Можно прочитать весь файл в массив байтов/символов, и это и будет, по сути, «строка». Никакой сложности в этом нет. А в том же Паскале можно даже так:

buf1: UnicodeString;
...
buf1 := TFPCustomHTTPClient.SimpleGet('https://site.org/page.html');
При этом будет скачан page.html, а всё его содержимое помещено в текстовую строку buf1, размер которой будет увеличен автоматически. И после этого можно будет работать с этой строкой стандартными строковыми функциями. Например, найти вхождение какого-то символа или какой-то подстроки, а затем скопировать n символов начиная с символа m.

saahriktu ★★★★★
()
Ответ на: комментарий от saahriktu

В libcurl скажем также, malloc(1), а дальше разберётся. А ты уверен в TFPCustomHTTPClient? Я вот в libcurl уверен, что она позволит мне сделать что угодно и так, как я попрошу. Но задача чтения из сети это несколько другое всё же.

размер которой будет увеличен автоматически

вот тут тебя тормозами и накроет, чтение неизвестного размера происходит буферами. Ты можешь изменять их размер и градацию, как в си? Если в заголовках указан точный размер пейлоада без всех этих чанков, то умный код считает тебе в массив как раз нужного размера. А если у тебя нет такой опции? Как ты будешь знать размер заранее, если тебе нужно распарсить тысячи пакетов неизвестного размера и продолжительности, чтобы узнать, где конец? Под файл можно выделять либо достаточно большими блоками, пока не прилетит EOF, либо под известный размер, а чтение побайтово помимо этого приведёт к миллиардам переключений контекста, отсюда и тормоза в первую очередь, и только в последнюю влияет количество релокейтов.

anonymous
()
Ответ на: комментарий от anonymous

А ты уверен в TFPCustomHTTPClient?

Да.

чтение побайтово помимо этого приведёт к миллиардам переключений контекста, отсюда и тормоза в первую очередь

Зачем? Вы что, думаете, что при побайтном чтении после каждого нового символа вызывают realloc()? В C, например, достаточно прочитать один раз в никуда, подсчитав кол-во байтов до EOF, затем сделать rewind(), выделить один раз память, а затем прочитать в этот только что выделенный буфер. В других языках тоже наверняка есть подобные методы.

Ты можешь изменять их размер и градацию, как в си?

Да, размер строк в Паскале можно менять. Однако, обычно это всё не так уж и актуально. Юзер может просто манипулировать строками. В т.ч. читать файлы строками.

Например, возьмём файл

111
2222
33333
Вот такой вот код:
program filetest2;
var
        fptr: text;
        s: UnicodeString;
        filebuf: UnicodeString = '';
begin
        assign(fptr,'test1.txt');
        reset(fptr);
        while not eof(fptr) do
        begin
                readln(fptr, s);
                filebuf := filebuf + s;
        end;
        close(fptr);
        writeln(filebuf);
end.
выведет
111222233333
А теперь возьмём текстовый файл на 37 Мб. Да, обрабатывается относительно долго (полминуты), но работает. При этом вручную размер строк нигде не меняется. Сплошная автоматика. Итоговый размер строки у меня получился 37451647.

saahriktu ★★★★★
()
Последнее исправление: saahriktu (всего исправлений: 1)
Ответ на: комментарий от saahriktu

У меня только вопрос, куда это в никуда, если это, очевидно, не то, что происходит? Просто любитель греть воздух или я чего-то не знаю?

при побайтном чтении после каждого нового символа вызывают

ядерный read, который вполне себе генерирует тормоза. Память в любом случае адресуется страницами, релокейты не так страшны как переключение контекста и прерывания.

anonymous
()
Ответ на: комментарий от saahriktu

Это был больше вопрос про TFPCustomHTTPClient, чтение из сети весьма нетривиальная задача. Твой код вообще не о том.

Хотя while not eof(fptr) мне уже нравится, а readln(fptr, s) намекает на то, что в случах когда строка неизвестного размера, будет чтение буферами неизвестного размера, перебор каждого байта в поисках конца строки и возврат содержимого до него. Сколько там аллокаций и релокейтов в процессе? А если строка больше буфера и переноса строки не нашлось, будет реаллоцировать блоки памяти и читать пока не найдёт?

anonymous
()
Ответ на: комментарий от saahriktu

Если побайтно читать/писать серьёзные объёмы в цикле, у тебя даже без сисколов оверхед просто из-за копирования и индирекции вызовов вылезет. Умные дяди mmap() не зря придумали.

anonymous
()
Ответ на: комментарий от anonymous

Если что, Free Pascal Compiler написан не на C, а на самом Паскале. Поэтому там могут быть отличия в алгоритмах.

saahriktu ★★★★★
()
Ответ на: комментарий от anonymous

куда это в никуда, если это, очевидно, не то, что происходит?

Просто в одну единственную переменную, каждый раз её перезаписывая и не сохраняя промежуточный результат. В самом конце она просто напросто становится равна EOF. В то время как «чтение куда-то» это:

  1. прочитать очередной байт в переменную;
  2. если он не равен EOF, то из этой переменной перенести его в буфер данных (если размера буфера недостаточно, то предварительно ещё и растянуть буфер);
  3. вернуться к началу цикла;

А «чтение в никуда» это просто

  1. прочитать очередной байт в переменную;
  2. если он не равен EOF, то вернуться к началу цикла;
saahriktu ★★★★★
()
Ответ на: комментарий от saahriktu

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

anonymous
()
Ответ на: комментарий от anonymous

В программах вообще нет пользовательских файлов. Это разные вещи. И куча привычных многим людям форматов файлов именно текстовые. Не говоря уже, повторяю, о том, что и бинарные файлы можно рассматривать как текстовые (но, не в юникоде, а в однобайтных кодировках).

И пользователи бывают разные. И фиолетово может быть как раз то, что там у некрасноглазых юзеров, у которых палец к мышке прирос.

saahriktu ★★★★★
()
Ответ на: комментарий от LongLiveUbuntu

Да, можно и так:

program stwtest;
    {$codepage UTF8}
    uses cwstring;

type
StudentRecord = Record
        s_name: String[16];
        s_age: Integer;
        s_weight: Real;
end;

var
        Student: StudentRecord;
        f: file of StudentRecord;

begin
        Assign(f,'students.dat');
        Rewrite(f);
        with Student do begin
                s_name := 'Ivan Petrovich';
                s_age := 48;
                s_weight := 74.592;
        end;
        Write(f, Student);
        Close(f);
end.
program strdtest;
    {$codepage UTF8}
    uses cwstring;

type
StudentRecord = Record
        s_name: String[16];
        s_age: Integer;
        s_weight: Real;
end;

var
        Student: StudentRecord;
        f: file of StudentRecord;
        i: Integer;

begin
        assign(f, 'students.dat');
        reset(f);
        while not eof(f) do
        begin
                read(f, Student);
                for i := 1 to 3 do
                begin
                        case i of
                        1: writeln('Name: ', Student.s_name);
                        2: writeln('Age: ', Student.s_age);
                        3: writeln('Weight: ', Student.s_weight);
                        end;
                end;
        end;
        close(f);
end.

saahriktu ★★★★★
()
Ответ на: комментарий от anonymous

ФФФ у нас скрипт на го 30 метров. Нормально в наше время - в Андроид и ИОС все проги тянут в сябя все либы в свою «папочку».... Выросло поколение..

demrnd
()
Ответ на: комментарий от anonymous

Присмотрелся ещё к этому блочному чтению. Оно по определению работает для элементов одинакового заранее известного веса в байтах. Что хорошо подходит как для однобайтных кодировок так и для UTF-16/UTF-32. С UTF-8 всё сложнее. Там уже точно без предварительного побайтного разбора не понять даже сколько символов в строке. В то время как в случае UTF-16/UTF-32 вес файла в байтах можно поделить на вес одного символа, а в случае однобайтных кодировок сколько байтов - столько и символов.

В С размер файла можно узнать и без предварительного побайтного чтения, да:

        FILE *fptr;
        long fsize;
        char *filebuf;

        fptr = fopen("test1.txt", "r");
        fseek(fptr, 0L, SEEK_END);
        fsize = ftell(fptr);
        rewind(fptr);
А в Паскале есть отдельная функция FileSize().

Вот так в Паскале выглядит чтение блоком в динамический массив однобайтных символов с последующим конвертированием из UTF-8 во внутреннее юникодное представление (UTF-16) и последующим выводом прочитанного уже из юникодной строки:

program filetest2;
    {$codepage UTF8}
uses strings, cwstring;
var
        fptr: File of Char;
        filebuf: PChar;
        unicodebuf: UnicodeString;
        fsize: Int64;
begin
        assign(fptr,'test1.txt');
        reset(fptr);
        fsize := FileSize(fptr);
        New(filebuf);
        GetMem(filebuf, fsize);
        BlockRead(fptr, filebuf^, fsize);
        filebuf[fsize - 1] := Chr(0);
        close(fptr);
        unicodebuf := UTF8Decode(filebuf); (* конвертируем байты из буфера в юникодную (UTF-16) строку *)
        Freemem(filebuf, fsize); (* старый буфер больше ненужен *)
        writeln(unicodebuf);
end.
Итоговая строка будет ровно нужного размера, а не, например, 256 символов если было прочитано всего 117. Работает этот вариант значительно быстрее, да, даже учитывая последующее конвертирование прочитанного.

saahriktu ★★★★★
()
Последнее исправление: saahriktu (всего исправлений: 1)
Ответ на: комментарий от LongLiveUbuntu

Bitch please

#include<cstdio>
int main(){
  struct{int a;int b;}c{666,777};
  printf("%d %d\n",c.a,c.b);
  c={1,2};
  printf("%d %d\n",c.a,c.b);
  }

anonymous
()
Ответ на: комментарий от slovazap

Ладно, понял. Похоже лучше распространять в zip'е (или в инсталляторе), но после распаковки/установки запускаемый файл пусть будет не сжатым, т.е. размером 4Мб.

Novator ★★★★★
() автор топика
Ответ на: комментарий от slovazap

Расскажи это пользователям репозиториев Ubuntu, PCLinuxOS, Centos и т.д.
А ещё MacOS и Android.

Ну и пользователям Windows, разумеется.

То есть расскажи это 99,9% пользователей.

Novator ★★★★★
() автор топика
Последнее исправление: Novator (всего исправлений: 2)
Ответ на: комментарий от Novator

Расскажи это

О, какой у вас чувствительный детектор. Башку только ещё включите, сказанное относится только к авторам софта. Специально обученные пакетные системы, разумеется, бинарники и должны распространять, при том только они. И ваши зипы с бинарями им нахрен не сдались.

Ну и пользователям Windows, разумеется.

Да, а эти пусть сосут лапу, мне не жалко. Распространяйте им сжатые бинарники сразу с криптолокерами, разрешаю.

slovazap ★★★★★
()
Ответ на: комментарий от saahriktu
{$codepage UTF8}
var
  f: file;
  b: PChar;
  fsize: Int64;
begin
  Assign(f,'test1.txt');
  Reset(f,1);
  fsize:=FileSize(f);
  GetMem(b,fsize+1);
  BlockRead(f,b^,fsize);
  b[fsize]:=#0;
  ...
  FreeMem(b);


Повнимательнее...

bormant ★★★★★
()
Последнее исправление: bormant (всего исправлений: 1)
Ответ на: комментарий от kickass

.kkrieger, 96K

Посмотри, сколько программа потребляет оперативной памяти в сжатом и не сжатом виде.

О, напомнило .kkrieger
>>> https://en.wikipedia.org/wiki/.kkrieger

The entire game uses only 97,280 bytes of disk space. In contrast, most contemporaneous first-person shooters filled one or more CDs or DVDs. According to the developers, .kkrieger itself would take up around 200–300 MB of space if it had been stored the conventional way.

Возможно стоит покопатся в коде
>>> https://github.com/farbrausch/fr_public

atsym ★★★★★
()
Ответ на: .kkrieger, 96K от atsym

.kkrieger

Ага, помню как у меня в своё время она тормозила и долго запускалась. Нафиг-нафиг. Лучше пусть гиг занимает, но не тормозит.

anonymous
()
Ответ на: комментарий от bormant

Спасибо.

А в официальной документации почему-то и выделяют память не на «+1» больше (видимо, у них там строка так и не закрывается) и FreeMem() передают именно 2 аргумента:

Program Example28;

{ Program to demonstrate the FreeMem and GetMem functions. }

Var P : Pointer;
    MM : Longint;

begin
  { Get memory for P }
  GetMem (P,80);
  FillChar (P^,80,' ');
  FreeMem (P,80);
end.

saahriktu ★★★★★
()
Ответ на: комментарий от slovazap

Специально обученные пакетные системы, разумеется, бинарники и должны распространять, при том только они. И ваши зипы с бинарями им нахрен не сдались.

Т.е. Шатлворку, Пэйджу, Джобсу и Балмеру доверять можно, а рядовым сборщикам OpenSource - нельзя, так что ли?

Корпоративный раб детектед!

Ну а про то, чтобы запаковать бинари под over9000 «пакетных систем» может рассуждать только юный молокосос-теоретик, к-й никогда этим не занимался.

Да, а эти пусть сосут лапу, мне не жалко. Распространяйте им сжатые бинарники сразу с криптолокерами, разрешаю.

Мне-то зачем? Я же не сотрудник АНБ, ФСБ, СБУ и подобного. Также я не корпоративный раб и шестерка, чтобы зонды пихать.

Более того, я как сторонник OpenSource, к zip-архиву с бинарями приложу ещё и исходники. Твои корпорасты что-то не особо удосуживаются так поступать.

Novator ★★★★★
() автор топика
Последнее исправление: Novator (всего исправлений: 1)
Ответ на: комментарий от Novator

Секурность она такая. Если доверять всем подряд, то злоумышленники воспользуются этим доверием.

Особенно это всё актуально в винде. Красноглазые виндузятники (и даже не очень красноглазые, но которые юзают ограниченный круг программ и нигде не лазят) предпочитают сидеть без антивирусов и качать *.exe'шники из проверенных и надёжных источников.

saahriktu ★★★★★
()
Ответ на: комментарий от saahriktu

А у них там и не ASCIIZ строка вовсе, просто 80 байт выделили, заполнили, освободили.

Второй параметр FreeMem — это синтаксическое наследие Turbo Pascal, оставлен ради совместимости. Менеджер динамической памяти знает, сколько было выделено по переданному указателю.

bormant ★★★★★
()
Ответ на: комментарий от saahriktu

А что мешает «красноглазым» (не важно под какой системой) самим скомпилировать бинари при наличии исходников?

Ещё раз: в 2000х, а особенно в 2010х «все подряд», которые «пользуются доверием», это нифига не активисты OpenSource, если ты ещё не понял.

Novator ★★★★★
() автор топика
Ответ на: комментарий от Novator

Красноглазые, да, могут и скомпилировать. Тем не менее, они не молчат про это своё отношение к бинарникам из непонятных источников. А пока человек не знает лично другого человека, который распространяет бинарники и не входит в число маинтейнеров надёжных и проверенных репозиториев, - он для него именно непонятный источник бинарников. Которые потенциально могут содержать вирусятину или выполнять вредоносные или шпионские действия.

Сторонники такой политики секурности могут даже предлогать таким одиночкам-активистам OpenSource объединяться вместе, чтобы бинарники в этом общем репозитории контролировали и другие люди (не просто выкладываешь своё, а ещё и проверяешь чужое на предмет вредоносности; и твоё на предмет вредоносности проверяют тоже).

saahriktu ★★★★★
()
Ответ на: комментарий от saahriktu

Под «красноглазыми» я так понимаю подразумеваются пользователи chocolatey? Естественно, никакие антивирусы нахрен не сдались, особенно теперь, когда появились нормальные браузеры с песочницами (пользуясь случаем передаю привет файрфоксу). Покупать программу для слежения за тобой, тормозящую работу системы и пожирающую ресурсы впустую, которая не защитит тебя ни от чего и никогда несмотря на заявления? Да ещё периодически будет тебя развлекать ложными срабатываниями. Увольте. Антивирусы для наивных дурачков.

anonymous
()
Ответ на: комментарий от anonymous

Не обязательно. У людей могут уже, например, быть составлены списки официальных сайтов нужного им софта с разделами загрузки.

saahriktu ★★★★★
()
Ответ на: комментарий от saahriktu

Бинарники из непонятных источников как правило поставляются с хешами и подписаны. В любом случае, ты либо веришь производителю ПО, либо нет. А вот поставщику хостинга лучше не верить (если только это не стим, но у тебя просто вариантов нет с ним).

anonymous
()
Ответ на: комментарий от saahriktu

Васяны в венде распространяли только софт типа openssl, там всё очень плохо было, нормальный софт напрямую от разрабов. Сейчас openssl вроде в состав венды включили наконец. Тот же постгрес тебе предлагают скачать за сборкой 2 разных шараг, 1 версия скомпилирована mingw, 2 msvc. А в линуксе практически весь софт Васянами собирается, лучше уж самому компилять.

anonymous
()
Ответ на: комментарий от saahriktu

Официальные сайты без стороннего надёжного репозитория с хэшами и подписями имеют свойство заражаться малварью. Т.е. все источники скачивания софта, которые ты можешь придумать, можно заразить. Сайты Оракла вон регулярно ломают, хотя казалось бы. Что уж про остальных говорить.

anonymous
()
Ответ на: комментарий от saahriktu

Как без анитивирусов, поголовно у всех уже дефендер стоит как минимум.
А в хроме для винды вроде отдельный встроенный антивирь поставляется.

качать *.exe'шники из проверенных и надёжных источников.

На вирус-тотал ссылку кидают сначала.

Exmor_RS ★★★
()
Ответ на: комментарий от Exmor_RS

Это не антивирусы, это любопытные программы посмотреть что и откуда ты скачал, не путай. Всегда из хоть что-то делающих антивирусов были только каспреский и нод32, и то при условии максимально своевременного обновления. Причём, чтобы остановить заразу, ими надо проверять по-очереди, лол. А то обычно один из них не найдёт настоящих вирусов, только другой. Ещё иногда только дрвеб (внезапно) заметит. Отличные активирусы, я считаю.

anonymous
()
Ответ на: комментарий от anonymous

Не знаю как ты там ими проверяешь по очереди если как минимум касперский и доктор веб просят удалить другие антивирусы (или уже не просят?).

В дефендере кстати как и в других вроде тоже есть своя типа виртуальная машина, для анализа кода, т.е. всё же более продвинут того же clamav'а, который только по сигнатурам сканирует.

Обычного человека один раз можно научить и он всегда будет маниакально проверять все флешки и файлы, лишь бы не остаться с грудой металла вместо компьютера.

Exmor_RS ★★★
()
Ответ на: комментарий от Exmor_RS

Логично, они почти все в нулевом кольце (а то и в отрицательном) работают. Плохо с соседями уживаются. Как как, один установил, просканировал, удалил, установил следующий. Всё с десятком перезагрузок в процессе.

А с флешками можно остаться с грудой мусора и без вирусов кстати.

anonymous
()
Ответ на: комментарий от anonymous

Как как, один установил, просканировал, удалил, установил следующий.

Вышел, напился, ограбил, в тюрьму; вышел, напился, ограбил, в тюрьму.

Exmor_RS ★★★
()
Ответ на: комментарий от Exmor_RS

Причём это были реальные вирусы и загрузчики вирусов: трояны-воры кредиток и аккаунтов, шпионское ПО, бэкдоры, локеры и всё остальное типа принудительной рекламы. Плохо детектируется, потому что файл может притворяться безобидным и под виртуалкой и сканерами прикидываться совсем другим файлом, и достаточно чуть изменить его и он уже не детектируется никакими сигнатурами.

Просто переходишь на использование максимально проверенного софта и софта из проверенных источников и про всё это забываешь. Флешки лучше вообще не совать, в крайнем случае в линукс. Хотя и автозапуск обычно достаточно отключить, но там всякие пдфки с жипегами завирусованные могут быть, это проблема. Скрипты так вообще не детектируются.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.