Jump to content
Sign in to follow this  
Ancient One

Our [c]ompiler

Recommended Posts

Our ©ompiler.

[General]

Aku sajer2 tulis ni sementara nak tunggu citer CSI. Matlamatnya adalah untuk memberikan

contoh bagaimana korang bleh menggunakan kebolehan compiler korang untuk menghasilkan

output yang korang kehendaki. Nota kecik ni cuma related kepada Win32 programming, C language

dan Microsoft Visual C++ compiler. Aku malas nak citer panjang2 pasal standard C. Mari kita

lupakan standard dan code portability buat sementara waktu smile.gif.

[Win32 programming]

Meh kita bina Win32 program yang ringkas untuk memahami tentang command line options yang disediakan

oleh 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 smile.gif... 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. Kita

cuma 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..

Share this post


Link to post
Share on other sites

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 for

Common Object File Format. Ia adalah format yang digunakan oleh M$ compiler dan lain2 untuk object

file. Selain dari COFF, M$ compiler juga boleh compile OMF (Object Module Format) object file yang biasanya

digunakan oleh Borland compilers. Since COFF dan OMF adalah dua format berbeza untuk mewakili benda yang

sama, 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 link

ok 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 library

file yang define symbol bagi dll tersebut. Contohnya kalau korang import

GetModulehandleA 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 satu

masalah plak... dia dah kasi sebijik free console plak dah. Seperti yang kiter tau, ader

beberapa subsystem kat Win32 - console, graphical, posix dan lain2 lagi. Kiter bleh gunakan

switch "/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. Secara

default, linker akan gunakan console subsystem untuk program dengan "main" entrypoint dan

gui subsystem untuk program dengan "WinMain" entrypoint... sambung lagi

Share this post


Link to post
Share on other sites

So, memandangkan kiter nak buat gui-based application dan tak perlukan console

window 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 yang

disediakan oleh compiler untuk digunakan dalam inline asm code untuk menentukan jumlah

size local variables dalam bytes....bersambung....

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...