🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Crash on running bytecode from 32bit on 64bit

Started by
12 comments, last by Alexander Orefkov 2 years, 8 months ago

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?

Advertisement

And important moment - bytecodes created on 32 bit and 64 bit was different.

Thanks for the report.

I'll try to repoduce this problem with the information you gave. If possible I should be able to give a fix soon enough, otherwise I'll come back and ask for more info.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I looked at your sources, where the bytecode is written. As I understand it, the generated code should be the same for both architectures. However, it turns out differently for me. Perhaps, I still have an error in configuring the engine. Here is a link to the archive with the received bytecode, maybe you can look at it and find out what the problem is.

https://yadi.sk/d/SomY46ZbZq3QpA

(in sended files 4 bytes at begining skiped by Urho3D)

I make output of engine config in both architectures - there is same - https://yadi.sk/d/1Qp4RmgGujnc-Q

I've not been able to reproduce this crash yet. On my test both 32bit and 64bit produces the exact same result.

		engine = asCreateScriptEngine();
		engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL);
		bout.buffer = "";

		RegisterStdString(engine);

		CBytecodeStream stream(__FILE__);

		const char *script = 
			"class ProductCoins : Purchase, ProductInfo, Purcheseable, PurchaseUpdate, ProductUILinkImpl { \n"
			"	uint count; \n"
			"	string _token; \n"
			"	ProductCoins(uint c) { \n"
			"		count = c; \n"
			"		_sku = \"coins\" + c; \n"
			"	} \n"
			"} \n"
			"mixin class Purchase { \n"
			"	string _sku; \n"
			"	bool purchased = false; \n"
			"	bool _isSubs = false; \n"
			"	PurchaseData@ info; \n"
			"} \n"
			"interface ProductInfo { \n"
			"} \n"
			"interface Purcheseable { \n"
			"} \n"
			"interface PurchaseUpdate { \n"
			"} \n"
			"mixin class ProductUILinkImpl : ProductUILink { \n"
			"	ProductItem@ _pi; \n"
			"} \n"
			"interface ProductUILink { \n"
			"} \n"
			"class PurchaseData {} \n"
			"class ProductItem {} \n"
			"ProductCoins coins(100); \n";

		mod = engine->GetModule(0, asGM_ALWAYS_CREATE); assert(mod != NULL);

		r = mod->AddScriptSection("main", script); assert(r >= 0);
		r = mod->Build(); assert(r >= 0);
		r = mod->SaveByteCode(&stream); assert(r >= 0);
		mod->Discard();

		asDWORD crc32 = ComputeCRC32(&stream.buffer[0], asUINT(stream.buffer.size()));
		if (crc32 != 0x3EA96EAF)
		{
			PRINTF("The saved byte code has different checksum than the expected. Got 0x%X\n", crc32);
			TEST_FAILED;
		}

		mod = engine->GetModule(0, asGM_ALWAYS_CREATE); assert(mod != NULL);

		r = mod->LoadByteCode(&stream);
		if (r < 0)
			TEST_FAILED;
		mod->Discard();

		if (bout.buffer != "")
		{
			PRINTF("%s", bout.buffer.c_str());
			TEST_FAILED;
		}

		r = engine->ShutDownAndRelease(); assert(r >= 0);

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

After setting the engine properties the same way that you have I was able to reproduce the problem.

		engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, 1);
		engine->SetEngineProperty(asEP_ALLOW_IMPLICIT_HANDLE_TYPES, 1);
		engine->SetEngineProperty(asEP_BUILD_WITHOUT_LINE_CUES, 1);





Now I just need to find and fix the cause of it.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I've fixed this problem in revision 2748.

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Thank you!

Hello again!
I again generated bytecode for different architectures for my app, and got four differences in the resulting bytecode. As it turned out, all four functions returned a handle to an object - either app or script object, no difference.

Here is the code for an example, turned out after being extremely simplified.

class TestClass {
    TestClass@ test() {
        return TestClass();
    }
};

This topic is closed to new replies.

Advertisement