A Stronger Defense Through Better Software
A Service Provided By
George Chastain, Huntsville, Alabama

Résumé
Home What's New? About Security Contact Submit Content Disclaimers

Search This Site


powered by FreeFind

Targeted Web Search

Departments
Development

Test, QA & Safety
The Biz

Information Security

Joint Resources

General
CDR
Systems
Weapons 101
DoD Links

Media
Office Resources
Defense News
Defense Magazines

Tech & World News

Reader's Corner

Books

 ©2006 G. Chastain

Map Files And DLL Rebasing

An Introduction Using Visual C++ 6.0

Or

Painless Proactive Problem Pinpointing

By George Chastain

3 November 2000

 

Have you ever noticed that when one of your programs crashes, like when you get a Dr. Watson error, it displays an address where the crash occurred? Did you know that there is a way to locate the place in the code where that error occurred without the use of a program like Visual C++ or even your binaries?! You know the story. You are working on Version 20 of your product using Windows 2010 and Visual C++ 10. Then a customer calls to report a crash in Version 5 of your product that was created with Visual C++ 5 on Windows NT 4.0. Your first reaction may be – "Oh Man! We have to dig up all the old versions of the dependencies and build a machine on that platform in order to locate the problem!"

Ah, but there is an easier solution. There is a simple text file that you can create with each build that can help you locate the source of the problem in minutes! You do not need a machine configured on that old platform. You don’t have to worry about your build being exactly like that old one delivered to the customer so that the crash address is the same. You don’t have to worry about duplicating the exact sequence of events to reproduce the problem. What is the key to this magic? It is the Map File. Yes ladies and gentlemen. Learn to use this and you will amaze your family and friends. Be the life of the party!

What is a Map File? Simply put, it is a text file containing information about your program’s global symbols, source file and source line numbers. You can create this when linking your executable or DLL by setting certain options for the linker. First, I will explain how to create a Map File in Visual C++ 6.0 and then I will explain how to use it.

Creating A Map File

You can create a Map File for either your Debug configuration or your Release configuration. It is the fact that you can create this file for your Release configuration that makes the Map File so valuable.

To create a Map File:

  1. Open your project in Visual C++
  2. Select Project | Settings
  3. Make sure that your release configuration is selected in the Settings For: combo box.
  4. Under the C/C++ tab, select "General" in the Category combo box
  5. In the Debug Info: list box, select "Line Numbers Only". This will add the compiler switch "/Zd" to store line number information in the obj files. Without setting this switch, you will only be able to find problems in libraries and DLLs that your executable uses. Turning this switch on will let you find problems in the executable as well. You should do this step for your .LIBs, .DLLs and your application’s main source. See Figure 1 Compiler Switches.

    Figure 1 Compiler Switches

  1. Under the Link tab select "Debug" in the Category combo box
  2. Check the Generate mapfile option
  3. Down in the Project Options: edit box, type the linker switches "/MAPINFO:EXPORTS" and "/MAPINFO:LINES". See Figure 2 Visual C++ Project Settings Map File Options.

 

    Figure 2 Visual C++ Project Settings Map File Options

  1. In the Mapfile name: edit box, type the location and name of the Map File to be generated or just use the default it provides. It is a good idea to keep the ".map" extension so that it is clear what the file is.
  2. Repeat these steps for the debug configuration if desired, skipping Steps 4 and 5 because the debug configuration will create all debug symbols including the line information.
  3. Select OK and save your project changes.
  4. Rebuild your executable or DLL. The Map File will be created at the location specified in step 9.
  5. SAVE THE MAP FILE IN YOUR SOURCE CONTROL SYSTEM SO THAT YOU KNOW THE VERSION OF SOURCE USED TO CREATE IT! You must maintain this file in your source control system with the source that was used to create it so that you can locate crashes that occur with that version in the future.

That’s all you have to do to create a Map File in Visual C++. You should do this for all executables and all of your DLLs used by the executable. Note that these steps are not creating a full debug build of your executable. It is merely generating line number information for the executable and its dependency static- and dynamic-link libraries.

Now let us see how to use the Map File.

The Clue

I created a demo executable and placed a statement in it that would guarantee that the executable would crash. The bad instruction is at line 120 in the source file DemoDlg.cpp. When I run my sample executable, the dialog box shown in Figure 3 Application Error Dialog Box is displayed on screen.

