📜 ⬆️ ⬇️

Creating a 1k / 4k intro for Linux, part 2

Less than six months! As you can, podnapryavshis, remember, last time we stopped at the gloom and promise to dive into the assembler.
Well, the boy said - the boy did. From this lurid pile of letters, you will learn how to initialize the OpenGL context in GNU / Linux into some 450 bytes, freeing up even more space for talent to unfold.

Under the cut, you will learn how to draw something like this in one kilobyte:


Those interested fasten and press the pedal to the floor, and the eye to the screen.

')
First, let's talk about this. Why are we so bad and worthless? What exactly adds to our weight and pulls down into the depths of degradation?
To answer these questions, we need tools for real men - readelf and objdump.

We uncover the first one and set the intro file for the rest from the last time - the same one that remains after processing with the sstrip file:

$ readelf -a intro ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x8048250 Start of program headers: 52 (bytes into file) Start of section headers: 0 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 8 Size of section headers: 40 (bytes) Number of section headers: 0 Section header string table index: 0 There are no sections in this file. There are no sections in this file. Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 RE 0x4 INTERP 0x000134 0x08048134 0x08048134 0x00015 0x00015 R 0x1 [Requesting program interpreter: /lib32/ld-linux.so.2] LOAD 0x000000 0x08048000 0x08048000 0x005ac 0x005ac RE 0x1000 LOAD 0x000f4c 0x08049f4c 0x08049f4c 0x000c4 0x00100 RW 0x1000 DYNAMIC 0x000f4c 0x08049f4c 0x08049f4c 0x000a8 0x000a8 RW 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4 GNU_RELRO 0x000f4c 0x08049f4c 0x08049f4c 0x000b4 0x000b4 R 0x1 PAX_FLAGS 0x000000 0x00000000 0x00000000 0x00000 0x00000 0x4 Dynamic section at offset 0xf4c contains 16 entries: Tag Type Name/Value 0x00000001 (NEEDED) Shared library: [libdl.so.2] 0x00000004 (HASH) 0x804814c 0x6ffffef5 (GNU_HASH) 0x8048164 0x00000005 (STRTAB) 0x80481ac 0x00000006 (SYMTAB) 0x804817c 0x0000000a (STRSZ) 45 (bytes) 0x0000000b (SYMENT) 16 (bytes) 0x00000015 (DEBUG) 0x0 0x00000003 (PLTGOT) 0x8049ff4 0x00000002 (PLTRELSZ) 16 (bytes) 0x00000014 (PLTREL) REL 0x00000017 (JMPREL) 0x8048210 0x6ffffffe (VERNEED) 0x80481e0 0x6fffffff (VERNEEDNUM) 1 0x6ffffff0 (VERSYM) 0x80481da 0x00000000 (NULL) 0x0 There are no relocations in this file. There are no unwind sections in this file. Histogram for bucket list length (total of 1 buckets): Length Number % of total Coverage 0 0 ( 0.0%) 1 0 ( 0.0%) 0.0% 2 1 (100.0%) 100.0% No version information found in this file. 

(specific figures, sizes and displacements depend on the entire tulchain and the age of your miles, May Varvara)
What do we see here? At the very beginning, of course, the standard ELF Header, you cannot throw a word out of it. Next, we see that sstrip has picked all the section headers (exercise: compare the output with readelf -a intro-orig, which is before sstrip), which is good. But then we see some holiday program headers and the dynamic section (at offset). Do they really need us so beautiful in a dress?

Spoiler: no! *
(* - amendment: yes, but not such)

We split up elves

Let's see what is the minimum size you can build the correct executable elf file. We take as a basis what we have now, but just throw out all our useful intestines.

simple.c:
 void _start(void) { asm( "xor %eax,%eax\n" "inc %eax\n" "int $0x80\n" ); } 

We will collect it (the script is taken from the Past):
 cc -Wall -m32 -c simple.c -Os -nostartfiles -o simple-co && \ ld -melf_i386 -dynamic-linker /lib32/ld-linux.so.2 simple-co -o simple-c-orig && \ cp simple-c-orig simple-c && \ sstrip simple-c && \ cat simple-c | 7z a dummy -tGZip -mx=9 -si -so > simple-c.gz && \ cat unpack_header simple-c.gz > simple-c.sh && \ wc -c simple-c.sh && chmod +x simple-c.sh && \ ./simple-c.sh 

We look at the sizes of the resulting and intermediate files and notice the following:
  1. sstrip decides - the size of the stripped simple-c (almost) is three times smaller than the size of simple-c-orig
  2. Compression almost does not matter - a compressed decompression file takes almost as much as an uncompressed one
  3. (if you add the -s parameter to ld, you can win about a third in the size of the unstitched file, without losing or adding in the amount of the intermediate striped binary, and _play_ 4 bytes in the size of the final compressed)

