Einzelnen Beitrag anzeigen
Alt 19.06.2011, 20:32   #11
leecher
Moderator
Noch ein paar weitere Infos für die technisch interessierten und eine mögliche Lösung des Problems:

Im Prinzip ist das Problem mit dem ungültigen Handle ein Folgefehler aus einer Speicherseite im Datensegment, die mit 00 gefüllt ist und daher kommt es zu einer falschen Zeigerdereferenzierung. Hier wird der oben fehlgeschlagene lseek-Vorgang durchgeführt:

Code:
02a8:27b8 E88800           call 2843 ($+88)
02a8:27bb 8B5D0A           mov  bx,[di+0A]
02a8:27be 368B5F08         mov  bx,ss:[bx+08]
02a8:27c2 8B5508           mov  dx,[di+08]
02a8:27c5 8B4D0C           mov  cx,[di+0C]
02a8:27c8 D1E2             shl  dx,1
02a8:27ca D1D1             rcl  cx,1
02a8:27cc D1E2             shl  dx,1
02a8:27ce D1D1             rcl  cx,1
02a8:27d0 D1E2             shl  dx,1
02a8:27d2 D1D1             rcl  cx,1
02a8:27d4 D1E2             shl  dx,1
02a8:27d6 D1D1             rcl  cx,1
02a8:27d8 D1E2             shl  dx,1
02a8:27da D1D1             rcl  cx,1
02a8:27dc D1E2             shl  dx,1
02a8:27de D1D1             rcl  cx,1
02a8:27e0 D1E2             shl  dx,1
02a8:27e2 D1D1             rcl  cx,1
02a8:27e4 D1E2             shl  dx,1
02a8:27e6 D1D1             rcl  cx,1
02a8:27e8 D1E2             shl  dx,1
02a8:27ea D1D1             rcl  cx,1
02a8:27ec B80042           mov  ax,4200
02a8:27ef CD21             int  21
Zum Zeitpunkt, wo der Fehler auftritt ergibt sich dann folgendes Bild:
Code:
Trap 3, system state: stopped
AX=0000  BX=284b  CX=0000  DX=ffff  SI=06f8  DI=0000  SP=fbfa  BP=0001
DS=7922  ES=371a  FS=0298  GS=c253  FL=3246
CS:IP=02a8:27bb       SS:SP=05ca:fbfa

d ds:di+0a

7922:0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7922:0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7922:0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7922:0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7922:0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7922:0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7922:0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7922:0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................



Trap 1, system state: stopped
AX=0000  BX=0000  CX=0000  DX=ffff  SI=06f8  DI=0000  SP=fbfa  BP=0001
DS=7922  ES=371a  FS=0298  GS=c253  FL=3346
CS:IP=02a8:27be       SS:SP=05ca:fbfa

02a8:27be 368B5F08         mov  bx,ss:[bx+08]
d ss:[bx+08]