Figure 3 Application Error Dialog Box

As you can see, it reports that the executable crashed at the instruction with the address 0x00401308. Write this address down before dismissing the dialog. This is the location of the error that we must find. You should also attempt to educate your customers to record that address as well when reporting crashes to customer support.

 

Finding The Error

At the top of the Map File is the module name, the timestamp when LINK.EXE linked the module, and the preferred load address (more on this later). The Map File then lists the sections the linker brought in from the various OBJ and LIB files. Then, the Map File begins to list the really helpful information.

I will now walk you through the steps of using the Map File. Refer to the excerpts of the Map File created with my demo program that are attached to this article.

  1. Locate the Map File
  2. The first step to finding the problem is to look in your project’s Map Files (you should have one for the executable and one each for all the DLLs that it uses). First, look at the "Preferred load address" at the top of the Map File and the last address listed in the public function section. If the address reported at the crash is within that range, then you have the correct Map File. The Public Function Section is shown in the excerpts using the color blue.

  3. Locate the Function Containing the Error
  4. After you have identified the Map File containing the address reported in the crash, you want to scan down the column of addresses with the heading "Rva+Base". These addresses are the addresses of the beginning of the functions. Find the first address that is larger than the address reported when the application crashed. The entry just before that one is the function in which the error occurred! This is getting exciting isn’t it!?

    Looking at the excerpts for the Map File, that entry is highlighted in yellow in the public function section. The function where the error occurred is OnInitDialog!

  5. Calculate the Crash Location Offset
  6. Now, you need to do a little hexadecimal arithmetic. To find the source line number where the error occurred, use the following formula:

    (crash address) – (preferred load address) – 0x1000

    The "crash address" is the address of the instruction reported in the dialog box when the executable crashed. The result of this calculation is the offset address from the beginning of the first code section. You’re almost there! Just hold on!

  7. Locate the Line of Source That Caused the Crash

Using my example, the calculation from Step 3 would yield:

0x00401308 – 0x00400000 – 0x1000 = 0x308

Now we know the function in which the error is located and we know the offset of the error from the beginning of the code section that contains the error. Since we know what the function is we know what source file contains the function. So we look through the Map File line information until we find the line list for that source file. The line information is shown in the excerpts using the color brown.

In this example, the OnInitDialog function is in the source file DemoDlg.cpp. Once we find the line listing for DemoDlg.cpp we look for the closest number that isn’t over the calculated value. The lines are shown as a line number followed by the offset from the beginning of the code section in which this line occurred.

So we find that the closest line that isn’t over 0x308. In my demo, we find a line entry for exactly offset 0x308. That entry is for line 120 and is highlighted in the excerpt in the color red. Note that had the /Zd switch not been set for the compiler, the line number information for DemoDlg.cpp and Demo.cpp would not be listed. Only the line number data for appmodul.cpp in mfcs42.dll would have been listed.

Now, with my demo, all I have to do is open the source file DemoDlg.cpp in a text editor and go to line 120. That is the line that caused the crash!

There is one catch when using Map Files to diagnose crashes in DLLs. You must know where the DLL is loading. And for that, you have to know where the DLL will be loaded into memory at the time the DLL is linked which is when the Map File is created. But why do we have to worry about this when using Map Files for DLLs? I will explain.

The Preferred Load Address and DLL Rebasing

All DLLs are created with an address known as the Preferred Load Address. This is the address where we want the DLL to be loaded into a process’ virtual address space. Unfortunately, many developers do not think to define a specific Preferred Load Address for a DLL that they create. Without explicitly defining one, Visual C++ assigns the default load address of 0x10000000. The result is that every time you run your application, you see messages that one or more DLLs had to be relocated. This is because they all want to load at the default Preferred Load Address of 0x10000000 and that is physically impossible.

When a DLL is loaded at runtime, the system checks to see if there is space to load the DLL at the Preferred Load Address. If the address space range from the Preferred Load Address to the load address plus the image size is not available, then the operating system loads the DLL somewhere else in memory. This means that not only must two or more DLLs not use the same Preferred Load Address, but they also must use addresses that ensure that they load outside the range required by each of the other DLLs to be loaded by the process. In Figure 4 Overlapping DLLs, A.DLL is loaded into the process’ address space first at the Preferred Load Address of 0x60000000. Then an attempt is made to load B.DLL at the Preferred Load Address of 0x60010000. However, the Preferred Load Address of B.DLL lies within the range of the address space occupied by A.DLL. B.DLL cannot overlap A.DLL in the process address space.

 

