"Use of Win32" demonstrations
Dynamic-link Libraries (Dll) tests

   What Dlls are
   Why Dlls are useful
   Dll loading, memory, stack and threads
   Calling with no arguments
   Calling with arguments via the stack
   Start-up code and run-time link
   Dll sending data to the calling exe
   Exporting data pointers
   Dll using function in the calling exe
   Calling a function by ordinal using GetProcAddress
   Calling a function directly by ordinal
   Calling a function by name-load
   Testing who called start-up code

Everyone knows that you can call a function in a Dll. You can also pass data to the function and receive data from the function on the stack or in the registers. But until I wrote my linker GoLink, it was difficult to try to do more than that. In previous versions of this help file I described these problems in detail, but now assembler programmers have achieved their independence there is no need to dwell on the past. Instead I examine why Dlls are useful, and the various things you can do with them.

What Dlls are
In a Win32 context, Dlls are executable files in Portable Executable format which have the value 2000h in the Characteristics flag (at +16h the file header). An ordinary executable file (an "Exe") has the value 2h. A Dll also normally has the extension ".Dll", although this is not obligatory. For example, ".ocx" files work the same way. The flag is inserted at link-time by the linker. To achieve this, you tell the linker you want to make a Dll rather than an Exe. When the Win32 system loads an Exe, any Dlls holding the imports required by the Exe will also be loaded. These are usually imported functions which can be called by the Exe but as we shall see it is also possible to import data addresses. The system loader knows what imports are required by the Exe and which Dll contains those imports because these are listed in the idata section of the Exe. This information is put in the Exe at link-time by the linker. The linker (GoLink anyway) gets the information it needs about which Dll holds which function, from a list of Dlls (or other executables) the user provides at link time.

Other linkers need a list of library ("Lib") files. A Lib file is usually created from the Dll and is a list of exports from the Dll. This usually done by a utility at compile-time although sometimes it is done by the linker itself. In the case of system Dlls, the Lib files are issued by Microsoft, but they can also be made by the programmer by passing the Dll to the correct utility. Lib files are still around because they can also contain other information about functions, but if you use GoLink this is no longer required.

Why Dlls are useful
Dlls permit sharing of functions. In Win32, the system Dlls contain the APIs (Application Programmers' Interface). These are a vast array of functions which are the programmer's access to all the functions available in Win32. This is a much more flexible system than the old DOS interrupt system, since each Dll can be updated when necessary, without having to update the operating system as a whole.
Using Dlls makes it easier and quicker to update parts of code or data in a particular application which may need regular updating. You just update the Dll not the main Exe.
Dlls save memory by file mapping. Since Win32 is a multi-tasking operating system, it is possible to have several applications running and sharing the code and data in the Dlls at the same time. But at any one time only one copy of the Dll will actually be in physical memory (see below).
Using a Dll it is possible to spy on other programs. This can be done by loading the Dll in the other program's memory space (as in my debugger's GoBugSpy.Dll).
Using run-time linking of a Dll it is possible to run an Exe even if certain functions are not available. The most likely reason such functions might not be available is if the user is running the program under an early version of the operating system. In a properly designed program this ought not to be fatal, but mean merely that certain program embellishments cannot be used. If the Dll cannot be loaded the user can be informed that a particular function cannot be performed because the operating system is out of date!

