This section describes how luai_undump1 is restored in the scenario.
/*** load one chunk from a file.** return list of functions found, headed by main, or NULL at EOF.*/TFunc* luaI_undump1(FILE* D){ while (1) { int c=getc(D); if (c==ID_CHUNK) { LoadChunk(D); return Main; } else if (c==EOF) return NULL; else lua_error("not a lua binary file"); }}
At the beginning of the program, check whether the first character of the file is id_chunk. If yes, load a block and return the main function.
This main is assigned a value in loadchunk (d.
If the first character is not id_chunk, it indicates that it is not the target binary file and return null.
Otherwise, an error occurs.
Next let's take a look at loadchunk.
static void LoadChunk(FILE* D){ LoadHeader(D); while (1) { int c=getc(D); if (c==ID_FUN) LoadFunction(D); else { ungetc(c,D); break; } }}
Recall the operation during dump. Here is the reverse process of dump. Compared with dump writing, the reading here is easier to read.
Read global information first, which corresponds to dumpheader.
First, recall dumpheader. The Code is as follows:
void DumpHeader(FILE* D){ Word w=TEST_WORD; float f=TEST_FLOAT; fputc(ID_CHUNK,D); fputs(SIGNATURE,D); fputc(VERSION,D); fwrite(&w,sizeof(w),1,D); fwrite(&f,sizeof(f),1,D);}
Let's look at loadheader again.
static void LoadHeader(FILE* D) /* TODO: error handling */{ Word w,tw=TEST_WORD; float f,tf=TEST_FLOAT; LoadSignature(D); getc(D); /* skip version */ fread(&w,sizeof(w),1,D); /* test word */ if (w!=tw) { swapword=1; warn("different byte order"); } fread(&f,sizeof(f),1,D); /* test float */ if (f!=tf) { Byte* p=(Byte*)&f; /* TODO: need union? */ Byte t; swapfloat=1; t=p[0]; p[0]=p[3]; p[3]=t; t=p[1]; p[1]=p[2]; p[2]=t; if (f!=tf) /* TODO: try another perm? */ lua_error("different float representation"); else warn("different byte order in floats"); }}
Does it look similar.
Read a signature first. If not, an error occurs. Each digit in the undump operation must be strictly equal. If any part is different from the Expected One, an error occurs.
GETC (d). Skip the version information and make the comments clearer.
Next, read test_word and test_float to determine the byte order. If the read is different from the macro definition, it indicates that the byte order needs to be exchanged. Swapword and swapfloat positions.
Return to loadchunk, loadheader, and start loadfunction, as in the order of dump.
During dump, the function starts after id_fun is marked. Therefore, loadfunction first checks whether it is id_fun. If not, it indicates that it is other content, puts it back into the characters, and returns the result.
static void LoadFunction(FILE* D){ TFunc* tf=new(TFunc); tf->next=NULL; tf->locvars=NULL; tf->size=LoadSize(D); tf->lineDefined=LoadWord(D); if (IsMain(tf)) /* new main */ { tf->fileName=LoadNewString(D); Main=lastF=tf; } else /* fix PUSHFUNCTION */ { CodeCode c; Byte* p; tf->marked=LoadWord(D); tf->fileName=Main->fileName; p=Main->code+tf->marked; c.tf=tf; *p++=c.m.c1; *p++=c.m.c2; *p++=c.m.c3; *p++=c.m.c4; lastF=lastF->next=tf; } tf->code=LoadBlock(tf->size,D); if (swapword || swapfloat) FixCode(tf->code,tf->code+tf->size); while (1) /* unthread */ { int c=getc(D); if (c==ID_VAR) /* global var */ { int i=LoadWord(D); char* s=LoadString(D); int v=luaI_findsymbolbyname(s); Unthread(tf->code,i,v); } else if (c==ID_STR) /* constant string */ { int i=LoadWord(D); char* s=LoadString(D); int v=luaI_findconstantbyname(s); Unthread(tf->code,i,v); } else { ungetc(c,D); break; } }}
Create a tfunc at the beginning of the function.
Then read its size and linedefined.
Determine whether the function is the main function. If it is the main function, set the file name and main field. The lastf field is the last function read.
If the function is not the main function, read its marked field. This field is missing in the dump section. The Marked of the non-main function refers to its bytecode offset in the main function.
Let's take a look at how it is used to assign the address of the current function to the corresponding bytecode of the main function.
p=Main->code+tf->marked; c.tf=tf; *p++=c.m.c1; *p++=c.m.c2; *p++=c.m.c3; *p++=c.m.c4;
After this assignment, the marked value after pushfunction in the original dump main function becomes the real memory address of this function.
Then, link the non-primary function to the function linked list;
Loadblock then reads the bytecode.
If you want to swap the byte order, you just use fixcode to swap the byte order.
The subsequent while (1) loop is to set the dump symbols and strings back to the runtime environment.
The symbol starts with id_var. Each time a symbol is set to the runtime environment, the corresponding bytecode is referenced to it as the corresponding unthread, this is the inverse process of threadcode During dump. Compared with threadcode, it is easier to understand the unthread here, or look at the previous dump example. It is also clear about what threadcode has done, and here it is restored back.
static void Unthread(Byte* code, int i, int v){ while (i!=0) { CodeWord c; Byte* p=code+i; get_word(c,p); i=c.w; c.w=v; p[-2]=c.m.c1; p[-1]=c.m.c2; }}
At this point, the program is ready to run and the virtual machine starts to run the bytecode, that is, the compiled content.
Lua2.4 scenario recover undump. c