Figure 4 Overlapping DLLs

 

 

In this situation, the system will move, or rebase, B.DLL, to a location where there is enough space to load it as shown in Figure 5 Relocated DLL.

Figure 5 Relocated DLL

Notice that B.DLL was not simply loaded at the address immediately following the end of A.DLL. Windows forces an image to be loaded on an even 64K boundary. So, if A.DLL doesn’t end on a 64K boundary, then B.DLL will be moved up in memory to the next 64K boundary address. We will need to remember this fact later when we look at contiguously basing DLLs in memory.

The Preferred Load Address is also often referred to as the "Base Address". However, I like the name "Preferred Load Address" because it more accurately reflects the fact that this is the location where we would prefer that the DLL be loaded but that the address may not be where the DLL actually gets loaded.

When the system dynamically changes (rebases) Preferred Load Address of the DLL it also updates the offset addresses of the functions within the DLL in the loaded copy. Then when a crash occurs within the DLL, the address of the crash that is reported will not match the data in the Map File created when that DLL was linked. Thus, any Map File you have for that DLL becomes useless.

So if you need to know the Preferred Load Address of a DLL in order to use a Map File to identify the location of a crash, how can you use the Map File if the address may change? The answer is to explicitly define a unique Preferred Load Address for the DLL that ensures that it will load in a free location in the process’ virtual address space.

Before I explain how to chose a Preferred Load Address for you DLL so that it will not conflict with other DLLs, I will explain how to set the Preferred Load Address in your project and describe one possible scheme suggested by the MSDN Library for assigning Preferred Load Addresses to your DLLs (it isn’t necessarily the only scheme).

Setting The Preferred Load Address In Your DLL Project

You set a Preferred Load Address for your DLL in Visual C++ as follows:

  1. Open your DLL project in Visual C++
  2. Select Project | Settings…
  3. Make sure that your release configuration is selected in the Settings For: combo box.
  4. Select the Link tab
  5. Select "Output" in the Category: combo box.
  6. Enter a specific Preferred Load Address into the Base address: edit box.
  7. Repeat these steps for the debug configuration if desired.
  8. Select OK and save the project settings.

A Suggested Preferred Load Address Assignment Scheme

The Microsoft documentation suggests the following scheme for defining a base address for your DLLs based on the first letter of the DLL name:

 

First Letter

Base Address

A – C

0x60000000

D – F

0x61000000

G – I

0x62000000

J – L

0x63000000

M – O

0x64000000

P – R

0x65000000

S – U

0x66000000

V – X

0x67000000

Y – Z

0x68000000

Figure 6 Base Address Assignment Scheme

 

According to the MSDN library documentation, the system DLLs are currently based in memory from 0x70000000 to 0x78000000 (0x68000000 to 0x78000000 on MIPS). Therefore, you should base your DLLs from 0x60000000 to 0x68000000. It is best to base DLLs from the top of the address range down, instead of from the bottom up. Dynamic memory is allocated from the bottom up and if a DLL tries to load where dynamic memory has been allocated, it will be relocated, just as if a DLL was loaded at that address.

As an example, consider the situation where we have the following DLLs:

A.dll

D.dll

D2.dll

D3.dll

G.dll

You would assign the base addresses as follows:

A.dll 0x60000000

D.dll 0x61000000

D2.dll 0x61100000

D3.dll 0x61200000

G.dll 0x62000000

Notice that for multiple DLLs starting with the same letter we just increment the next digit. Due to the wide separation of the addresses, this should prevent the relocating of the DLLs at runtime and help ensure that the crash address reported would be one that you can locate in the Map Files for your project. However, you may need to check the image size of the DLLs in your application to ensure that they do not overlap. Now let us see how to choose a Preferred Load Address for a new DLL.

Will This Other DLL Force Mine To Be Rebased?

Choosing a unique base address for your DLLs will not by itself completely guarantee that there still will be no conflict with another DLL in your project or with some third party DLL that your application uses. However, if you use a good scheme for assigning addresses to the DLLs you can improve your odds significantly by providing a wide range of available load addresses that are widely separated. You may, in fact, use addresses down to and including 0x10000000 (the default base address for DLLs created by Visual C++) although I wouldn’t recommend using 0x10000000 as this is the default address assigned to a new DLL by Visual C++.