Dll loading, memory, stack and threads
We have seen that at load-time any required Dlls will also be loaded. This means that the Dll file will be read and loaded into physical memory, ready for execution. However, the Dll may already be in physical memory if another running application has loaded it. This often happens in the case of system Dlls. In that case instead of loading another copy of the Dll into physical memory the system maps the file and supplies the Exe with the correct addresses for the functions it needs in the Dll. These addresses are supplied as virtual addresses. When the Exe calls the function, it calls the virtual address, but in reality this accesses the Dll in physical memory. This is possible because the processor works in "protected" mode. This means that all addresses are virtual addresses rather than actual physical addresses. The system converts the virtual address into a real address. All this is controlled by the system and the programmer need not worry about it.
The use of virtual addresses means that each Exe can be run in its own address (or memory) space. This adds greatly to robustness in Win32 because it means that running programs cannot interfere with each other's memory areas. The non-system Dlls used by the Exe are also loaded in the address space of the Exe itself. However, in Windows 95, 98 and ME system Dlls are loaded into shared memory, which is above 7FFFFFFFh. This area of memory is "shared" in the sense that the same address space is available to all running programs and all addresses in the shared area will therefore be the same. It is different in Windows NT, 2000 and XP. In those versions of the operating system even addresses in shared memory are unique so that system Dlls are in their own address space. In theory system Dlls could have different addresses for different programs, although in practice they normally don't. Normally the system will not allow a program to write to shared memory, so even in Win95, 98 and ME it is difficult to corrupt the Dlls which are loaded in shared memory.
The significance of all this is that an Exe can regard code in its Dlls as effectively its own code. It may call the function required in the normal way even though the function resides in the Dll (see no arguments).
It also means that the Dll can access, read or modify all data-in-memory stored by the Exe. However, in order to do this, the Dll will need to know the address of the data (see below).
The Dll shares the same stack as the Exe. So when calling a function in a Dll it is possible to send data or data addresses to the function on the stack (see arguments via the stack), or to receive them via the stack.
A Dll also shares the same thread as the Exe. When an Exe calls the Dll it is the same thread which runs in the Dll. This means that the registers are exactly the same registers as the Exe is using. Therefore data or data addresses can be sent to the Dll in the registers, or received from the Dll in registers (see exporting data indirectly to Testbug).
An Exe or Dll can export a data pointer to another executable. This is easy to do using GoAsm and GoLink - see exporting data pointers.
Just as an Exe can regard a Dll's code as its own, so a Dll may regard the Exe's code as its own, which it can use when it needs to. Whereas this used to be difficult, using GoAsm and GoLink it is easy - see using function in Testbug.exe itself.

Calling with no arguments
This demonstrates a simple call by the Exe of a function contained in the Dll. When watching with a debugger, use the breakpoint DLL_TEST1A. When the function DLL_TEST1 is called, note that the thread handle does not change. Note the address of the code before and after the call to the Dll. After the call the code is in a higher address in memory where the Dll has been loaded. Note the values of ESP and EBP which change to suit the call only, showing that the same stack is being used. Note that the thread is the same thread as the caller. Here is the code used in the Dll:-

DLL_TEST1:
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Dll test (no arguments)'
PUSH ADDR DLLMESS1
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
RET

Calling with arguments via the stack
Here 3 arguments (or parameters) are sent to the Dll by the caller on the stack. When watching with a debugger, use the breakpoint DLL_TEST2A. It's important to know how to send data to and receive data from a Dll since as we shall see later, only data pointers can be exported to and imported from executables. Here putting the data on the stack is simple. It is just PUSHed before the call is made. Each PUSH causes the ESP register to reduce by 4 bytes and you have to make sure that it is restored to the original value when returning to the caller's code. If this is not done, previous calls will not return to the correct code place, local data may not be addressed properly and stack memory will be eroded due to a "memory leak". If data is pushed on the stack before the call in this way you can either restore ESP by using POP (in this case 3 times), or by increasing ESP by 12 bytes after the call has returned, or as in this case, using RET 0Ch in the called procedure itself, which does the same thing. In fact, this is exactly how the system functions deal with data PUSHed on the stack.
Here is the code in Testbug.Exe which provides the arguments and sets up the call:-