Looking into the future, it can be noted that these comments will be useless a little less than completely, but we are not in the future yet!

Now you can set readelf on the binary and see what remains of it, what it has become, what it has become:
 $ readelf -a simple-c ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x8048094 Start of program headers: 52 (bytes into file) Start of section headers: 0 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 3 Size of section headers: 40 (bytes) Number of section headers: 0 Section header string table index: 0 There are no sections in this file. There are no sections in this file. Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x08048000 0x08048000 0x0009e 0x0009e RE 0x1000 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4 PAX_FLAGS 0x000000 0x00000000 0x00000000 0x00000 0x00000 0x4 There is no dynamic section in this file. There are no relocations in this file. There are no unwind sections in this file. No version information found in this file. 

So, sstrip seems to be a good fellow, the section of our elf-maiden, removed all these unnecessary section headings. But at the same time, our lady is not ready yet - she still has a bra (headings GNU_STACK and PAX_FLAGS) and pants that can be found if you pay attention to the fact that the FileSiz header for some reason is smaller than the size of the file itself, which means that it is not cowards at all, but simply rubbish at the end of the file, which must be moaning to derail.
In addition, if you are very picky and decompile (objdump -D, will only work on unstitched file, since objdump reads section headers, not program) that what we have gained here is extremely not to be noticed for the first freshness Socks from a little more than completely harmful prologue and epilogue functions:
 00 55 push ebp 01 89e5 mov ebp, esp 03 31c0 xor eax, eax 05 40 inc eax 06 cd80 int 0x80 08 5d pop ebp 09 c3 ret 

The ratio of 5 bytes of the "useful" code to 5 - the useless cannot warm our young hearts. And what will warm our young hearts? Burning pressed remains of dinosaurs and swamps. In these most compacted swamps and I suggest, together with you, my inquisitive reader, to dive.

Making an elf with your finger

As it turns out, it’s pretty easy to assemble an elf from nothing, you just have to turn off starcraft, open the /usr/include/elf.h file, don’t understand anything, google elf.pdf, read it diagonally and rip it off from your fingertips following:
 bits 32 ;   32-  org 0x00040000 ;        , ,       - $$ ;      ?    . ( ) ;  elf- db 0x7f, 'ELF' ; magic   ,    ,   ELF db 1 ; EI_CLASS = ELFCLASS32 db 1 ; EI_DATA ELFDATA2LSB db 1 ; EI_VERSION = EV_CURRENT times 9 db 0 ; 9  ,          dw 2 ; e_type = ET_EXEC --   dw 3 ; e_machine = EM_386 dd 1 ; e_version = EV_CURRENT dd _start ; e_entry --   ,       dd phdrs - $$ ; e_phoff --    ,    program headers dd 0 ; e_shoff -- --//-- section headers,   ,  , ,  0 dd 0 ; e_flags --      dw ehsize ; e_ehsize --  ELF- (52 ) dw phsize ; e_phentsize --   program header (32 ) dw 1 ; e_phnum --   dw 0 ; e_shentsize --  section header dw 0 ; e_shnum --   () dw 0 ; e_shstrndx -- -    ,    ehsize equ ($-$$) ; $       (+ org),   ehsize    elf- phdrs: ;  program header dd 1 ; p_type = PT_LOAD --   "     , " dd 0 ; p_offset --    ,   dd $$ ; p_vaddr --   ,   dd $$ ; p_paddr --  , - - ,    ,           dd file_size ; p_filesz --   ,      dd file_size ; p_memsz --     .  ,  e_filesz,    .   --      6 . . !!11   ,  ,     .             . dd 7 ; p_flags (=PF_RWX) --     ,   . dd 0x1000 ; p_align -- ,    ,     . 0x1000     phsize equ ($-phdrs) ;    program header _start: ;  xor eax, eax ; eax = 0 inc eax ; eax = 1 (exit syscall) int 0x80 ;  syscall file_size equ ($-$$) ;        

(Actually, instead of giving this rough, but slegonetsa commented asm file, I wanted you, my darlings are darling, hug, draw a large ELF format scheme, but then I realized that it would postpone the article for another couple years old)

You need to compile this file with nasm with a compilation directive into a “flat” binary, without any extra dummy headers:
 $ nasm -f bin simple.asm -o simple-asm 

This line will create us a simple-asm file of 89 bytes in size, which will be a complete functional analog of the 206-byte monster that we created earlier. Not to mention the fact that those 206 bytes are, in fact, 657 bytes of the original uncompressed unstripped madness.
Can
 chmod +x simple-asm 
