Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

333 lines
11 KiB

  1. How to write flat 32->16 thunks
  2. 1. INTRO
  3. --------------------------------------------------------------------
  4. - What are flat 32->16 thunks?
  5. THUNK.EXE is really three compilers that share a common parser.
  6. This file describes the "flat thunk" mode, which generates 32->16
  7. thunks that are mostly 32-bit (the other two modes are 16->32 thunks
  8. and 32->16 thunks that are mostly 16-bit).
  9. - Why should I use flat thunks?
  10. Flat thunks do most of their work in 32-bit mode. This means better
  11. performance since they use a minimum number of selector loads
  12. and replace far16 calls with near32 calls. Also, the
  13. flat compiler is an aggressive optimizer: early results show
  14. a 30-40% code size reduction over the 16-bit code generator.
  15. - Then why use 16-bit 32->16 thunks at all?
  16. Approximately 200 thunks in Chicago have hand-coded portions which
  17. need to be ported by hand to 32-bit mode. For compatibility,
  18. 16-bit thunks will be with us for some time to come.
  19. - Can I have both 16-bit and 32-bit thunks in my component?
  20. Yes, but you will need two thunk scripts: one for the 16-bit thunks
  21. and one for the 32-bit thunks. There is no way to mix types in a single
  22. script.
  23. - How does the thunk compiler work?
  24. The thunk compiler's input is a "thunk script", which is a list
  25. of C-style function prototypes and typedefs. It outputs a .asm file which
  26. is really two .asm files in one. Assemble it with the "-DIS_16"
  27. flag and you get a 16-bit .obj which you link into your 16-bit
  28. component. Assemble it with the "-DIS_32" flag and you get a 32-bit
  29. .obj which you link into your 32-bit component.
  30. The 16-bit component contains a jump table containing the 16:16
  31. address of each function named in your thunk scripts (these functions
  32. must exist elsewhere as PASCAL functions in your 16-bit component).
  33. The 32-bit half contains a STDCALL function for each thunk which
  34. converts its parameters to 16-bit and then calls (through some
  35. kernel32 magic) the 16-bit target named in the jump table. When
  36. a 32-bit app invokes a thunked api, it calles these compiler-generated
  37. STDCALL functions directly.
  38. For example, the thunk declaration for the LineTo api looks like this:
  39. typedef int INT;
  40. typedef unsigned int UINT;
  41. typedef UINT HANDLE;
  42. typedef HANDLE HDC;
  43. BOOL LineTo(HDC, INT, INT) =
  44. BOOL LineTo(HDC, INT, INT)
  45. {
  46. }
  47. The first function "prototype" declares the form of the 16-bit target
  48. and second declares the form the of the 32-bit target. As in most
  49. thunks, the two prototypes are identical. Like the C compiler, the
  50. thunk compiler interprets "int" as "short" in the 16-bit prototype
  51. and "long" in the 32-bit prototype.
  52. When this is fed through the thunk compiler, this is what pops out.
  53. On the 16-bit half, there is a jump table:
  54. externDef LineTo:far16
  55. FT_GdiFThkTargetTable:
  56. ...
  57. dw offset LineTo
  58. dw seg LineTo
  59. and LineTo is (say) entry #79. The 32-bit half contains the code:
  60. ; LineTo(16) = LineTo(32) {}
  61. ;
  62. ; dword ptr [ebp+8]: param1
  63. ; dword ptr [ebp+12]: param2
  64. ; dword ptr [ebp+16]: param3
  65. ;
  66. public LineTo@12
  67. LineTo@12:
  68. FAPILOG16 1377 ;DEBUG only -- log api call
  69. push ebp
  70. mov ebp,esp
  71. sub esp,40 ;Work-space for kernel32
  72. push word ptr [ebp+8] ;param1: dword->word
  73. push word ptr [ebp+12] ;param2: dword->word
  74. push word ptr [ebp+16] ;param3: dword->word
  75. mov cl,79 ;Thunk index
  76. call QT_Call16_ShortToLong
  77. leave
  78. ret 12
  79. When a Win32 app calls "LineTo", it transfers directly to this
  80. routine, which builds a 16-bit call frame and calls a local routine
  81. asking it to please invoke api #79 in the 16-bit jump table and
  82. sign-extend the return value (each component gets its own set
  83. of QT_ routines which knows what jump table to use.)
  84. 2. PROCEDURE FOR ADDING FLAT THUNKS
  85. ------------------------------------------------------------------------
  86. 1. Write a thunk script containing thunk declarations and typedef's
  87. as above. core\thunk\gdifthk.thk and core\thunk\usrfthk.thk are
  88. good examples to start from. Put the lines:
  89. enablemapdirect3216 = true;
  90. flatthunks = true;
  91. at the start of your script. This tells the compiler you intend
  92. to write 32->16 thunks and to generate 32-bit code.
  93. The naming convention for flat thunk scripts is FooFThk.thk where
  94. "Foo" identifies your component. If you keep your script in
  95. the core\thunk directory, please follow this convention.
  96. 2. Compile your thunk script:
  97. $(TNT) $(THUNK) -ynTb -t FooFThk FooFThk.thk FooFThk.asm
  98. $(TNT) = dev\tools\c\bin\tnt.exe
  99. $(THUNKCOM) = dev\tools\binr\thunk.exe
  100. The "-t FooFThk" provides a string which the thunk compiler uses
  101. to individualize identifier names. By convention, use the
  102. stem of your thunk script filename.
  103. For debug builds, eliminate the "-b" flag.
  104. The makefile in core\thunk has all this set up so it's easiest
  105. to check your thunk script there.
  106. 3. Create a empty header file "FooFThk.inc". The 32-bit half of the *.asm
  107. file includes this header. This is where you put special-case
  108. code for your thunks.
  109. 4. Link the 16-bit half of FooFThk.asm into your 16-bit component.
  110. Pass these flags to the assembler:
  111. -DIS_16
  112. Add the export:
  113. FT_FOOFTHKTHKCONNECTIONDATA
  114. to your *.def file and mark it internal. The 32-bit half
  115. dynalinks to this symbol to access the 16-bit jump table.
  116. 5. Link the 32-bit half of FooFThk.asm into your 32-bit component.
  117. Pass these flags to the assembler:
  118. -DIS_32
  119. Core\thunk\fltthk.inc and FooFThk.inc must be in the
  120. include path.
  121. Do not pass the -DFT_DEFINEFTCOMMONROUTINES flag to activate
  122. the "ifdef"'d part of the .asm file. The "ifdef'd" part
  123. contains common support code that's to be linked into kernel32
  124. only. Including it in another module wastes code.
  125. 6. In your DLL initialization procedure, execute the following
  126. for each PROCESS_ATTACH call:
  127. FT_FooFThkConnectToFlatThkPeer PROTO near
  128. pszDll16:dword,
  129. pszDll32:dword
  130. pszDll16 db 'foo16.dll',0 ;name of your 16-bit dll
  131. pszDll32 db 'foo32.dll',0 ;name of your 32-bit dll
  132. ...
  133. invoke FT_FooFThkConnectToFlatThkPeer,
  134. offset pszDll16,
  135. offset pszDll32
  136. or eax,eax
  137. jz failed
  138. ; success
  139. This initializes the flat thunks. The call executes a loadlibrary
  140. and getprocess address on the 16-bit module. The init routine
  141. itself is generated by the thunk compiler in the 32-bit half
  142. of the .asm file.
  143. 7. Link your 32-bit module with dev\lib\kernel32.lib if you're
  144. not doing so already. The thunk code needs the import records
  145. for the support routines in kernel32.
  146. 8. Build the components and test. Under debug, you
  147. can get a debug-port message for each flat thunk by
  148. setting the "fapilog16" variable in win32c.dll to 1.
  149. The "[F]" before the api name tells you that it's a flat thunk.
  150. 3. WHAT'S IN AND OUT FOR FLAT THUNKS
  151. -------------------------------------------------------------------------
  152. The flat code generator supports:
  153. - Structures passed by value or reference.
  154. - Structures within structures.
  155. - Pointers within structures, provided that the object
  156. pointed to doesn't require repacking. The object can be
  157. another structure.
  158. - Arrays of scalars embedded in structures.
  159. - The "input", "output" and "inout" qualifiers for pointer
  160. arguments. Default is "input".
  161. - "passifhinull" for pointer arguments.
  162. - The "hinstance" primitive type (for mapping instance handles)
  163. - "passifnull" for hinstances
  164. - "structsize" for integer structure fields
  165. - Returning pointers provided that the object pointed to requires
  166. no repacking. The object can be a structure.
  167. - The "voidtotrue" and "voidtofalse" qualifiers.
  168. Not supported:
  169. - Arrays of pointers or arrays of structures.
  170. - The "deleted" qualifier.
  171. - The "byname" qualifier
  172. - The "maptoretval" semantic.
  173. - The "sizeof" and "countof" semantics.
  174. - The "localheap" semantic.
  175. - The "reverserc" semantic.
  176. - The "callback" semantic.
  177. - "body = special", "raw pack/unpack", "push", "special".
  178. No hand-coding for flat thunks is allowed. Use wrappers
  179. instead to thunk complex routines.
  180. 4. SPECIAL-CASING A THUNK BODY
  181. ---------------------------------------------------------------------------
  182. **** HAND-CODING FOR THUNK BODIES IS BEING PHASED OUT. USE
  183. WRAPPERS TO THUNK COMPLEX API. SEE ATSUSHIK IF YOU NEED HELP
  184. ON THIS.
  185. ****
  186. 13. APPENDIX I:
  187. ------------------------------------------------------------------------------
  188. NEW for the flat code generator:
  189. Revision 1: STRUCTSIZE and HINSTANCES
  190. Structure size fields:
  191. You can now thunk those fields that contain the size of its
  192. containing structure. Just put the "structsize" keyword after
  193. the field name, like this:
  194. typedef struct tagFOO {
  195. DWORD cbSize structsize;
  196. LPSTR this;
  197. LPSTR that;
  198. } FOO;
  199. The compiler will insert the 16-bit structure size when packing in,
  200. and the 32-bit structure size when packing out. You can mark
  201. any integral type field as "structsize", including UINT.
  202. HInstance:
  203. There's a new primitive data type "hinstance" (lowercase). I'll
  204. add a typedef for HINSTANCE (uppercase) as soon as I've cleaned out
  205. the old usage of HINSTANCE.
  206. "hinstance" maps to a 32-bit value for the 32-bit side and a 16-bit
  207. value for the 16-bit side. Hinstances can appear anywhere an integer
  208. can (except as the return value type).
  209. NULL gets mapped to the current hinstance by default. You can make
  210. NULL map to NULL instead by adding the "passifnull" qualifier.
  211. If the hinstance is a structure field, add the "passifnull" qualifier
  212. as you would "structsize". If it's a parameter, put
  213. paramname = passifnull;
  214. inside the curly braces.
  215. Everyone is reminded that the 16-bit "hinstance" for a 32-bit app is
  216. really the hmodule. This works because most api that ask for hinstances
  217. really want hmodules.