Ancient One 3 Report post Posted July 3, 2004 Our ©ompiler.[General] Aku sajer2 tulis ni sementara nak tunggu citer CSI. Matlamatnya adalah untuk memberikancontoh bagaimana korang bleh menggunakan kebolehan compiler korang untuk menghasilkanoutput yang korang kehendaki. Nota kecik ni cuma related kepada Win32 programming, C languagedan Microsoft Visual C++ compiler. Aku malas nak citer panjang2 pasal standard C. Mari kitalupakan standard dan code portability buat sementara waktu .[Win32 programming] Meh kita bina Win32 program yang ringkas untuk memahami tentang command line options yang disediakanoleh compiler kiter. Kat sini aku taknak letak master include file untuk Win32 program (windows.h).Buat masa ni kita tak akan include aper2 file pun. /*-------small.c-----------------------------------------------*/ int _stdcall MessageBoxA(unsigned, void *, void *, unsigned); #define MessageBox MessageBoxA void _cdecl main() {MessageBox(0, "Hello Putera.com", "Small", 0);} /*-------------------------------------------------------------*/ Okeh... Kita ader satu function proto nama dia MessageBoxA. Kita ader satu preprocessor directive #define. Kita ader satu function definition/declaration "main". Kita nak 3 baris kod ni menghasilkan program yang memaparkan mesej "Hello Putera.com" ke skrin. Dalam Win32 GUI interface kita bleh gunakan MessageBoxA (ascii) atau MessageBoxW (unicode-16) (lepas ni aku refer kedua-duanya sebagai MessageBox). MessageBox function adalah exported function daripada USER32.DLL. Maknanya kita kena la import fungsi ni dalam source kita. Dengan function proto, kita leh refer external symbol yang kita tak define dalam source kita. #define directive kat atas hanyalah untuk provide generic name for MessageBoxA. Kat sini takla nampak sangat gunanya. Tapi bila kita nak bina ascii/unicode version program kita, maka kita leh buat conditional test macam : #ifdef UNICODE #define MessageBox MessageBoxW #else #define MessageBox MessageBoxA #endif Dengan generic name MessageBox kita leh hasilkan kod yang bleh dikompil untuk ascii/unicode api : /*-------small.c-----------------------------------------------*/ int _stdcall MessageBoxA(unsigned, void *, void *, unsigned); int _stdcall MessageBoxW(unsigned, void *, void *, unsigned); /*uncomment below for UNICODE*/ /*#define UNICODE*/ #ifdef UNICODE #define MessageBox MessageBoxW #else #define MessageBox MessageBoxA #endif void _cdecl main() {MessageBox(0, "Hello Putera.com", "Small", 0);} /*-------------------------------------------------------------*/ Tapi ni tak cukup lagi since kiter nyer string still dalam ascii tak kire kita define UNICODE atau tidak. So meh kita tambah TEXT macro : /*-------small.c-----------------------------------------------*/ int _stdcall MessageBoxA(unsigned, void *, void *, unsigned); int _stdcall MessageBoxW(unsigned, void *, void *, unsigned); /*uncomment below for UNICODE*/ /*#define UNICODE*/ #ifdef UNICODE #define MessageBox MessageBoxW #define TEXT(str) L##str #else #define MessageBox MessageBoxA #define TEXT(str) str #endif void _cdecl main() {MessageBox(0, TEXT("Hello Putera.com"), TEXT("Small"), 0);} /*-------------------------------------------------------------*/ Salah satu kegunaan windows include file adalah memberi kemudahan kepada kiter untuk membina ascii- atau unicode-based program. Seperti yang korang tengok dalam Platform SDK nyer doc, MessageBox nyer proto : int MessageBox( HWND hWnd, // handle to owner window LPCTSTR lpText, // text in message box LPCTSTR lpCaption, // message box title UINT uType // message box style ); For assembly coders, hampir semer data types ni adalah jenis dword (4 bytes). Tak kire la pointer ke, integer ke, HWND ke, HBITMAP ke. Thats why kalau korang tengok function proto kat MASM source semernya guna DWORD. Dalam Win32, kita pass arguments kepada function on stack adalah dalam saiz dword (or multiple of it, e.g for int64 or double precision fp constant). Even kalau function kita terima "char" data type (1 byte), ia masih dipush ke stack dalam saiz dword. Kalau ianya signed character, ia akan di"sign-extend" kepada dword, if unsigned, zero-extended to dword size. You cannot push a byte into the stack anyway. Aku gunakan void * data type since at the lower level, all pointers are the same anyway (as long as it's a 32-bit linear address). char * data type is just the same as char ** data type. Sama juga dengan int *, atau float * dan sebagainya. At the lower level, cuma saiz je yang kiter kire. Byte (8 bit), Word (16 bit), Dword (32 bit) dan Qword (64 bit) adalah basic data types yang kita gunakan. pointer bersaiz 32-bit, sama juga dengan int. Kepada pemproses semer nilai yang ia proses hanyalah nombor. 12345678 bleh jadi address, nombor, code, string dan sebagainya. It's all depends on the context. Compiler cuma perlukan data types supaya ia dapat menghasilkan machine code yang betul dan at the compilation level supaya korang gunakan setiap data types ni dengan betul (for example, kalau kita push int * untuk parameter yang perlukan char *, ia akan berikan warning but dia still boleh kompil kod nih. Unless kita manipulate value yang ditunjuk oleh pointer tu, any pointer types are same). Kalau kiter tengok kat function proto/definition kat kod kita, ader menatang seperti _stdcall dan _cdecl. Aper tuh? Ni basically aper yang kita panggil calling convention (cc). It defines camne kita invoke suatu fungsi seperti turutan arguments yang kita pass kepada function, stack cleanup (since kita gunakan stack untuk pass arguments) dan name decoration/mangling. Dengan _stdcall cc, kod kita akan pass arguments kepada function tersebut daripada kanan ke kiri. So, dalam kes MessageBox kat atas, uType argument akan di"push" terlebih dahulu, kemudian lpCaption, kemudian lpText dan akhir sekali hWnd. Kemudian function yang kita panggil perlulah buang arguments ni daripada stack. Dalam kes MessageBox kat atas, function MessageBox kenalah buang 16 bytes of arguments daripada stack (add 16 bytes to ESP register). So.. basically dalam asm korang akan nampak kod kita kat atas dikompil sebagai : ;code section _main : push 0 push MsgTitle push MsgText push 0 call [_MessageBoxA@16] ret ... ;data section MsgTitle db "Small", 0 MsgText db "Hello Putera.com", 0 .. dan somewhere in MessageBoxA function in USER32.DLL korang akan lihat instruction "ret 0x10" sebagai last instruction. Instruction ni akan tambah 0x10 bytes kepada ESP (removing 4 dword arguments from the stack) dan kemudian return to caller. Korang tengok jugak simbol MessageBoxA kita dah jadi _MessageBoxA@16 plak dah. Ni yang kita panggil name decoration. Dengan _stdcall cc, "_" akan ditambah di awal simbol dan "@SIZE" akan ditambah di akhir simbol di mana SIZE adalah saiz arguments untuk function tersebut. So, katakan kita define sebijik function dengan simbol "foo" dan ia terima 40 bytes of arguments (10 dword arguments) dengan _stdcall cc, maka simbol ni akan jadi "_foo@40". Camne plak dengan _cdecl. _cdecl adalah C calling convention yang digunakan dalam C language. Turutan arguments passing sama dengan _stdcall cc (kanan ke kiri) tapi fungsi yang dipanggil takkan buang arguments daripada stack. CC ni cantik digunakan untuk define function yang terima variable length arguments (cam printf dan scanf). Dengan _cdecl, simbol "foo" akan jadi "_foo" ("_" simbol ditambah di awal nama). Lets just assume MessageBox gunakan _cdecl cc. Maka kita akan tengok kod kat atas jadi seperti di bawah : ;code section _main : push 0 push MsgTitle push MsgText push 0 call [_MessageBoxA@16] add esp, 16 ... ;data section MsgTitle db "Small", 0 MsgText db "Hello Putera.com", 0 .. dan somewhere in MessageBoxA function in USER32.DLL korang cuma akan dapat lihat "ret" Win32 APIs gunakan _stdcall cc, kecuali yang menerima variable length arguments seperti wsprintf. Korang akan lihat pelbagai lagi jenis cc yang lain seperti "fastcall" cc (using general registers dan stack untuk pass arguments, usually used in Win32 app framework that use oo language), "pascal" cc (used in pascal language) dan "ancient" cc (arguments are passed on the general and fpu registers ... why not?). Err..aku tak compile lagi rupanya kod kite tadi ye.. Nanti la sat. Aku nak citer pasal fungsi main aku dulu. Dalam Win32 programming biasanya kita akan gunakan "main" atau "Winmain" sebagai entrypoint program kita. Function "main" normally terima 2 arguments yang related to command line parameters manakala Winmain terima 4 arguments yang maner satu darinya tak guner langsung. Mari kita tengok proto WinMain. int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ); hPrevInstance adalah tinggalan 16-bit programming so just ignore it. It's usually set to 0. hInstance ringkasnya adalah image base program kita. (image base adalah permulaan address dalam memory di mana loader "load"kan fail program korang dengan teknik memory-mapped file-mapping). lpCmdLine adalah command line parameter kepada program korang (program name korang tak masuk sekali). So kalau korang invoke program korang dengan "small -foo bar /foobar" contohnya, maka lpCmdLine adalah pointer kepada string "-foo bar /foobar". nCmdShow adalah constant yang berkaitan dengan windowing dalam program korang. Soalannya, dari maner kita dapat arguments ni? Saper yang pass kepada fungsi "WinMain" kita. Aper yang kita tahu, WinMain adalah permulaan program kita. Actually, it's not. Bila kita build program kita, linker akan link object file kita dengan libc.lib (default). Ia akan sertakan cebisan startup kod sebagai real entrypoint program kiter dan function ni la yang pass parameters kepada "main" atau "WinMain" kiter. Just for note, libc.lib adalah crt library (for static link, single threaded). Selain daripada libc korang bleh gunakan libcmt (multithread, static link), msvcrt (dynamic link) dan juga debug version of each libraries (e.g libcd.lib is a debug version of libc.lib). System tak pass aper2 kat enrtypoint program kiter. [The building process] This section teaches us how to use many of the command line switch of Visual C++ compiler. cl.exe adalah program yang mengawal building process termasuklah link process (link.exe adalah nama linker). But for now, kita takkan link terus program nih. Kita just compile je untuk hasilkan object file. Lets just invoke cl.exe with filename containing our first code above (save it as small.c). Please note you must set some environment vars correctly before using the command line tools. Korang bleh lakukannya daripada DOS prompt kalau tak set lagi : set path=%path%;<your compiler tools dir full path> set lib=%lib%;<your library dir full path> set include=%include%;<your include dir full path> -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:small.exe small.obj small.obj : error LNK2001: unresolved external symbol _MessageBoxA@16 small.exe : fatal error LNK1120: 1 unresolved externals E:\Projek\VC\junk> -----------------------------console output-------------------------------- Aik.. error plak dah. Error message small.obj : error LNK2001: unresolved external symbol _MessageBoxA@16 sebenarnya adalah error daripada linker dan bukannya compiler. LNK - Linker. Kalau kat compiling phase ader error/warning, dia akan kater "error Cxxxx" atau "warning Cxxxx". So, object file kita selamat dilahirkan dengan nama small.obj. Kita bleh buang link phase dengan switch "/c". Note that command line switch adalah case-sensitive, maknanya "/c" dengan "/C" tak sama! So.. mari kita try compile dengan "/c" switch : -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk> -----------------------------console output-------------------------------- Haa.. macam tu laaa.. Okeh. Tapi camne plak kalau kiter nak rename object file ni? Dah tentulah bleh. Kita gunakan "/Fo" switch. Kalau aku nak rename object file ni kepada "test.obj" maka aku kena letak switch "/Fotest.obj" : -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c /Fotest.obj small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk>dir/w -----------------------------console output-------------------------------- Note that by default, compiler akan tengok file extension untuk tentukan jenis file tuh. So kalau dia tengok .c extension, maka dia akan assume kod program korang adalah dalam C. Maknanya kalau korang guna .c, korang takleh la guna c++ feature. Meh kita ubah kod kita : -----------------small.c------------------------------------- int _stdcall MessageBoxA(unsigned, void *, void *, unsigned); #define MessageBox MessageBoxA void _cdecl main() { MessageBox(0, "Hello Putera.com", "Small", 0); int foo = 0; } -----------------small.c------------------------------------- dan compile -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c small.c(11) : error C2143: syntax error : missing ';' before 'type' -----------------------------console output-------------------------------- Tengok tu... kan dah ader error. Dalam C takde menatang camtuh. Setiap variables kena ader scope. Function scope, block scope, etc. Kalau kita letak variable foo kat permulaan function (sebelum any code statements), then it's OK. Satu cara lagi adalah enclosed variable foo dalam {}. Contohnya : -----------------small.c------------------------------------- int _stdcall MessageBoxA(unsigned, void *, void *, unsigned); #define MessageBox MessageBoxA void _cdecl main() { MessageBox(0, "Hello Putera.com", "Small", 0); { int foo = 0; } } -----------------small.c------------------------------------- Camne plak kalau kita rename small.c jadik foo.bar (extension .bar)? Aper2 extension yang dia tak kenal, dia akan anggap sebagai object file (yang gerenti akan sebabkan error). Ader tak switch yang membolehkan compiler sentiasa anggap file tersebut sebagai C file tak kire aper pun extensionnya? Jawapannya adalah "/Tc" switch. Selepas "/Tc" switch kita kena letakkan nama fail yang kita nak compile since ia cuma compile satu fail sebagai c file. -----------------------------console output------------------------------------ E:\Projek\VC\junk>copy small.c foo.bar 1 file(s) copied. E:\Projek\VC\junk>cl /Tc /c foo.bar Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. Command line warning D4024 : unrecognized source file type 'foo.bar', object fi e assumed c fatal error C1083: Cannot open source file: '/c': No such file or directory E:\Projek\VC\junk>cl /Tc foo.bar /c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. foo.bar -----------------------------console output------------------------------------ Okeh.. satu lagi switch adalah "/Tp" yang treat file sebagai C++ source file. Korang bleh treat semer file sebagai C atau C++ file dengan switch "/TC" atau "/TP" masing2. Errr... camne plak nak tengok listing file (asm code/machine code) kod kiter ek? Bleh tak? Kalau setakat kod atas aku bleh buat listing sendiri. Listing file berguna kalau kita nak tengok kod kita pada level terendah. Korang bleh gunakan switch "/FA" dan juga "/Fa". "/Fa" ader mcm2 jenis. Kalau nak create listing file untuk setiap source kita bleh guna "/Fa" je. Kalau kita nak rename listing file, contohnya kepada foo.asm, kita gunakan "/Fafoo.asm". Kalau kita tak letak extension, dia akan gunakan .cod. Nak format listing file plak kita guna "/FA". Korang bleh gunakan "/FAc", "/FAs", "FAcs" dan "/FA" tu sendiri (note that A - assembly , s - source, c - code). So kalau aku nak hasilkan listing file yang lengkap untuk small.c aku dengan nama foo.asm (ni small.c yang ader "int foo = 0" tuh): -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c /FAcs /Fafoo.asm small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk> -----------------------------console output-------------------------------- Cantek... meh kita tengok isi kandungan foo.asm : TITLE small.c .386P include listing.inc if @Version gt 510 .model FLAT else _TEXT SEGMENT PARA USE32 PUBLIC 'CODE' _TEXT ENDS _DATA SEGMENT DWORD USE32 PUBLIC 'DATA' _DATA ENDS CONST SEGMENT DWORD USE32 PUBLIC 'CONST' CONST ENDS _BSS SEGMENT DWORD USE32 PUBLIC 'BSS' _BSS ENDS _TLS SEGMENT DWORD USE32 PUBLIC 'TLS' _TLS ENDS FLAT GROUP _DATA, CONST, _BSS ASSUME CS: FLAT, DS: FLAT, SS: FLAT endif PUBLIC _main EXTRN _MessageBoxA@16:NEAR _DATA SEGMENT $SG37 DB 'Small', 00H ORG $+2 $SG38 DB 'Hello Putera.com', 00H _DATA ENDS _TEXT SEGMENT _foo$39 = -4 _main PROC NEAR ; 9 : void _cdecl main() { 00000 55 push ebp 00001 8b ec mov ebp, esp 00003 51 push ecx ; 10 : MessageBox(0, "Hello Putera.com", "Small", 0); 00004 6a 00 push 0 00006 68 00 00 00 00 push OFFSET FLAT:$SG37 0000b 68 00 00 00 00 push OFFSET FLAT:$SG38 00010 6a 00 push 0 00012 e8 00 00 00 00 call _MessageBoxA@16 ; 12 : int foo=0; 00017 c7 45 fc 00 00 00 00 mov DWORD PTR _foo$39[ebp], 0 ; 14 : } 0001e 8b e5 mov esp, ebp 00020 5d pop ebp 00021 c3 ret 0 _main ENDP _TEXT ENDS END Hmm.. Kita amik satu asm instruction 00006 68 00 00 00 00 push OFFSET FLAT:$SG37 00006 - offset. 68 00 00 00 00 - machine code for push immediate-value-32-bit (it's not complete yet). push OFFSET FLAT:$SG37 - asm code (yang push string "Small" pointer ke stack). As we can see here, kod kita dalam object file tak sempurna. Sebabnya data kat object file ni mungkin berubah dan compiler tak dapat la letak fixed address lagi. Tengok gak kat call _MessageBoxA@16 instruction tuh. Dia tak dapat jumper simbol _MessageBoxA@16. Salah satu tugas linker nak patch benda alah ni la, lubang2 kat dalam kod. Aikk.. kod atas tak optimize langsung! Apsal plak ader instruction "push ecx"? Dan sepatutnya kod "{int foo = 0;}" dibuang la since ia tak affect execution. Bleh tak kiter optimize kod kiter? Aku nak supaya asm code dia cuma include call to MessageBox je. Kat MSDN kater ader global optimization option, "/Og". Meh kiter try : -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c /FAcs /Fafoo.asm /Og small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk> -----------------------------console output-------------------------------- Tengok listing file : ... 00000 6a 00 push 0 00002 68 00 00 00 00 push OFFSET FLAT:$SG37 00007 68 00 00 00 00 push OFFSET FLAT:$SG38 0000c 6a 00 push 0 0000e e8 00 00 00 00 call _MessageBoxA@16 ; 11 : {int foo = 0;} ; 12 : } 00013 c3 ret 0 ... Haaa... camtu la yang aku nak. Stack frame pun dia tak create. Bagusss. Aper sebenarnya switch "/Og" ni buat? Mengikut doc, antara optimization yang dilakukannya adalah : -Local and global common subexpression elimination -Automatic register allocation -Loop optimization cl ader banyak switch untuk optimization, semernya start dengan "/O". Basically, when talking about optimization, ader dua kategori - speed dan size. Note that selalunya bila kita optimize for speed saiz kod akan bertambah beso. Sebabnya kadangkala machine perlukan lebih masa untuk habis memproses satu instruction yang kompleks berbanding untuk habiskan beberapa instructions yang ringkas. Meh kita kompil satu contoh ringkas: -----------------small.c------------------------------------- int _cdecl main() { int i = 0; return ++i; } -----------------small.c------------------------------------- Untuk size atau speed optimization kita bleh gunakan "/O1" atau "/O2" switch masing2. Untuk kod ringkas camni, aku blehla tolong kompil untuk korang. <size optimization> main: push 1 ;2 bytes atau xor eax, eax ;2 bytes pop eax ;1 byte inc eax ;1 byte ret ;1 byte ret <speed optimization> main: mov eax, 1 ;5 bytes ret ;1 byte Biasanya speed optimization akan menghasilkan code yang memeningkan kepada korang (even for experts). Contoh kat atas ok lagi.. simple je. Selain daripada language optimization, kiter ader optimization specific kepada certain architecture ("/G3, "/G4", "/G5", "/G6" dan "/GB"), optimization specific kepada windows app ("/GA") dan optimization specific kepada DLL ("/GD"). Aper yang terjadi bila kita ubah code kita kepada kod kat bawah (2 calls to MessageBox) : /*-------small.c-----------------------------------------------*/ int _stdcall MessageBoxA(unsigned, void *, void *, unsigned); #define MessageBox MessageBoxA void _cdecl main() { MessageBox(0, "Hello Putera.com", "Small", 0); MessageBox(0, "Hello Putera.com", "", 0); } /*-------------------------------------------------------------*/ Kiter ader 2 strings "Hello Putera.com" yang sama. Tapi compiler create 2 copy string yang sama(even with global optimization). Takleh kiter push pointer yang sama? Bleh. Takde hal. Kitacuma perlu enable string pooling (multiple copies of same string dijadikan 1). Switchnya adalah"/Gf" - normal string pooling dan "/GF" - read-only string pooling. Meh kita compile dengan "/GF".Dan korang bleh tengok perbezaannya dalam listing file. Errkk.. citer aku dah start la..Sambung nanti.. Quote Share this post Link to post Share on other sites
deejagothic 1 Report post Posted July 8, 2004 Goshh..u shoot my serebrum! Quote Share this post Link to post Share on other sites
Ancient One 3 Report post Posted July 10, 2004 Kiter dah compile source file kiter ye tak. Hasilnya adalah object file small.obj (COFF format).Actually aper sebenarnya isi kandungan small.obj ni? Aper menatangnya COFF nih? COFF stands forCommon Object File Format. Ia adalah format yang digunakan oleh M$ compiler dan lain2 untuk objectfile. Selain dari COFF, M$ compiler juga boleh compile OMF (Object Module Format) object file yang biasanyadigunakan oleh Borland compilers. Since COFF dan OMF adalah dua format berbeza untuk mewakili benda yangsama, ader beberapa features yang ader ada OMF tidak terdapat pada COFF dan sebaliknya.Meh kiter compile kod kiter semula. /*-------small.c-----------------------------------------------*/ int _stdcall MessageBoxA(unsigned, void *, void *, unsigned); #define MessageBox MessageBoxA void _cdecl main() {MessageBox(0, "Hello Putera.com", "Small", 0);} /*-------------------------------------------------------------*/ Kemudian gunakan utility dumpbin.exe dengan switch /ALL untuk tengok maklumat yang terkandung di dalam small.obj. Switch /ALL dump all info except code disassembly. -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk>dumpbin /ALL small.obj > _c_small.txt -----------------------------console output-------------------------------- Kiter tengok output kat fail _c_small.txt : Microsoft (R) COFF Binary File Dumper Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. Dump of file small.obj File Type: COFF OBJECT FILE HEADER VALUES 14C machine (i386) 3 number of sections 40EA3159 time date stamp Tue Jul 06 12:58:01 2004 10B file pointer to symbol table D number of symbols 0 size of optional header 0 characteristics SECTION HEADER #1 .drectve name 0 physical address 0 virtual address 26 size of raw data 8C file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 100A00 flags Info Remove 1 byte align RAW DATA #1 00000000: 2D 64 65 66 61 75 6C 74 6C 69 62 3A 4C 49 42 43 -defaultlib:LIBC 00000010: 20 2D 64 65 66 61 75 6C 74 6C 69 62 3A 4F 4C 44 -defaultlib:OLD 00000020: 4E 41 4D 45 53 20 NAMES Linker Directives ----------------- -defaultlib:LIBC -defaultlib:OLDNAMES SECTION HEADER #2 .text name 0 physical address 0 virtual address 22 size of raw data B2 file pointer to raw data D4 file pointer to relocation table 0 file pointer to line numbers 3 number of relocations 0 number of line numbers 60500020 flags Code 16 byte align Execute Read RAW DATA #2 00000000: 55 8B EC 51 6A 00 68 00 00 00 00 68 00 00 00 00 U‹ìQj.h....h.... 00000010: 6A 00 E8 00 00 00 00 C7 45 FC 00 00 00 00 8B E5 j.è....ÇEü....‹å 00000020: 5D C3 ]Ã RELOCATIONS #2 Symbol Symbol Offset Type Applied To Index Name -------- ---------------- ----------------- -------- ------ 00000007 DIR32 00000000 C $SG37 0000000C DIR32 00000000 B $SG38 00000013 REL32 00000000 8 _MessageBoxA@16 SECTION HEADER #3 .data name 0 physical address 0 virtual address 19 size of raw data F2 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers C0300040 flags Initialized Data 4 byte align Read Write RAW DATA #3 00000000: 53 6D 61 6C 6C 00 00 00 48 65 6C 6C 6F 20 50 75 Small...Hello Pu 00000010: 74 65 72 61 2E 63 6F 6D 00 tera.com. COFF SYMBOL TABLE 000 00000000 DEBUG notype Filename | .file small.c 002 000A1FE8 ABS notype Static | @comp.id 003 00000000 SECT1 notype Static | .drectve Section length 26, #relocs 0, #linenums 0, checksum 3B42D719 005 00000000 SECT2 notype Static | .text Section length 22, #relocs 3, #linenums 0, checksum 81859259 007 00000000 SECT2 notype () External | _main 008 00000000 UNDEF notype () External | _MessageBoxA@16 009 00000000 SECT3 notype Static | .data Section length 19, #relocs 0, #linenums 0, checksum A2D488 00B 00000008 SECT3 notype Static | $SG38 00C 00000000 SECT3 notype Static | $SG37 String Table Size = 0x14 bytes Summary 19 .data 26 .drectve 22 .text Basically COFF dan PE format mempunyai beberapa headers yang sama. Instead of starting with IMAGE_DOS_HEADER, COFF format start dengan IMAGE_FILE_HEADER. COFF format juga biasanya takde optional header. So, lepas file header, akan terdapat array of IMAGE_SECTION_HEADERS. So secara ringkasnya layout COFF file adalah : FILE_HEADER ------------------ SECTION_HEADERS ------------------ Sections' raw data Kiter tengok pada section pertama yang bernama ".drectve". Section ni adalah untuk specify switch kepada linker. Ia cuma terdiri daripada text string yang mengandungi switch untuk linker. Kat atas kiter leh tengok yang ia berikan switch -defaultlib:libc. Camne kiter nak tambah linker switch kat sini? Kiter bleh gunakan "#pragma comment" directive dengan "linker" sebagai comment-type. Sebagai contoh, katakanlah aku nak specify entrypoint program aku pada main, maka aku kena buat camni la : /*-------small.c-----------------------------------------------*/ #pragma comment(linker, "/entry:main") int _stdcall MessageBoxA(unsigned, void *, void *, unsigned); #define MessageBox MessageBoxA void _cdecl main() {MessageBox(0, "Hello Putera.com", "Small", 0);} /*-------------------------------------------------------------*/ dan ia akan masukkan string "/entry:main" pada section ".drectve" : RAW DATA #1 00000000: 2F 65 6E 74 72 79 3A 6D 61 69 6E 20 2D 64 65 66 /entry:main -def 00000010: 61 75 6C 74 6C 69 62 3A 4C 49 42 43 20 2D 64 65 aultlib:LIBC -de 00000020: 66 61 75 6C 74 6C 69 62 3A 4F 4C 44 4E 41 4D 45 faultlib:OLDNAME 00000030: 53 20 S / dengan - sama je. Section yang menyimpan switch kepada linker hendaklah bernama ".drectve" dengan attribute IMAGE_SCN_LNK_INFO. Seperti yang kiter tengok, compiler mengumpulkan semer data (initialized dan writeable) pada satu section dengan nama ".data", kod kiter pada section ".text". Camne plak kalau kiter nak letak data dalam section lain yang kiter namakan sendiri? Secara default, read only data diletakkan pada ".rdata" section, initialized read/write data diletakkan pada ".rdata" section, code pada ".text" section dan uninitialized data pada ".bss" section. Kalau korang tengok kat atas, text "Hello Putera.com" dan "Small" kiter berada pada ".data" section which is writeable. Since kiter sure strings ni kiter takkan ubah, meh kiter letakkannya di dalam ".rdata" section. Well.. ader banyak cara korang bleh lakukan, tapi dalam C kan kiter ader "const" keyword yang bermaksud variable tersebut adalah pemalar tetap? Meh kiter try: /*-------small.c-----------------------------------------------*/ const char title[] = "Small"; const char msg[] = "Hello Putera.com"; /*ubah proto untuk elakkan warning*/ int _stdcall MessageBoxA(unsigned, const char *, const char *, unsigned); #define MessageBox MessageBoxA void _cdecl main() {MessageBox(0, msg, title, 0);} /*-------------------------------------------------------------*/ Kompil dan gunakan dumpbin ke atas small.obj kiter akan lihat yang string kiter dah diletakkan dalam ".rdata" section : ..... SECTION HEADER #2 .rdata name 0 physical address 0 virtual address 19 size of raw data B2 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 40300040 flags Initialized Data 4 byte align Read Only RAW DATA #2 00000000: 53 6D 61 6C 6C 00 00 00 48 65 6C 6C 6F 20 50 75 Small...Hello Pu 00000010: 74 65 72 61 2E 63 6F 6D 00 tera.com. ..... Okeylah.. sebelum kiter citer lebih banyak lagi, rasanya lebih baik kiter link dulu program nih. Instead of using cl to control the linker, kiter akan invoke link.exe directly. First sekali kiter try tanpa sebarang options : -----------------------------console output-------------------------------- E:\Projek\VC\junk>link small.obj Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. small.obj : error LNK2001: unresolved external symbol _MessageBoxA@16 small.exe : fatal error LNK1120: 1 unresolved externals E:\Projek\VC\junk> -----------------------------console output-------------------------------- linker paparkan error unresolved external symbol. Ni logik la kan since kiter ader simbol MessageBox yang digunakan dalam kod kiter tapi takde definition. So, linker cuba cari kat object files lain (yang di"link"kan bersama, jika ader), termasuklah dalam default library (dalam kes kiter kat atas, libc.lib dan oldnames.lib). Oleh kerana dia tak jumpe simbol ni, maka tak dapat la dia resolved symbol nih. Seperti yang korang tengok, simbol MessageBoxA kiter akan dikompil menjadi _MessageBoxA@16 (_stdcall calling convention). The linker needs to find this symbol. Seperti yang kiter tau, MessageBox adalah fungsi yang diekspot oleh USER32.DLL. In order to use function exported from DLL, kiter kena link dengan library file untuk DLL tersebut. In this case, USER32.LIB (usually diletakkan under "lib" dir, subdir of your Visual C++ atau PSDK main directory). Camne, nak specify user32.lib kepada linker? Meh kiter tengok dulu camne nak invoke linker dengan betul : -----------------------------console output-------------------------------- E:\Projek\VC\junk>link /? Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. usage: LINK [options] [files] [@commandfile] options: /ALIGN:# /BASE:{address|@filename,key} /COMMENT:comment /DEBUG /DEBUGTYPE:{CV|COFF} /DEF:filename /DEFAULTLIB:library /DELAY:{NOBIND|UNLOAD} /DELAYLOAD:dll /DLL /DRIVER[:{UPONLY|WDM}] /ENTRY:symbol /EXETYPE:DYNAMIC /EXPORT:symbol /FIXED[:NO] /FORCE[:{MULTIPLE|UNRESOLVED}] /GPSIZE:# /HEAP:reserve[,commit] /IMPLIB:filename /INCLUDE:symbol /INCREMENTAL:{YES|NO} /LARGEADDRESSAWARE[:NO] /LIBPATH:dir /LINK50COMPAT /MACHINE:{ALPHA|ARM|IX86|MIPS|MIPS16|MIPSR41XX|PPC|SH3|SH4} /MAP[:filename] /MAPINFO:{EXPORTS|FIXUPS|LINES} /MERGE:from=to /NODEFAULTLIB[:library] /NOENTRY /NOLOGO /OPT:{ICF[,iterations]|NOICF|NOREF|NOWIN98|REF|WIN98} /ORDER:@filename /OUT:filename /PDB:{filename|NONE} /PDBTYPE:{CON[SOLIDATE]|SEPT[YPES]} /PROFILE /RELEASE /SECTION:name,[E][R][W][S][D][K][L][P][X] /STACK:reserve[,commit] /STUB:filename /SUBSYSTEM:{NATIVE|WINDOWS|CONSOLE|WINDOWSCE|POSIX}[,#[.##]] /SWAPRUN:{CD|NET} /VERBOSE[:LIB] /VERSION:#[.#] /VXD /WARN[:warninglevel] /WINDOWSCE:{CONVERT|EMULATION} /WS:AGGRESSIVE E:\Projek\VC\junk> -----------------------------console output-------------------------------- So basically, kiter bleh specify input files selepas semer switch kepada linker (jika ader). Meh kiter try tambahkan library user32.lib kepada linker : -----------------------------console output-------------------------------- E:\Projek\VC\junk>link small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. E:\Projek\VC\junk> -----------------------------console output-------------------------------- Kite leh letakkan semer object files yang perlu di"link" bersama seperti di atas. Dah linkok dah. Nampaknya linker bleh jumpe simbol _MessageBoxA@16 kat user32.lib. Secara umumnya,bila kiter nak gunakan (import) fungsi yang diekspot oleh DLL, kiter kenalah link dengan libraryfile yang define symbol bagi dll tersebut. Contohnya kalau korang importGetModulehandleA daripada KERNEL32.DLL, maka korang kena la link dengan kernel32.lib.Output program kat atas adalah small.exe. Meh try run program ni. Nampaknya OK.. tapi ader satumasalah plak... dia dah kasi sebijik free console plak dah. Seperti yang kiter tau, aderbeberapa subsystem kat Win32 - console, graphical, posix dan lain2 lagi. Kiter bleh gunakanswitch "/SUBSYSTEM" untuk menentukan kat subsystem maner kiter nak program kiter dilaksanakan.In this case, kiter nak GUI (WINDOWS). Actually console dengan gui tak banyak bezanya. Secaradefault, linker akan gunakan console subsystem untuk program dengan "main" entrypoint dangui subsystem untuk program dengan "WinMain" entrypoint... sambung lagi Quote Share this post Link to post Share on other sites
Ancient One 3 Report post Posted July 17, 2004 So, memandangkan kiter nak buat gui-based application dan tak perlukan consolewindow nih, kiter blehla tambah switch "/SUBSYSTEM:WINDOWS" kepada linker -----------------------------console output-------------------------------- E:\Projek\VC\junk>link small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. LINK : fatal error LNK1104: cannot open file "small.exe" E:\Projek\VC\junk>link /SUBSYSTEM:WINDOWS small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. LIBC.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16 small.exe : fatal error LNK1120: 1 unresolved externals E:\Projek\VC\junk> -----------------------------console output-------------------------------- Error ni kuar sebab sekarang linker expect kita define simbol WinMain sebagai entrypoint. Meh kiter edit source file kiter. As you can see, the startup code in libc needs the function WinMain that use __stdcall calling convention dan yang menerima 16 bytes (4 dwords) of arguments. Korang bleh tengok proto yang betul dalam SDK untuk define WinMain function dalam source file korang. Di sini, aku cuma penuhi kriterianya sahaja since i don't need them for now. So meh kiter try tukar kod kiter : /*-------small.c-----------------------------------------------*/ const char title[] = "Small"; const char msg[] = "Hello Putera.com"; int _stdcall MessageBoxA(unsigned, const char *, const char *, unsigned); #define MessageBox MessageBoxA void _stdcall WinMain(int a, int b, int c, int d) {MessageBox(0, msg, title, 0);} /*-------------------------------------------------------------*/ kompil dan link semula : -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk>link small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. E:\Projek\VC\junk> -----------------------------console output-------------------------------- Sekarang dah kompil dan link OK! Dan kiter jugak dah create application untuk subsystem yang betul (WINDOWS). Seperti yang korang tengok output kiter bersaiz 24kb! Bukankah itu terlalu beso untuk aplikasi yang tak lakukan aper2 selain daripada paparkan message box nih? Paling banyak pun (with 4 bytes alignment for data) title = 8 bytes msg = 20 bytes fungsi WinMain : xor eax, eax ;2 bytes push eax ;1 push title ;5 push msg ;5 push eax ;1 call [_MessageBoxA@16] ;6 ret ;1 ;atau call Kernel32.ExitProcess tambah import descriptor kira kasar tak sampai pun 1k. Camne plak output kita sampai 24kb? Ader beberapa sebab. Yang pertama sebab kiter link dengan libc.lib (static link). Ia akan include startup code yang akan call fungsi WinMain kiter. Bukan tu aje, ia banyak include kod ke dalam program kiter untuk menyokong standard C language features, functions daripada C library yang kiter gunakan, OS specific features dan sebagainya. Kalau kiter tak bergantung kepada semer ni, kita bleh buang kesemua kod yang di"link"kan bersama object file kiter daripada libc.c. But first, meh kiter try guna msvcrt.lib instead of libc.lib sebab ia dynamic. Bila kita tak gunakan switch "/M*" semasa compilation, maka ia akan gunakan libc.lib sebagai default library. Untuk gunakan msvcrt.lib sebagai default library, kita leh compiler small.c dengan switch "/MD". Meh kiter try : -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c /MD small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk>link small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. E:\Projek\VC\junk>dir small.exe Volume in drive E is DATA I Volume Serial Number is BC37-A3E4 Directory of E:\Projek\VC\junk 07/10/2004 22:52 16,384 small.exe 1 File(s) 16,384 bytes 0 Dir(s) 373,010,432 bytes free E:\Projek\VC\junk> -----------------------------console output-------------------------------- Okeh.. nampaknya file kiter dah jadi 16kb dah since kiter gunakan dynamic linking instead of static. Linker switch "/DEFAULTLIB:MSVCRT" juga boleh digunakan untuk tujuan ini but since libc.lib juga adalah default library, konflik akan berlaku. Kiter cuma bleh specify salah satu daripada 3 libraries ni - libc, libcmt, atau msvcrt (dan juga debug version ketiga-tiganya). Untuk buang default libraries daripada disertakan semasa link, gunakan switch "/NODEFAULTLIB". Contohnya : -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk>link /nodefaultlib:libc /defaultlib:msvcrt small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. E:\Projek\VC\junk> -----------------------------console output-------------------------------- 16 kb masih lagi beso untuk application kiter ni. Aper kater kita tak include langsung code daripada c libraries nih. Lagipun setakat ni kiter tak gunakannya pun. Korang bleh gunakan switch "/nodefaultlib" untuk removes semer default libraries daripada linker. Tapi sekarang, linker dah tak jumpe entrypoint (symbol) yang sepatutnya ditemui dalam maner2 satu default libraries ni (in our case, simbol tersebut adalah _WinMainCRTStartup. Samada korang define simbol WinMainCRTStartup (__cdecl calling convention) dalam kod korang seperti : /*-------small.c-----------------------------------------------*/ const char title[] = "Small"; const char msg[] = "Hello Putera.com"; int _stdcall MessageBoxA(unsigned, const char *, const char *, unsigned); #define MessageBox MessageBoxA void _cdecl WinMainCRTStartup() {MessageBox(0, msg, title, 0);} /*-------small.c-----------------------------------------------*/ dan kompil : -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk>link /nodefaultlib /subsystem:windows small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. E:\Projek\VC\junk> -----------------------------console output-------------------------------- ataupun korang bleh gunakan switch "/entry" kepada linker untuk specify entrypoint dalam program korang : /*-------small.c-----------------------------------------------*/ const char title[] = "Small"; const char msg[] = "Hello Putera.com"; int _stdcall MessageBoxA(unsigned, const char *, const char *, unsigned); #define MessageBox MessageBoxA void _stdcall WinMain(int a, int b, int c, int d) {MessageBox(0, msg, title, 0);} /*-------small.c-----------------------------------------------*/ -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk>link /entry:WinMain small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. E:\Projek\VC\junk> -----------------------------console output-------------------------------- Kod di atas mempunyai bug yang ketara. Kiter nyer WinMain sekarang adalah entrypoint sebenar yang sepatutnya tak terima aper2 arguments (since it is being called by kernel tanpa sebarang parameters). Kiter bleh buang arguments ni daripada function definition ataupun gunakan _cdecl calling convention so ia taklah buang arguments ni dari stack ataupun cara yang betul adalah entrypoint yang tak terima aper2 arguments daripada system. Aper2 pun sekarang program kiter dah jadi 12 kb. Sekarang program kiter takde kod daripada C libraries. But still masih beso gak. Meh kiter dump info program kiter, small.exe dengan dumpbin. Kiter cuma nak tengok PE header dia je, so kita gunakan switch /HEADERS : -----------------------------console output-------------------------------- E:\Projek\VC\junk>dumpbin /headers small.exe Microsoft (R) COFF Binary File Dumper Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. Dump of file small.exe PE signature found File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (i386) 2 number of sections 40F006FB time date stamp Sat Jul 10 23:10:51 2004 0 file pointer to symbol table 0 number of symbols E0 size of optional header 10F characteristics Relocations stripped Executable Line numbers stripped Symbols stripped 32 bit word machine OPTIONAL HEADER VALUES 10B magic # 6.00 linker version 1000 size of code 1000 size of initialized data 0 size of uninitialized data 1000 RVA of entry point 1000 base of code 2000 base of data 400000 image base 1000 section alignment 1000 file alignment 4.00 operating system version 0.00 image version 4.00 subsystem version 0 Win32 version 3000 size of image 1000 size of headers 0 checksum 2 subsystem (Windows GUI) 0 DLL characteristics 100000 size of stack reserve 1000 size of stack commit 100000 size of heap reserve 1000 size of heap commit 0 loader flags 10 number of directories 0 [ 0] RVA [size] of Export Directory 2024 [ 28] RVA [size] of Import Directory 0 [ 0] RVA [size] of Resource Directory 0 [ 0] RVA [size] of Exception Directory 0 [ 0] RVA [size] of Certificates Directory 0 [ 0] RVA [size] of Base Relocation Directory 0 [ 0] RVA [size] of Debug Directory 0 [ 0] RVA [size] of Architecture Directory 0 [ 0] RVA [size] of Special Directory 0 [ 0] RVA [size] of Thread Storage Directory 0 [ 0] RVA [size] of Load Configuration Directory 0 [ 0] RVA [size] of Bound Import Directory 2000 [ 8] RVA [size] of Import Address Table Directory 0 [ 0] RVA [size] of Delay Import Directory 0 [ 0] RVA [size] of Reserved Directory 0 [ 0] RVA [size] of Reserved Directory SECTION HEADER #1 .text name 20 virtual size 1000 virtual address 1000 size of raw data 1000 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 60000020 flags Code Execute Read SECTION HEADER #2 .rdata name 6E virtual size 2000 virtual address 1000 size of raw data 2000 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 40000040 flags Initialized Data Read Only Summary 1000 .rdata 1000 .text E:\Projek\VC\junk> -----------------------------console output-------------------------------- Seperti yang korang tengok, setiap section kiter bersaiz dalam unit 1000 bytes dalam fail (see file alignment field). Dan kalau korang tengok small.exe dalam binary file editor/viewer, korang akan dapati terlalu banyak null bytes! Semer ni hanyalah padding bytes sebab setiap section perlu align kepada 1000 bytes. Meh kita kurangkan sampah sarap ni. First sekali kiter tengok switch "/OPT:NOWIN98". Seperti yang dinyatakan dalam MSDN, WIN98 atau NOWIN98 arguments kepada "/OPT" switch mengawal section alignment dalam output program kiter. Bila kiter specify WIN98, linker akan berikan value 0x1000 kepada section alignment (note : section alignment is the section alignment in memory only. It will affect memory size but not file size). Meh aku amik sedikit kater2 dari MSDN about "/OPT:WIN98|NOWIN98" : WIN98 and NOWIN98 control the section alignment in the final image. For Windows 98 applications, it is optimal to align sections on a 4K boundary to improve load time (allows Windows 98 memory manager to cache executable images with a minimum of wasted space). This is on by default in the linker, so you need to specify /OPT:NOWIN98 to get a trimmed-down (but slower on Windows 98) version of the application. WIN98 is on by default. WIN98 is not on when: /ALIGN is used. /MACHINE does not target x86. /SUBSYSTEM specifies something other than WINDOWS or CONSOLE. /OPT:WIN98 is not enabled by default for images that would grow (according to the average growth equations, below) by more than 25 percent. In other words, /OPT:WIN98 would not be enabled for smaller images. You should enable /OPT:WIN98 explicitly to ensure that you are not affected by this tuning. Specify /OPT:NOWIN98 to get a smaller, but slower, on Window 98, version of your application. The enhancements in Windows 98 only work when the sections in a portable executable image begin on a page boundary. The /OPT:WIN98 option performs the necessary file alignment. If you are building components that run only on Windows NT or Windows 2000, you should use /OPT:NOWIN98. Ringkasnya bila kita specify "/OPT:NOWIN98" switch, kiter gunakan file alignment sebanyak 512 bytes manakala section alignment adalah 4096 bytes. Meh kiter try : -----------------------------console output-------------------------------- E:\Projek\VC\junk>link /entry:WinMain /opt:nowin98 small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. E:\Projek\VC\junk>dir small.exe Volume in drive E is DATA I Volume Serial Number is BC37-A3E4 Directory of E:\Projek\VC\junk 07/11/2004 01:38 1,536 small.exe 1 File(s) 1,536 bytes 0 Dir(s) 373,018,624 bytes free E:\Projek\VC\junk> -----------------------------console output-------------------------------- hmmm... sekarang kiter dah dapat 1.5kb output. Menarik. PE specification bagitau kiter bleh set file alignment pada gandaan kuasa dua bermula daripada 512 bytes sehingga 64kb. Jika section alignment kurang daripada 4096 bytes (page size for Win32 x86), maka file alignment hendaklah sama dengan section alignment. Please note that Win9x takleh load program dengan file alignment lebih kecik daripada 512 bytes (not sure about this), tapi tidak dalam nt/2k/xp. Since aku tengah guna XP sekarang ni, meh kiter kecikkan lagi program ni. Meh kiter try dengan section alignment = 2. Untuk tujuan ni, kita gunakan plak switch "/ALIGN". Switch ni adalah untuk modify section alignment dalam memory dan bukannya dalam file. Tapi seperti yang aku dah sebutkan kat atas, bila kiter berikan nilai yang lebih kecik daripada 4096 bytes, alignment dalam file juga akan berubah mengikut nilai yang diberikan untuk section alignment. Dengan "/ALIGN" switch, "/OPT:NOWIN98" dah takde guna dah so kiter tak perlu sertakan : -----------------------------console output-------------------------------- E:\Projek\VC\junk>link /entry:WinMain /align:2 small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. LINK : warning LNK4108: /ALIGN specified without /DRIVER or /VXD; image may not run small.obj : fatal error LNK1164: section 0x2 alignment (4) greater than /ALIGN v alue E:\Projek\VC\junk> -----------------------------console output-------------------------------- Opppsss... fatal error plak dah. Tapi pertama sekali meh kiter tengok warning tuh. Dalam MSDN kater : The /ALIGN option specifies the alignment of each section within the linear address space of the program. The number argument is in bytes and must be a power of two. The default is 4K (4096). The linker issues a warning if the alignment produces an invalid image. Tengok error tu plak. Basically dia kater alignment untuk section kedua adalah 4 bytes dan lebih tinggi daripada yang kiter berikan pada "/ALIGN" switch. Use dumpbin to view our object file dan korang dapat tengok yang alignment untuk section ".rdata" (2) adalah 4 bytes dan alignment untuk ".text" section adalah 16 bytes. Since 16 bytes adalah yang terbesar, kiter nyer nilai untuk "/ALIGN" switch must be equal to or larger than 16 bytes. Meh kiter try 16 bytes alignment : -----------------------------console output-------------------------------- E:\Projek\VC\junk>link /entry:WinMain /align:16 small.obj user32.lib Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. LINK : warning LNK4108: /ALIGN specified without /DRIVER or /VXD; image may not run E:\Projek\VC\junk>dir small.exe Volume in drive E is DATA I Volume Serial Number is BC37-A3E4 Directory of E:\Projek\VC\junk 07/11/2004 02:04 656 small.exe 1 File(s) 656 bytes 0 Dir(s) 373,018,624 bytes free E:\Projek\VC\junk> -----------------------------console output-------------------------------- Bagus.. cuma 656 bytes je. Bleh tak kiter gunakan alignment yang lebih kecik? Untuk itu, kiter kenalah specify alignment yang lebih kecik untuk ".rdata" dan ".text" section dalam object file kiter. Meh kiter gunakan "editbin" utility : -----------------------------console output-------------------------------- E:\Projek\VC\junk>editbin /? Microsoft (R) COFF Binary File Editor Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. usage: EDITBIN [options] [files] options: /BIND[:PATH=path] /HEAP:reserve[,commit] /LARGEADDRESSAWARE[:NO] /NOLOGO /REBASE[:[BASE=address][,BASEFILE][,DOWN]] /RELEASE /SECTION:name[=newname][,[[!]{cdeikomprsuw}][a{1248ptsx}]] /STACK:reserve[,commit] /SUBSYSTEM:{NATIVE|WINDOWS|CONSOLE|WINDOWSCE|POSIX}[,#[.##]] /SWAPRUN:{[!]CD|[!]NET} /VERSION:#[.#] /WS:[!]AGGRESSIVE E:\Projek\VC\junk>editbin /section:.rdata,a1 /section:.text,a1 small.obj Microsoft (R) COFF Binary File Editor Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. -----------------------------console output-------------------------------- -----------------------------console output-------------------------------- E:\Projek\VC\junk>dumpbin /all small.obj Microsoft (R) COFF Binary File Dumper Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. ... SECTION HEADER #2 .rdata name 0 physical address 0 virtual address 19 size of raw data B2 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 40100040 flags Initialized Data 1 byte align Read Only RAW DATA #2 00000000: 53 6D 61 6C 6C 00 00 00 48 65 6C 6C 6F 20 50 75 Small...Hello Pu 00000010: 74 65 72 61 2E 63 6F 6D 00 tera.com. SECTION HEADER #3 .text name 0 physical address 0 virtual address 1A size of raw data CB file pointer to raw data E5 file pointer to relocation table 0 file pointer to line numbers 3 number of relocations 0 number of line numbers 60100020 flags Code 1 byte align Execute Read RAW DATA #3 00000000: 55 8B EC 6A 00 68 00 00 00 00 68 00 00 00 00 6A U‹ìj.h....h....j 00000010: 00 E8 00 00 00 00 5D C2 10 00 .è....]Â.. .... E:\Projek\VC\junk> -----------------------------console output-------------------------------- Okeh.. kiter dah dapat dah 1 byte alignment for each section. Maknanya blehla kiter cuba dengan value 2 untuk "/ALIGN" switch... dan seperti yang korang akan try nanti, still ader error, kali nih daripada user32.lib plak sebab alignment paling kecik kat data section adalah 4 bytes. Meh kiter buang reference to MessageBoxA dan letak cuma kod yang secukup raser : /*-------small.c-----------------------------------------------*/ void _stdcall WinMain() { return; } /*-------small.c-----------------------------------------------*/ -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk>editbin /section:.text,a1 small.obj Microsoft (R) COFF Binary File Editor Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. E:\Projek\VC\junk>link /entry:WinMain /subsystem:windows /align:2 small.obj Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. LINK : warning LNK4108: /ALIGN specified without /DRIVER or /VXD; image may not run E:\Projek\VC\junk>dir small.exe Volume in drive E is DATA I Volume Serial Number is BC37-A3E4 Directory of E:\Projek\VC\junk 07/12/2004 12:57 478 small.exe 1 File(s) 478 bytes 0 Dir(s) 372,989,952 bytes free E:\Projek\VC\junk> -----------------------------console output-------------------------------- Memandangkan linker langsung takde idea mengenai subsystem yang kiter kehendaki, nak tak nak kiter kena bagitau la. Kalau korang nak tau samada image file ni valid atau tidak, load it under a debugger; kalau bleh load, okey ler tuh. As you can see, kiter dah minimize size kepada 478 bytes. This is not the minimum you can get. Kat bawah aku letakkan fasm code yang kompil menjadi 312 bytes. Ia ader import descriptor yang valid dan call MessageBoxA using ordinal (477). Since ia pass null pointer as string, MessageBoxA akan gunakan default "Error" string untuk title, dan MessageBoxA's ordinal adalah 477 pada WinXP Pro aku, so as long korang nyer MessageBoxA in USER32.DLL mempunyai ordinal 477, program ni akan paparkan message box. Note that aku remove dos stub dan juga 14 elements of data directory entries and the section contains import address table tak perlu mempunyai writeable attribute. ;----------small.asm----------------------------------- ;kompil dengan fasm IMAGE_FILE_32BIT_MACHINE=0x0100 IMAGE_FILE_EXECUTABLE_IMAGE=0x0002 IMAGE_FILE_MACHINE_I386=0x014c IMAGE_SUBSYSTEM_WINDOWS_GUI=2 IMAGE_SCN_CNT_CODE=0x00000020 IMAGE_SCN_MEM_READ=0x40000000 IMAGE_SCN_MEM_WRITE=0x80000000 ImageBase=0x10000 DOS_HEADER: dw "MZ" ;dos mz signature rb 0x3A dd NT_HEADERS ;file offset to new header NT_HEADERS: dw "PE", 0 ;nt header signature FILE_HEADER: dw IMAGE_FILE_MACHINE_I386, 1 ;Machine Id dan jugak bilangan seksyen dd 0, 0, 0 dw SizeOfOptionalHeader, IMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_EXECUTABLE_IMAGE OPTIONAL_HEADER: dw 0x10b ;Magic db 6, 0 ;linker version dd 0, 0, 0, Main, 0, 0, ImageBase, 1, 1 dw 5, 0, 0, 0, 5, 0 dd 0, SizeOfImage, SizeOfHeader, 0 dw IMAGE_SUBSYSTEM_WINDOWS_GUI, 0 dd 0, 0, 0, 0, 0, 2;kiter specify 2 directories je. ;export dir dq 0 ;import dir dd id, id.size ;yang lain takde! SizeOfOptionalHeader=$-OPTIONAL_HEADER SECTION_HEADER: ;1 dq ".ANCIENT" dd _end-Main dd Main dd _end-Main, Main, 0, 0, 0, IMAGE_SCN_CNT_CODE or IMAGE_SCN_MEM_READ SizeOfHeader=$ use32 Main: xor eax, eax push eax eax eax eax call [ImageBase+MessageBoxA];we're in MEMORY now, use the base ImageBase ret ;start of import descriptor id: dd 0, 0, 0, szUser32, iat dd 0, 0, 0, 0, 0 .size=$-id ;import descriptor size szUser32 db "USER32.DLL",0 iat: align 4;avoid slight delays on startup MessageBoxA dd 0x80000000+477 ;use ordinal dd 0 _end: SizeOfImage=$ ;----------small.asm----------------------------------- Kalau tak cukup lagi, meh kiter remove import descriptor dan completely removes data directories. Di bawah adalah VALID executable for a TRUE Win32 platform dan ia bersaiz kurang separuh disk sector size (226 bytes). Since takde debuggers dalam simpanan aku yang bleh load program nih, aku kena letak instruction "jmp $" so korang bleh tengok kat dalam SoftIce bila2 masa. All it does adalah infinite loop. ;----------small.asm----------------------------------- IMAGE_FILE_32BIT_MACHINE=0x0100 IMAGE_FILE_EXECUTABLE_IMAGE=0x0002 IMAGE_SUBSYSTEM_WINDOWS_GUI=2 IMAGE_SCN_CNT_CODE=0x00000020 IMAGE_SCN_MEM_READ=0x40000000 DOS_HEADER: dw "MZ" rb 0x3A dd NT_HEADERS NT_HEADERS: dw "PE", 0 FILE_HEADER: dw 0x014c, 1 dd %t, 0, 0 dw 224, IMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_EXECUTABLE_IMAGE OPTIONAL_HEADER: dw 0x10b db 6, 0 dd 0, 0, 0, Main, 0, 0, 0x10000, 1, 1 dw 5, 0, 0, 0, 5, 0 dd 0, SizeOfImage, HeaderSize, 0 dw IMAGE_SUBSYSTEM_WINDOWS_GUI, 0 dd 0, 0, 0, 0, 0, 0 ;takde langsung directory entries kat sini. SECTION_HEADER: ;1 dq ".ANCIENT" dd 1 dd Main dd 1, Main, 0, 0, 0, IMAGE_SCN_CNT_CODE or IMAGE_SCN_MEM_READ HeaderSize=$ Main: jmp $ SizeOfImage=$ ;----------small.asm----------------------------------- Okeylah.. meh kiter kembali ke dunia nyata sebab kiter takleh nak buat program berguna pun dengan saiz sekecik ni under Windows. Errr.. dah kat maner kiter nih. Oh yer.. aper kater kita bincangkan mengenai M$ extension kepada C language. Ini penting untuk kiter semer untuk mengetahui aper yang disokong oleh M$ compiler dan aper yang tidak. First sekali, meh kiter tengok mengenai calling convention sekali lagi. Kiter dah tengok tang _cdecl dan _stdcall. Sekarang kiter tengok tang _fastcall plak. M$ compiler gunakan 2 general purpose regs untuk pass 2 arguments yang pertama dan selebihnya akan dipush ke stack dari kanan ke kiri. Argument pertama (dari kiri) diletakkan pada ecx register manakala yang kedua plak kat edx. So, let say kiter tulis : void _fastcall foo(int dummy1,int dummy2,int dummy3,int dummy4) {} void _cdecl WinMain() { foo(1,2,3,4); } Maka ia akan dikompil menjadi (lebih kurang la) : _WinMain: ... mov ecx, 1 mov edx, 2 push 4 push 3 call @foo@16;decorated name for _fastcall function sama je mcm ;_stdcall, cuma kat depan ganti ngan @. ... ret @foo@16: ... ret 8 ;callee removes arguments daripada stack. Bila korang tengok disassembly suatu functions, korang selalu nampak pada permulaan function kod seperti : push ebp mov ebp, esp sub esp, ... ;atau takde ... dan diakhir kod ... mov esp, ebp pop ebp ret actually aper menatangnya nih? esp digunakan sebagi stack pointer. Stack adalah data structure yang digunakan untuk pass parameters kepada suatu function. Basically in Win32, kiter akan push data dalam unit 32 bits. So kalau kiter pass double data type (double precision fp), kiter kena push dua kali la untuk 8 bytes data type tersebut. ebp act as pointer into the stack frame yang kiter create. Dengan ini kite bleh rujuk arguments dan locals dengan ebp sebagai base. Korang immediately bleh tau braper total size local vars dengan melihat nilai constant pada instruction "sub esp, ..." kat atas. Selain daripada cara ni, kiter bleh gak gunakan instructions "enter" untuk create stack frame dan "leave" untuk destroy stack frame. Ok.. meh kiter tengok kod nih : void _stdcall foo(char arg1, int arg2) { char local1; int local2; local1 = arg2; local2 = arg1; } void _cdecl WinMain() { foo(1,2); return; } Camne kiter convert kod ni ke asm? Kalau korang build kod kat atas, maka korang akan dapat kod asm seperti kat bawah : _foo@8: push ebp mov ebp, esp sub esp, 8 mov al, [ebp+c] mov [ebp-4], al mov ecx, [ebp+8] mov [ebp-8], ecx mov esp, ebp pop ebp ret 8 _WinMain: push ebp mov ebp, esp push 2 push 1 call _foo@8 pop ebp ret Kalau kiter gunakan standard stack frame creation seperti ni, maka kiter bleh refer kepada function arguments bermula daripada ebp+8 manakala local vars bermula daripada ebp-4. Ini disebabkan "call" instruction implicitly push address after call instruction onto the stack dan "push ebp" instruction ush current ebp value onto stack. Using M$ compiler, kiter bleh gunakan "naked" attribute untuk suatu function tuh. Ni bermakna kiter kena create sendiri function prolog dan epilog. Meh kiter tambah naked attribute kepada function "foo" : __declspec(naked) void _stdcall foo(int arg1, int arg2) { char local1; int local2; local1 = arg2; local2 = arg1; } void _cdecl WinMain() { foo(1,2); return; } -----------------------------console output-------------------------------- E:\Projek\VC\junk>cl /c /Facs small.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. small.c E:\Projek\VC\junk> -----------------------------console output-------------------------------- kalau korang tengok listing file, korang akan dapati fungsi foo sekarang jadi camni : _foo@8 PROC NEAR ; File small.c ; Line 11 mov al, BYTE PTR _arg2$[ebp] mov BYTE PTR _local1$[ebp], al ; Line 12 mov ecx, DWORD PTR _arg1$[ebp] mov DWORD PTR _local2$[ebp], ecx _foo@8 ENDP Di mana ia tak sertakan function prolog dan epilog (including the return value) tapi still refer local variables dan function arguments menggunakan ebp sebagai frame pointer!! Please note naked attribute ni takde kena mengena dengan function type.. it's only related to removal of function prolog and epilog. Meh kiter bina prolog dan epilog kiter sendiri. Untuk ini kiter perlu gunakan __asm keyword untuk masukkan inline asm code : __declspec(naked) void _stdcall foo(int arg1, int arg2) { char local1; int local2; __asm { push ebp mov ebp, esp sub esp, __LOCAL_SIZE } local1 = arg2; local2 = arg1; __asm { leave ret 8 } } void _cdecl WinMain() { foo(1,2); return; } Korang dapat tengok yang kiter gunakan __LOCAL_SIZE constant. Ini adalah constant yangdisediakan oleh compiler untuk digunakan dalam inline asm code untuk menentukan jumlahsize local variables dalam bytes....bersambung.... Quote Share this post Link to post Share on other sites
nirel 0 Report post Posted December 31, 2004 omg...u're really the master... Quote Share this post Link to post Share on other sites
slier 28 Report post Posted October 28, 2010 very inspiring.. u will be missed Al-Fatihah Quote Share this post Link to post Share on other sites