and see that it is actually valid and even starts. I can say that 89 bytes is far from the limit, and it is possible to strangle this file about two times - to put an elf header on a single program header, and get not that a super-valid elf, but quite starting and smaller in size, than the elf header itself (the last 6 bytes from it will be automatically finished with zeros, as they should be) [1] . Involuntarily it comes to mind that under Linux there are wonderful files / dev / fb0 and / dev / dsp, which means you can do intros in 128-256-512 bytes of DOS-style. We, however, will not deal with this here - this is a topic for a separate pile of signs.
And we will do what we figure out how to connect OpenGL here.

Demonic linking

As we, probably, vaguely remember from the previous part, to create an OpenGL context, we used the SDL library, which was connected dynamically, as libSDL.so. Besides it, of course, we also need OpenGL itself, which comes in the form of the library libGL.so.
It is not difficult, as they say, to see that we need to learn how to make our self-made binary able to link dynamically to anything else.
The mechanism of dynamic linking under Linux at a low level is very funny. When the binary is loaded, the system detects a program header with a PT_INTERP type that indicates the name of the interpreter file. “Yes, you went to hell with this dynamic linking, sort it out yourself, once so smart,” the system says, and instead of continuing to load our binary, it loads the interpreter and transfers control to it.
The interpreter is already loading the application - it, as if nothing had happened, independently loads the PT_LOAD sections into the process and, most importantly, reads PT_DYNAMIC, which contains a link to the description of everything needed for dynamic linking — the names of the necessary libraries, functions, where , and, importantly, how to load them. The data format referenced by PT_DYNAMIC itself is quite simple: a table of double word pairs d_tag ​​and d_val, where d_tag ​​is the parameter code and d_val is its value, which for many parameters is an address in the process already loaded into memory (or so about that).
What parameters and values ​​need to be specified? Let's go back up to the readelf dump of our original file and look at all THAT FOR POKEMON THAT ?! after the line "Dynamic section at offset ...".

Fearfully!

Calm yourself with the thought that we will not need a third of them at all. Excitement, however, does not recede - the remaining fields are still terrible. However, I will not explain in detail their meaning - partly because I myself don’t remember (pick it up a long time ago), and I would prefer to forget what I remember.
Therefore, to you, dear reader, I give you a ready-made recipe, neatly sprinkled with a thin layer of comments to your taste. Use on health.

This piece is added to phdrs immediately after calculating phsize:
 dd 2 ; p_type = PT_DYNAMIC dd dynamic - $$ ; p_offset dd dynamic ; p_vaddr dd dynamic ; p_paddr dd dynamic_size ; p_filesz dd dynamic_size ; p_memsz dd 6 ; p_flags = PF_RW dd 4 ; p_align dd 3 ; p_type = PT_INTERP dd interp - $$ ; p_offset dd interp ; p_vaddr dd interp ; p_paddr dd interp_size ; p_filesz dd interp_size ; p_memsz dd 4 ; p_flags = PF_R dd 1 ; p_align ;   PT_DYNAMIC ;     dynamic: dd 1, st_libdl_name ; DT_NEEDED --     ,   st_libdl_name (   ) ;  ,    ,       ;dd 1, st_libSDL_name ;dd 1, st_libGL_name dd 4, dt_hash ; DT_HASH --     dd 5, dt_strtab ; DT_STRTAB --    .            dd 6, dt_symtab ; DT_SYMTAB --     dd 10, dt_strtab_size ; DT_STRSZ --    dd 11, dt_symtab_size ; DT_SYMENT --    dd 17, dt_rel ; DT_REL --     dd 18, dt_rel_size; DT_RELSZ --    dd 19, 8 ; DT_RELENT --       dd 0, 0 ; DT_NULL --    DT_DYNAMIC dynamic_size equ $ - dynamic ;   DT_HASH ;     -      ;    ,         ;     dt_hash: dd 1, 3, 0, 0, 0, 0 ;   DT_SYMTAB ;      ,       dt_symtab: ; 0 --    (?!) dd 0, 0, 0 dw 0, 0 ; SHN_UNDEF ; 1 'dlopen' dd st_dlopen_name, 0, 0 dw 0x12 ; = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), ..,  ,   --   dw 0 ; SHN_UNDEF ,      ,      ; 2 'dlsym' dd st_dlsym_name, 0, 0 dw 0x12, 0 ; --//-- dt_symtab_size equ $ - dt_symtab ;   DT_REL ;  . ,          dt_rel: dd rel_dlopen ; ,   dd 0x0101 ; ELF32_R_INFO(1,R_386_32) : dt_symtab[1] ('dlopen'),  =    + r_addend(=0  ) dd rel_dlsym ; --//-- dd 0x0201 ; ELF32_R_INFO(2,R_386_32) : dt_symtab[2] ('dlsym'), --//-- dt_rel_size equ $ - dt_rel ;    -- ,       ;   DT_STRTAB ;  .    ,   PT_DYNAMIC --      dt_strtab: st_libdl_name equ $ - dt_strtab ;       db 'libdl.so.2', 0 ;   -- - st_dlopen_name equ $ - dt_strtab db 'dlopen', 0 st_dlsym_name equ $ - dt_strtab db 'dlsym', 0 dt_strtab_size equ $ - dt_strtab ;       interp: db '/lib/ld-linux.so.2', 0 interp_size equ $ - interp 