05ca:ffffffff 00 E4 58 00 00 A9 07 A8 02 A8 02 CA 05 AF 07 06  .dX..).(.(.J./..
05ca:000f FC FE FB 84 09 34 19 3C 19 92 09 68 09 44 46 16  |~{..4.<...h.DF.
05ca:001f 00 C3 2F 30 00 00 00 00 00 84 09 00 00 00 00 0A  .C/0............
05ca:002f 00 0A 00 CF 0E 00 00 00 00 01 00 00 00 00 00 00  ...O............
05ca:003f 00 00 00 00 00 42 78 FA 33 0A 00 0A 00 0A 00 00  .....Bxz3.......
05ca:004f 00 00 00 00 00 00 00 00 00 52 2A FF 9F 80 00 03  .........R....
05ca:005f 00 A0 71 0E 01 28 0B 00 00 00 00 52 2B 00 00 00  . q..(.....R+...
05ca:006f 00 00 00 05 00 02 3D 00 00 00 00 00 00 00 00 00  ......=.........
Das Programm lädt also was aus Seite 0 des Stacksegments, da steht natürlich nicht das drin, was es gerne hätte, nämlich das zu verwendende Filehandle. Also habe ich mich einmal mithilfe eines erweiterten Debuggers auf die Suche gemacht und mittels memory-Breakpoint die Stelle gesucht, an welcher die Seite mit 0 überschrieben wird. Hier landen wir scheinbar, wenn ich das richtig erkennen kann, in der Heapverwaltung der Runtime:

Code:
 000027CC: F7450AFFFF                   test      w,[di][0000A],0FFFF
 000027D1: 7404                         je        0000027D7  
 000027D3: 33DB                         xor       bx,bx
 000027D5: EB03                         jmps      0000027DA
 000027D7: 8B5D0C                       mov       bx,[di][0000C]
 000027DA: 8B4D0E                       mov       cx,[di][0000E]
 000027DD: 0BDB                         or        bx,bx
 000027DF: 7410                         je        0000027F1   ; Wenn BX 0 ist, dann leeren:
 000027E1: 51                           push      cx        ; Sichern von cx Register
 000027E2: B90080                       mov       cx,08000 ;0x8000 Words (=64k)...
 000027E5: F3AB                         repe      stosw    ;...auf ES:DI schreiben
 000027E7: 81C20010                     add       dx,01000 ; Nächstes Segment
 000027EB: 8EC2                         mov       es,dx
 000027ED: 4B                           dec       bx       ; ggf. noch ein Segment leeren
 000027EE: 75F2                         jne       0000027E2
 000027F0: 59                           pop       cx        ; CX rücksichern
 000027F1: D1E9                         shr       cx,1
 000027F3: 7301                         jae       0000027F6
 000027F5: AA                           stosb
In der Löschschleife (repe stosw) wird also unser Segment mit Nullern überschrieben. Jede Speicherseite des Heapmanagers hat eine 16byte große Verwaltungsstruktur, welche verschiedene Informationen zur Speicherseite enthält, die vom Heapmanager verwendet werden. Wenn auf einer Seite auf Offset C eine 0 steht, dann wird also recht ordentlich aufgeräumt, indem die ganzen 64k, die ein Segment nunmal hat, mit 0 überschrieben werden. Dummerweise werden damit auch Daten aus dem Weg geräumt, die OA scheinbar noch braucht. Jetzt wäre es natürlich noch interessant, wie oft das auftritt. Scheinbar ziemlich selten, ein Breakpoint zeigt nämlich, dass diese Aufräumaktion nur unmittelbar vor unserem Fehler ausgeführt wird. Die Netzwerkversion überlebt das irgendwie nicht wirklich, aber die Standalone-Version scheinbar schon.. Warum das ?

Meine Vermutung: Es hat was mit dem geringeren Speicherbedarf der Standalone Version zu tun.
Ein Haltepunkt an derselben Stelle bei der Standalone-Version offenbart nämlich, dass die Bedingung dort scheinbar bei der Indexreparatur einfach nicht auftritt.

Nun kommt einem unweigerlich die Idee, wie man das Problem lösen könnte: Man treibt dem Programm die Aufräumaktion einfach ein für Allemal aus, indem man den conditional Jump auf 27DF einfach in einen absoluten Jump verwandelt, sodass der Aufräumtrupp nie gerufen wird. Siehe da, patcht man den Jump im Programmspeicher, so rennt auch die Netzwerkversion bei der Indexerstellung brav durch und der Index scheint nichtmal Fehler zu haben.

Jetzt kommt das abschließende Problem: Wie patcht man das Ganze?

Meine Vermutung was ja zunächst, dass sich das Ganze in form von Pcode in der oa4.spi befindet, aber nachdem es sich hier um "echten" Code handelt und nicht irgendwelche interpretierten Werte, scheint der Heapmanager Teil der Runtime zu sein und befindet sich - man glaubt es kaum - in oa4.exe. Nimmt man die Hex-Werte von oben als Suchzeichenkette für die OA4.EXE, so erhält man dann

Code:
74 10 51 B9 00 80
Die 74 (JE) ändern wir einfach in EB (JMP), und schon sollte das Problem aus der Welt sein. Allerdings möchte ich vor unvorhergesehenen Konsequenzen warnen, denn das Löschen der Seiten wird wohl schon irgendeinen Sinn haben und der alte Speicher bleibt nun ja schließlich stehen. Nachdem diese Funktion allerdings scheinbar sogut wie nie aufgerufen wird, und bei Aufruf ja scheinbar zu Problemen führt, könnte diese Funktion evtl. auch die Ursache für andere kuriose OA4-Fehler (Systemdiskette einlegen, Ende des Speichers, etc.) sein.

Wer also wagemutig ist, kann mal ausprobieren, OA4.EXE zu patchen.
Damits einfacher geht, hab ich wieder mal nen Patcher geschrieben und beigelegt.
Hoffentlich hilft es was und ich hab mein Wochenende nicht umsonst verplempert
Angehängte Dateien
Dateityp: com HEAPP.COM‎ (793 Bytes, 25x aufgerufen)

Geändert von leecher (19.06.2011 um 20:48 Uhr) Grund: 16 byte hat die Verwaltungsstruktur natürlich, nicht 16kb
leecher ist offline   Mit Zitat antworten