Mercurial > projects > ddmd
view dmd/Library.d @ 178:e3afd1303184
Many small bugs fixed
Made all classes derive from TObject to detect memory leaks (functionality is disabled for now)
Began work on overriding backend memory allocations (to avoid memory leaks)
author | korDen |
---|---|
date | Sun, 17 Oct 2010 07:42:00 +0400 |
parents | e7769d53e750 |
children | 190ba98276b3 |
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); } } }