In addition, it is necessary to correct the following:
  1. obviously change e_phnum to 3
  2. add to the very end of the file
     ; BSS-,      absolute $ bss: ;      rel_dlopen: resd 1 rel_dlsym: resd 1 mem_size equ ($-$$) 
  3. in the PT_LOAD header p_memsz set to mem_size


Everything, now our man-made elf can link dynamically. Check it out.
Since this is no longer just a test, but a potential intra, we will rename the file to intro.asm. We will collect it:
 $ nasm -f bin intro.asm -o intro && chmod +x intro 

And run through strace to check that he is really trying to read all sorts of things:
 $ strace ./intro execve("./intro", ["./intro"], [/* 67 vars */]) = 0 [ Process PID=24135 runs in 32 bit mode. ] ... open("/lib32/libdl.so.2", O_RDONLY) = 3 ... 

Now you can look at the file size - 368 uncompressed bytes. You can independently verify that in the usual way (cc + ld), a similar uncompressed file will immediately swell to 4 kilobytes.
How much will the compressed file weigh?
 nasm -f bin intro.asm -o intro && chmod +x intro && \ cat intro | 7z a dummy -tGZip -mx=9 -si -so > intro.gz && \ cat unpack_header intro.gz > intro.sh && \ wc -c intro.sh && chmod +x intro.sh && \ ./intro.sh 

254 bytes.
But he is not doing anything.

Let him do it!

Now, as soon as we have got the dlopen and dlsym at our disposal, we can finally load some functions from libSDL and libGL and try to tinker with something.
We do not particularly show off and just port to the assembler everything that we did on these lines:
 ;    ,     libs_to_dl: st_libSDL_name equ $ - dt_strtab db 'libSDL-1.2.so.0', 0 ;  -  ,      db 'SDL_Init', 0 db 'SDL_SetVideoMode', 0 db 'SDL_PollEvent', 0 db 'SDL_GetTicks', 0 db 'SDL_ShowCursor', 0 db 'SDL_GL_SwapBuffers', 0 db 'SDL_Quit', 0 db 0 ;    =   st_libGL_name equ $ - dt_strtab db 'libGL.so.1', 0 db 'glViewport', 0 db 'glCreateShader', 0 db 'glShaderSource', 0 db 'glCompileShader', 0 db 'glCreateProgram', 0 db 'glAttachShader', 0 db 'glLinkProgram', 0 db 'glUseProgram', 0 db 'glRectf', 0 db 0, 0 ;    =   _start: ;  mov ebp, bss ;  ebp     bss --  , ! ; - ,   %define BSSADDR(a) ebp + ((a) - bss) %define F(f) [ebp + ((f) - bss)] ;    mov esi, libs_to_dl+1 ; +1, .. ld_load ,          lea edi, [BSSADDR(libs_syms)] ; edi =  ,       ld_load: dec esi ;          1,    ;    dlopen,     push 1 ; RTLD_LAZY push esi ;    call F(rel_dlopen) ; eax = dlopen([esi], 1) ;  ,    <s>   </s>     mov ebx, eax ;  ,  dlopen  ,  ebx ;    0 ld_skip_to_zero: lodsb test al, al jnz ld_skip_to_zero ;    \0     lodsb test al, al jz ld_second_zero dec esi ;    1  push esi ;      push ebx ;   dlopen     call F(rel_dlsym) ; eax = dlsym([ebx], [esi]) stosd ;  eax (   )  [edi], edi += 4 jmp ld_skip_to_zero ;     ld_second_zero: ;    ,   - ! lodsb test al, al jnz ld_load ;     ! ;   ! xor eax, eax ; eax = 0 inc eax ; ex = 1 (exit syscall) int 0x80 ;  syscall file_size equ ($-$$) ;        ; BSS-,      absolute $ bss: ;      libdl_syms: rel_dlopen: resd 1 rel_dlsym: resd 1 libs_syms: SDL_Init: resd 1 SDL_SetVideoMode: resd 1 SDL_PollEvent: resd 1 SDL_GetTicks: resd 1 SDL_ShowCursor: resd 1 SDL_GL_SwapBuffers: resd 1 SDL_Quit: resd 1 glViewport: resd 1 glCreateShader: resd 1 glShaderSource: resd 1 glCompileShader: resd 1 glCreateProgram: resd 1 glAttachShader: resd 1 glLinkProgram: resd 1 glUseProgram: resd 1 glRectf: resd 1 mem_size equ ($-$$) 

