64位lua引擎如何支持32位luac編譯出來(lái)的二進(jìn)制字節(jié)碼啄糙?

今天要研究wax的熱更方案,重拾l(fā)ua云稚。面對(duì)64位lua的問題隧饼。阿里給出的方案是:分別編譯。也就是說64位引擎只支持64位編譯器生產(chǎn)的字節(jié)碼静陈。32位引擎只支持32位編譯器產(chǎn)生的字節(jié)碼燕雁。為此,阿里給出了一組編譯腳本來(lái)解決這個(gè)問題,在我看來(lái)是小題大做了贵白。
而且率拒,這個(gè)方案有個(gè)小小的問題,那就是iOS應(yīng)用目前還是一份代碼同時(shí)編譯arm64和arm32版本的(比如在iPhone 5上的APP安裝得到的是32位的執(zhí)行代碼)禁荒。

我的解決辦法是:直接修改一下lua的引擎就好了猬膨。
我們先來(lái)看為什么64位的引擎不能執(zhí)行32位luac編譯出來(lái)的字節(jié)碼?

  1. 第一個(gè)是加載頭的時(shí)候
void luaU_header (char* h)
{
 int x=1;
 memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1);
 h+=sizeof(LUA_SIGNATURE)-1;
 *h++=(char)LUAC_VERSION;
 *h++=(char)LUAC_FORMAT;
 *h++=(char)*(char*)&x;                /* endianness */
 *h++=(char)sizeof(int);
 *h++=(char)sizeof(size_t);
 *h++=(char)sizeof(Instruction);
 *h++=(char)sizeof(lua_Number);
 *h++=(char)(((lua_Number)0.5)==0);        /* is lua_Number integral? */
}

其中的*h++=(char)sizeof(size_t);是平臺(tái)依賴的呛伴,size_t在32位平臺(tái)下是4字節(jié)勃痴,在64位平臺(tái)下是8字節(jié)。
修改的辦法就是改成:*h++=(char)sizeof(int);热康。

  1. 第二個(gè)地方是加載字符串的時(shí)候
static TString* LoadString(LoadState* S)
{
 size_t size;
 LoadVar(S,size);
 if (size==0)
  return NULL;
 else
 {
  char* s=luaZ_openspace(S->L,S->b,size);
  LoadBlock(S,s,size);
  return luaS_newlstr(S->L,s,size-1);        /* remove trailing '\0' */
 }
}

size的類型是size_t沛申,而LoadVar的實(shí)現(xiàn)是這樣的;

#define LoadVar(S,x)        LoadMem(S,&x,1,sizeof(x))

它根據(jù)size的類型從緩沖區(qū)獲得數(shù)據(jù)姐军,原因同上铁材,這里它取了8個(gè)字節(jié)的長(zhǎng)度。而實(shí)際上32位luac編譯產(chǎn)生字節(jié)碼的時(shí)候奕锌,這個(gè)長(zhǎng)度只用了4個(gè)字節(jié)來(lái)表示著觉。
修改的辦法也很簡(jiǎn)單,改成:int size;即可惊暴。

其實(shí)饼丘,lua這種做法不夠地道,字節(jié)碼格式本應(yīng)該使用一種平臺(tái)無(wú)關(guān)的格式來(lái)定義才是辽话。

那么肄鸽,怎么讓luac編譯產(chǎn)生一個(gè)平臺(tái)無(wú)關(guān)的格式呢?簡(jiǎn)單修改一下luac的實(shí)現(xiàn)也是可以辦到的油啤。
首先典徘,我們需要假定就用4個(gè)字節(jié)表示字符串長(zhǎng)度。(當(dāng)然啦益咬,你也可以假定字符串的長(zhǎng)度就是8字節(jié)逮诲,不過,因?yàn)榇蟛糠脂F(xiàn)存系統(tǒng)上的luac都是32位編譯的础废,他們沒有被修改之前汛骂,產(chǎn)生的字節(jié)碼中,字符串的長(zhǎng)度是用4字節(jié)表示的评腺。)

然后帘瞭,修改一下這個(gè)方法:

static void DumpString(const TString* s, DumpState* D)
{
 if (s==NULL || getstr(s)==NULL)
 {
  size_t size=0;
  DumpVar(size,D);
 }
 else
 {
  size_t size=s->tsv.len+1;        /* include trailing '\0' */
  DumpVar(size,D);
  DumpBlock(getstr(s),size,D);
 }
}