If your application will load third party DLLs and you want to ensure that yours do not load at the same address (or overlap) as one of these other DLLs, you can determine the Preferred Load Address of any DLL after it has been built. You can also determine the address space range required by any DLL so that you can pick a Preferred Load Address for your DLL that does not conflict with any other. There are a number of tools available, many as freeware, that provide this capability. I will discuss those that are available to the typical Visual C++ developer.

Dependency Walker

One excellent tool is delivered with Visual C++ Professional and Enterprise Editions. It is called Dependency Walker (depends.exe). It shows you all dependencies for an executable or a DLL along with their dependencies in a tree view windowpane. It will show you if that version of a dependency DLL provides interfaces that satisfy all calls to that DLL by the client. But since we are discussing the Preferred Load Address of DLLs, I will explain how to determine this using Dependency Walker. Refer to Dependency Walker’s help for further details on using this excellent utility.

NOTE: Beginning with Visual Studio 6.0, Microsoft implemented a type of linking known as delay import or delay load. Using this, a dependency DLL will still be implicitly linked to its client but will not be loaded until the first reference to it is actually made. This ability allows for increased startup performance of an application that has many dependencies. The version of Dependency Walker delivered with Visual Studio 6.0 is incapable of detecting and listing delay loaded DLLs. Therefore, you may not see all the dependencies when you examine an executable or DLL using Dependency Walker. Please refer to the MSDN library for details on implementing delay loading with the DLLs that you create.

To determine the Preferred Load Address of a DLL using Dependency Walker:

  1. Run depends.exe
  2. Select File | Open…
  3. Browse to the DLL for which you wish to determine the Preferred Load Address and select it to open.
  4. Look in the bottom windowpane, the "Module List View".
  5. Look for the name of that DLL in the list displayed in the "Module List View".
  6. Look in the column labeled Base. There you will see the Preferred Load Address, or Base Address, for that DLL.

Figure 7 Identifying Base Address Of A DLL In Dependency Walker illustrates where to find the Preferred Load Address of an example DLL named DemoDLL.dll. In this example, the DLL has the default Preferred Load Address of 0x10000000. The line of interest is selected in the "Module List View" window.

Figure 7 Identifying Base Address Of A DLL In Dependency Walker

 

Dumpbin

Another tool you can use to determine the Preferred Load Address of a DLL is the command-line utility dumpbin.exe, also delivered with Visual C++.

  1. Open an MS-DOS window
  2. cd to the folder containing the DLL.
  3. Assuming you have dumpbin.exe in your path (it’s in the bin folder of you Visual C++ installation path), just enter:

    dumpbin /headers your.dll | more

    This will page information about the DLL to the screen.
  4. Look for the line that says "image base". That is the Preferred Load Address, or Base Address.

Figure 8 Example Output of dumpbin.exe illustrates the second page of output from dumpbin on the same DemoDLL.dll. The data displayed is for the "Optional Header Values". The line of interest reads

10000000 image base

That is the default Preferred Load Address for the example DLL.

Figure 8 Example Output of dumpbin.exe

 

 

Link

Still another way to determine the Preferred Load Address, or Base Address, of any DLL is to use the Linker itself.

  1. Open an MS-DOS window
  2. cd to the folder containing the DLL
  3. Assuming you have Link.exe in your path, just enter:

    link –dump –headers your.dll | more

    This will page information about the DLL in the same manner as dumpbin.exe.

The output from this command is exactly the same as that shown in Figure 8 above.

Now that we know how to determine the Preferred Load Address of a third party DLL, how do we know how much room to allow for it when we pick a load address for our own DLL? Again, the first DLL can tell us.

Contiguous DLL Basing

The best way to ensure that none of your DLLs conflict with each other is to pick a base address for one and set it in that DLL's project settings as described above. Then obtain the image size of that DLL after you build it and use that with the address you picked for that DLL's base and calculate the base address to assign to the next DLL that you create. Earlier when I was discussing the dynamic rebasing of DLLs I mentioned that the system will load the DLL on an even 64K boundary. When assigning a Preferred Load Address for a DLL within Visual C++, you should pick a number that is an even multiple of 64K. If you do not, the Linker will round the value you specify up to the next multiple of 64K anyway, thus assigning the DLL a Preferred Load Address other than what you specified. But since the linker assigns the address, the Map File created by the Linker will still be valid. Earlier, we learned that the default Preferred Load Address assigned to a DLL by Visual C++ is 0x10000000 (Visual Basic assigns a different default Preferred Load Address). The value 0x10000000 is a multiple of 64K (65536). It is also a multiple of 4096 (page size).