DLL_TEST2A:
PUSH [hWnd]
PUSH 'Pointers to the title and this message, and the hWnd have been passed on the stack'
PUSH 'Dll test number 2'
CALL DLL_TEST2B         ;this function is in TESTBUG1.DLL
XOR EAX,EAX
RET
And this is the code in the Dll. Note that the EBP register is used within a "stack frame" to address the arguments passed on the stack. This is normally a matter of convenience, since it allows the arguments to be addressed and can also local data to be held on the stack, all addressed using EBP without any need to worry about ESP. If you want to use the stack to hold local data subtract the correct number of bytes from ESP after giving it to EBP. Then the local data can be addressed using EBP minus an amount, and the parameters can be addressed by using EBP plus an amount.
DLL_TEST2B:
PUSH EBP                ;receiving 3 parameters from caller
MOV EBP,ESP
;now [EBP+8]=3rd param, [EBP+0Ch]=2nd param, [EBP+10h]=1st param
PUSH 40h                ;information+ok button only
PUSH [EBP+8]            ;title
PUSH [EBP+0Ch]          ;message
PUSH [EBP+10h]          ;hWnd to use
CALL MessageBoxA        ;wait till ok pressed
MOV ESP,EBP
POP EBP
RET 0Ch

Start-up code and run-time link
There are two demonstrations here. The first is Dll start-up and close-down code. The second is run-time linking.
When loading any Dll, the system looks for the starting address. This value is contained in the PE file, in the same way as the starting address of an Exe is contained in the PE file. When the system loads the Dll it calls the start-up code. The function at that address must return EAX=1 otherwise loading is aborted. However this is the opportunity for the Dll to arrange its own affairs. The most useful thing it can do is to set up a memory area of its own in which to keep its own data. When the Exe is ready it can call the Dll and ask for the address of this memory area so that it can access it. In the demonstration here, the Dll simply shows a message box with the correct message. On closure, the Dll starting address will again be called, but with a different flag set in one of the arguments. This is the opportunity for the Dll to clear up, for example it may close the memory that it made earlier.
It is not essential to provide start-up and close-down code in Dlls, and it can be safely omitted.
Run-time linking.
All Dlls known to contain functions used by the Exe are loaded when the Exe is loaded. The normal way to call such functions are to call them by name. This causes the linker at compile time to mark the function as an import causing the system to load the Dll when the Exe is loaded. However there is another way to call a function in a Dll (or another Exe for that matter). This is by calling the function directly using the API GetProcAddress. Sometimes this is a real advantage because if a function in a Dll is missing at load time the system will not load the Exe and it cannot be run at all. If there is any chance that this could happen, for example if the function is only available in later versions of the operating system, then instead of calling the API by name, it is much better to call the API using GetProcAddress. If GetProcAddress returns with an error because the API cannot be found, then your program can find some other way to achieve the task or gracefully drop out of the task. GetProcAddress must be given the handle to the Dll. In practice, this is simply the virtual address in the program's memory space where the Dll is loaded. Of course, your program must ensure that the Dll is loaded (if there are no functions called-by-name it will not have been loaded at load-time). This is done by the API LoadLibrary. If LoadLibrary finds that the Dll is loaded already, it does not try to load the Dll again, but simply increments the load count. FreeLibrary decrements the count, and unloads the Dll if it is then zero. It would be possible for the Exe to load all required Dlls itself on start-up keeping all returned handles (LoadLibrary returns the handle in EAX). This would avoid having to call LoadLibrary each time just before GetProcAddress and FreeLibrary just after.
When watching this code with a debugger, use the breakpoint DLL_TEST3A in Testbug.Exe to view the caller's code. Then single-step/jump over the API call LoadLibrary. Note that the Dll's start-up code will be executed before the return from LoadLibrary (you can see this because the message box made in the start-up code will appear). The LoadLibrary code calls the Dll's start-up code, loads the Dll and then returns. Then the function in the Dll. You can see that happening because another message box is called. Finally a third message box appears and this happens in the call to FreeLibrary. Each of the message boxes you see also show the value of FS:[18h]. This is the address in memory of the Thread Environment Block (TEB) which is a thread-specific value [in Windows 9x and ME this is called the Thread Information Block (TIB)]. This demonstrates, therefore, which thread is calling the Dll at start up, which thread is calling the function and which thread is calling the Dll when it is freed. In fact you can see that it is the same thread in each case. It is not a special system thread. What is happening is that the system is using the application's own thread to call the Dlls start-up and unload code. This is obviously sensible to avoid system conflicts. See test 10 which checks which thread calls the start-up code when the Dll is loaded at the same time as the main Exe. Have a guess - would it be the same thread as the main Exe, or would it be a system thread?
Here is the caller's code in Testbug.Exe; one interesting thing about this code is that the API FreeLibrary needs one argument (the handle to the dll returned by LoadLibrary), but in assembler it is possible to PUSH this after the call to LoadLibrary if successful rather than immediately before the call to FreeLibrary itself:-

