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)
Ответ на: комментарий от anonymous

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

Нет. У Basic'а гораздо меньше возможностей, но и на нём можно многое писать. А уж на Паскале...

ну так бери макросы и радуйся жизни, в чём разница то?

В количестве строк кода и с чем конкретно работать. С абстракциями или с нет.

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

Можно, на макросах так наворачивают что выходит код как на C.

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

program DataFiles;
type
StudentRecord = Record
s_name: String;
s_addr: String;
s_batchcode: String;
end;
var
Student: StudentRecord;
f: file of StudentRecord;
begin
Assign(f,'students.dat');
Rewrite(f);
Student.s_name := 'John Smith';
Student.s_addr := 'United States of America';
Student.s_batchcode := 'Computer Science';
Write(f,Student);
Close(f);
end.
program DataFiles;
type
StudentRecord = Record
s_name: String;
s_addr: String;
s_batchcode: String;
end;
var
Student: StudentRecord;
f: file of StudentRecord;
begin
assign(f, 'students.dat');
reset(f);
while not eof(f) do
begin
read(f,Student);
writeln('Name: ',Student.s_name);
writeln('Address: ',Student.s_addr);
writeln('Batch Code: ', Student.s_batchcode);
end;
close(f);
end.
Всего пара десятков строк. И не нужно разбирать прочитанные данные самостоятельно. Раз - и структура уже разобрана в памяти. Так нельзя даже на C.

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

Это разные уровни.

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

Там код вообще писать не надо, всё исключительно мышевозное

Это вообще программированием назвать сложно, да. Настоящее программирование в текстовых редакторах (или в IDE со встроенным тектовым редактором).

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

Ещё, да, есть fprintf() и fscanf(). Однако, тут, во-первых, появятся длинные строки, поскольку придётся расписывать все отдельные элементы структуры. Вместо того, чтобы скормить всю структуру целиком. Сразу целиком писать/читать нельзя. А, во-вторых, тут нужно будет вручную перепроверять где и что могло пойти не так. Надёжнее просто читать по байтам, а потом разбирать эти байты самостоятельно.

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

Точнее, __attribute__((packed)) после struct.

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

__attribute__((packed))struct{int x;}saahriktu_is_lamer_and_faggot;

Это не катит для строк произвольной длины. Кстати я что-то с ходу не смог нагуглить в каком формате паскаль пишет, вдруг мы захочем из другого языка данные прочитать.
В С также наверно можно что-то намутить с помощью, в С++ еще проще думаю. Но вот в стандартную библиотеку такое при моей жизни не добавят. Тут на добавление starts_with и ends_with для строк понадобилось 20 лет, еще через 20 лет может быть добавят split и trim для строк.

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

А если в структуре 37 элементов 9 из которых - строки произвольной длины? Побайтово писать и разбирать - это в целом не проблема. Суть в том, что тут больше ручной возни чем при записи/чтении сразу целиком.

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

Если строки произвольной длины, то под капотом придётся считать их длину, аллоцировать память. В С так делать не принято ибо zero cost abstractions.

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

Кстати я что-то с ходу не смог нагуглить в каком формате паскаль пишет

В таком:

$ hexdump -C students.dat
00000000  0a 4a 6f 68 6e 20 53 6d  69 74 68 00 00 00 00 00  |.John Smith.....|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000100  18 55 6e 69 74 65 64 20  53 74 61 74 65 73 20 6f  |.United States o|
00000110  66 20 41 6d 65 72 69 63  61 00 00 00 00 00 00 00  |f America.......|
00000120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000200  10 43 6f 6d 70 75 74 65  72 20 53 63 69 65 6e 63  |.Computer Scienc|
00000210  65 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |e...............|
00000220  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000300  0c 56 61 73 79 61 20 50  65 74 72 6f 76 00 00 00  |.Vasya Petrov...|
00000310  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000400  17 4b 6f 6c 68 6f 7a 20  4b 72 61 73 6e 79 69 79  |.Kolhoz Krasnyiy|
00000410  20 4f 6b 74 79 61 62 72  61 00 00 00 00 00 00 00  | Oktyabra.......|
00000420  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000500  09 56 65 74 65 72 69 6e  61 72 53 63 69 65 6e 63  |.VeterinarScienc|
00000510  65 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |e...............|
00000520  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000600
Это результат
Student.s_name := 'John Smith';
Student.s_addr := 'United States of America';
Student.s_batchcode := 'Computer Science';
Write(f,Student);
Student.s_name := 'Vasya Petrov';
Student.s_addr := 'Kolhoz Krasnyiy Oktyabr';
Student.s_batchcode := 'Veterinar';
Write(f,Student);
Чтение работает как и положено:
Name: John Smith
Address: United States of America
Batch Code: Computer Science
Name: Vasya Petrov
Address: Kolhoz Krasnyiy Oktyabr
Batch Code: Veterinar

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

кроме вендузятников

У них обычно 'microsoft visual c++ redistributable', который они не линкуют статикой

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

В С так делать не принято

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

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

У тебя на поцкале так и не сделано.

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

Это строки фиксированной длины.

Не совсем. Первый байт в начале строки указывает на то, сколько актуальных символов в строке. И это учитывается при работе с этими строками без участия человека.

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

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

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