For maximum load performance of your application and to ensure that your DLLs are not dynamically rebased, you should pick a Preferred Load Address for each DLL such that each DLL is loaded into memory contiguously. That is, one after the other. Having the DLL's contiguous in memory in this way improves the startup performance of the software because you are sparing the system from having to make these space checks and rebase calculations every time you run the application.

When Win32 allocates space in which to load an image (executable or DLL) it does so, by default, in 4096 byte chunks (page size). Therefore, when the system loads a DLL whose size is not an even multiple of 4096 bytes, the system will round up the image size to the next multiple of 4096 bytes. Note that the value of 4096 bytes is also known as the Section Alignment of an image (which may be overridden by the developer).

 

Figure 9 Example of Contiguous Basing

 

 

 

But how do we determine the image size of a DLL? You cannot use the file size of the DLL as the image size.

 

Determining Image Size

As a Visual C++ developer, you have at your disposal a utility that can tell you exactly what the image size of a DLL is. We’ve already looked at it in the discussion about determining the Preferred Load Address of a DLL. It is the dumpbin.exe utility. Using the same command described above, you will see a line output that shows a hex value and a statement that this is the "size of image". See Figure 8 above. This is the true image size of the DLL rounded up to the nearest 64K boundary. Add this hex value to the base address for that DLL and then round the sum to the next multiple of 64K and assign that value as the base address of the next DLL you build. Do this in succession for each DLL that you create.

In addition to the dumpbin.exe utility, a more user-friendly utility is provided. It is the Microsoft Quick View utility. Simply browse to the folder containing the DLL in Windows Explorer. Right-click on the DLL and select the "Quick View" option from the context menu. Quick View will run and display information about the DLL. Scroll down until you see the information labeled "Image Optional Header". See Figure 10 Quick View of DLL. You will notice the same information that was displayed by dumpbin.exe for the "Optional Header Values" in Figure 8.

Figure 10 Quick View of DLL

You may also access Quick View from within Dependency Walker.

  1. Select the DLL for which you wish to view data on. Figure 11 Dependency Tree View shows the example DLL selected in the "Dependency Tree View" windowpane. You could also select it in the "Dependency List View" pane as in Figure 7.
  2. Right-click on the selected DLL and select "External Viewer" from the context menu. Alternately, you may select "External Viewer" from the "View" menu. The Microsoft Quick View application will run on the selected DLL as shown in Figure 10 above.

Figure 11 Dependency Tree View

As a side note, if you need to know the dependencies of an executable or a DLL but you do not have Dependency Walker, you may use Quick View to obtain the list. When you view the executable or DLL with Quick View, scroll down until you see the section labeled "Import Table". This section lists all direct dependencies of the executable or DLL that you are viewing with Quick View. However, Quick View does not "walk" the dependencies and provide a tree view of all of them, as does Dependency Walker. You will have to note all dependencies shown by Quick View, locate those dependencies on your machine manually, and then run Quick View on each of those to obtain their dependencies. You would then have to repeat this until you reach the end of the dependencies. Obviously, Dependency Walker is a superior tool for this reason as well as for all the additional information it provides.

Anything Else to Consider?

We’ve looked at how to ensure that each of your application’s DLLs do not attempt to load in the same address ranges in memory. But is that all? No. You must also ensure that your DLLs do not interfere with the executable itself. You may use the same techniques described above to determine the Preferred Load Address and Image Size for the executable and pick your DLL Preferred Load Addresses to ensure that they are outside the range required by the executable. However, it is not recommended that you modify the Preferred Load Address of the executable. By convention, EXE files load at address 0x400000 (4MB) in the process address space and get the first choice for a load address (ahead of DLLs).

Rebasing For Performance Only

Microsoft provides another utility with Visual C++ called rebase.exe. This utility allows you to automatically rebase DLLs from the command line. You can provide several DLL names on the command line to rebase all of them at once. This is perfect if all you are concerned with is the startup performance of your application. However, since this changes the Preferred Load Address of the DLL images outside the control of the Linker, any Map File you may have for those DLLs will be useless. You must determine the Preferred Load Address at the time the image is linked in order to use the Map File. Refer to the MSDN library for more details on rebase.exe.

