Hi.
I created mobile game for Android using Urho3D engine and AngelScript for game logic. In productive I used precompiled bytecode for running. Bytecode created on Windows with 32bit version of AngelScript compiler, and was running perfectly on 32 bit ARM and 64 bit ARM. BUT…
I recently updated the AngelScript in the Urho3D engine to the latest version from git and found out that the bytecode created in the 32 bit version crashes when launched on the 64 bit. At the same time, starting from source works fine on both architectures. Also, the code created in the 64-bit version also works fine in both architectures. The error is repeated not only on the ARM, but also on x86 / x64. I researched the problem and found out the following. The crash occurred when loading bytecode when calling the constructor of a global variable. It is variable of a simple class with a couple of string fields, an integer and two boolean fields.
There is source (cutted) for class:
class ProductCoins : Purchase, ProductInfo, Purcheseable, PurchaseUpdate, ProductUILinkImpl {
uint count;
String _token;
ProductCoins(uint c) {
count = c;
_sku = "coins" + c;
}
...
}
mixin class Purchase {
String _sku;
bool purchased = false;
bool _isSubs = false;
PurchaseData@ info;
...
}
interface ProductInfo {
...
}
interface Purcheseable {
...
}
interface PurchaseUpdate {
...
}
mixin class ProductUILinkImpl : ProductUILink {
ProductItem@ _pi;
...
}
interface ProductUILink {
...
}
With the help of AS_DEBUG, I got the constructor bytecode in both architectures.
Here is the bytecode created with 32bit version (with some my comments.
ProductCoins::ProductCoins(uint)
Temps: 1, 4, 5
Variables:
-001: uint c
000: ProductCoins this
004: String {noname}
- 177,9 -
VarDecl 0
0 5 * PshVPtr v0
1 6 * ADDSi v24, 134218263
3 6 * ALLOC 0xbc4c9f8, 139 (type:String, {no func}) // init of _token
- 124,9 -
6 5 * PshVPtr v0
7 6 * ADDSi v28, 134218263
9 6 * ALLOC 0xbc4c9f8, 139 (type:String, {no func}) // init of _sku
- 125,7 -
12 5 * SetV1 v5, 0x0
14 5 * LoadThisR v32, 134218263 // purchased = false
16 5 * WRTV1 v5
- 126,7 -
17 5 * SetV1 v5, 0x0 // _isSubs = false
19 5 * LoadThisR v33, 134218263
21 5 * WRTV1 v5
- 179,3 -
22 5 * LoadThisR v20, 134218263 // count = c
24 5 * WRTV4 v-1
- 180,3 -
25 5 * PGA 232194836 // load constant "coin"
27 6 * RefCpyV v1, 197446136 // copy to temp var
29 6 * PopPtr
30 5 * PshV4 v-1
31 6 * PSF v4
32 7 * PshVPtr v1
33 8 * CALLSYS 36217 (String String::opAdd(uint) const)
ObjInfo v4, 1
35 5 * VAR v4
36 6 * PshVPtr v0
37 7 * ADDSi v28, 134218263
39 7 * RDSPtr
40 7 * GETREF 1
41 7 * CALLSYS 1465 (String& String::opAssign(const String&in))
43 5 * PSF v4
44 6 * CALLSYS 1422 (String::~String())
- 181,3 -
ObjInfo v4, 0
0:
46 5 * RET 2
Here is the bytecode created in 64bit version.
ProductCoins::ProductCoins(uint)
Temps: 2, 6, 7
Variables:
-002: uint c
000: ProductCoins this
006: String {noname}
- 177,9 -
VarDecl 0
0 7 * PshVPtr v0
1 9 * ADDSi v36, 134218263
3 9 * ALLOC 0x1855c0faef0, 139 (type:String, String::String())
- 124,9 -
7 7 * PshVPtr v0
8 9 * ADDSi v44, 134218263
10 9 * ALLOC 0x1855c0faef0, 139 (type:String, String::String())
- 125,7 -
14 7 * SetV1 v7, 0x0
16 7 * LoadThisR v52, 134218263
18 7 * WRTV1 v7
- 126,7 -
19 7 * SetV1 v7, 0x0
21 7 * LoadThisR v53, 134218263
23 7 * WRTV1 v7
- 179,3 -
24 7 * LoadThisR v32, 134218263
26 7 * WRTV4 v-2
- 180,3 -
27 7 * PGA 0x64edfe80 (str:coins)
30 9 * RefCpyV v2, 0x5c0faef0 (type:String)
33 9 * PopPtr
34 7 * PshV4 v-2
35 8 * PSF v6
36 10 * PshVPtr v2
37 12 * CALLSYS 36217 (String String::opAdd(uint) const)
ObjInfo v6, 1
39 7 * VAR v6
40 9 * PshVPtr v0
41 11 * ADDSi v44, 134218263
43 11 * RDSPtr
44 11 * GETREF 2
45 11 * CALLSYS 1465 (String& String::opAssign(const String&in))
47 7 * PSF v6
48 9 * CALLSYS 1422 (String::~String())
- 181,3 -
ObjInfo v6, 0
0:
50 7 * RET 3
So, when loading the bytecode created on 32bit arch on 64bit arch, instead instructions
14 7 * SetV1 v7, 0x0
19 7 * SetV1 v7, 0x0
30 9 * RefCpyV v2, 0x5c0faef0
it real become
14 7 * SetV1 v6, 0x0
19 7 * SetV1 v6, 0x0
30 9 * RefCpyV v1, 0x5c0faef0
When instruction 30 9 * RefCpyV v1, 0x5c0faef0
for copy obect is executed, variable used not as a FP + 2, but as a FP + 1. And since FP + 0 contains this
, it gets corrupted, the instruction 40 9 * PshVPtr v0
gets a corrupted address and in 43 11 * RDSPtr
programm crashed.
Can you fix this bug?