This piece should be inserted instead of all that we have immediately after _start, including the _start itself.
We compile, we get a file of 455 bytes in size, we start, we check that it does not fall. If it falls, we try to uncomment the DT_NEEDED lines for libSDL and libGL, and watch what happens, we suffer and stop reading further.

If everything is good, you can move on and finally finally initialize OpenGL with shaders. There is nothing particularly tricky here (except for what is written in the comments), we just repeat in an assembler what we did earlier on this.
 ; nasm -f bin intro.asm -o intro && chmod +x intro && \ ; cat intro | 7z a dummy -tGZip -mx=9 -si -so > intro.gz && \ ; cat unpack_header intro.gz > intro.sh && \ ; wc -c intro.sh && chmod +x intro.sh && \ ; ./intro.sh %define WIDTH 640 %define HEIGHT 360 %define FULLSCREEN 0 ;%define FULLSCREEN 0x80000000 bits 32 ;   32-  org 0x00040000 ;        , ,       - $$ ;      ?    . ( ) ;  elf- db 0x7f, 'ELF' ; magic   ,    ,   ELF db 1 ; EI_CLASS = ELFCLASS32 db 1 ; EI_DATA ELFDATA2LSB db 1 ; EI_VERSION = EV_CURRENT times 9 db 0 ; 9  ,          dw 2 ; e_type = ET_EXEC --   dw 3 ; e_machine = EM_386 dd 1 ; e_version = EV_CURRENT dd _start ; e_entry --   ,       dd phdrs - $$ ; e_phoff --    ,    program headers dd 0 ; e_shoff -- --//-- section headers,   ,  , ,  0 dd 0 ; e_flags --      dw ehsize ; e_ehsize --  ELF- (52 ) dw phsize ; e_phentsize --   program header (32 ) dw 3 ; e_phnum --   dw 0 ; e_shentsize --  section header dw 0 ; e_shnum --   () dw 0 ; e_shstrndx -- -    ,    ehsize equ ($-$$) ; $       (+ org),   ehsize    elf- phdrs: ;  program header dd 1 ; p_type = PT_LOAD --   "     , " dd 0 ; p_offset --    ,   dd $$ ; p_vaddr --   ,   dd $$ ; p_paddr --  , - - ,    ,           dd file_size ; p_filesz --   ,      dd mem_size ; p_memsz --     .  ,  e_filesz,    .   --      6 . . !!11   ,  ,     .             . dd 7 ; p_flags (=PF_RWX) --     ,   . dd 0x1000 ; p_align -- ,    ,     . 0x1000     phsize equ ($-phdrs) ;    program header dd 2 ; p_type = PT_DYNAMIC dd dynamic - $$ ; p_offset dd dynamic ; p_vaddr dd dynamic ; p_paddr dd dynamic_size ; p_filesz dd dynamic_size ; p_memsz dd 6 ; p_flags = PF_RW dd 4 ; p_align dd 3 ; p_type = PT_INTERP dd interp - $$ ; p_offset dd interp ; p_vaddr dd interp ; p_paddr dd interp_size ; p_filesz dd interp_size ; p_memsz dd 4 ; p_flags = PF_R dd 1 ; p_align ;   PT_DYNAMIC ;     dynamic: dd 1, st_libdl_name ; DT_NEEDED --     ,   st_libdl_name (   ) ;  ,    ,       ;dd 1, st_libSDL_name ;dd 1, st_libGL_name dd 4, dt_hash ; DT_HASH --     dd 5, dt_strtab ; DT_STRTAB --    .            dd 6, dt_symtab ; DT_SYMTAB --     dd 10, dt_strtab_size ; DT_STRSZ --    dd 11, 16 ; DT_SYMENT --       dd 17, dt_rel ; DT_REL --     dd 18, dt_rel_size; DT_RELSZ --    dd 19, 8 ; DT_RELENT --       dd 0, 0 ; DT_NULL --    DT_DYNAMIC dynamic_size equ $ - dynamic ;   DT_HASH ;     -      ;    ,         ;     dt_hash: dd 1, 3, 0, 0, 0, 0 ;   DT_SYMTAB ;      ,       dt_symtab: ; 1 --    (?!) dd 0, 0, 0 dw 0, 0 ; SHN_UNDEF ; 2 'dlopen' dd st_dlopen_name, 0, 0 dw 0x12 ; = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), ..,  ,   --   dw 0 ; SHN_UNDEF ,      ,      ; 3 'dlsym' dd st_dlsym_name, 0, 0 dw 0x12, 0 ; --//-- ;   DT_REL ;  . ,          dt_rel: dd rel_dlopen ; ,   dd 0x0101 ; ELF32_R_INFO(1,R_386_32) : dt_symtab[1] ('dlopen'),  =    + r_addend(=0  ) dd rel_dlsym ; --//-- dd 0x0201 ; ELF32_R_INFO(2,R_386_32) : dt_symtab[2] ('dlsym'), --//-- dt_rel_size equ $ - dt_rel ;    -- ,       ;       interp: db '/lib/ld-linux.so.2', 0 interp_size equ $ - interp ;   DT_STRTAB ;  .    ,   PT_DYNAMIC --      dt_strtab: st_libdl_name equ $ - dt_strtab ;       db 'libdl.so.2', 0 ;   -- - st_dlopen_name equ $ - dt_strtab db 'dlopen', 0 st_dlsym_name equ $ - dt_strtab db 'dlsym', 0 dt_strtab_size equ $ - dt_strtab ;    ,     libs_to_dl: st_libSDL_name equ $ - dt_strtab db 'libSDL-1.2.so.0', 0 ;  -  ,      db 'SDL_Init', 0 db 'SDL_SetVideoMode', 0 db 'SDL_PollEvent', 0 db 'SDL_GetTicks', 0 db 'SDL_ShowCursor', 0 db 'SDL_GL_SwapBuffers', 0 db 'SDL_Quit', 0 db 0 ;    =   st_libGL_name equ $ - dt_strtab db 'libGL.so.1', 0 db 'glViewport', 0 db 'glCreateShader', 0 db 'glShaderSource', 0 db 'glCompileShader', 0 db 'glCreateProgram', 0 db 'glAttachShader', 0 db 'glLinkProgram', 0 db 'glUseProgram', 0 db 'glRectf', 0 db 0, 0 ;    =   shader_vtx: db 'varying vec4 p;' db 'void main(){gl_Position=p=gl_Vertex;pz=length(p.xy);}' db 0 shader_frg: db 'varying vec4 p;' db 'void main(){' db 'float ' db 'z=1./length(p.xy),' db 'a=atan(px,py)+sin(p.z+z);' db 'gl_FragColor=' db '2.*abs(.2*sin(pz*3.+z*3.)+sin(p.z+a*4.)*p.xyxx*sin(vec4(z,a,a,a)))+(z-1.)*.1;' db '}', 0 _start: ;  mov ebp, bss ;  ebp     bss --  , ! ; - ,   %define BSSADDR(a) ebp + ((a) - bss) %define F(f) [ebp + ((f) - bss)] ;    mov esi, libs_to_dl+1 ; +1, .. ld_load ,          lea edi, [BSSADDR(libs_syms)] ; edi =  ,       ld_load: dec esi ;          1,    ;    dlopen,     push 1 ; RTLD_LAZY push esi ;    call F(rel_dlopen) ; eax = dlopen([esi], 1) ;  ,    <s>   </s>     mov ebx, eax ;  ,  dlopen  ,  ebx ;    0 ld_skip_to_zero: lodsb test al, al jnz ld_skip_to_zero ;    \0     lodsb test al, al jz ld_second_zero dec esi ;    1  push esi ;      push ebx ;   dlopen     call F(rel_dlsym) ; eax = dlsym([ebx], [esi]) stosd ;  eax (   )  [edi], edi += 4 jmp ld_skip_to_zero ;     ld_second_zero: ;    ,   - ! lodsb test al, al jnz ld_load ;    ! push 0x21 ; SDL_INIT_ TIMER | VIDEO call F(SDL_Init) ; SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO); push 2 | FULLSCREEN ; SDL_OPENGL push 32 ; 32    push HEIGHT push WIDTH call F(SDL_SetVideoMode) ; SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_OPENGL|FULLSCREEN); ; WxH    ! cdecl ftw! push 0 push 0 call F(glViewport) ; glViewport(0, 0, WIDTH, HEIGHT); call F(SDL_ShowCursor) ; SDL_ShowCursor(0); ;   call F(glCreateProgram) ; eax = glCreateProgram(); mov edi, eax ; edi = program_id push 0x8b31 pop esi ; esi = GL_VERTEX_SHADER ;     4    ebp  temp  --       dlopen,   mov dword [ebp], shader_vtx push esi call F(glCreateShader) ; eax = glCreateShader(GL_VERTEX_SHADER); mov ebx, eax push 0 push ebp push 1 push eax call F(glShaderSource) ; glShaderSource(shader_id, 1, &shader_vtx, 0); push ebx ;  nVidia  ,     call F(glCompileShader) ; glCompileShader(shader_id); push ebx ;     ! push edi call F(glAttachShader) ; glAttachShader(program_id, shader_id); dec esi ; esi = GL_FRAGMENT_SHADER mov dword [ebp], shader_frg ;   ,   =  ! push esi call F(glCreateShader) mov ebx, eax push 0 push ebp push 1 push eax call F(glShaderSource) push ebx call F(glCompileShader) push ebx push edi call F(glAttachShader) push edi call F(glLinkProgram) ; glLinkProgram(program_id); call F(glUseProgram) ; glUseProgram(program_id); mainloop: call F(SDL_GetTicks) ; eax == SDL_GetTicks(); --    mov [ebp], eax fninit ;    FPU,        fild dword [ebp] ; st(0) = eax == time  , st(1) = 1000 push 400 ;    ,   ,    fild dword [esp] ; st(0) = 1000 fdiv ; st(0) /= 1000 =    fld1 ; st(0) = 1, st(1) =    faddp st1 ; st(0) =    + 1 fst dword [ebp] mov eax, [ebp] ; eax = (float-ieee)t   fchs ; st(0) = -st(0) fstp dword [ebp] mov ebx, [ebp] ; ebx = -(float-ieee)t   push ebx push ebx push eax push eax call F(glRectf) ; glRectf(-t,-t,t,t) times 5 pop eax ; ,  ,    , ..          call F(SDL_GL_SwapBuffers) lea edx, [BSSADDR(SDL_Event)] ;     SDL_Event push edx call F(SDL_PollEvent) ; SDL_PollEvent(&SDL_Event); pop edx ;  edx cmp byte [edx], 2 ; SDL_Event.type != SDL_KEYDOWN jnz mainloop call F(SDL_Quit) ;      ;   ! xor eax, eax ; eax = 0 inc eax ; ex = 1 (exit syscall) int 0x80 ;  syscall file_size equ ($-$$) ;        ; BSS-,      absolute $ bss: ;      libdl_syms: rel_dlopen: resd 1 rel_dlsym: resd 1 libs_syms: SDL_Init: resd 1 SDL_SetVideoMode: resd 1 SDL_PollEvent: resd 1 SDL_GetTicks: resd 1 SDL_ShowCursor: resd 1 SDL_GL_SwapBuffers: resd 1 SDL_Quit: resd 1 glViewport: resd 1 glCreateShader: resd 1 glShaderSource: resd 1 glCompileShader: resd 1 glCreateProgram: resd 1 glAttachShader: resd 1 glLinkProgram: resd 1 glUseProgram: resd 1 glRectf: resd 1 SDL_Event: resb 24 mem_size equ ($-$$) 

