In the article, Shrinking Windows EXEs and DLLs we describe four new optimizationsOc,
Oi, Oa, and Orthat you can use to eliminate unnecessary
information from these files. The four optimizations affect fixup
chaining, iterating data, minimizing the segment-alignment value,
and minimizing the resource-alignment value.
In a Windows application, code segments don't usually load at the same memory address each time you run the application. As a result, when a function in one segment (funcA()) needs to call a function in another segment (funcB()), Windows needs to provide a facility for computing the correct address for funcB() when it loads the application at run-time. (Until run-time, funcA() will contain a placeholder or "dummy" address for funcB(), since there's no way for the compiler or linker to know what the real address will be when the application runs.)
To compute the addresses of relocatable functions, each Windows application must have a table of pointers that specifies the locations of the functions in each segment. Using this relocation table, or fixup table, Windows can then insert the correct address of funcB() into the appropriate locations within the body of funcA(). This process, also known as "fixing up" function addresses, is illustrated in Figure A.
Figure A - Windows uses information in the fixup table to locate function addresses from other segments.
Unfortunately, if you call the same function numerous timesand if that function is in a different segment from the function that called itthe linker will, by default, create a new fixup table entry for each function call. This is true even though much of the information in the subsequent fixup entries is redundant.
Instead, you can apply fixup chaining to the redundant fixup entries. Fixup chaining replaces the redundant entries in the fixup table with a single entry. Then, it substitutes a series of linked offsets in place of the dummy addresses the linker would normally insert for an inter-segment function call, as shown in Figure B.
Figure B - Fixup chaining eliminates the redundant fixup table entries and utilizes previously unused space.
To apply fixup chaining to your Windows EXEs or DLLs, you'll
need to link them using the Oc option with TLINK 7.0, the
command-line linker. (None of the Ox optimizations
are directly available using the Integrated Development Environment's
[IDE's] built-in linker.)
If you're familiar with the compression techniques found in various disk compression utilities, you'll understand how iterating data reduces the size of an EXE or DLL. If the linker detects any significant pattern of bytes within a data segment, the linker will replace that block of data with a description of the data.
For example, if the linker detects the pattern
FF FE FF FE FF FE FF FE
it will recognize that the sequence FF FE repeats four
times. If it can create a description of the pattern that takes
up less space than the pattern of data itself, the linker will
do so when you use the Oi option.
When the linker assigns various blocks of code to segments (groups of functions or data that Windows will load together in a single chunk), it must decide how to arrange those segments within the EXE or DLL disk file. By default, the linker will write a segment to the disk file on 512-byte boundaries or, in other words, in 512-byte sectors. (For the remainder of this article, we'll use the term sector to identify these 512-byte segment-alignment intervals in the EXE file, not to describe anything related to file sectors on a disk.)
Unfortunately, if a given segment doesn't end at one of these 512-byte intervals, the linker will skip ahead to the beginning of the next sector before writing the next segment. For example, when you're using the default 512-byte sectors, if a given segment is 1026 bytes long (two 512-byte sectors plus two extra bytes), that segment will actually occupy 1536 bytes within the file. The 510 remaining bytes in the third sector are completely wasted, as shown in Figure C.
Figure C - The default 512-byte segment-alignment (or sector) size will frequently waste space in your EXE and DLL files.
The Oa option tells the linker to create the smallest possible sector value that's also a power of 2, assuming that such a sector size is appropriate for the EXE file size. For example, to completely eliminate wasted space for a 1026-byte segment, the linker could use a sector size of 2 (since 1026 is evenly divisible by 2 but not by 4, 8, 16, 32, 64, 128, 256, 512, or 1024).
However, if the size of the EXE file that contains this 1026-byte segment is greater than 131,072 bytes, the linker will need to use larger sectors and therefore tolerate some wasted space. (The size limitation occurs because the EXE file stores the starting point of each segment as a two-byte sector index that can identify up to 65536 sectors. Obviously, the linker can only use a sector value of 2 on files up to 65536 x 2 bytes in size.)
You don't need to worry about the size of the EXE file,
though, because the Oa option calculates the optimum value
for you. The linker will use the smallest possible sectors, but
no smaller than a value that it can use to specify a position
anywhere within the EXE file.
The -Or option tells the linker to use the same technique for
resource information (bitmaps, string lists, icons, and so on)
that the -Oa option applies to code information. Depending on
how many resources your application uses, applying this option
can yield significant savings.
Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.