Pull to refresh

Comments 49

хорошая зарядка для ума :-) круто вобщем, прям будни перфекциониста :-)
А если серьезно, то задача здоровская и позволяет поподробней разобраться в структуре приложения и понять как там все работает. Так что можно пименять для воспитания молодого поколения.
Я, как молодое поколение, в восторге!
Меня давно тянет к ассемблеру, но пока что я смотрю на него, как на праздничный торт, который ещё пока что есть нельзя, но скоро ты его попробуешь :)
к глубочайшему сожалению, таких подавляющее меньшинство. Но раз интересно — дерзайте, в будущем в любом случае пригодится.
Но концентрация на хабре таких любознательных достаточно большая, поэтому мне и нравится этот ресурс. Так что продолжайте делать и главное делиться этим с другими :-)
Спасибо за статью. Я тоже когда-то ковырялся с этим… Как админ я не сталкиваюсь с оптимизацией кода, но для айтишника понимание как функционирует система на низком уровне, я считаю, обязательно (за редким исключением). Недавно была проблема на HP-UX с одной программой, если бы я не знал что такое truss, не разложил ее на системные вызовы и не увидел бы где проблема — пришлось бы тяжко.
Никогда бы не подумал, что такая маленькая программа может столько занимать места, на старинных компьютерах в несколько килобайт влезала довольно объемная игра.
72-байтное решение:

BITS 32;
ORG 00400000h;

DB 7Fh, "ELF";
DD 01h;
DD 00h;
DD $$;
DW 02h;
DW 03h;
DD @main;
DD @main;
DD 04h;

@main:
  MOV ECX, @text;
  JMP SHORT @code;
  NOP;

DW 34h;
DW 20h;
DD 01h;

@code:
  LEA EDX, [EBX+11];
  LEA EAX, [EBX+4];
  INC EBX;
  INT 80h;
  MOV EAX, EBX;
  INT 80h;
@text:
  DB "ELF, hello!";

Компилируется так:

yasm -f bin имяфайла.asm
chmod u+x имяфайла

Прошу меня извинить, ошибся немного.
Нужно заменить
DD 01h;

@code:

на
DW 01h;

@code:

т.к. e_phnum — это тоже WORD, а не DWORD.

Итого, экономим ещё 2 байта — и на выходе получаем 70 байт.
Прошу прощения, а это часом не Вы автор XTrooper, выкладывавшегося в своё время на code.darthman.com?
Эх.
Вот и настало мне время горько пожалеть о старом винчестере, безвременно ушедшем в мир иной.
Я ж этот Ваш XTrooper со всеми ресурсами и скайбоксом (пережав его в JPEG, правда, но при моих тогдашних 1024×768 заметно не было) смог тогда упаковать в 92 килобайта =(
А потом Вам стрела попала в колено?
ааа. Любим, помним, скайрим
Здесь есть целая коллекция маленьких программ, включая 62-байтный HelloWorld и маленькие аналоги стандартных утилит типа base64 (256 байт), hexdump (202 байта), ls (1017 байт) и даже компилятор BrainF*ck в 166 байт, создающий столь же маленькие программы: http://www.muppetlabs.com/~breadbox/software/tiny/.
Рядом лежат несколько утилит для работы с ELF: http://www.muppetlabs.com/~breadbox/software/elfkickers.html.
Попытался собрать тамошний 62-байтный Hello World, но наткнулся на упорство транслятора, не желающего видеть DWORD впереди четвёрки, и вопреки логике выдающего «короткий вариант» ADD (83 C0 04) вместо «длинного» (05 04 00 00 00). Пришлось править вручную…

Аналогичная программа, перепиленная под строку «ELF, hello!» занимает 60 байт (взамен моих 70-ти), однако исходник, по вышеописанной причине, выложить не могу.
Хотя стоп, почему не могу?

BITS 32;
ORG 05430000h;

DB 0x7F, "ELF";
DD 01h, 00h, $$;
DW 02h, 03h;
DD @main;
DW @main - $$;

@main:
  INC EBX;
  DB 05h; <-- ADD EAX,
  DD 04h; <-- LONG(04h)
  MOV ECX, @text;
  MOV DL, 11;
  INT 80h;
  AND EAX, 00010020h;
  XCHG EAX, EBX;
  INT 80h;

@text:
  DB "ELF, hello!";

Здесь автор статьи с Muppetlabs развернулся на всю катушку: каждый байтик в вышеприведённом коде используется если не по три, то по два раза точно!
Я тоже недавно мучал эльфов,
в некоторых случаях можно запихать
программный хидер в эльфовый.
В формате 128 байт осталось место
и для графики, выводимой через фреймбуфер:
fsqrt.blogspot.com/2012/01/128b-lintro.html
думаю, что туда можно звук еще засунуть, если очень постараться — пожертвовать сложностью графики и совместить их синтез.
аж руки зачесались, жаль занят сейчас другим проектом :D
Можно, конечно.
Но выглядеть и звучать будет жутко.
Да и одновременное наличие dsp и fb*
ещё менее вероятно, так что я забил.
не обязательно хуже, чем средний c music oneliner
Эх.
Надо писать crinkler для никсов, с поддержкой максимального стрипа хидеров заодно.
Вот это будет серьёзно.
Тогда можно будет конкурентоспособные 1к, 4к делать.
Да, я тоже думал об этом. Бывали времена, когда я открывал почтовик и почти начинал клянчить у Ментора разрешение/помощь для разбора его алгоритма паковки, но всегда останавливало то, что там есть нерешенные проблемы ещё до самого паковщика:
1) Динамическая линковка в эльфе не то, чтобы особо sizecoding-friendly. У меня в urotsuki (и нерелизнутых потомках) есть наработки, но они не шибко вылизанные; про них всё не доходят руки написать в части 2 про линукс-1/4k.
2) Свой линкер. Это тоже относительно объёмная и утомительная задача. У которой, к тому же, нет и явного выхлопа самой по себе. Можно, конечно, попробовать избавиться от gzip-дроппинга, прилинковавшись к какой-нибудь системной zlib, но из соображений размера тут будет только поражение по очевидным причинам.
Хотя, конечно, если линкер поможет избавить от выпиливания эльф-заголовков под каждый чих вручную, да ещё и сможет переставлять секции и варьировать прочие свободные параметры, оптимизируя их для гзипа, будет удобно.