Look, kittens, we laid in 750 bytes what last time barely climbed in 1024. Is it possible to improve this result?
Sure you may:
  1. many structures begin on the same thing as other structures
  2. it is useful to place similar data side by side (loose headings together, strings - together, x86 instructions - not to interleave in any way)
  3. change org
  4. throw out the traversal - for example, comment out everything with SDL_ShowCursor

It should be remembered that the size of a compressed file is far from monotonous depending on the size of uncompressed: for example, times 5 pop eax noticeably wins in size before add esp, 20.
Thus, moving all the lines to the end of the file gives a gain of 16 bytes; , if you comment out the last unnecessary three fields of the elf header (e_shentsize and the next two geese), delete zeroes and other similar data between phdrs and dynamic, dt_hash and dt_symtab.
Total: 718 bytes for the same.
I do not know about you, but my hands are already itching - finally, you can do what we all have gathered here today - with creativity! And we have as many as 306 bytes for it (even more, considering the fact that it is possible to completely replace the shaders with the nastily tunnel)!
What can be done with such an unimaginably huge canvas?

For example, something like that





(carefully, you need a powerful video card):
 shader_vtx: db 'varying vec4 p,v;' db 'void main()' db '{' db 'gl_Position=gl_Vertex;' db 'p=vec4(mat3(cos(length(gl_Vertex.xy)),0.,sin(length(gl_Vertex.xy)),0.,1.,0.,-sin(length(gl_Vertex.xy)),0.,cos(length(gl_Vertex.xy)))*vec3(gl_Vertex.xy*.1,-.9),length(gl_Vertex.xy));' db 'v=vec4(mat3(cos(length(gl_Vertex.xy)),0.,sin(length(gl_Vertex.xy)),0.,1.,0.,-sin(length(gl_Vertex.xy)),0.,cos(length(gl_Vertex.xy)))*vec3(gl_Vertex.xy*.1,.1),length(gl_Vertex.xy));' db '}' db 0 shader_frg: db 'varying vec4 p,v;' ;db 'float mx(vec3 a){return max(ax,max(ay,az));}' db 'float mn(vec3 a){return min(ax,min(ay,az));}' db 'float F(vec3 a){return min(mn(vec3(1.)-abs(a)),-mn(abs(mod(a+vec3(.1),vec3(.4))-vec3(.2))-.15));}' ;db 'float F(vec3 a){return min(mn(vec3(1.)-abs(a)),length(mod(a,vec3(.4))-vec3(.2))-.06);}' db 'vec3 n(vec3 a){' db 'vec3 e=vec3(.0001,.0,.0);' db 'return normalize(vec3(F(a)-F(a+e.xyy),F(a)-F(a+e.yxy),F(a)-F(a+e.yyx)));' db '}' db 'vec4 tr(vec3 E,vec3 D){' db 'D=normalize(D);' db 'float L=.01;' db 'int i=0;' db 'for(i;i<512;++i){' db 'float d=F(E+D*L);' db 'if(d<.0001)break;' db 'L+=d;' db '}' ;db 'return vec2(L,float(i)/512.);' db 'return vec4(E+D*L,float(i)/512.);' db '}' db 'float I(vec3 a){' db 'vec3 l=vec3(sin(pw*1.3),cos(pw*4.2),sin(pw*3.2))*.9,la=la;' db 'return length(tr(a,la).xyz-a)*dot(n(a),-normalize(la))/dot(la,la)+.01;' ;db 'return tr(a,-lv).x*F(a+lv)/dot(lv,lv)+.01;' db '}' db 'void main(){' db 'vec4 t=tr(p.xyz,v.xyz);' db 'gl_FragColor=I(t.xyz)*(abs(t)+vec4(tw*5.));' ;db 'vec2 t=tr(p.xyz,v.xyz);' ;db 'vec3 q=p.xyz+normalize(v.xyz)*tx;' ;db 'gl_FragColor=I(q)+vec4(ty);' db '}' db 0 