DLL_TEST3A:
PUSH 'TESTBUG3.DLL'
CALL LoadLibraryA       ;load the dll and return handle in eax
OR EAX,EAX              ;see if worked
JZ >L825                ;no
PUSH EAX                ;save the handle for later
;************
PUSH 'DLL_TEST3B'       ;this function is in TESTBUG3.DLL
PUSH EAX                ;DLL handle
CALL GetProcAddress
OR EAX,EAX
JZ >L824
CALL EAX                ;call the correct address
L824:
CALL FreeLibrary        ;free the dll (handle pushed earlier)
L825:
XOR EAX,EAX
RET
And here is the code in the Dll. Firstly the start-up and close-down code (Code symbol MAIN), then the actual function called by Testbug.Exe (DLL_MESS3B) followed by HEXROTATE8 which writes a 32-bit hex number to [EDI].
Note: it is important always to save the registers EBX,EDI,and ESI if they are used.
MAIN:                   ;starting address
PUSH EBP                ;receiving 3 parameters from caller
MOV EBP,ESP
PUSH EBX,EDI,ESI
;now [EBP+8]=hInstDLL, [EBP+0Ch]=fdwReason, [EBP+10h]=reserved
MOV ESI,ADDR DLLMESS1
MOV EDI,ADDR M1
CMP D[EBP+0Ch],1        ;see if DLL_PROCESS_ATTACH
JZ >L200                ;yes
MOV EDI,ADDR M3
MOV ESI,ADDRR DLLMESS3
JNB >L202               ;b=DLL_PROCESS_DETACH
L200:
FS MOV EAX,[18h]
CALL HEXROTATE8         ;write hex number from eax into edi
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Example of run-time dynamic linking and start-up code'
PUSH ESI
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
L202:
MOV EAX,1               ;return true to continue loading/action
POP ESI,EDI,EBX
MOV ESP,EBP
POP EBP
RET 0Ch
;
DLL_TEST3B:
MOV EDI,ADDR M2
FS MOV EAX,[18h]
CALL HEXROTATE8         ;write hex number from eax into edi
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Example of run-time dynamic linking'
PUSH ADDR DLLMESS2
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
RET
;
HEXROTATE8:             ;write hex number from eax into edi
MOV ECX,8
L200:
ROL AX,4                ;get high order nibble into al
MOV DL,AL
AND DL,0Fh              ;use only least sig nibble
ADD DL,48D              ;convert to ascii char
CMP DL,57D
JNA >L201
ADD DL,7                ;write hex letter if necessary A=65D
L201:
MOV [EDI],DL            ;write the nibble
INC EDI                 ;ready for next
LOOP L200
RET

Dll sending data to the calling exe
This is a simple test showing the use of registers to send data from a Dll. This is possible because the thread used by the Dll is the same as that which calls the Dll so the registers are not changed when the call occurs or when execution returns to the caller. In this demonstration the function in the Dll passes a data symbol DLLMESS4 via EAX back to the caller. Obviously you could do this the other way too, and use a register or registers to pass data to the Dll.
Here is the code in Testbug.Exe:-

