John R. Sheets | 1e8e5ba | 2000-08-08 01:24:00 +0000 | [diff] [blame] | 1 | <chapter id="implementation"> |
| 2 | <title>Low-level Implementation</title> |
| 3 | <para>Details of Wine's Low-level Implementation...</para> |
| 4 | |
| 5 | <sect1 id="builtin-dlls"> |
| 6 | <title>Builtin DLLs</title> |
| 7 | |
| 8 | <para> |
John R. Sheets | d9e064f | 2000-12-13 21:52:37 +0000 | [diff] [blame] | 9 | Written by &name-juergen-schmied; <email>&email-juergen-schmied;</email> |
John R. Sheets | 1e8e5ba | 2000-08-08 01:24:00 +0000 | [diff] [blame] | 10 | </para> |
| 11 | <para> |
| 12 | (Extracted from <filename>wine/documentation/internal-dll</filename>) |
| 13 | </para> |
| 14 | |
| 15 | <para> |
| 16 | This document describes some points you should know before |
| 17 | implementing the internal counterparts to external DLL's. |
| 18 | Only 32 bit DLL's are considered. |
| 19 | </para> |
| 20 | |
| 21 | <sect2> |
| 22 | <title>1. The LibMain function</title> |
| 23 | |
| 24 | <para> |
| 25 | This is the way to do some initializing when a process or |
| 26 | thread is attached to the dll. The function name is taken |
| 27 | from a <filename>*.spec</filename> file line: |
| 28 | </para> |
| 29 | <programlisting> |
| 30 | init YourFunctionName |
| 31 | </programlisting> |
| 32 | <para> |
| 33 | Then, you have to implement the function: |
| 34 | </para> |
| 35 | <programlisting> |
| 36 | BOOL32 WINAPI YourLibMain(HINSTANCE32 hinstDLL, |
| 37 | DWORD fdwReason, LPVOID lpvReserved) |
| 38 | { if (fdwReason==DLL_PROCESS_ATTACH) |
| 39 | { ... |
| 40 | } |
| 41 | .... |
| 42 | } |
| 43 | </programlisting> |
| 44 | </sect2> |
| 45 | |
| 46 | <sect2> |
| 47 | <title>2. Using functions from other built-in DLL's</title> |
| 48 | |
| 49 | <para> |
| 50 | The problem here is, that you can't know if you have to call |
| 51 | the function from the internal or the external DLL. If you |
| 52 | just call the function you will get the internal |
| 53 | implementation. If the external DLL is loaded the executed |
| 54 | program will use the external DLL and you the internal one. |
| 55 | When you -as an example- fill an iconlist placed in the |
| 56 | internal DLL the application won't get the icons from the |
| 57 | external DLL. |
| 58 | </para> |
| 59 | <para> |
| 60 | To work around this, you should always use a pointer to call |
| 61 | such functions: |
| 62 | </para> |
| 63 | <programlisting> |
| 64 | /* definition of the pointer type*/ |
| 65 | void (CALLBACK* pDLLInitComctl)(); |
| 66 | |
| 67 | /* getting the function address this should be done in the |
| 68 | LibMain function when called with DLL_PROCESS_ATTACH*/ |
| 69 | |
| 70 | BOOL32 WINAPI Shell32LibMain(HINSTANCE32 hinstDLL, DWORD fdwReason, |
| 71 | LPVOID lpvReserved) |
| 72 | { HINSTANCE32 hComctl32; |
| 73 | if (fdwReason==DLL_PROCESS_ATTACH) |
| 74 | { /* load the external / internal DLL*/ |
| 75 | hComctl32 = LoadLibrary32A("COMCTL32.DLL"); |
| 76 | if (hComctl32) |
| 77 | { /* get the function pointer */ |
| 78 | pDLLInitComctl=GetProcAddress32(hComctl32,"InitCommonControlsEx"); |
| 79 | |
| 80 | /* check it */ |
| 81 | if (pDLLInitComctl) |
| 82 | { /* use it */ |
| 83 | pDLLInitComctl(); |
| 84 | } |
| 85 | |
| 86 | /* free the DLL / decrease the ref count */ |
| 87 | FreeLibrary32(hComctl32); |
| 88 | } |
| 89 | else |
| 90 | { /* do some panic*/ |
| 91 | ERR(shell,"P A N I C error getting functionpointers\n"); |
| 92 | exit (1); |
| 93 | } |
| 94 | } |
| 95 | .... |
| 96 | </programlisting> |
| 97 | </sect2> |
| 98 | |
| 99 | <sect2> |
| 100 | <title>3. Getting resources from a <filename>*.rc</filename> file linked to the DLL</title> |
| 101 | |
| 102 | <para> |
| 103 | < If you know how, write some lines> |
| 104 | </para> |
| 105 | </sect2> |
| 106 | </sect1> |
| 107 | |
| 108 | <sect1 id="accel-impl"> |
| 109 | <title>Accelerators</title> |
| 110 | |
| 111 | <para> |
| 112 | Findings researched by Uwe Bonnes, Ulrich Weigand and Marcus Meissner. |
| 113 | </para> |
| 114 | <para> |
| 115 | (Extracted from <filename>wine/documentation/accelerators</filename>) |
| 116 | </para> |
| 117 | |
| 118 | <para> |
| 119 | Some notes concerning accelerators. |
| 120 | </para> |
| 121 | <para> |
| 122 | There are <emphasis>three</emphasis> differently sized |
| 123 | accelerator structures exposed to the user. The general layout |
| 124 | is: |
| 125 | </para> |
| 126 | <programlisting> |
| 127 | BYTE fVirt; |
| 128 | WORD key; |
| 129 | WORD cmd; |
| 130 | </programlisting> |
| 131 | <para> |
| 132 | We now have three different appearances: |
| 133 | </para> |
| 134 | |
| 135 | <orderedlist> |
| 136 | <listitem> |
| 137 | <para> |
| 138 | Accelerators in NE resources. These have a size of 5 byte |
| 139 | and do not have any padding. This is also the internal |
| 140 | layout of the global handle <type>HACCEL</type> (16 and |
| 141 | 32) in Windows 95 and WINE. Exposed to the user as Win16 |
| 142 | global handles <type>HACCEL16</type> and |
| 143 | <type>HACCEL32</type> by the Win16/Win32 API. |
| 144 | </para> |
| 145 | </listitem> |
| 146 | <listitem> |
| 147 | <para> |
| 148 | Accelerators in PE resources. These have a size of 8 byte. |
| 149 | Layout is: |
| 150 | </para> |
| 151 | <programlisting> |
| 152 | BYTE fVirt; |
| 153 | BYTE pad0; |
| 154 | WORD key; |
| 155 | WORD cmd; |
| 156 | WORD pad1; |
| 157 | </programlisting> |
| 158 | <para> |
| 159 | They are exposed to the user only by direct accessing PE |
| 160 | resources. |
| 161 | </para> |
| 162 | </listitem> |
| 163 | <listitem> |
| 164 | <para> |
| 165 | Accelerators in the Win32 API. These have a size of 6 |
| 166 | bytes. Layout is: |
| 167 | </para> |
| 168 | <programlisting> |
| 169 | BYTE fVirt; |
| 170 | BYTE pad0; |
| 171 | WORD key; |
| 172 | WORD cmd; |
| 173 | </programlisting> |
| 174 | <para> |
| 175 | These are exposed to the user by the |
| 176 | <function>CopyAcceleratorTable</function> and |
| 177 | <function>CreateAcceleratorTable</function> functions in |
| 178 | the Win32 API. |
| 179 | </para> |
| 180 | </listitem> |
| 181 | </orderedlist> |
| 182 | |
| 183 | <para> |
| 184 | Why two types of accelerators in the Win32 API? We can only |
| 185 | guess, but my best bet is that the Win32 resource compiler |
| 186 | can/does not handle struct packing. Win32 <type>ACCEL</type> |
| 187 | is defined using <function>#pragma(2)</function> for the |
| 188 | compiler but without any packing for RC, so it will assume |
| 189 | <function>#pragma(4)</function>. |
| 190 | </para> |
| 191 | |
| 192 | </sect1> |
| 193 | |
| 194 | <sect1 id="file-handles"> |
| 195 | <title>File Handles</title> |
| 196 | |
| 197 | <para> |
John R. Sheets | d9e064f | 2000-12-13 21:52:37 +0000 | [diff] [blame] | 198 | Written by (???) |
John R. Sheets | 1e8e5ba | 2000-08-08 01:24:00 +0000 | [diff] [blame] | 199 | </para> |
| 200 | <para> |
| 201 | (Extracted from <filename>wine/documentation/filehandles</filename>) |
| 202 | </para> |
| 203 | |
| 204 | <para> |
| 205 | DOS treats the first 5 file handles as special cases. They |
| 206 | map directly to <filename>stdin</filename>, |
| 207 | <filename>stdout</filename>, <filename>stderr</filename>, |
| 208 | <filename>stdaux</filename> and <filename>stdprn</filename>. |
| 209 | Windows 16 inherits this behavior, and in fact, win16 handles |
| 210 | are interchangable with DOS handles. Some nasty windows |
| 211 | programs even do this! |
| 212 | </para> |
| 213 | <para> |
| 214 | Windows32 issues file handles starting from |
| 215 | <literal>1</literal>, on the grounds that most GUI processes |
| 216 | don't need a <filename>stdin</filename>, |
| 217 | <filename>stdout</filename>, etc. |
| 218 | </para> |
| 219 | <para> |
| 220 | The Wine handle code is implemented in the Win32 style, and |
| 221 | the Win16 functions use two macros to convert to and from the |
| 222 | two types. |
| 223 | </para> |
| 224 | |
| 225 | <para> |
| 226 | The macros are defined in <filename>file.h</filename> as follows: |
| 227 | </para> |
| 228 | <programlisting> |
| 229 | #define HFILE16_TO_HFILE32(handle) \ |
| 230 | (((handle)==0) ? GetStdHandle(STD_INPUT_HANDLE) : \ |
| 231 | ((handle)==1) ? GetStdHandle(STD_OUTPUT_HANDLE) : \ |
| 232 | ((handle)==2) ? GetStdHandle(STD_ERROR_HANDLE) : \ |
| 233 | ((handle)>0x400) ? handle : \ |
| 234 | (handle)-5) |
| 235 | |
| 236 | #define HFILE32_TO_HFILE16(handle) ({ HFILE32 hnd=handle; \ |
| 237 | ((hnd==HFILE_ERROR32) ? HFILE_ERROR16 : \ |
| 238 | ((handle>0x400) ? handle : \ |
| 239 | (HFILE16)hnd+5); }) |
| 240 | </programlisting> |
| 241 | |
| 242 | <warning> |
| 243 | <para> |
| 244 | Be careful not to use the macro |
| 245 | <function>HFILE16_TO_HFILE32</function> on functions with |
| 246 | side-effects, as it will cause them to be evaluated several |
| 247 | times. This could be considered a bug, but the use of this |
| 248 | macro is limited enough not to need a rewrite. |
| 249 | </para> |
| 250 | </warning> |
| 251 | <note> |
| 252 | <para> |
| 253 | The <literal>0x400</literal> special case above deals with |
| 254 | LZW filehandles (see <filename>misc/lzexpand.c</filename>). |
| 255 | </para> |
| 256 | </note> |
| 257 | </sect1> |
| 258 | |
| 259 | <sect1 id="hardware-trace"> |
| 260 | <title>Doing A Hardware Trace In Wine</title> |
| 261 | |
| 262 | <para> |
John R. Sheets | d9e064f | 2000-12-13 21:52:37 +0000 | [diff] [blame] | 263 | Written by &name-jonathan-buzzard; <email>&email-jonathan-buzzard;</email> |
John R. Sheets | 1e8e5ba | 2000-08-08 01:24:00 +0000 | [diff] [blame] | 264 | </para> |
| 265 | <para> |
| 266 | (Extracted from <filename>wine/documentation/ioport-trace-hints</filename>) |
| 267 | </para> |
| 268 | |
| 269 | <para> |
| 270 | The primary reason to do this is to reverse engineer a |
| 271 | hardware device for which you don't have documentation, but |
| 272 | can get to work under Wine. |
| 273 | </para> |
| 274 | <para> |
| 275 | This lot is aimed at parallel port devices, and in particular |
| 276 | parallel port scanners which are now so cheap they are |
| 277 | virtually being given away. The problem is that few |
| 278 | manufactures will release any programming information which |
| 279 | prevents drivers being written for Sane, and the traditional |
| 280 | technique of using DOSemu to produce the traces does not work |
| 281 | as the scanners invariably only have drivers for Windows. |
| 282 | </para> |
| 283 | <para> |
| 284 | Please note that I have not been able to get my scanner |
| 285 | working properly (a UMAX Astra 600P), but a couple of people |
| 286 | have reported success with at least the Artec AS6e scanner. I |
| 287 | am not in the process of developing any driver nor do I intend |
| 288 | to, so don't bug me about it. My time is now spent writing |
| 289 | programs to set things like battery save options under Linux |
| 290 | on Toshiba laptops, and as such I don't have any spare time |
| 291 | for writing a driver for a parallel port scanner etc. |
| 292 | </para> |
| 293 | <para> |
| 294 | Presuming that you have compiled and installed wine the first |
| 295 | thing to do is is to enable direct hardware access to your |
| 296 | parallel port. To do this edit <filename>wine.conf</filename> |
| 297 | (usually in <filename>/usr/local/etc</filename>) and in the |
| 298 | ports section add the following two lines |
| 299 | </para> |
| 300 | <programlisting> |
| 301 | read=0x378,0x379,0x37a,0x37c,0x77a |
| 302 | write=0x378,x379,0x37a,0x37c,0x77a |
| 303 | </programlisting> |
| 304 | <para> |
| 305 | This adds the necessary access required for SPP/PS2/EPP/ECP |
| 306 | parallel port on LPT1. You will need to adjust these number |
| 307 | accordingly if your parallel port is on LPT2 or LPT0. |
| 308 | </para> |
| 309 | <para> |
| 310 | When starting wine use the following command line, where |
| 311 | <literal>XXXX</literal> is the program you need to run in |
| 312 | order to access your scanner, and <literal>YYYY</literal> is |
| 313 | the file your trace will be stored in: |
| 314 | </para> |
| 315 | <programlisting> |
| 316 | wine -debugmsg +io XXXX 2> >(sed 's/^[^:]*:io:[^ ]* //' > YYYY) |
| 317 | </programlisting> |
| 318 | <para> |
| 319 | You will need large amounts of hard disk space (read hundreds |
| 320 | of megabytes if you do a full page scan), and for reasonable |
| 321 | performance a really fast processor and lots of RAM. |
| 322 | </para> |
| 323 | <para> |
| 324 | You might well find the log compression program that <email>David |
| 325 | Campbell campbell@torque.net</email> wrote helpful in |
| 326 | reducing the size of the log files. This can be obtained by |
| 327 | the following command: |
| 328 | </para> |
| 329 | <programlisting> |
| 330 | sh ioport-trace-hints |
| 331 | </programlisting> |
| 332 | <para> |
| 333 | This should extract <filename>shrink.c</filename> (which is |
| 334 | located at the end of this file. Compile the log compression |
| 335 | program by: |
| 336 | </para> |
| 337 | <programlisting> |
| 338 | cc shrink.c -o shrink |
| 339 | </programlisting> |
| 340 | <para> |
| 341 | Use the <command>shrink</command> program to reduce the |
| 342 | physical size of the raw log as follows: |
| 343 | </para> |
| 344 | <programlisting> |
| 345 | cat log | shrink > log2 |
| 346 | </programlisting> |
| 347 | <para> |
| 348 | The trace has the basic form of |
| 349 | </para> |
| 350 | <programlisting> |
| 351 | XXXX > YY @ ZZZZ:ZZZZ |
| 352 | </programlisting> |
| 353 | <para> |
| 354 | where <literal>XXXX</literal> is the port in hexidecimal being |
| 355 | accessed, <literal>YY</literal> is the data written (or read) |
| 356 | from the port, and <literal>ZZZZ:ZZZZ</literal> is the address |
| 357 | in memory of the instruction that accessed the port. The |
| 358 | direction of the arrow indicates whether the data was written |
| 359 | or read from the port. |
| 360 | </para> |
| 361 | <programlisting> |
| 362 | > data was written to the port |
| 363 | < data was read from the port |
| 364 | </programlisting> |
| 365 | <para> |
| 366 | My basic tip for interperating these logs is to pay close |
| 367 | attention to the addresses of the IO instructions. Their |
| 368 | grouping and sometimes proximity should reveal the presence of |
| 369 | subroutines in the driver. By studying the different versions |
| 370 | you should be able to work them out. For example consider the |
| 371 | following section of trace from my UMAX Astra 600P |
| 372 | </para> |
| 373 | <programlisting> |
| 374 | 0x378 > 55 @ 0297:01ec |
| 375 | 0x37a > 05 @ 0297:01f5 |
| 376 | 0x379 < 8f @ 0297:01fa |
| 377 | 0x37a > 04 @ 0297:0211 |
| 378 | 0x378 > aa @ 0297:01ec |
| 379 | 0x37a > 05 @ 0297:01f5 |
| 380 | 0x379 < 8f @ 0297:01fa |
| 381 | 0x37a > 04 @ 0297:0211 |
| 382 | 0x378 > 00 @ 0297:01ec |
| 383 | 0x37a > 05 @ 0297:01f5 |
| 384 | 0x379 < 8f @ 0297:01fa |
| 385 | 0x37a > 04 @ 0297:0211 |
| 386 | 0x378 > 00 @ 0297:01ec |
| 387 | 0x37a > 05 @ 0297:01f5 |
| 388 | 0x379 < 8f @ 0297:01fa |
| 389 | 0x37a > 04 @ 0297:0211 |
| 390 | 0x378 > 00 @ 0297:01ec |
| 391 | 0x37a > 05 @ 0297:01f5 |
| 392 | 0x379 < 8f @ 0297:01fa |
| 393 | 0x37a > 04 @ 0297:0211 |
| 394 | 0x378 > 00 @ 0297:01ec |
| 395 | 0x37a > 05 @ 0297:01f5 |
| 396 | 0x379 < 8f @ 0297:01fa |
| 397 | 0x37a > 04 @ 0297:0211 |
| 398 | </programlisting> |
| 399 | <para> |
| 400 | As you can see their is a repeating structure starting at |
| 401 | address <literal>0297:01ec</literal> that consists of four io |
| 402 | accesses on the parallel port. Looking at it the first io |
| 403 | access writes a changing byte to the data port the second |
| 404 | always writes the byte <literal>0x05</literal> to the control |
| 405 | port, then a value which always seems to |
| 406 | <literal>0x8f</literal> is read from the status port at which |
| 407 | point a byte <literal>0x04</literal> is written to the control |
| 408 | port. By studying this and other sections of the trace we can |
| 409 | write a C routine that emulates this, shown below with some |
| 410 | macros to make reading/writing on the parallel port easier to |
| 411 | read. |
| 412 | </para> |
| 413 | <programlisting> |
| 414 | #define r_dtr(x) inb(x) |
| 415 | #define r_str(x) inb(x+1) |
| 416 | #define r_ctr(x) inb(x+2) |
| 417 | #define w_dtr(x,y) outb(y, x) |
| 418 | #define w_str(x,y) outb(y, x+1) |
| 419 | #define w_ctr(x,y) outb(y, x+2) |
| 420 | |
| 421 | /* |
| 422 | * Seems to be sending a command byte to the scanner |
| 423 | * |
| 424 | */ |
| 425 | int udpp_put(int udpp_base, unsigned char command) |
| 426 | { |
| 427 | int loop,value; |
| 428 | |
| 429 | w_dtr(udpp_base, command); |
| 430 | w_ctr(udpp_base, 0x05); |
| 431 | |
| 432 | for (loop=0;loop<10;loop++) |
| 433 | if (((value=r_str(udpp_base)) & 0x80)!=0x00) { |
| 434 | w_ctr(udpp_base, 0x04); |
| 435 | return value & 0xf8; |
| 436 | } |
| 437 | |
| 438 | return (value & 0xf8) | 0x01; |
| 439 | } |
| 440 | </programlisting> |
| 441 | <para> |
| 442 | For the UMAX Astra 600P only seven such routines exist (well |
| 443 | 14 really, seven for SPP and seven for EPP). Whether you |
| 444 | choose to disassemble the driver at this point to verify the |
| 445 | routines is your own choice. If you do, the address from the |
| 446 | trace should help in locating them in the disassembly. |
| 447 | </para> |
| 448 | <para> |
| 449 | You will probably then find it useful to write a script/perl/C |
| 450 | program to analyse the logfile and decode them futher as this |
| 451 | can reveal higher level grouping of the low level routines. |
| 452 | For example from the logs from my UMAX Astra 600P when decoded |
| 453 | futher reveal (this is a small snippet) |
| 454 | </para> |
| 455 | <programlisting> |
| 456 | start: |
| 457 | put: 55 8f |
| 458 | put: aa 8f |
| 459 | put: 00 8f |
| 460 | put: 00 8f |
| 461 | put: 00 8f |
| 462 | put: c2 8f |
| 463 | wait: ff |
| 464 | get: af,87 |
| 465 | wait: ff |
| 466 | get: af,87 |
| 467 | end: cc |
| 468 | start: |
| 469 | put: 55 8f |
| 470 | put: aa 8f |
| 471 | put: 00 8f |
| 472 | put: 03 8f |
| 473 | put: 05 8f |
| 474 | put: 84 8f |
| 475 | wait: ff |
| 476 | </programlisting> |
| 477 | <para> |
| 478 | From this it is easy to see that <varname>put</varname> |
| 479 | routine is often grouped together in five successive calls |
| 480 | sending information to the scanner. Once these are understood |
| 481 | it should be possible to process the logs further to show the |
| 482 | higher level routines in an easy to see format. Once the |
| 483 | highest level format that you can derive from this process is |
| 484 | understood, you then need to produce a series of scans varying |
| 485 | only one parameter between them, so you can discover how to |
| 486 | set the various parameters for the scanner. |
| 487 | </para> |
| 488 | |
| 489 | <para> |
| 490 | The following is the <filename>shrink.c</filename> program. |
| 491 | </para> |
| 492 | |
| 493 | <programlisting> |
| 494 | cat > shrink.c <<EOF |
| 495 | #include <stdio.h> |
| 496 | #include <string.h> |
| 497 | |
| 498 | void |
| 499 | main (void) |
| 500 | { |
| 501 | char buff[256], lastline[256]; |
| 502 | int count; |
| 503 | |
| 504 | count = 0; |
| 505 | lastline[0] = 0; |
| 506 | |
| 507 | while (!feof (stdin)) |
| 508 | { |
| 509 | fgets (buff, sizeof (buff), stdin); |
| 510 | if (strcmp (buff, lastline) == 0) |
| 511 | { |
| 512 | count++; |
| 513 | } |
| 514 | else |
| 515 | { |
| 516 | if (count > 1) |
| 517 | fprintf (stdout, "# Last line repeated %i times #\n", count); |
| 518 | fprintf (stdout, "%s", buff); |
| 519 | strcpy (lastline, buff); |
| 520 | count = 1; |
| 521 | } |
| 522 | } |
| 523 | } |
| 524 | EOF |
| 525 | </programlisting> |
| 526 | </sect1> |
| 527 | |
| 528 | </chapter> |
| 529 | |
| 530 | <!-- Keep this comment at the end of the file |
| 531 | Local variables: |
| 532 | mode: sgml |
John R. Sheets | d9e064f | 2000-12-13 21:52:37 +0000 | [diff] [blame] | 533 | sgml-parent-document:("wine-doc.sgml" "set" "book" "part" "chapter" "") |
John R. Sheets | 1e8e5ba | 2000-08-08 01:24:00 +0000 | [diff] [blame] | 534 | End: |
| 535 | --> |