You should also $ cvs rm -f documentation/wine.texinfo ChangeLog Dimitrie O. Paun <dpaun@rogers.com> Merge the non-obsolete bits from wine.texinfo into the Wine Developers Guide. Index: documentation/wine-devel.sgml =================================================================== RCS file: /var/cvs/wine/documentation/wine-devel.sgml,v retrieving revision 1.8 diff -u -r1.8 wine-devel.sgml --- documentation/wine-devel.sgml 22 Sep 2003 21:30:45 -0000 1.8 +++ documentation/wine-devel.sgml 25 Sep 2003 16:38:29 -0000 @@ -83,6 +83,10 @@ <surname>Pouech</surname> </author> <author> + <firstname>Douglas</firstname> + <surname>Ridgway</surname> + </author> + <author> <firstname>John</firstname> <surname>Sheets</surname> </author> Index: documentation/implementation.sgml =================================================================== RCS file: /var/cvs/wine/documentation/implementation.sgml,v retrieving revision 1.8 diff -u -r1.8 implementation.sgml --- documentation/implementation.sgml 22 Sep 2003 19:35:47 -0000 1.8 +++ documentation/implementation.sgml 25 Sep 2003 21:08:00 -0000 @@ -2,6 +2,132 @@ <title>Low-level Implementation</title> <para>Details of Wine's Low-level Implementation...</para> + <sect1 id="undoc-func"> + <title>Undocumented APIs</title> + + <para> + Some background: On the i386 class of machines, stack entries are + usually dword (4 bytes) in size, little-endian. The stack grows + downward in memory. The stack pointer, maintained in the + <literal>esp</literal> register, points to the last valid entry; + thus, the operation of pushing a value onto the stack involves + decrementing <literal>esp</literal> and then moving the value into + the memory pointed to by <literal>esp</literal> + (i.e., <literal>push p</literal> in assembly resembles + <literal>*(--esp) = p;</literal> in C). Removing (popping) + values off the stack is the reverse (i.e., <literal>pop p</literal> + corresponds to <literal>p = *(esp++);</literal> in C). + </para> + + <para> + In the <literal>stdcall</literal> calling convention, arguments are + pushed onto the stack right-to-left. For example, the C call + <function>myfunction(40, 20, 70, 30);</function> is expressed in + Intel assembly as: + <screen> + push 30 + push 70 + push 20 + push 40 + call myfunction + </screen> + The called function is responsible for removing the arguments + off the stack. Thus, before the call to myfunction, the + stack would look like: + <screen> + [local variable or temporary] + [local variable or temporary] + 30 + 70 + 20 + esp -> 40 + </screen> + After the call returns, it should look like: + <screen> + [local variable or temporary] + esp -> [local variable or temporary] + </screen> + </para> + + <para> + To restore the stack to this state, the called function must know how + many arguments to remove (which is the number of arguments it takes). + This is a problem if the function is undocumented. + </para> + + <para> + One way to attempt to document the number of arguments each function + takes is to create a wrapper around that function that detects the + stack offset. Essentially, each wrapper assumes that the function will + take a large number of arguments. The wrapper copies each of these + arguments into its stack, calls the actual function, and then calculates + the number of arguments by checking esp before and after the call. + </para> + + <para> + The main problem with this scheme is that the function must actually + be called from another program. Many of these functions are seldom + used. An attempt was made to aggressively query each function in a + given library (<filename>ntdll.dll</filename>) by passing 64 arguments, + all 0, to each function. Unfortunately, Windows NT quickly goes to a + blue screen of death, even if the program is run from a + non-administrator account. + </para> + + <para> + Another method that has been much more successful is to attempt to + figure out how many arguments each function is removing from the + stack. This instruction, <literal>ret hhll</literal> (where + <symbol>hhll</symbol> is the number of bytes to remove, i.e. the + number of arguments times 4), contains the bytes + <literal>0xc2 ll hh</literal> in memory. It is a reasonable + assumption that few, if any, functions take more than 16 arguments; + therefore, simply searching for + <literal>hh == 0 && ll < 0x40</literal> starting from the + address of a function yields the correct number of arguments most + of the time. + </para> + + <para> + Of course, this is not without errors. <literal>ret 00ll</literal> + is not the only instruction that can have the byte sequence + <literal>0xc2 ll 0x0</literal>; for example, + <literal>push 0x000040c2</literal> has the byte sequence + <literal>0x68 0xc2 0x40 0x0 0x0</literal>, which matches + the above. Properly, the utility should look for this sequence + only on an instruction boundary; unfortunately, finding + instruction boundaries on an i386 requires implementing a full + disassembler -- quite a daunting task. Besides, the probability + of having such a byte sequence that is not the actual return + instruction is fairly low. + </para> + + <para> + Much more troublesome is the non-linear flow of a function. For + example, consider the following two functions: + <screen> + somefunction1: + jmp somefunction1_impl + + somefunction2: + ret 0004 + + somefunction1_impl: + ret 0008 + </screen> + In this case, we would incorrectly detect both + <function>somefunction1</function> and + <function>somefunction2</function> as taking only a single + argument, whereas <function>somefunction1</function> really + takes two arguments. + </para> + + <para> + With these limitations in mind, it is possible to implement more stubs + in Wine and, eventually, the functions themselves. + </para> + </sect1> + <sect1 id="accel-impl"> <title>Accelerators</title> -- Dimi.