Dark Bit Factory & Gravity
PROGRAMMING => Freebasic => Topic started by: Jim on July 09, 2007
-
I was looking at nino's latest tiny executables, and trying to see how to link them without linking in freebasic's startup code and default libraries. These are really large, so it's important to be able to get rid of them for tiny exes. The problem is that FB's linker and compiler hard code a few things and that means the startup code always gets linked.
So here's my solution to the problem.
First, write your program as usual, obviously NOT using an built-in FB functions like print, graphics, etc.
Here's my test program (tiny.bas)
#include "windows.bi"
declare function WinMainCRTStartup alias "WinMainCRTStartup"
function WinMainCRTStartup() as integer
messagebox(0,"Very Small FB EXE Sample","Hello",MB_OK)
return 0
end function
compile this using
fbc -c tiny.bas
The -c tells the FB compiler just to make a .o file and not an .exe. .o files are the building blocks used by linkers to make executable files.
Next, we need to stub the functions that FB has hardcoded.
I used assembler for this (stub.asm)
.386p
.model flat
.code
public _fb_RtInit@0
_fb_RtInit@0:
end
I assembled it with MASM, but it can be built with lots of other assemblers.
Command line
ml /c /coff stub.asmThis produces stub.obj.
Finally, you need to link these all together. FB is no use for this, you need a proper linker. I've used crinkler, which has the added advantage of compressing while it links. You can download it from www.crinkler.net (http://www.crinkler.net)
The command line I used to do the link is
crinkler /SUBSYSTEM:WINDOWS /OUT:tiny.exe /LIBPATH:"C:\program files\microsoft platform sdk for windows server 2003 r2\lib" kernel32.lib user32.lib tiny.o stub.obj
You will need the libs from Microsoft Platform SDK to perform this step. You will need to change LIBPATH to point to wherever you have them installed. You can change the name of the output file by changing OUT.
And that produces an exe which is just 605 bytes in size!
Included in the archive are:
All the source from this article, stub.obj for those who don't have MASM, and the final exe.
Crinkler is here www.crinkler.net (http://www.crinkler.net)
Have fun!
Jim
-
cool k+ jim!
lots of new stuff there for me to have fun with cheers.
-
Yep, very cool Jim.
-
Here's taj's pixel shader 3.0 demo in FB. Using the above technique it comes out at 1081 bytes. Can anyone get it under the magic 1Kb?
Nino, this will definitely help you port trinity :)
' Chris Thornborrow (auld)... lalala credit would be nice ... lalala
' Example OGL + shaders in 1k
' Requires crinkler
' VS2005 modifications by benny!weltenkonstrukteur.de
' Freebasic modifications by Jim 9/7/2007
option explicit
#include "windows.bi"
#include "GL/gl.bi"
#include "GL/glu.bi"
#include "GL/glext.bi"
declare sub WinMainCRTStartup alias "WinMainCRTStartup"
' Draws the labyrinth using a pair of shaders...
' Vertex shader does NOT transform incoming vertex but stores that in p instead for later use
' The fragment shader is a complicated. It essentially raycasts into a constantly changing
' equation, similar in concept to chladni :-). Its carfully tuned so we dont crash into it as we move
' and so we get a feeling of depth. The for loop could be smaller when written as a do .. while but then
' it wouldnt work on NVidia cards of 7xxx series or below.
' Fragment shader has 2 lines for setup, 1 line for raycasting and function testing, 1 line for colour.
dim shared labvsh as zstring * 68 => "varying vec4 p;void main(){p=ftransform();gl_Position=gl_Vertex;}"
dim shared labfsh as zstring * 260 => "varying vec4 p;void main(){vec3 V=vec3(0,3.5*sin(p.z),p.z);vec3 D=vec3(p.x,p.y,0.5)*0.08;for(int i=800;i>0;i--,V+=D)if(length(fract(abs(sin(V))))>1.5+0.5*sin(V.z)*sin(p.z*0.4))i=0;gl_FragColor=vec4(1,0.8,0.8,0)*dot(vec3(0.4),fract(V))-length(V-p.xyz)*0.1;}"
type GenFP as sub () ptr
dim shared glFP(6) as GenFP
dim shared glnames(6) as zstring * 16 => {"glCreateShader", "glShaderSource", "glCompileShader", "glCreateProgram", "glAttachShader", "glLinkProgram", "glUseProgram"}
dim shared pfd as PIXELFORMATDESCRIPTOR
dim shared dmScreenSettings as DEVMODE
' declare everything global to save bytes
dim shared q as zstring ptr
dim shared i as integer
dim shared as GLuint v,f,p
dim shared hDC as HDC
dim shared m(15) as GLfloat ={1,-0.01f,0,0,0.01f,1,0,0,0,0,1,0,0,0,0.04f,1}
sub WinMainCRTStartup()
dmScreenSettings.dmSize=sizeof(dmScreenSettings)
dmScreenSettings.dmPelsWidth = 640
dmScreenSettings.dmPelsHeight = 480
'dmScreenSettings.dmBitsPerPel = 32
' its risky to remove the flag and bits but probably safe on compo machine :-)
dmScreenSettings.dmFields=DM_PELSWIDTH Or DM_PELSHEIGHT
ChangeDisplaySettings(@dmScreenSettings,CDS_FULLSCREEN)
' minimal windows setup code for opengl
pfd.cColorBits = 32
pfd.cDepthBits = 32
pfd.dwFlags = PFD_SUPPORT_OPENGL Or PFD_DOUBLEBUFFER
hDC = GetDC(CreateWindow("edit", 0, WS_POPUP Or WS_VISIBLE Or WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0))
SetPixelFormat(hDC, ChoosePixelFormat(hDC, @pfd), @pfd)
wglMakeCurrent(hDC, wglCreateContext(hDC))
' create the shaders inlined to save bytes
for i=0 to 6
glFP(i) = cast(GenFP, wglGetProcAddress(glnames(i)))
next
v = cast(PFNGLCREATESHADERPROC, glFP(0))(GL_VERTEX_SHADER)
f = cast(PFNGLCREATESHADERPROC, glFP(0))(GL_FRAGMENT_SHADER)
p = cast(PFNGLCREATEPROGRAMPROC, glFP(3))()
q = @labvsh
cast(PFNGLSHADERSOURCEPROC, glFP(1))(v, 1, cast(byte ptr ptr, @q), NULL)
cast(PFNGLCOMPILESHADERPROC, glFP(2))(v)
q = @labfsh
cast(PFNGLSHADERSOURCEPROC, glFP(1))(f, 1, cast(byte ptr ptr, @q), NULL)
cast(PFNGLCOMPILESHADERPROC, glFP(2))(f)
cast(PFNGLATTACHSHADERPROC, glFP(4))(p,v)
cast(PFNGLATTACHSHADERPROC, glFP(4))(p,f)
cast(PFNGLLINKPROGRAMPROC, glFP(5))(p)
cast(PFNGLUSEPROGRAMPROC, glFP(6))(p)
ShowCursor(FALSE)
'**********************
' NOW THE MAIN LOOP...
'**********************
' there is no depth test or clear screen...as we draw in order and cover
' the whole area of the screen.
while GetAsyncKeyState(VK_ESCAPE) = 0
' move forward and rotate slightly each frame
' glMultMatrix is smaller (20 bytes) than glTranslatef and glRotatef!!
glMultMatrixf(@m(0))
glRecti(-1,-1,1,1)
SwapBuffers(hDC)
wend
' necessary under vista it seems... :-( extra bytes required.
ExitProcess(0)
end sub
Jim
-
wow ... that's a fat tut ... k+ m8 ... and thanks for this :)
-
Cool cheers jim that helps a lot!
-
Also fails to compile here It is failing in glu.bi
Any clues?
-
I am not using FB ... but this is a very interesting tutorial, Jim.
K++ !!!
-
Nifty.
-
Also fails to compile here It is failing in glu.bi
Any clues?
Different versions of FB. Me and nino are both using 0.16b. Has everyone else upgraded while we weren't looking? :P
I'll try to get a fix for 0.17b later.
Jim
-
I'm using 0.17b and it's working here.
-
I've just downloaded 0.17b and it works for me too (I just had to remove 'option explicit'). Which version of FB are you using Shockwave?
Jim
-
it could be 0.15 he is yousing as the problem he is getting is an incompatibility between glu.bi and windows.bi im sure check my trinity port thread for further details.
-
Any idea where I can download 0.15b - it's not available at freebasic.net any more!
Jim
-
not sure if it can be downloaded any more jim as i dont think many people use it anymore :-\
-
I have it here on my hd.
-
Found it. It's hidden on sourceforge :)
Jim
-
I'm really sorry Shockwave, there's no way 0.15b will ever compile this code. It's easy to get round the 'max' problem, but then it fails on all the function pointer stuff :(
If anyone else wants a look, the 'max' fix is just
#define max
#include "windows.bi"
#undef max
Jim
-
bugger thats a real shame is there no way you could update sw?
-
You can install as many versions of fb into different directories/program groups as you like. I've now got 0.15/0.16/0.17 all installed at the same time :) You just have to change the default values the installer gives you.
Jim
-
Karma boost!
-
i followed all above steps and so far so good now im on to the platform sdk will the newer windows sdk work ok? and its over a gigs worth of download!
i already have visual studio so do i still need the platfom sdk?
-
Any version of the SDK should do. Visual Studio will have them in, I think.
Mine are here
C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Lib
Jim
-
phew thanks jim they are there! :) i thought i was going to be on the think end of a huge download.
-
im getting a linker error now in the form of
C:\Documents and Settings\nino\Desktop\trinity>Fbc.exe -c trinity.Bas
C:\Documents and Settings\nino\Desktop\trinity>\MASM32\BIN\ml /c /coff stub.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: stub.asm
C:\Documents and Settings\nino\Desktop\trinity>crinkler /SUBSYSTEM:WINDOWS /OUT:
trinity.exe /LIBPATH:"C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\
Lib" kernel32.lib user32.lib trinity.o stub.obj
Crinkler 1.0a (Jan 7 2007) (c) 2005-2007 Aske Simon Christensen & Rune Stubbe
Target: trinity.exe
Subsystem type: WINDOWS
Compression mode: FAST
Hash size: 100 MB
Hash tries: 20
Order tries: 0
Transforms: NONE
Replace DLLs: NONE
Range DLLs: NONE
loading:
kernel32.lib user32.lib trinity.o stub.obj
: fatal error: LNK 0: could not find symbol '_ChoosePixelFormat@8'
C:\Documents and Settings\nino\Desktop\trinity>pause
Press any key to continue . . .
have i missed a lib out?
-
You need to add opengl32.lib glu32.lib gdi32.lib to the link.
Jim
-
doh of course,
cheers jim the best i can get the trinity example to is 1028 bytes thats with rearanging the code and messing around with crinkler.
-
hey jim i had a little mess about with your example and the best i could get was 1066 but one thing i noticed was streight away it was linking to 1076 bytes so i guess i must be getting slightly better compresion using these switches.
crinkler /SUBSYSTEM:WINDOWS /ORDERTRIES:10000 /HASHTRIES:500 /OUT:trinity.exe /LIBPATH:"C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Lib" kernel32.lib user32.lib trinity.o stub.obj opengl32.lib glu32.lib gdi32.lib
-
Thanks Jim for the excellent help as always! K+
I will have to install 1.7 I think. I don't use the laptop anymore for programming so there's no reason not to update any more I guess.
-
I tried this with a couple of programs, one using tinyptc and one using glfw. On both attempts I get a linker error that says "could not find symbol WinMainCRTStartup".
What am i missing here?!
-
i think you might have to rework some of the steps to get tinyptc or glfw working with this as it was geard towards aulds 1k ogl framework also remember with this you cant youse any of fbs commands as jim stubbs them off to get tiny fb executables.
-
ok, I solved the problem that I had... I didn't undertand that there has to be a function called "WinMainCRTStartup" in your code.
But now I get other linking errors, some kind of rnd stuff. I guess that's because I used RND which is a freebasic command.
I'll try some more to get the glfw code to work. I'll need a static glfw.lib I guess, gonna try to figure out how to make one :)
-
yeah the rnd errors are because rnd belongs to freebasic not sure about glfw but ptc stuff should be possible i think.
-
Here's a trick that makes small exes. It won't be as small as crinkler,
but it is fairly easy to set-up, and you need no other resources except what's packaged with fbc 0.18.2.
Assume that FreeBASIC is installed at:
C:\FreeBASIC\
and that our "trick" is installed at:
F:\tinyfb\
Create some directories like so:
mkdir F:\tinyfb\
mkdir F:\tinyfb\bin\
mkdir F:\tinyfb\bin\win32\
mkdir F:\tinyfb\lib\
mkdir F:\tinyfb\lib\win32\
Copy two files from C:\FreeBASIC\ to our new directory:
copy C:\FreeBASIC\bin\win32\i386pe.x F:\tinyfb\bin\win32\i386pe.x
copy C:\FreeBASIC\bin\win32\ld.exe F:\tinyfb\bin\win32\ld.exe
Go into F:\tinyfb\lib\win32\ and create some empty object files just to make the fbc.exe command line happy:
F:
cd \tinyfb\lib\win32\
echo REM > crt2.bas
fbc -c crt2.bas
copy crt2.o crtbegin.o
copy crt2.o crtend.o
copy crt2.o fbrt0.o
cd ..\..
Now let's create our example:
'' hello.bas
#include once "windows.bi"
function WinMainCRTStartup cdecl _
alias "WinMainCRTStartup" () as integer
Messagebox( 0, "Small EXE example", "Hello", MB_OK )
return 0
end function
Finally, the last two steps are to compile, then to link our exe:
fbc -c hello.bas
fbc hello.o -s gui -prefix F:\tinyfb -p "C:\FreeBASIC\lib\win32"
The "-prefix F:\tinyfb" option tells fbc where to pick up the extra bits of code. Since we provided empty object files, no size is added. The "-p" options tells fbc where to find all the import libs.
For me, this compiles to 2560 bytes.
I know this is an old-ish thread, but I meant to post something here after I saw this, but never did.
Also, if you are making a Windows App and are planning to use MSVCRT, but want to make sure you don't get any fb-runtime stuff, the fbc command line option '-nodeflibs' was updated not to include 'fbrt0.o'. Don't know if that's any help, but thought I would mention.
-
Some good tips there CoderJeff :) Have some good Karma for that and welcome to the forum (I know you have been registered a while, but it's always nice to see people posting, especially good stuff like that!).
-
k+ coderjeff,
its great to hear of other ways size can be reduced in fb. nice one!
-
Okay I finally got this to work (Just Jim's tiny.exe) it is really my first time working with a commandline tool
now I have a question
Is it better to use OpenGL or tinyptc for creating small exe's if all I'm asking from them is to display a buffer of rgb values (like a xor texture or plasma effect)
-
Thanks Shockware, ninogenio. Yeah, I lurk in here from time to time. You guys put out some impressive stuff. And use FreeBASIC in ways nobody else is even thinking about. It's always a pleasure to visit.