DLL_TEST4A:
CALL DLL_TEST4B         ;this function is in TESTBUG1.DLL
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Exported text from TESTBUG1.DLL via call'
PUSH EAX                ;address of text passed via EAX by TESTBUG1.DLL
PUSH [hWnd]
CALL MessageBoxA        ;wait till ok pressed
XOR EAX,EAX
RET
And here is the code in the Dll:-
DLL_TEST4B:             ;symbol export
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Dll test - symbol export'
PUSH ADDR DLLMESS3
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
MOV EAX,OFFSET DLLMESS4
RET

Exporting data pointers
Here is another way of exporting data using an Exe or a Dll. Prior to GoLink, this was quite difficult for assembler programmers to achieve. The data cannot be exported directly, instead you have to send a pointer to the data and then the pointer is used to get the data. What is being sent is only the address of the data in the same way as the address of a function can be sent. This enables an Exe and a Dll to share data easily. Testbug2.Dll is used in this example.

First you need to declare the data in the Dll in the usual way in the data section (in GoAsm) preceded by the word EXPORT:-

EXPORT EXPORTED_SYMBOL DD 0DAFFC0FFh
Then in the main Exe you can use the data declared in the Dll as follows (use the breakpoint DLL_TEST5 to view this in action):-
DLL_TEST5:
MOV EDI,ADDR DLLMESS10
ADD EDI,78D             ;get to place for value
MOV EAX,[EXPORTED_SYMBOL]  ;variable declared in TESTBUG2.DLL
MOV EAX,[EAX]           ;you only get its offset, not its value
CALL HEXWRITE
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Data symbol exported from TESTBUG2.DLL'
PUSH ADDR DLLMESS10
PUSH [hWnd]
CALL MessageBoxA        ;wait till ok pressed
XOR EAX,EAX
RET

Dll using function in the calling exe
There will be many standardised functions which will be called frequently by your code. These may reside in a Dll, but why should they? It is possible to have them in the main Exe. Then if the Dll needs to use them it will call the main Exe.
Exporting a function either from an exe file or a Dll, can be achieved in GoAsm by using the word EXPORT just before the function to be exported:-

EXPORT HEXWRITE:
;function code goes here
Alternatively in GoAsm you can declare the EXPORT if no section has yet been declared eg.
EXPORT HEXWRITE
The only remaining thing to do is to tell GoLink to look in Testbug.exe for exports when making the Dll. This is achieved by including Testbug.exe in GoLink's command line or command file. Note this does nor work with other linkers.
To see the export HEXWRITE in Testbug.exe in action set a breakpoint at DLL_TEST6 in Testbug1.Dll and run Dll Test 6. This calls the function to write a hex number to a message box.

Calling a function by ordinal using GetProcAddress
A function in a Dll does not have to be called by name. It is possible to call it by number (known as "ordinal"). The only advantage of this is that the program may load a little quicker because with functions called by name, at start-up the loader has to find all the addresses of the functions and write them into the Exe as loaded in memory. It does not do this with functions loaded by ordinal. One way to call a function by ordinal is by loading its address using GetProcAddress. This API must be given the handle to the appropriate Dll so LoadLibrary has to be called at some point to get this value, and its probably a good idea to call FreeLibrary on closure of the program.
To see this in action, set a breakpoint on DLL_TEST7 (in Testbug.Exe).
You must fix the value of the ordinal for the function, and you do this (using GoAsm) by including this line in the assembler source script (this makes the DLL_TEST7B function an export with an ordinal of 10) :-

EXPORTS DLL_TEST7B:10
Here is the code to make the call-by-ordinal using GetProcAddress:-
DLL_TEST7A:
PUSH 'TESTBUG1.DLL'
CALL LoadLibraryA       ;load the dll and return handle in eax
OR EAX,EAX              ;see if worked
JZ >L4                  ;no
PUSH EAX                ;save the handle for later
;
PUSH 10D                ;this function is in TESTBUG1.DLL, called by ordinal
PUSH EAX                ;DLL handle
CALL GetProcAddress
OR EAX,EAX
JZ >L3
CALL EAX                ;call the correct address
L3:
CALL FreeLibrary        ;free the dll (handle pushed earlier)
L4:
XOR EAX,EAX
RET  
And here is the actual function DLL_TEST7B:-
DLL_TEST7B:             ;will be exported as an ordinal
PUSH 40h                ;information+ok button only
PUSH 'Testbug - calling a function by ordinal'
PUSH OFFSET DLLMESS5
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
RET