Так нельзя даже на C.

Просто сдампить структуру и загрузить обратно можно на C. Вот с более сложными данными уже да, без рефлексии не сериализуешь так просто.

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

Можно и без этого, если нет сложных данных (указателей на что-то что надо сохранить)

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

Потому что у них там строки по 256 байт, а он их налепил в структуру и прямо так пишет.

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

Не 256, там какое-то выравнивание по 16 байт. Первая строка занимает 32 байта в файле, при длине в 10 символов.

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

аааааа, там пропуск пустых байт у него. Я не заметил. Да это просто дамп структуры, на C ничего дополнительного для точно такого же результата писать не нужно, а если написать, то можно не хило сэкономить на размере файла.

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

256 байт

Это дефолт для строки. Для юникодной строки (UnicodeString) дефолтом уже будет 512 байт или 1 Кб. Дефолт можно переопределить нужным кол-вом символов (для неюникодных строк это, по сути, размер в байтах).

Например:

$ hexdump -C students.dat
00000000  0e 49 76 61 6e 20 50 65  74 72 6f 76 69 63 68 00  |.Ivan Petrovich.|
00000010  00 00 30 00 00 00 00 00  d9 ce f7 53 e3 a5 52 40  |..0........S..R@|
00000020
$ du -b students.dat
32      students.dat
Это результат
type
StudentRecord = Record
s_name: String[16];
s_age: Integer;
s_weight: Real;
end;
...
Student.s_name := 'Ivan Petrovich';
Student.s_age := 48;
Student.s_weight := 74.592;
Write(f,Student);
Т.е. тут помимо укороченной строки записаны ещё и 2 числа. И всё это одна структура.

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

Впрочем, конкретно тут __attribute__((packed)) и не нужен.

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

так можно

Писать и читать целыми структурами? В C нет аналогов

type
StudentRecord = Record
s_name: String[16];
s_age: Integer;
s_weight: Real;
end;
var
Student: StudentRecord;
f: file of StudentRecord;
...
write(f, Student);
...
read(f, Student);
Только если разбить всё это на кучу отдельных низкоуровневых операций.

И «__attribute__((packed))», кстати, не входит в стандарт C. Это не кроссплатформенно.

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

Прекрати уже аттракцион своего идиотизма

struct saahriktu_is_lamer_and_faggot2 lol;
write(file,&lol,sizeof lol);
И кстати __attribute__((packed)) является де-факто стандартом, тк поддерживается всеми релевантными компиляторами.

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

В C нет аналогов

Только не надо сжигать меня на костре. Я не колдун! Просто немного знаю C.

#include <stdio.h>
#include <string.h>

struct StudentRecord
{
	char s_name[16];
	int s_age;
	float s_weight;
};

void main()
{
	FILE *f;
	struct StudentRecord Student;
	
	strcpy(Student.s_name, "Ivan Petrovich");
	Student.s_age = 48;
	Student.s_weight = 74.592;
	
	f = fopen("students.dat", "wb");
	fwrite(&Student, sizeof(struct StudentRecord), 1, f);
	fclose(f);
	
	memset(&Student, 0, sizeof(struct StudentRecord));
	// На самом деле можно было не переоткрывать файл, а использовать "w+" вместо "wb" и тут выполнить fseek
	f = fopen("students.dat", "rb");
	fread(&Student, sizeof(struct StudentRecord), 1, f);
	fclose(f);
	
	printf("s_name: %s\n", Student.s_name);
	printf("s_age: %d\n", Student.s_age);
	printf("s_weight: %f\n", Student.s_weight);
}

выхлоп

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

А чё будет? Ну сишная весия прочитает мусорный байт из файла от паскалевской версии, ещё что? Порядок байт или представление флоатов отличается?

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

Массив структур, не? Или что там ещё.

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

В C это называется структура. И я использую структуру, если ты не заметил.

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

На платформе с иным порядком байт (LE/BE/ME) будет сюрприз для Integer/Real. Это не кроссплатформенный по данным вариант.

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

На самом деле достаточно для данных принять тот или иной порядок Endianess, на платформе с иным корректировать прочитанное (условная компиляция рулит).

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

Прежде чем выставлять себя идиотом на лоре нужно открывать хотя бы туториалы для детсадовцев.

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

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

anonymous
()

Ну например на i386 работает, а на x64 уже нет,

c UPX на x64 сталкивался - часто не работает (или 50\50), если не ошибаюсь с ASLR связано.

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

у тебя синтаксис маздайный кстати, фуфуфу таким быть

Не маздайный, а интеловский. Многим так привычнее, в том числе потому что в официальных даташитах от интела он такой.

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

Вот только в реальной жизни file of StudentRecord применения не находит, поскольку выясняется, что нужна сигнатура файла, заголовок, ссылки на что-то ещё... В результате _реальная_ программа на паскале не сильно проще сишного аналога (а в некоторых случаях и сложнее).

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

реальными форматами файлов

plaintext самый реальный формат, и не всем нужно больше форматов. А для него больше чем fputc()/fgetc() или fputwc()/fgetwc() ни разу не обязательно.

Собственно, plaintext в однобайтной кодировке вполне себе тот же бинарный формат и наоборот (любая последовательность байтов является вполне валидным текстом в однобайтной кодировке).

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

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

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