|
|
#1 |
|
Moderator
|
OpenAccess IV String handling bug
Hallo zusammen,
Mich ereilte kürzlich ein weiterer Bugreport zu OpenAccess IV: Problembeschreibung Versucht man, in einem OA4 Programm eine dynamische View mithilfe von mehreren Variablen zu erstellen, so hängt sich das kompilierte Programm unter bestimmten Bedingungen in einer Endlosschleife auf: 1. Es müssen mehrere variablen miteinander verkettet werden (zumindest 2) 2. Am Ende der Bedingung muss ein Anführungszeichen gefolgt von einer beliebigen Anzahl von Zeichen stehen (also kein direktes Ende des Strings) Code:
STR v1="" STR v2="" BOOL pause=TRUE PUT "OAT", DO NEWLINE, DO NEWLINE ;GET pause PUT "Mit Leerzeichen aber ohne 2. Variable";GET pause v1='FROM kunden WHERE kdnr>1 AND name>"" ' ! kein Absturz LOCAL VIEW daten=v1 USE daten USE daten END PUT DO NEWLINE, "Geht!", DO NEWLINE PUT "mit 2. Variable aber ohne Zeichen nach Anfz.";GET pause v1='FROM kunden WHERE kdnr>1 AND name>""' v2="" LOCAL VIEW daten=v1&v2 USE daten USE daten END PUT DO NEWLINE, "Geht!", DO NEWLINE PUT "mit Leerzeichen nach Anfuerhrungsz. -> Absturz";GET pause v1='FROM kunden WHERE kdnr>1 AND name>" " ' v2=" " LOCAL VIEW daten=v1&v2 USE daten USE daten END PUT DO NEWLINE, "Geht nicht!", DO NEWLINE PUT "anderes Beispiel fuer Zeichen nach Anfz.";GET pause v1='FROM kunden WHERE kdnr>1 AND name>"" affe' ! Absturz v2="blablabla" LOCAL VIEW daten=v1&v2 USE daten USE daten END PUT DO NEWLINE, "Geht auch nicht!", DO NEWLINE Ursache Die Suche nach diesem Fehler gestaltet sich sehr schwierig. OA4 bleibt nämlich im virtuellen P-Code hängen, über den ich ja schonmal in diesem Thread erzählt habe. Kurz gesagt sind das virtuelle opcodes, welche eine bestimmte Bedeutung haben und dann von der PCode-Runtime, auf der OpenAccess basiert, ausgeführt werden. Bislang hatte ich ja keinerlei Informationen zu diesen Opcodes, aber durch intensive Internet-Recherche habe ich tatsächlich eine Doku zum von OA verwendeten SofTech P-System gefunden! 1-140.41.A_pSysInternArc_83.pdf Durch ein break in einem Debugger konnte ich zumindest im Assemblercode einmal die Opcodes lesen, welche ausgeführt werden und habe dadurch dann die Stelle in der RT.LIB mit dem P-Code indentifiziert, welcher dort immer in einer Endlosschleife geloopt ist. Zu beachten ist, dass der Code ursprünglich in der RT.LIB liegt, aber beim Einbinden von Programmen auch in die APP.SPI kopiert wird. Dh. die RT.LIB ist in jedem Fall zu patchen und die APP.SPI, falls sich dort der Code drinnen befindet (also beim Patcher nicht wundern, wenn er die APP.SPI nicht patcht). Zur weiteren Analyse habe ich mir dann einen eigenen Disassembler geschrieben, welcher die binären Opcodes in lesbaren "P-Code Assemblercode" wandelt. Ausgestattet damit habe ich anschließend den P-Code disasembliert und die Stelle aufgezeichnet, wo die Endlosschleife auftritt. In den Kommentaren stehen die Werte, die zum Zeitpunkt der Ausführung im Speicher waren (konnte ich wiederum über den debugger lesen). Um den Code zu verstehen empfiehlt sich die Lektüre des oben erwähten Handbuchs: Code:
00000000 87 80 89 LDL 137 ; 0x04 00000003 87 80 88 LDL 136 ; 0x0B 00000006 B3 GEQI ; 0x04 >= 0x0B -> False 00000007 9F BNOT ; !False -> True 00000008 D5 1D 02 FJPL 541 (->552); If False, then Exit 00000011 87 80 8A LDL 138 ; 0x00 = False 00000014 D4 13 FJP 19 (->35) ; False Jump to 35! 00000016 87 80 89 LDL 137 00000019 ED INCI 00000020 A4 80 89 STL 137 00000023 00 SLDC0 00000024 86 09 LAO 9 00000026 87 80 89 LDL 137 00000029 94 0D 04 CXG 13 , 4 00000032 6B SSTL4 00000033 01 SLDC1 00000034 69 SSTL2 00000035 80 20 LDCB 32 : 32 00000037 6E SSTL7 ; Store 32 into 7 00000038 23 SLDL4 ; 0x7802 00000039 E7 03 INC 3 ; 0x7808 00000041 00 SLDC0 ; 0 00000042 A7 LDB ; DS:[7808] = D6 = 214 = Length of string 00000043 6D SSTL6 ; Store D6 into 6 00000044 21 SLDL2 ; 0xD7 00000045 25 SLDL6 ; 0xD6 00000046 B2 LEQI ; SLDL2 (0xD7) <= SLDL6 (0xD6) -> False 00000047 23 SLDL4 ; 0x7802 00000048 E7 03 INC 3 ; 0x7808 00000050 21 SLDL2 ; 0xD7 00000051 A7 LDB ; DS:[0x7808+0xD7] = DS[0x78DF] = 0 00000052 80 27 LDCB 39 ; 39 = ' 00000054 B1 NEQI ; 0 <> 39 -> True 00000055 A1 LAND ; False AND True -> False 00000056 23 SLDL4 ; 0x7802 00000057 E7 03 INC 3 ; 0x7808 00000059 21 SLDL2 ; 0xD7 00000060 A7 LDB ; DS:[0x7808+0xD7] = DS[0x78DF] = 0 00000061 80 22 LDCB 34 ; 34 = " 00000063 B1 NEQI ' 0 <> 34 -> True 00000064 A1 LAND ; False AND True -> False 00000065 D4 05 FJP 5 (->72) ; False Jump to 72 00000067 21 SLDL2 00000068 ED INCI 00000069 69 SSTL2 00000070 8A E4 UJP -28 (->44) 00000072 21 SLDL2 ; 0xD7 00000073 25 SLDL6 ; 0xD6 (strlen) 00000074 B2 LEQI ; SLDL2 (0xD7) <= SLDL6 (0xD6) -> False 00000075 D5 D7 01 FJPL 471 (->549); False Jump to 549 -> Jump to 0 ...... 00000105 21 SLDL2 ; 0xD7 00000106 25 SLDL6 ; 0xD6 00000107 B2 LEQI ; SLDL2 (0xD7) <= SLDL6 (0xD6) -> False 00000108 9F BNOT ; !False -> True 00000109 D5 A3 01 FJPL 419 (->531); False Jump 531 ..... 00000529 8A 12 UJP 18 (->549) 00000531 21 SLDL2 ; 0xD7 00000532 25 SLDL6 ; 0xD6 00000533 B3 GEQI ; 0xD7>=0xD6 -> True 00000534 F1 09 TJP 9 (->545) ; Jump on True 00000536 21 SLDL2 00000537 ED INCI 00000538 69 SSTL2 00000539 00 SLDC0 00000540 A4 80 8A STL 138 00000543 8A 04 UJP 4 (->549) 00000545 01 SLDC1 ; 1 00000546 A4 80 8A STL 138 ; Load next String = 1 00000549 8B D8 FD UJPL -552 (->0) 00000552 96 81 8B RPU 395 In unserem Fall also nicht. Der lokale Wert 138, der anschließend geprüft wird, scheint ein Boolean-Wert zu sein, der besagt, ob der nächste String gelesen werden soll oder nicht. Bei uns ist er 0, also springt er weiter auf Adresse 35, wo der aktuelle String verarbeitet wird. Der aktuelle String hatte eine Länge von 214 (=0xD6 hex) Bytes. Die Länge wird in Arbeitsregister 6 gespeichert. Der aktuelle Zeiger für das zu verarbeitende Zeichen (in Arbeitsregister 2) ist jedoch schon hinter der Stringlänge, nämlich auf 215 (=0xD7 hex). Es folgen 2 Prüfungen auf einfache und doppelte Anführungszeichen, die beide nicht erfüllt werden können, da man sich ja schon hinter der Stringlänge befindet. Wenn der aktuelle Stringzeiger eben größer der Stringlänge ist, dann wird auf Adresse 549 gesprungen, welche wiederum an den Anfang der Routine zurückspringt und so loopt OA4 fröhlich in einer Endlosschleife vor sich hin. Bei genauerer Betrachtung der Routine findet man an Adresse 531, welche auch an Adresse 109 referenziert wird, dieselbe Prüfung, die jedoch bei Überschreiten der maximalen Stringlänge den Boolean-Wert in 138 auf 1 setzt und erst dann zurückspringt. Wenn der Wert 1 am Anfang ist, wird der nächste String gelesen, also eigentlich genau das, was man in diesem Fall möchte. Daraus ergibt sich ein denkbar einfacher Patch: Man ändert der Sprung an Adresse 75 vom Ziel 549, welches ja direkt wieder loopt, auf 545, wo dann auch korrekterweise der BOOL-Wert gesetzt wird, um zum nächsten String zu springen. Ergibt also einfach eine Änderung der des Hex-Werts D7 an Offset 75 in D3 und der Fehler ist behoben ![]() Wuerde man den Assemblercode als C-Pseudocode schreiben, so kann man das Problem einfacher nachvollziehen: Code:
// strings == Apex-String mit pString[0] = Stringlaenge
void ParseStrings(char **strings, int nStrings)
{
int iStr; // Aktueller String
int iPos, iStrLen;
int iPos2, iStrLen2;
char *pString;
BOOL bNext = TRUE;
char cByte;
for (iStr=0; iStr<nStrings; )
{
if (bNext)
{
pString = strings[++iStr];
iPos = 1;
}
cByte=' ';
iStrLen = pString[0];
/* Seek zum ersten Anfuehrungszeichen */
/* Beim 2. Durchlauf kommen wir hier an das Ende des Strings, denn
es gibt kein Anfuehrungszeichen mehr! */
while (iPos <= iStrLen && pString[iPos]<>"'" && pString[iPos]<>"\"")
iPos++;
/* Beim 2 Durchlauf ist hier iPos > iStrLen, aber bNext ist FALSE! */
if (iPos > iStrLen) continue; // FEHLER: Kein bNext gesetzt, ENDLOSSCHLEIFE!!
cByte = pString[iPos]; // Aktuelles Anfuehrungszeichen in cByte
iPos++;
/* Weiterlaufen bis zum naechsten Anfuehrungszeichen = Ende des gequoteten Strings */
while (iPos <= iStrLen && (pString[iPos] != cByte))
iPos++;
/* Ab hier ist bNext gesetzt, damit keine Gefahr mehr bis Schleifenende! */
bNext = TRUE;
/* Wenn das letzte Zeichen ein Anfuehrungszeichen ist, dann ist hier
Schluss und OK */
if (iPos > iStrlen) continue;
/* Wenn alle Strings verarbeitet, abbruch, also auch kein Problem */
if (iStr > nStrings) continue;
/* Auf zum naechten String */
pString = strings[++iStr];
iStrLen2[0] = pString[0];
/* Im naechsten String wieder nach nem Anfuehrungszeichen suchen. */
iPos2 = 1;
while (iPos2 <= iStrLen2 && (pString[iPos2] != cByte))
iPos2++;
/* Achtung: Im urspruenglichen String in iPos befinden wir uns immer
noch auf einer Position VOR dem Ende!!
Sehen wir also wieder am den Beginn der Schleife weiter. */
// ....
// restlicher Code fuer Bug uninteressant
//
bNext = FALSE; // Ab hier wieder Gefahr eines Haengers!
}
}Einen praktischen kleinen Patcher für den Hausgebrauch gibts wie immer im Anhang. Lg. und gute Nacht
|
|
|
|
|
|
#2 |
|
Registrierter Benutzer
|
Funktioniert perfekt - vielen Dank!
Gruß Hans Jürgen |
|
|
|
|
|
#3 |
|
Entwickler
|
Für alle die sich NICHT registrieren wollen, gibt es den Patcher natürlich wie immer auch auf der Download Seite: http://www.waldbauer.com/tmp/reference.php
|
|
|
|
Antwort schreiben... |
| Themen-Optionen | Thema durchsuchen |
|
|
Ähnliche Themen
|
||||
| Thema | Autor | Forum | Antworten | Letzter Beitrag |
| Nützliche Tips (Ergänzungen zum Handbuch von Heinz Richartz) | waldbauer.com | SPI OA4 Open Access II/III/IV (2,3,4) Anwender Forum | 204 | 08.11.2012 14:11 |
| OpenAccess IV Anbindung via Internet langsam!? | Schwarzenberger | SPI OA4 Open Access II/III/IV (2,3,4) Anwender Forum | 15 | 05.04.2007 09:50 |