One final note; You can produce Map Files in Visual Basic as well but the technique for doing so is not as user-friendly as it is for Visual C++. You should refer to the documentation for Link.exe and Visual Basic for further details.

Now you’ve learned what a Map File is and how easy it is to create one and to use it. While doing so, maybe you’ve learned a little more about how DLLs are processed. I hope that you find this both educational and useful in your future debugging efforts.

Example Map File

Demo

Timestamp is 39579654 (Mon Jun 26 12:43:48 2000)

Preferred load address is 00400000

Start Length Name Class

0001:00000000 000008f0H .text CODE

0001:000008f0 00000072H .text$x CODE

0002:00000000 000001dcH .idata$5 DATA

0002:000001e0 00000324H .rdata DATA

0002:00000508 000000a0H .xdata$x DATA

0002:000005a8 00000050H .idata$2 DATA

0002:000005f8 00000014H .idata$3 DATA

0002:0000060c 000001dcH .idata$4 DATA

0002:000007e8 000001ceH .idata$6 DATA

0002:000009b6 00000000H .edata DATA

0003:00000000 00000004H .CRT$XCA DATA

0003:00000004 00000004H .CRT$XCL DATA

0003:00000008 00000004H .CRT$XCU DATA

0003:0000000c 00000004H .CRT$XCZ DATA

0003:00000010 00000004H .CRT$XIA DATA

0003:00000014 00000004H .CRT$XIZ DATA

0003:00000020 00000094H .data DATA

0003:000000b8 000000ecH .bss DATA

0004:00000000 000001d8H .rsrc$01 DATA

0004:000001e0 00000968H .rsrc$02 DATA

Address Publics by Value Rva+Base Lib:Object

0001:00000000 ?_GetBaseMessageMap@CDemoApp@@KGPBUAFX_MSGMAP@@XZ 00401000 f Demo.obj

0001:00000010 ?GetMessageMap@CDemoApp@@MBEPBUAFX_MSGMAP@@XZ 00401010 f Demo.obj

0001:00000020 ??0CDemoApp@@QAE@XZ 00401020 f Demo.obj

0001:00000040 ??_GCDemoApp@@UAEPAXI@Z 00401040 f i Demo.obj

0001:00000040 ??_ECDemoApp@@UAEPAXI@Z 00401040 f i Demo.obj

0001:00000060 ??1CDemoApp@@UAE@XZ 00401060 f i Demo.obj

0001:000000b0 ?InitInstance@CDemoApp@@UAEHXZ 004010b0 f Demo.obj

0001:00000130 ??1CAboutDlg@@UAE@XZ 00401130 f i Demo.obj

0001:00000130 ??1CDemoDlg@@UAE@XZ 00401130 f i Demo.obj

0001:00000140 ?Serialize@CObject@@UAEXAAVCArchive@@@Z 00401140 f i Demo.obj

0001:00000140 ?Dump@CObject@@UBEXAAVCDumpContext@@@Z 00401140 f i Demo.obj

0001:00000150 ?AssertValid@CObject@@UBEXXZ 00401150 f i Demo.obj

0001:00000160 ??0CAboutDlg@@QAE@XZ 00401160 f DemoDlg.obj

0001:00000180 ??_ECDemoDlg@@UAEPAXI@Z 00401180 f i DemoDlg.obj

0001:00000180 ??_GCDemoDlg@@UAEPAXI@Z 00401180 f i DemoDlg.obj

0001:00000180 ??_GCAboutDlg@@UAEPAXI@Z 00401180 f i DemoDlg.obj

0001:00000180 ??_ECAboutDlg@@UAEPAXI@Z 00401180 f i DemoDlg.obj

0001:000001a0 ?_GetBaseMessageMap@CDemoDlg@@KGPBUAFX_MSGMAP@@XZ 004011a0 f DemoDlg.obj

0001:000001a0 ?_GetBaseMessageMap@CAboutDlg@@KGPBUAFX_MSGMAP@@XZ 004011a0 f DemoDlg.obj

0001:000001b0 ?GetMessageMap@CAboutDlg@@MBEPBUAFX_MSGMAP@@XZ 004011b0 f DemoDlg.obj