Calling a function directly by ordinal
GoAsm and GoLink provide a very simple way of calling an ordinal function in a Dll using the following code in the assembler source script (for example):-

CALL Testbug2:12
CALL Testbug2:13
The first call is to Testbug2.dll function number 12, which is yet another message box to demonstrate this working. Inside the Dll source code the function is preceded by EXPORT:12 to set up the export by ordinal.
The second call is to Testbug2.dll function number 13. Unlike the first call, only the ordinal number appears inside the Dll. This is achieved using the following source code in the Dll (preceding the function):-
EXPORT:13:NONAME
This is why, when you look at this in the debugger, no name for the function is displayed. You might want to do this to make it more difficult to see what parts of your code do what. Set the breakpoint to DLL_TEST8 and run Test 8.

Calling a function by name-load
This demonstration shows how at run-time a Dll can be loaded and an API in the Dll can be called using GetProcAddress. This is useful if there is a possibility that a particular Dll is not available on the machine which is running the application. If that is the case and you call the API directly, the application will not load at all and you get the message "Missing Dll" or similar. This happens because when an application is loaded, the system tries to load all its Dlls as well. If one cannot be found the application cannot load.

Here we want to use the API HtmlHelpA which resides in the Windows Dll hhctrl.ocx. In earlier machines without the new help version loaded this Dll will be absent.

PUSH 'hhctrl.ocx'
CALL LoadLibraryA       ;load htmlhelp dll
OR EAX,EAX              ;see if failed
JZ >.exit               ;yes
PUSH 'HtmlHelpA'
PUSH EAX
CALL GetProcAddress     ;get address of html help
OR EAX,EAX              ;see if failed
JZ >.exit               ;yes
PUSH 0,0
PUSH "Testbug.chm"      ;compressed help file to show
PUSH [hWnd]	   ;window handle
CALL EAX	              ;call HtmlHelpA API
When finished with hhctrl.ocx you should call FreeLibrary.

If you want, at start-up you can get all the addresses of the APIs you want to call. I found code similar to this in the particularly nasty MTX virus, so I am pleased to put something so destructive to some valuable use. The code here is particularly compact and also acts as a good demonstrator of GoBug. What happens here is that the names of each API to be called is passed to GetProcAddress with the appropriate handle to the Dll containing the API (in this case all are in Kernel32.dll). The address is then stored ready to be called directly. One unusual feature about this code is that in its original version all the name strings and addresses were kept in the code section rather than in data. This made it much easier for the virus to infect executables because it could limit itself to one section in the executable. However, from Windows 98 the systems gives more protection to code areas and it does not allow a write to code at run-time. The API VirtualProtect can be called to remove this protection, but that API itself provides an output and it will fail if it cannot write to the output address. So this protection effectively stops this code from working. For this reason in this example the data section is used to keep the name strings and to store the addresses of the calls.
Set a breakpoint at DLL_TEST9 and run the test.
When the breakpoint is reached, in GoBug open an inspector to look at KERNEL_NAME (use the menu item "inspect, exe/dll data symbols", then "view as data") and enlarge the inspector until you see an area of memory labelled "CALLS".
Then single-step down the code and see the addresses being loaded into CALLS (you can use a code breakpoint and F9 to make this quicker).
A little further down in the code pane you will see two calls, the first to [CALLS+18h] and the second to GetTickCount. The first of these is a call to the address now inserted in memory at 24 bytes beyond the label CALLS. In fact, this is a call to GetTickCount, which was the last API address loaded by the loop containing GetProcAddress. The second is an ordinary call to GetTickCount to demonstrate that the first one works. Single-step (trace over) these calls using F6 and watch the eax register which will contain the output from these two calls. There will be a slight difference in the values in eax after these calls because the tick (the Windows clock tick) will have advanced a little between the two.
Here is the code used:-