Or this:


 shader_vtx: db 'varying vec4 p,v;' db 'void main()' db '{' db 'gl_Position=gl_Vertex;' db 'p=vec4(mat3(cos(length(gl_Vertex.xy)),0.,sin(length(gl_Vertex.xy)),0.,1.,0.,-sin(length(gl_Vertex.xy)),0.,cos(length(gl_Vertex.xy)))*vec3(gl_Vertex.xy*.1,-.9),length(gl_Vertex.xy));' db 'v=vec4(mat3(cos(length(gl_Vertex.xy)),0.,sin(length(gl_Vertex.xy)),0.,1.,0.,-sin(length(gl_Vertex.xy)),0.,cos(length(gl_Vertex.xy)))*vec3(gl_Vertex.xy*.1,.1),length(gl_Vertex.xy));' db '}' db 0 shader_frg: db 'varying vec4 p,v;' db 'float mn(vec3 a){return min(ax,min(ay,az));}' db 'float F(vec3 a){return min(mn(vec3(1.)-abs(a)),length(mod(a,vec3(.4))-vec3(.2))-.06);}' db 'vec3 n(vec3 a){' db 'vec3 e=vec3(.0001,.0,.0);' db 'return normalize(vec3(F(a)-F(a+e.xyy),F(a)-F(a+e.yxy),F(a)-F(a+e.yyx)));' db '}' db 'vec3 tr(vec3 E,vec3 D){' db 'D=normalize(D);' db 'float L=.01;' db 'int i=0;' db 'for(i;i<512;++i){' db 'float d=F(E+D*L);' db 'if(d<.001)break;' db 'L+=d;' db '}' db 'return E+D*L;' db '}' db 'vec3 I(vec3 a,vec3 l,vec3 c){' db 'return c*(clamp(length(tr(a,la)-a),0.,length(la))*dot(n(a),normalize(al))/dot(la,la));' db '}' db 'void main(){' db 'vec3 t=tr(p.xyz,v.xyz);' db 'gl_FragColor=vec4(' db 'I(t,vec3(sin(pw*1.3),cos(pw*4.2),sin(pw*3.2))*.7,vec3(.9,.6,.2))+' db 'I(t,vec3(sin(pw*3.2),sin(pw*4.2),sin(pw*1.3))*.7,vec3(.0,.3,.5)),1.);' db '}' db 0 

Why do these shaders give such a picture? Following this good tradition of posting frequency, I will tell you about it in March next year! Keywords for impatient and independent: raymarching distance fields ( this guy has a lot of information ).

Epilogue

Despite the fact that this text is full of mistakes, non-optimalities and just blatant lies (count how many times I have abused trust), I hope that it turned out to be useful, and that at least someone now will not make another startup, but instead will create something truly complex, interesting and worthwhile.

Despite the fact that with such pictures we should no longer be ashamed in front of classmates, and in general we should already hear the growing roar of heels and other squeals of female fans, there is still much to develop, and this is not the end, where it is going, this is not the end !
The next time you are waiting for the story of how in the programming language "C" to get the swinging bass out of nothing and blow up the dance floor.
See you in October!

Source: https://habr.com/ru/post/143766/


All Articles