0001:000001c0 ??0CDemoDlg@@QAE@PAVCWnd@@@Z 004011c0 f DemoDlg.obj

0001:00000230 ?DoDataExchange@CDemoDlg@@MAEXPAVCDataExchange@@@Z 00401230 f DemoDlg.obj

0001:00000230 ?DoDataExchange@CAboutDlg@@MAEXPAVCDataExchange@@@Z 00401230 f DemoDlg.obj

0001:00000240 ?GetMessageMap@CDemoDlg@@MBEPBUAFX_MSGMAP@@XZ 00401240 f DemoDlg.obj

0001:00000250 ?OnInitDialog@CDemoDlg@@MAEHXZ 00401250 f DemoDlg.obj

0001:00000350 ?OnSysCommand@CDemoDlg@@IAEXIJ@Z 00401350 f DemoDlg.obj

0001:000003d0 ?OnPaint@CDemoDlg@@IAEXXZ 004013d0 f DemoDlg.obj

0001:00000490 ?OnQueryDragIcon@CDemoDlg@@IAEPAUHICON__@@XZ 00401490 f DemoDlg.obj

0001:000004a0 ?BeginModalState@CWnd@@UAEXXZ 004014a0 f i DemoDlg.obj

0001:000004b0 ?EndModalState@CWnd@@UAEXXZ 004014b0 f i DemoDlg.obj

0001:000004c0 ?OnHelp@CWinApp@@IAEXXZ 004014c0 f mfc42:MFC42.DLL

0001:000004c6 ?WinHelpA@CWinApp@@UAEXKI@Z 004014c6 f mfc42:MFC42.DLL

More similar data has been deleted for brevity.

entry point at 0001:00000700

Static symbols

0001:00000070 _$E207 00401070 f Demo.obj

0001:00000080 _$E204 00401080 f Demo.obj

0001:00000090 _$E206 00401090 f Demo.obj

0001:000000a0 _$E205 004010a0 f Demo.obj

0001:000008d3 _$E373 004018d3 f mfcs42:appmodul.obj

0001:000008d8 _$E372 004018d8 f mfcs42:appmodul.obj

Line numbers for .\Release\DemoDlg.obj(D:\george\junk\Demo\DemoDlg.cpp) segment .text

41 0001:00000160 44 0001:00000172 53 0001:000001a0 53 0001:000001b0

64 0001:000001c0 69 0001:000001f7 70 0001:00000214 73 0001:00000230

80 0001:00000240 92 0001:00000250 93 0001:0000026b 103 0001:00000270

104 0001:00000285 106 0001:00000289 107 0001:00000292 108 0001:000002a1

110 0001:000002ac 111 0001:000002c0 113 0001:000002cf 117 0001:000002e0

118 0001:000002f7 120 0001:00000308 126 0001:00000317 129 0001:00000340

130 0001:0000034e 132 0001:00000366 133 0001:0000036f 134 0001:00000380

139 0001:00000391 137 0001:000003a2 139 0001:000003a7 146 0001:000003c0

147 0001:000003c6 149 0001:000003da 151 0001:000003e4 154 0001:00000401

155 0001:0000040b 157 0001:00000413 162 0001:00000422 163 0001:00000457

168 0001:00000463 166 0001:00000467 168 0001:0000046f 173 0001:00000480

175 0001:00000483

Line numbers for .\Release\Demo.obj(C:\Program Files\Microsoft Visual Studio\VC98\MFC\INCLUDE\afx.inl) segment .text

21 0001:00000140 37 0001:00000150

Line numbers for .\Release\Demo.obj(D:\george\junk\Demo\Demo.cpp) segment .text

17 0001:00000000 17 0001:00000010 29 0001:00000020 32 0001:00000030

37 0001:00000080 43 0001:000000b0 44 0001:000000cb 52 0001:000000d5

57 0001:000000dc 58 0001:000000e7 59 0001:000000eb 73 0001:000000ff

74 0001:00000110

Line numbers for C:\Program Files\Microsoft Visual Studio\VC98\MFC\LIB\mfcs42.lib(appmodul.cpp) segment .text

27 0001:00000892 30 0001:000008a7 36 0001:000008aa 38 0001:000008af

42 0001:000008b3 46 0001:000008b7 47 0001:000008c4 49 0001:000008cd

50 0001:000008d0 71 0001:000008d8

 

Constructive comments?  I would like to hear them.