DLL_TEST9:              ;calling function by name-load
PUSH ADDR KERNEL_NAME
CALL GetModuleHandleA   ;get handle of Kernel32.dll
MOV EBX,EAX             ;keep in ebx
MOV EDI,ADDR CALLS      ;get place to put API addresses
MOV ESI,ADDR NAME_STRINGS    ;get list of API names
L1:
PUSH ESI,EBX
CALL GetProcAddress
OR EAX,EAX              ;see if successful
JZ >L4                  ;no
STOSD                   ;insert API address into CALLS
L2:
LODSB                   ;get to end of this API name
OR AL,AL                ;see if end of string yet
JNZ L2                  ;no
LODSB 
CMP AL,0FFh             ;see if finished function list
JZ >L3                  ;yes
DEC ESI
JMP L1
L3:
CALL [CALLS+18h]        ;ie. GetTickCount
CALL GetTickCount       ;proof correct thing was called
L4:
RET
;****** now for the strings (originally all held in code but since W98, in data)
DATA SECTION
KERNEL_NAME  DB 'KERNEL32.DLL',0
NAME_STRINGS DB 'CopyFileA',0
             DB 'DeleteFileA',0
             DB 'lstrlen',0
             DB 'lstrcat',0
             DB 'GetSystemDirectoryA',0
             DB 'GetWindowsDirectoryA',0
             DB 'GetTickCount',0
             DB 0FFh
;****** and where the addresses will be put
CALLS DD 7 DUP 0

Testing who called start-up code
As we saw when looking at start-up code and run-time link, when a Dll is loaded at run-time it is the application's own thread (ie the thread calling LoadLibrary) which is used to call the start-up code in the Dll. This test shows that the application's own main thread is also used to run the start-up code when a Dll is loaded in the usual way at start-up.
When watching this code with a debugger, use the breakpoint DLL_TEST10 in Testbug3.Dll. Then run Dll Test 10. You can see from the code fragment below that on start up the value of the Thread Environment Block (a thread-specific value obtained at FS:[18h]) is kept in memory. This same value is then put in the first part of the message box when Test 10 is run. The second part of the message box shows the address of the Thread Environment Block for the thread calling the function in the Dll. This of course is the application's main thread. The two values are the same proving that the same thread is used in each case.
First here is the start-up code in Testbug3.Dll which keeps the value at FS:[18h] in the data symbol KEEPTEB:-

MAIN:                   ;start-up code
PUSH EBP                ;receiving 3 parameters from caller
MOV EBP,ESP
;now [EBP+8]=hInstDLL, [EBP+0Ch]=fdwReason, [EBP+10h]=reserved
CMP D[EBP+0Ch],1        ;see if DLL_PROCESS_ATTACH
JNZ >L30                ;yes
FS MOV EAX,[18h]        ;to see which thread calls this
MOV [KEEPTEB],EAX
L30:
MOV EAX,1               ;return true to continue loading/action
MOV ESP,EBP
POP EBP
RET 0Ch
Now when DLL_TEST10 is called, the value of KEEPTEB and also the TEB for the current thread are written into the message DLLMESS9 (M9 and M10 are labels at the appopriate place in that message).
DLL_TEST10:
MOV EDI,ADDR M9
MOV EAX,[KEEPTEB]
CALL HEXROTATE8         ;write hex number from eax into edi
MOV EDI,ADDR M10
FS MOV EAX,[18h]
CALL HEXROTATE8         ;write hex number from eax into edi
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Testing who called the start-up code'
PUSH ADDR DLLMESS9
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
RET
See also how to use the thread information block given by FS.