如果你真正讀懂了文章前面的部分,那么這里怎么改蒿讥,應(yīng)該很清楚了吧蝶念?
好了抛腕,重新編譯出來(lái)的luac,無(wú)論你用32位方式編譯媒殉,還是64位方式編譯担敌,最終得到的編譯器(luac)編譯的lua字節(jié)碼,它都是能被上面修改過的引擎正確加載了廷蓉。
再也不用折騰32位一個(gè)版本全封,64位一個(gè)版本了~。


我是怎么定位到這些需要修改的地方的呢桃犬?
首先刹悴,我們要有問題的環(huán)境,也就是一個(gè)64位編譯出來(lái)的lua64運(yùn)行引擎攒暇,一個(gè)32位luac32編譯出來(lái)的字節(jié)碼luac32.out土匀。
然后,我們用這個(gè)lua64執(zhí)行這個(gè)luac32.out形用。這時(shí)候就轧,會(huì)出現(xiàn)第一個(gè)錯(cuò)誤信息:
bad header in precompiled chunk
在源代碼里搜索這個(gè)錯(cuò)誤提示,沒有找到任何的結(jié)果田度。于是妒御,把錯(cuò)誤提示縮小一些(因?yàn)椋覀兤綍r(shí)也也經(jīng)常寫"error:%s"這樣的日志輸出)每币。直到查找"bad header"的時(shí)候携丁,定位到一處函數(shù)了LoadHeader琢歇,不需要費(fèi)太多力氣大概就知道是最終這個(gè)函數(shù)luaU_header的結(jié)果出錯(cuò)導(dǎo)致的錯(cuò)誤日志兰怠。好,第一處修改點(diǎn)就是這么定位出來(lái)的了李茫。
繼續(xù)跑測(cè)試用例揭保,這次直接crash了

malloc: *** mach_vm_map(size=8461182042380451840) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

一看就是分配了一個(gè)非常非常大的內(nèi)存了。這里魄宏,是需要一些靈感的秸侣,我自己看到這個(gè)錯(cuò)誤的第一直覺是,加載字符串的時(shí)候出錯(cuò)了(不要問我為什么有這樣的直覺宠互,一般只有犯過錯(cuò)的人才有這樣的直覺)味榛。
這個(gè)錯(cuò)誤沒有什么日志信息可以輔助定位,大概只能單步調(diào)試一個(gè)函數(shù)一個(gè)函數(shù)的執(zhí)行過去予跌,看看到那個(gè)函數(shù)執(zhí)行就報(bào)這個(gè)錯(cuò)了搏色。
然后依次縮小范圍。幸運(yùn)的是券册,這只是一個(gè)很單純的單線程程序频轿,很快就可以定位到是LoadFunction-->f->source=LoadString(S)這個(gè)語(yǔ)句出現(xiàn)的問題了垂涯,果然和我的猜想一樣。
就這樣航邢,問題搞定啦:)

后記:
有一位讀者受到文章啟發(fā)耕赘,針對(duì)5.3.4版本做出了對(duì)應(yīng)的調(diào)整。社區(qū)的互動(dòng)感覺還是蠻神奇的膳殷,放上鏈接操骡,有需要的可以參考一下:
http://www.reibang.com/p/67d9baabd318

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赚窃,隨后出現(xiàn)的幾起案子当娱,更是在濱河造成了極大的恐慌,老刑警劉巖考榨,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跨细,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡河质,警方通過查閱死者的電腦和手機(jī)冀惭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)掀鹅,“玉大人散休,你說我怎么就攤上這事±肿穑” “怎么了戚丸?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)扔嵌。 經(jīng)常有香客問我限府,道長(zhǎng),這世上最難降的妖魔是什么痢缎? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任胁勺,我火速辦了婚禮,結(jié)果婚禮上独旷,老公的妹妹穿的比我還像新娘署穗。我一直安慰自己,他們只是感情好嵌洼,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布案疲。 她就那樣靜靜地躺著,像睡著了一般麻养。 火紅的嫁衣襯著肌膚如雪褐啡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天回溺,我揣著相機(jī)與錄音春贸,去河邊找鬼混萝。 笑死,一個(gè)胖子當(dāng)著我的面吹牛萍恕,可吹牛的內(nèi)容都是我干的逸嘀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼允粤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼崭倘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起类垫,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤司光,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后悉患,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體残家,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年售躁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坞淮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陪捷,死狀恐怖回窘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情市袖,我是刑警寧澤啡直,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站苍碟,受9級(jí)特大地震影響酒觅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驰怎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一阐滩、第九天 我趴在偏房一處隱蔽的房頂上張望二打。 院中可真熱鬧县忌,春花似錦、人聲如沸继效。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瑞信。三九已至厉颤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凡简,已是汗流浹背逼友。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工精肃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帜乞。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓司抱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親黎烈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子习柠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容