# # icode.icn - code for handling icode files # Original Author : Eric Munson # Updated and maintained by : Ziad Al-Sharif # e-mail : zsharif@gmail.com # # link cfunc if you have loadfunc() and wish to enable big-endian test $ifdef NOTDEF link cfunc $else $define pack 0 $define unpack 0 $endif record header ( hsize, # /* size of interpreter code */ trace, # /* initial value of &trace */ Records, # Ftab, # /* location of record/field table */ Fnames, # /* location of names of fields */ Globals, # /* location of global variables */ Gnames, # /* location of names of globals */ Statics, # /* location of static variables */ Strcons, # /* location of identifier table */ Filenms, # /* location of ipc/file name table */ linenums, # /* location of ipc/line number table */ config, # /* [16]; icode version */ #ifdef FieldTableCompression FtabWidth,# /* width of field table entries, 1 | 2 | 4 */ FoffWidth,# /* width of field offset entries, 1 | 2 | 4 */ Nfields, # /* number of field names */ Fo, # /* The start of the Fo array */ Bm # /* The start of the Bm array */ #endif /* FieldTableCompression */ ) # # This class contains the main icode parsing facility # class Icode( icode, # A string with the executable file hdr, # An instance of the header() record IntBits, # number of bits in an integer WordBits, # number of bits in a unicon word wordSize, # 4 on a 32-bit and 8 on a 64-bit machine FTC, # a flag; when it is not null, it holds FT from icode v icodeVersion # String holds the Icode Version 32/64 compressed/uncompressed ) # # Convert binary number (read in as string data) to integer. # This appears to be unfinished. # method littleord(s) local o, i, i1, i2 o := ord(s) i1 := 0 i2 := 0 every i := 1 to 8 do { if iand(o,2^(i-1))>0 then { i1 +:= 2^(i-1) } } every i := 1 to 8 do { if iand(o,2^(8-i))>0 then { i2 +:= 2^(i-1) } } return o end # # Produce binary integer that was read in (as a string) from a file. # Integer value depends on big-endian vs. little-endian interpretation. # method endian(s) local i, xl, xb, s1 static isBigEndian initial { if (&features=="external functions") & \pack~===0 then { s1 := pack(1, "n") xl := unpack(s1, "l") xb := unpack(s1, "b") if xl = 1 then write("littleendian") if xb = 1 then { write("bigendian"); isBigEndian := 1 } } } i := 0 if /isBigEndian then s := reverse(s) every i := littleord(!s) + i * 256 return i end # # not sure whether this is still needed # #method bigendian(s) # local i # i := 0 # every i := littleord(!s) + i * 256 # return i #end # # checks the accessability of the executable and reads the icode # method openIcodeFile(exeFile) local statrec, f statrec := stat(exeFile) | stop("can't stat ", image(exeFile)) if not (f := open(exeFile,"ru")) then { write(&errout, "cannot open file : ", exeFile) fail } # reads all of the executable into the icode icode := reads(f, statrec.size) close(f) if *icode < statrec.size then { write("file \"", exeFile, "\" is too short") fail } return end # # Initialize the hdr class variable base on the Icode wordSize # method readIcodeHeader() local i, w hdr := header() every i := 1 to 11 do { w := move(wordSize) hdr[i] := endian(w) } hdr.config := move(16 * wordSize) if \FTC then{ # Reading both of FtabWidth, and FoffWidth every i := 13 to 14 do{ w := move(wordSize/2) hdr[i] := endian(w) } # Reading all of Nfields, Fo, and Bm every i := 15 to 17 do{ w := move(wordSize) hdr[i] := endian(w) } } end # # prints the Icode header information based on the Header Record in the Icode # method printIcodeHeader() local fn write(" Icode Version : " || icodeVersion) every fn := fieldnames(hdr) do if fn ~=="config" then write(" header.", left(fn,8," "), " = ", image(hdr[fn])) end # # Icode Version depends on the size of the Unicon word, # 4-bytes on a 32-bit machine or 8-bytes on a 64-bit machine # method checkIcodeVersion(p) # See if icode is for a 32-bit machine, word size 4 wordSize := 4 readIcodeHeader() # What kind of icode do we have? Field Table compressed? if hdr.config ? (="I12.U." & tab(-2) & ="FT") then { write("found 32-bit icode ", cstr(hdr.config)) FTC := "FT" &pos := p # start over as field-table aware; this may be unimplemented readIcodeHeader() } else if match("I12.U.",hdr.config) then{ write("found 32-bit icode ", cstr(hdr.config)) icodeVersion := "32-bit" } else { &pos := p # start over as a 64-bit machine wordSize := 8 readIcodeHeader() if not match("I12.U",hdr.config) then write("Warning: did not recognize icode version:\n", image(hdr.config)) icodeVersion := "64-bit" } end method cstr(s) s ? return tab(find("\0")|0) end # # UnCompress the Icode if it is Compressed # method readCompressedIcode() local ftmp # we need to uncompress the rest brute force means: # write it out and then read it in an uncompress mode ftmp := open("tmpfilename","w") | stop("cannot open a tmpfilename") writes(ftmp, tab(0)) close(ftmp) if ftmp := open("tmpfilename","rz") then { &subject := reads(ftmp, hdr.hsize) | stop("can't read compressed icode") close(ftmp) } else stop("cannot open a tmpfilename") end # # I code will be in a compressed format if it exceeds the 1 MB limit # method checkCompressedIcode() local Z, size # check whether it is a compressed icode or not hdr.config ? { tab(find("/")+1) | stop("can't find /") IntBits := size := integer(tab(many(&digits)))# tabs the IntBits move(1) # moves over the / WordBits := integer(tab(many(&digits))) # tabs the WordBits Z := move(1) # get the Z letter } if Z == "Z" then { readCompressedIcode() icodeVersion ||:= " Compressed Icode" } else icodeVersion ||:= " Uncompressed Icode" end # # Collect the Source files from the executable Icode # method getSrcFileNames(exeFile) local fNames, fMembers, LLL, p1, x, i, j openIcodeFile(exeFile) # look at header information icode ? { if find(".exe", exeFile) then { tab(find("exit\^m\nrem [executable Icon binary follows]")) } tab((x := find("\^l"))+3) # \^l\n\0 terminate shell header p1 := &pos checkIcodeVersion(p1) checkCompressedIcode() i := hdr.Filenms move(i) LLL := [] fNames := [] fMembers := set() while i < hdr.linenums do { move(wordSize) put(LLL, j := endian(move(wordSize))) i +:= (wordSize * 2) } every i := 1 to *LLL do { move(LLL[i] + hdr.Strcons - hdr.linenums) & (x := tab(find("\0")\1)) & &fail if not member(fMembers,x) & x ~== "__faux.icn" then { put(fNames,x) insert(fMembers,x) } } } return fNames end # # Initialization # initially() icodeVersion := "unknown?" IntBits := WordBits := -1 #need to check: which endian are we? end