CHAPTER 10
HOW TO CREATE AN EXE FILE.
OVERVIEW:
Once you have the program code, it has to be converted into a final *.exe file. The code is first translated by the NASM assembler from assembly language into binary machine code, which creates an *.obj output file. A linker, like the Microsoft Link.exe, then converts the *.obj file to a final *.exe file, linking together any other binary files, including a binary resource, or *.res file, if required. The obj file contains the program machine code, but it is not thereby the equivalent of an exe file, because it doesn't construct all the required file headers. A
Windows exe file has what is called the PE executable file format, and has a series of file headers that have to be created by the linker, as they are not normally created by the assembler. Also, the linker locates the code and data sections in terms of 'page' sizes, or memory blocks of specified size, both on disk, and when loaded into RAM. Thus, a linker is necessary if the assembler, as is normally the case, does not produce an output file in accordance with the PE executable format.
RESOURCES OVERVIEW:
An executable file can have a variety of what are called 'resources' entered into it when it is being compiled. These can be menus, associated with the window menu bar, or window icons, bitmaps, fonts, strings, to mention the most common types.
The programmer creates definitions of these resources in C language files, which are then converted by a resource compiler into binary resources, independently of the assembler and the program code.
The resource compiler creates a *.res file, and then
the linker links together the binary data in the *.res file with the binary *.obj file data to create the final executable, or *.exe, file. Microsoft's resource compiler consists of the two files rc.exe and rcdll.dll, which work together. We shall see, in the chapter on menus, how resource file source codes are to be created.
It is also possible to directly create resources from within the program code, without using a resource compiler. This possibility will be discussed in more detail elsewhere.
THE LINKER:
We have seen, above, that the assembler produces an *.obj file, which is the machine language into which the assembler translates the assembly language code.
This does not contain all the information necessary to form an exe file, because an exe file has a sequence of different file headers that cause the operating system to recognise the file as an executable file, rather than some other kind of file. These headers are created
and added to the contents of the *.obj file by a 'linker', and this process is what creates the final exe file.
The linker can, in addition, add in, or 'link in', further binary files, including resource, or *.res files, as described above, in forming the final executable file. This is not always the case, however, and the linker can work with only the *.obj file, and simply add the headers to create the exe file from the *.obj file alone.
THE MAKEFILE:
A so-called makefile is the file that contains a sequence of commands to activate, or run, the assembler and linker in order to create the final exe file.
The NASM assembler and Microsoft linker have been created to work in the old pre-Windows DOS operating system, and not in Windows, and the makefile thus has to be a DOS batch file, usually called Make.bat, which contains a sequence of DOS commands.
Windows was built on top of the older DOS system, which meant that DOS, or a DOS emulation facility, remained available, if required, even after Windows became the normal operating system. I do not intend to complicate this text by entering, here, into a
discussion of DOS, and will merely describe the relevant Make.bat file commands.
A *.bat file is a DOS executable file, and can be opened or double-clicked in the same way as with a Windows exe file, though it actually runs in DOS, and not in Windows. The following shows the contents of the Make.bat file used for compiling an executable file I have called Program.exe, as a general example, which could be any desired exe filename.
A dos batch or bat file is very easy to make. You simply type the commands into an ordinary text file, according to the proper syntax, and then simply rename the *.txt file as a *.bat file, and it becomes a dos executable file.
@ECHO OFF
@ECHO Compiling Menu.rc
rc Menu.rc
h2ash Menu.h
@ECHO Assembling Program.asm
nasmw -f win32 Program.asm
@ECHO Linking Program
link /entry:WinMain /subsystem:windows /libpath:C:\Lib/ Program.obj Menu.res
del Program.obj
pause
On a Windows screen, DOS displays a kind of window, which is really only a box for displaying a DOS text screen. @ECHO specifies comments to be displayed in the DOS 'window', to enable you to see what is happening. @ECHO OFF switches off the default commentary, so you can control the number of comments that are displayed. rc Menu.rc is a command line for the DOS-type exe file rc.exe (There are DOS-type as well as Windows-type exe files, which are not the same). The name of the exe file, without the .exe extension, is always specified, as a command to run the file, rc.exe in this case.
The command line can associate parameters with a Windows executable file, if the executable file is programmed to read them. It is possible to do other things with a command line, which I will not examine further here.
In the above case, rc.exe converts the menu resource text file, Menu.rc, to an equivalent in binary form. The Menu.rc parameter tells rc.exe that Menu.rc is the source file it is to convert to the binary resource file, Menu.res.
h2ash Menu.h is the next command, which tells h2ash.exe to convert the C source file Menu.h to Menu.ash. Menu.h is a file that contains C type definitions of equates, and h2ash.exe converts them to their assembly language equivalents in an *.ash file. h2ash is short for (*.H type file to (2) an ASsembly *.H type equivalent file).
Both of the source files Menu.rc and Menu.h are created simply as text files, with the correct syntax, and renamed.
The resultant Menu.ash file is included in the assembly language file, just like any other include file. In fact, you can easily carry out the equate definition conversions by hand, and avoid using h2ash altogether. We shall see more about this when dealing with menu resources.
The next command is nasmw -f win32 Program.asm, which is the command to run the NASM assembler, nasmw.exe. -f is a command line parameter that specifies the assembly output file format. That is -f win32 specifies that the output file should be a win32 type output file, used for creating a Windows exe file.
Program.asm specifies the asm source file that nasmw is to assemble, and the default output file name of the file containing the binary machine code will be Program.obj.
link is the command to run Microsoft's linker link.exe, and is followed by the parameters required to enable it to compile the exe file, which include the code entry point WinMain, a specification indicating the windows subsystem, the folder in which the library files are located, and the files to be included in the final exe file (Program.obj, and the binary resource file, Menu.res).
del Program.obj is a command to delete the Program.obj file after the exe file has been created, as it is no longer needed.
pause is a command to leave the DOS box on the screen until the user presses any key on the keyboard. This may or may not be necessary, depending of the version of Windows you have.
NASM COMPILATION FAULT MESSAGES:
The reason why it is desirable to leave the DOS box on the screen is that, if there are faults in syntax in the source code, the *.exe file will not be created, and the faults will be displayed on the DOS screen, so that you can correct them. An example is as follows:
If, for example, in your code, you have an instruction mov [esi],10, in your asm source file, on line 50, say, the DOS box will display the following fault:
Program.asm:50: operation size not specified.
This message is created by nasmw, to warn you that the code instruction should have been mov [esi], byte 10, mov [esi], word 10, or mov [esi], dword 10, because esi contains a memory address, and [esi] specifies its contents, but does not specify how many bytes are being indicated by [esi]. The nasm syntax, however, requires that this must be specified in order to create consistent code.
Another possible fault might be, if you have an instruction like mov eax,[Var] on line 151:
Program.asm:151: symbol 'Var' undefined.
This will mean that you have forgotten to define the name Var as a memory location, in either of the data sections of your code. nasm will print the fault if it comes across any symbol name in the code that has not been properly defined.
Another common fault will be
Program.asm:150: invalid combination of opcode and operands.
This fault message will appear if, on line 150, you have something like add eax,bx. This is because you can't have an instruction that involves parameters of
different sizes, like the dword eax and the word bx. You have to write either add eax,ebx, or add ax,bx.
Another common type of fault will involve the use of external function names. Suppose, for example, you called the API function ShowWindow, but forgot to define the name as 'extern' in the Windows.inc file. You will then get the following error message:
Program.asm:60: symbol '_ShowWindow@8' undefined
If you did define the symbol correctly in the Windows.inc file, but wrote the following instruction in the asm file:
api ShowWindow,[WINDOW.Handle]
thus leaving out the flag parameter that should appear after the window handle, you will get the following error message:
Program.asm:60: symbol '_ShowWindow@4' undefined
Notice that the error message specifies '@4', rather than '@8'. This is because nasm counts the total number of bytes of the pushed dword parameters and, if these do not conform to the specification in the extern definition (_ShowWindow@8), prints a 'symbol undefined' error message, which is a help to ensure that you have included the correct number of parameters. Fault messages, in general, in this way, are an indispensable help in detecting faults in syntax, and the like. They can't, of course, detect operational faults, such as improper addresses, because the code itself is correct, even though the address may, for example, be outside the permitted address space.
DYNAMIC LINK LIBRARIES:
In order to introduce the concept of a dynamic link library, we may ask, again: where, in fact, are the codes for API functions like ShowWindow actually located in the operating system?
The answer is that these are all contained in *.dll files, which are libraries that contain the codes for all the different API functions. The purpose of a *.dll file is to store code that can be used, from outside, by *.exe files.
The ShowWindow API function code, for example, is stored in the system library file, user32.dll.
You can create your own *.dll library files also, if desired. If you create a number of *.exe files, it may happen that all of them use several of the same procedures, calling them from different parts of their codes. In the ordinary case, each of the *.exe files would contain the code for each of the called
procedures. However, you can decide to store all these procedure codes, in one place, separately from all the *.exe files, in a library, and call the procedures from within each of the *.exe file codes. The purpose of this is to reduce the size of the *.exe files on the hard disk, especially if some of the procedures are of significant size.
Since such a library will contain procedure codes, it has to be an executable file, just like an *.exe file and, indeed, there is little difference between such a library file and an *.exe file, the main difference being that the library files are used by programs, and are not used as programs in themselves. The programs dynamically load the libraries into their code address space, and thus link their codes to them. The libraries are thus dynamically linked to the programs at run time, not permanently linked at compile time, and hence the name 'dynamic link library', having the appropriate file extension *.dll.
LINKER LIBRARY FILES:
The linker command line, in the makefile, contains the parameter /libpath:C:\Lib/, Lib being a folder containing *.lib type binary files. These are used by the linker in order to check the name definitions and number of parameters of external function names, like Windows API functions. This task has, in fact, largely been completed by the nasm assembler, as indicated in the case of the ShowWindow API function above. If it were not done by nasm, however, you would get the following error message from the linker:
Program.obj : error LNK2001: unresolved external symbol _ShowWindow@8
The linker does this by using binary files that contain information relating to the *.dll files, and the API functions they contain, and these *.lib files have to be made availale to the Microsoft linker.
For example, the code for the API function, ShowWindow, is contained in the dynamic link library file, User.dll, which is a Windows system file. The linker must have a corresponding binary file,
called User.lib, which contains information that enables the linker to verify the correct use, in the code, of the external function. Also, in the Windows.inc files, you will have the following special section to indicate the library file names for all the API functions used in the code:
[section .drectve info]
;
; Automatically link these libraries
;
db '/defaultlib:kernel32.lib /defaultlib:gdi32.lib /
and so on for all the required *.lib file names. That is, the entries should specify all the dll-name *.lib files corresponding to all the *.dll files associated with all the API functions used in your code. If one of these library files is not specified, the linker will print the error message 'unresolved external symbol', referred to before, for every use of an API function name that belongs to the omitted *.lib corresponding to the particular *.dll.
Part I: Part I Introduction
Chapter 1: Binary numbers, code, and procedures
Chapter 3: Assembly Language
Chapter 4: Assembly Language Code example
Chapter 5: Macros and HLA
Chapter 6: HLA code example
Chapter 7: The Windows operating system
Chapter 8: Data Structures
Chapter 9: How to Create a Windows Program
Chapter 10: How to Create an Exe File
Chapter 11: Structured Programming and OOP
Part II: Part II Introduction
Chapter 12: Debugging a Windows Program Code
Chapter 13: Painting the Window Client Area
Chapter 14: Creating Window Menus
Chapter 15: How to Create Toolbars
Chapter 16: How to Create Popup Menus
Chapter 17: About the Windows Clipboard
Chapter 18: How to Create Bitmaps
Chapter 19: Icons and the Ico Format
Chapter 20: Common Dialog Boxes
Chapter 21: Working with Files
Chapter 22: Scrollbars and Scrolling
Chapter 23: How to Send Data to the Printer
Chapter 24: Miscellaneous Topics
©Alen, June 2014
alen@alenspage.net
Material on this page may be reproduced
for personal use only.