Вообще, думаю, что даже с одним гзип-дроппингом можно делать вполне конкурентноспособные вещи — по субъективным ощущениям, для 1к по крайней мере, места для творчества остается больше, чем под виндой. Ну и вообще в 1-4к очень много всего неизведанного. Как минимум, например, ни один из существующих синтов меня не устраивает звучанием.

Другое дело, что современная sizecoding линукс-сцена, похоже, состоит только из нас двоих :D.
А слабо не переключаться на ассемблер и остаться в рамках обычного Си?
К сожалению на Си такие низкоуровневые фокусы не пройдут. Тут даже асм транслятор не всегда генерерует именно те машинные коды, какие нам нужны, что уж говорить о Си компиляторе.
Сравнение размера С++, Си и asm версий абсолютно не корректно.
Потому что для C++ у вас линкуется весь iostream (cout), для Си — немалый кусок stdio (printf), а для асма — тупо используется системный вызов write.
Никто не мешает сделать так же на Си:
int main() {
    return write(1,"Hello, world!\n",14);
}
Не согласен. Сравнение корректно, т. к. код выполнен с учётом парадигмы того языка, на котором он написан. Можно и на C++ писать через сисколы, однако это не соответствует стилю С++. К тому же ваш пример всё равно слинкуется с сишной библиотекой, и в итоге результат тот-же, 568К.
Любой вменяемый линковщик оставит только те символы, на которые есть ссылки из программы, разве не так?
Если у вас есть скомпилированный .o файл, то он будет линковаться целиком, а не «по частям». Т. е. не зависимо от того, какие функции из него вы используете, слинкован будет весь код (если речь идёт о статической линковке). В вашем примере вы используете функцию write, и функция _start у вас не реализованна (она тоже в стандартной библиотеке).
«Если у вас есть скомпилированный .o файл, то он будет линковаться целиком, а не “по частям”.»
Пруфлинк?
Провел опыт, вы не правы.
Линкуется только то, на что есть ссылки (нету например половины системных вызовов).

Размер в самом деле чудовищный, видимо в _start происходит много интересного. Можно еще dietlibc попробовать.
Я тоже провёл опыт. Создал два файла. В первом:

void _start(){
        int a=3;
        int b=5;
        exit1();
}


Во втором:

void exit1(){
asm("movl $1,%eax;"
        "xorl %ebx,%ebx;"
        "int  $0x80");
}
void exit2(){
asm("movl $1,%eax;"
        "xorl %ebx,%ebx;"
        "int  $0x80");
}


В итоге после компиляции и линковки objdump выдал:

a.out:     file format elf32-i386
Disassembly of section .text:

080480b8 <_start>:
 80480b8:       55                      push   %ebp
 80480b9:       89 e5                   mov    %esp,%ebp
 80480bb:       83 ec 18                sub    $0x18,%esp
 80480be:       c7 45 f0 03 00 00 00    movl   $0x3,-0x10(%ebp)
 80480c5:       c7 45 f4 05 00 00 00    movl   $0x5,-0xc(%ebp)
 80480cc:       e8 03 00 00 00          call   80480d4 <exit1>
 80480d1:       c9                      leave
 80480d2:       c3                      ret
 80480d3:       90                      nop

080480d4 <exit1>:
 80480d4:       55                      push   %ebp
 80480d5:       89 e5                   mov    %esp,%ebp
 80480d7:       b8 01 00 00 00          mov    $0x1,%eax
 80480dc:       31 db                   xor    %ebx,%ebx
 80480de:       cd 80                   int    $0x80
 80480e0:       5d                      pop    %ebp
 80480e1:       c3                      ret

080480e2 <exit2>:
 80480e2:       55                      push   %ebp
 80480e3:       89 e5                   mov    %esp,%ebp
 80480e5:       b8 01 00 00 00          mov    $0x1,%eax
 80480ea:       31 db                   xor    %ebx,%ebx
 80480ec:       cd 80                   int    $0x80
 80480ee:       5d                      pop    %ebp
 80480ef:       c3                      ret


Слинкованы обе функции хотя вызывается только одна. Опишите подробнее ваш опыт. Я думаю лучше в ЛС. Возможно это тема для отдельной статьи.
Вообще-то при программировании МК часто используют флаг компилятора (GCC) -ffunction-sections и флаг компоновщика -gc-sections: первый кладёт все компилируемые функции в отдельные секции, второй удаляет неиспользуемые секции. Думаю, подобное можно провернуть и с libc.
Я когда-то, тоже страдал сабжем статьи — меня удивляло другое: почему в проге печатающей «привет мир» сорок (или около того апиай вызовов), а в собраной на МАСМЕ только два (распечатать текст+завершить программу).
Не знаю как там C выводит текст, но почему-то мне кажется, что int $0x80 — это читерство :)
Классика, к тому же на старых процах sysenter не пройдёт.
115 байт! В ~100 000 раз меньше чем первоначальный вариант


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

Им, наверное, еще и премии дают, и штрафуют, от объема кода, чтобы непременно был не меньше, чем в прошлой версии тот же системы. :)
Sign up to leave a comment.

Articles