Mercurial > projects > ddmd
view dmd/Library.d @ 188:83a36bdd5d14
Missed one in last commit.
author | Abscissa |
---|---|
date | Tue, 07 Jun 2011 23:51:08 -0400 |
parents | 190ba98276b3 |
children | eb38fdcb3e62 |
line wrap: on
line source
module dmd.Library; import dmd.common; import dmd.File; import dmd.Array; import dmd.StringTable; import dmd.OutBuffer; import dmd.ObjModule; import dmd.String; import dmd.Global; import dmd.File; import dmd.FileName; import dmd.Util; import dmd.StringValue; import dmd.String; import core.stdc.string; import core.stdc.stdlib; import std.string; import core.memory; align(1) struct LibHeader { ubyte recTyp; // 0xF0 ushort pagesize; int lSymSeek; ushort ndicpages; ubyte flags; } align(1) struct Libheader { ubyte recTyp; ushort recLen; int trailerPosn; ushort ndicpages; ubyte flags; char[6] filler; } struct ObjSymbol { string name; ObjModule* om; } /************************** * Record types: */ enum HASHMOD = 0x25; enum BUCKETPAGE = 512; enum BUCKETSIZE = (BUCKETPAGE - HASHMOD - 1); /+ #define RHEADR 0x6E #define REGINT 0x70 #define REDATA 0x72 #define RIDATA 0x74 #define OVLDEF 0x76 #define ENDREC 0x78 #define BLKDEF 0x7A #define BLKEND 0x7C #define DEBSYM 0x7E +/ enum THEADR = 0x80; enum LHEADR = 0x82; /+#define PEDATA 0x84 #define PIDATA 0x86 +/ enum COMENT = 0x88; enum MODEND = 0x8A; enum M386END = 0x8B; /* 32 bit module end record */ /+ #define EXTDEF 0x8C #define TYPDEF 0x8E +/ enum PUBDEF = 0x90; enum PUB386 = 0x91; /+ #define LOCSYM 0x92 #define LINNUM 0x94 +/ enum LNAMES = 0x96; /+ #define SEGDEF 0x98 #define GRPDEF 0x9A #define FIXUPP 0x9C /*#define (none) 0x9E */ #define LEDATA 0xA0 #define LIDATA 0xA2 #define LIBHED 0xA4 #define LIBNAM 0xA6 #define LIBLOC 0xA8 #define LIBDIC 0xAA #define COMDEF 0xB0 #define LEXTDEF 0xB4 #define LPUBDEF 0xB6 #define LCOMDEF 0xB8 #define CEXTDEF 0xBC +/ enum COMDAT = 0xC2; /+#define LINSYM 0xC4 +/ enum ALIAS = 0xC6; enum LLNAMES = 0xCA; enum LIBIDMAX = (512 - 0x25 - 3 - 4); // max size that will fit in dictionary extern (C) extern char* strdup(const(char)* ptr); static uint parseName(ubyte** pp, char* name) { ubyte* p = *pp; uint len = *p++; if (len == 0xFF && *p == 0) // if long name { len = p[1] & 0xFF; len |= cast(uint)p[2] << 8; p += 3; assert(len <= LIBIDMAX); } memcpy(name, p, len); name[len] = 0; *pp = p + len; return len; } static ushort parseIdx(ubyte** pp) { ubyte* p = *pp; ubyte c = *p++; ushort idx = cast(ushort)((0x80 & c) ? ((0x7F & c) << 8) + *p++ : c); *pp = p; return idx; } extern (C) int D_NameCompare(const(void*) a, const(void*) b) { ObjSymbol** p1 = cast(ObjSymbol**)a; ObjSymbol** p2 = cast(ObjSymbol**)b; return cmp((*p1).name, (*p2).name); } version (Windows) { /******************************************* * Write a single entry into dictionary. * Returns: * 0 failure */ extern (C) extern uint _rotl(uint value, int shift); extern (C) extern uint _rotr(uint value, int shift); static int EnterDict(ubyte* bucketsP, ushort ndicpages, ubyte* entry, uint entrylen) { ushort uStartIndex; ushort uStep; ushort uStartPage; ushort uPageStep; ushort uIndex; ushort uPage; ushort n; uint u; uint nbytes; ubyte* aP; ubyte* zP; aP = entry; zP = aP + entrylen; // point at last char in identifier uStartPage = 0; uPageStep = 0; uStartIndex = 0; uStep = 0; u = entrylen; while ( u-- ) { uStartPage = cast(ushort)_rotl( uStartPage, 2 ) ^ ( *aP | 0x20 ); uStep = cast(ushort)_rotr( uStep, 2 ) ^ ( *aP++ | 0x20 ); uStartIndex = cast(ushort)_rotr( uStartIndex, 2 ) ^ ( *zP | 0x20 ); uPageStep = cast(ushort)_rotl( uPageStep, 2 ) ^ ( *zP-- | 0x20 ); } uStartPage %= ndicpages; uPageStep %= ndicpages; if ( uPageStep == 0 ) uPageStep++; uStartIndex %= HASHMOD; uStep %= HASHMOD; if ( uStep == 0 ) uStep++; uPage = uStartPage; uIndex = uStartIndex; // number of bytes in entry nbytes = 1 + entrylen + 2; if (entrylen > 255) nbytes += 2; while (1) { aP = &bucketsP[uPage * BUCKETPAGE]; uStartIndex = uIndex; while (1) { if ( 0 == aP[ uIndex ] ) { // n = next available position in this page n = aP[ HASHMOD ] << 1; assert(n > HASHMOD); // if off end of this page if (n + nbytes > BUCKETPAGE ) { aP[ HASHMOD ] = 0xFF; break; // next page } else { aP[ uIndex ] = cast(ubyte)(n >> 1); memcpy( (aP + n), entry, nbytes ); aP[ HASHMOD ] += (nbytes + 1) >> 1; if (aP[HASHMOD] == 0) aP[HASHMOD] = 0xFF; return 1; } } uIndex += uStep; uIndex %= 0x25; /*if (uIndex > 0x25) uIndex -= 0x25;*/ if( uIndex == uStartIndex ) break; } uPage += uPageStep; if (uPage >= ndicpages) uPage -= ndicpages; if( uPage == uStartPage ) break; } return 0; } import dmd.TObject; class Library : TObject { File libfile; Array objmodules; // ObjModule[] Array objsymbols; // ObjSymbol[] StringTable tab; this() { register(); libfile = null; objmodules = new Array(); objsymbols = new Array(); } /*********************************** * Set the library file name based on the output directory * and the filename. * Add default library file name extension. */ void setFilename(string dir, string filename) { string arg = filename; if (arg.length == 0) { // Generate lib file name from first obj name string n = (cast(String)global.params.objfiles.data[0]).str; n = FileName.name(n); FileName fn = FileName.forceExt(n, global.lib_ext); arg = fn.toChars(); } if (!FileName.absolute(arg)) arg = FileName.combine(dir, arg); FileName libfilename = FileName.defaultExt(arg, global.lib_ext); libfile = new File(libfilename); } /*************************************** * Add object module or library to the library. * Examine the buffer to see which it is. * If the buffer is null, use module_name as the file name * and load the file. */ void addObject(string module_name, void *buf, size_t buflen) { version (LOG) { printf("Library.addObject(%s)\n", module_name ? module_name : ""); } if (!buf) { assert(module_name); scope FileName f = new FileName(module_name); scope File file = new File(f); file.readv(); buf = file.buffer; buflen = file.len; file.ref_ = 1; } uint g_page_size; ubyte* pstart = cast(ubyte*)buf; int islibrary = 0; /* See if it's an OMF library. * Don't go by file extension. */ /* Determine if it is an OMF library, an OMF object module, * or something else. */ if (buflen < LibHeader.sizeof) { Lcorrupt: error("corrupt object module"); } LibHeader* lh = cast(LibHeader*)buf; if (lh.recTyp == 0xF0) { /* OMF library * The modules are all at buf[g_page_size .. lh.lSymSeek] */ islibrary = 1; g_page_size = lh.pagesize + 3; buf = cast(void*)(pstart + g_page_size); if (lh.lSymSeek > buflen || g_page_size > buflen) goto Lcorrupt; buflen = lh.lSymSeek - g_page_size; } else if (lh.recTyp == '!' && memcmp(lh, "!<arch>\n".ptr, 8) == 0) { error("COFF libraries not supported"); return; } else { // Not a library, assume OMF object module g_page_size = 16; } /* Split up the buffer buf[0..buflen] into multiple object modules, * each aligned on a g_page_size boundary. */ ObjModule* om = null; int first_module = 1; ubyte* p = cast(ubyte*)buf; ubyte* pend = p + buflen; ubyte* pnext; for (; p < pend; p = pnext) // for each OMF record { if (p + 3 >= pend) goto Lcorrupt; ubyte recTyp = *p; ushort recLen = *cast(ushort*)(p + 1); pnext = p + 3 + recLen; if (pnext > pend) goto Lcorrupt; recLen--; /* forget the checksum */ switch (recTyp) { case LHEADR : case THEADR : if (!om) { char name[LIBIDMAX + 1]; om = new ObjModule(); om.flags = 0; om.base = p; p += 3; parseName(&p, name.ptr); if (first_module && module_name && !islibrary) { // Remove path and extension string fname = FileName.name(module_name); string ext = FileName.ext(fname); if (ext.length != 0) { fname = fname[0..$-ext.length-1]; } om.name = fname; } else { /* Use THEADR name as module name, * removing path and extension. */ string fname = FileName.name(fromStringz(name.ptr)); string ext = FileName.ext(fname); if (ext.length != 0) { fname = fname[0..$-ext.length-1]; } om.name = fname; om.flags |= MFtheadr; } if (strcmp(name.ptr, "C".ptr) == 0) // old C compilers did this { om.flags |= MFgentheadr; // generate our own THEADR om.base = pnext; // skip past THEADR } objmodules.push(cast(void*)om); first_module = 0; } break; case MODEND : case M386END: if (om) { om.page = cast(ushort)((om.base - pstart) / g_page_size); om.length = pnext - om.base; om = null; } // Round up to next page uint t = pnext - pstart; t = (t + g_page_size - 1) & ~cast(uint)(g_page_size - 1); pnext = pstart + t; break; default: // ignore ; } } if (om) goto Lcorrupt; // missing MODEND record } void addLibrary(void *buf, size_t buflen) { assert(false); } void write() { if (global.params.verbose) writef("library %s\n", libfile.name.toChars()); scope OutBuffer libbuf = new OutBuffer(); WriteLibToBuffer(libbuf); // Transfer image to file libfile.setbuffer(libbuf.data, libbuf.offset); libbuf.extractData(); string p = FileName.path(libfile.name.toChars()); FileName.ensurePathExists(p); libfile.writev(); } private: void addSymbol(ObjModule* om, string name, int pickAny = 0) { version (LOG) { printf("Library.addSymbol(%s, %s, %d)\n", om.name, name, pickAny); } Object* s = tab.insert(name); if (!s) { // already in table if (!pickAny) { s = tab.lookup(name); assert(s); ObjSymbol* os = *cast(ObjSymbol**)s; error("multiple definition of %s: %s and %s: %s", om.name, name, os.om.name, os.name); } } else { ObjSymbol* os = new ObjSymbol(); os.name = name; os.om = om; *s = cast(Object)cast(void*)os; /// !!!! objsymbols.push(os); } } void scanObjModule(ObjModule* om) { int easyomf; uint u; ubyte result = 0; char name[LIBIDMAX + 1]; scope Array names = new Array(); names.push(null); // don't use index 0 assert(om); easyomf = 0; // assume not EASY-OMF ubyte* pend = om.base + om.length; ubyte* pnext; for (ubyte* p = om.base; 1; p = pnext) { assert(p < pend); ubyte recTyp = *p++; ushort recLen = *cast(ushort*)p; p += 2; pnext = p + recLen; recLen--; // forget the checksum switch (recTyp) { case LNAMES: case LLNAMES: while (p + 1 < pnext) { uint len = parseName(&p, name.ptr); names.push(cast(void*)new String(name[0..len].idup)); } break; case PUBDEF: if (easyomf) recTyp = PUB386; // convert to MS format case PUB386: if (!(parseIdx(&p) | parseIdx(&p))) p += 2; // skip seg, grp, frame while (p + 1 < pnext) { uint len = parseName(&p, name.ptr); p += (recTyp == PUBDEF) ? 2 : 4; // skip offset parseIdx(&p); // skip type index addSymbol(om, name[0..len].idup); } break; case COMDAT: if (easyomf) recTyp = COMDAT+1; // convert to MS format case COMDAT+1: int pickAny = 0; if (*p++ & 5) // if continuation or local comdat break; ubyte attr = *p++; if (attr & 0xF0) // attr: if multiple instances allowed pickAny = 1; p++; // align p += 2; // enum data offset if (recTyp == COMDAT+1) p += 2; // enum data offset parseIdx(&p); // type index if ((attr & 0x0F) == 0) // if explicit allocation { parseIdx(&p); // base group parseIdx(&p); // base segment } uint idx = parseIdx(&p); // public name index if( idx == 0 || idx >= names.dim) { //debug(printf("[s] name idx=%d, uCntNames=%d\n", idx, uCntNames)); error("corrupt COMDAT"); return; } //printf("[s] name='%s'\n",name); addSymbol(om, (cast(String)names.data[idx]).str, pickAny); break; case ALIAS: while (p + 1 < pnext) { uint len = parseName(&p, name.ptr); addSymbol(om, name[0..len].idup); parseName(&p, name.ptr); } break; case MODEND: case M386END: result = 1; goto Ret; case COMENT: // Recognize Phar Lap EASY-OMF format { enum ubyte[7] omfstr = [0x80,0xAA,'8','0','3','8','6']; if (recLen == omfstr.sizeof) { for (uint i = 0; i < omfstr.sizeof; i++) if (*p++ != omfstr[i]) goto L1; easyomf = 1; break; L1: ; } } // Recognize .IMPDEF Import Definition Records { enum ubyte[3] omfstr = [0, 0xA0, 1]; if (recLen >= 7) { p++; for (uint i = 1; i < omfstr.sizeof; i++) if (*p++ != omfstr[i]) goto L2; p++; // skip OrdFlag field uint len = parseName(&p, name.ptr); addSymbol(om, name[0..len].idup); break; L2: ; } } break; default: // ignore ; } } Ret: ; ///for (u = 1; u < names.dim; u++) /// free(names.data[u]); } /*********************************** * Calculates number of pages needed for dictionary * Returns: * number of pages */ ushort numDictPages(uint padding) { ushort ndicpages; ushort bucksForHash; ushort bucksForSize; uint symSize = 0; for (int i = 0; i < objsymbols.dim; i++) { ObjSymbol* s = cast(ObjSymbol*)objsymbols.data[i]; symSize += ( s.name.length + 4 ) & ~1; } for (int i = 0; i < objmodules.dim; i++) { ObjModule* om = cast(ObjModule*)objmodules.data[i]; size_t len = om.name.length; if (len > 0xFF) len += 2; // Digital Mars long name extension symSize += ( len + 4 + 1 ) & ~1; } bucksForHash = cast(ushort)((objsymbols.dim + objmodules.dim + HASHMOD - 3) / (HASHMOD - 2)); bucksForSize = cast(ushort)((symSize + BUCKETSIZE - padding - padding - 1) / (BUCKETSIZE - padding)); ndicpages = (bucksForHash > bucksForSize ) ? bucksForHash : bucksForSize; //printf("ndicpages = %u\n",ndicpages); // Find prime number greater than ndicpages enum uint[] primes = [ 1,2,3,5,7,11,13,17,19,23,29,31,37,41,43, 47,53,59,61,67,71,73,79,83,89,97,101,103, 107,109,113,127,131,137,139,149,151,157, 163,167,173,179,181,191,193,197,199,211, 223,227,229,233,239,241,251,257,263,269, 271,277,281,283,293,307,311,313,317,331, 337,347,349,353,359,367,373,379,383,389, 397,401,409,419,421,431,433,439,443,449, 457,461,463,467,479,487,491,499,503,509, //521,523,541,547, 0 ]; for (int i = 0; 1; i++) { if ( primes[i] == 0 ) { // Quick and easy way is out. // Now try and find first prime number > ndicpages uint prime; for (prime = (ndicpages + 1) | 1; 1; prime += 2) { // Determine if prime is prime for (uint u = 3; u < prime / 2; u += 2) { if ((prime / u) * u == prime) goto L1; } break; L1: ; } ndicpages = cast(ushort)prime; break; } if (primes[i] > ndicpages) { ndicpages = cast(ushort)primes[i]; break; } } return ndicpages; } /******************************************* * Write the module and symbol names to the dictionary. * Returns: * 0 failure */ int FillDict(ubyte* bucketsP, ushort ndicpages) { ubyte entry[4 + LIBIDMAX + 2 + 1]; //printf("FillDict()\n"); // Add each of the module names for (int i = 0; i < objmodules.dim; i++) { ObjModule* om = cast(ObjModule*)objmodules.data[i]; ushort n = cast(ushort)om.name.length; if (n > 255) { entry[0] = 0xFF; entry[1] = 0; *cast(ushort*)(entry.ptr + 2) = cast(ushort)(n + 1); memcpy(entry.ptr + 4, om.name.ptr, n); n += 3; } else { entry[ 0 ] = cast(ubyte)(1 + n); memcpy(entry.ptr + 1, om.name.ptr, n ); } entry[ n + 1 ] = '!'; *(cast(ushort*)( n + 2 + entry.ptr )) = om.page; if ( n & 1 ) entry[ n + 2 + 2 ] = 0; if ( !EnterDict( bucketsP, ndicpages, entry.ptr, n + 1 ) ) return 0; } // Sort the symbols qsort( objsymbols.data, objsymbols.dim, 4, /*(cmpfunc_t)*/&D_NameCompare ); // Add each of the symbols for (int i = 0; i < objsymbols.dim; i++) { ObjSymbol* os = cast(ObjSymbol*)objsymbols.data[i]; ushort n = cast(ushort)os.name.length; if (n > 255) { entry[0] = 0xFF; entry[1] = 0; *cast(ushort*)(entry.ptr + 2) = n; memcpy(entry.ptr + 4, os.name.ptr, n); n += 3; } else { entry[ 0 ] = cast(ubyte)n; memcpy( entry.ptr + 1, os.name.ptr, n ); } *(cast(ushort*)( n + 1 + entry.ptr )) = os.om.page; if ( (n & 1) == 0 ) entry[ n + 3] = 0; if ( !EnterDict( bucketsP, ndicpages, entry.ptr, n ) ) { return 0; } } return 1; } /********************************************** * Create and write library to libbuf. * The library consists of: * library header * object modules... * dictionary header * dictionary pages... */ void WriteLibToBuffer(OutBuffer libbuf) { /* Scan each of the object modules for symbols * to go into the dictionary */ for (int i = 0; i < objmodules.dim; i++) { ObjModule* om = cast(ObjModule*)objmodules.data[i]; scanObjModule(om); } uint g_page_size = 16; /* Calculate page size so that the number of pages * fits in 16 bits. This is because object modules * are indexed by page number, stored as an unsigned short. */ while (1) { Lagain: version (LOG) { printf("g_page_size = %d\n", g_page_size); } uint offset = g_page_size; for (int i = 0; i < objmodules.dim; i++) { ObjModule* om = cast(ObjModule*)objmodules.data[i]; uint page = offset / g_page_size; if (page > 0xFFFF) { // Page size is too small, double it and try again g_page_size *= 2; goto Lagain; } // Write out the object module m if (om.flags & MFgentheadr) // if generate THEADR record { size_t size = om.name.length; assert(size <= LIBIDMAX); offset += size + 5; //offset += om.length - (size + 5); offset += om.length; } else offset += om.length; // Round the size of the file up to the next page size // by filling with 0s uint n = (g_page_size - 1) & offset; if (n) offset += g_page_size - n; } break; } /* Leave one page of 0s at start as a dummy library header. * Fill it in later with the real data. */ libbuf.fill0(g_page_size); /* Write each object module into the library */ for (int i = 0; i < objmodules.dim; i++) { ObjModule* om = cast(ObjModule*)objmodules.data[i]; uint page = libbuf.offset / g_page_size; assert(page <= 0xFFFF); om.page = cast(ushort)page; // Write out the object module om if (om.flags & MFgentheadr) // if generate THEADR record { uint size = om.name.length; ubyte header[4 + LIBIDMAX + 1]; header [0] = THEADR; header [1] = cast(ubyte)(2 + size); header [2] = 0; header [3] = cast(ubyte)size; assert(size <= 0xFF - 2); memcpy(4 + header.ptr, om.name.ptr, size); // Compute and store record checksum uint n = size + 4; ubyte checksum = 0; ubyte* p = header.ptr; while (n--) { checksum -= *p; p++; } *p = checksum; libbuf.write(header.ptr, size + 5); //libbuf.write(om.base, om.length - (size + 5)); libbuf.write(om.base, om.length); } else libbuf.write(om.base, om.length); // Round the size of the file up to the next page size // by filling with 0s uint n = (g_page_size - 1) & libbuf.offset; if (n) libbuf.fill0(g_page_size - n); } // File offset of start of dictionary uint offset = libbuf.offset; // Write dictionary header, then round it to a BUCKETPAGE boundary ushort size = (BUCKETPAGE - (cast(short)offset + 3)) & (BUCKETPAGE - 1); libbuf.writeByte(0xF1); libbuf.writeword(size); libbuf.fill0(size); // Create dictionary ubyte* bucketsP = null; ushort ndicpages; ushort padding = 32; for (;;) { ndicpages = numDictPages(padding); version (LOG) { printf("ndicpages = %d\n", ndicpages); } // Allocate dictionary if (bucketsP) bucketsP = cast(ubyte*)GC.realloc(bucketsP, ndicpages * BUCKETPAGE); else bucketsP = cast(ubyte*)GC.malloc(ndicpages * BUCKETPAGE); assert(bucketsP); memset(bucketsP, 0, ndicpages * BUCKETPAGE); for (uint u = 0; u < ndicpages; u++) { // 'next available' slot bucketsP[u * BUCKETPAGE + HASHMOD] = (HASHMOD + 1) >> 1; } if (FillDict(bucketsP, ndicpages)) break; padding += 16; // try again with more margins } // Write dictionary libbuf.write(bucketsP, ndicpages * BUCKETPAGE); ///if (bucketsP) /// free(bucketsP); // Create library header Libheader libHeader; memset(&libHeader, 0, Libheader.sizeof); libHeader.recTyp = 0xF0; libHeader.recLen = 0x0D; libHeader.trailerPosn = offset + (3 + size); libHeader.recLen = cast(ushort)(g_page_size - 3); libHeader.ndicpages = ndicpages; libHeader.flags = 1; // always case sensitive // Write library header at start of buffer memcpy(libbuf.data, &libHeader, libHeader.sizeof); } } } // version (Windows) else version(TARGET_LINUX) { import dmd.TObject; class Library : TObject { void setFilename(string dir, string filename) { assert(0); } void addObject(string module_name, void *buf, size_t buflen) { assert(0); } void write() { assert(0); } } } else version (TARGET_OSX) { import dmd.TObject; class Library : TObject { void setFilename(string dir, string filename) { assert(0); } void addObject(string module_name, void *buf, size_t buflen) { assert(0); } void write() { assert(0); } } }