| 1 |
/* |
|---|
| 2 |
* Copyright (c) 1986-1995 by Symantec |
|---|
| 3 |
* Copyright (c) 2000-2009 by Digital Mars |
|---|
| 4 |
* All Rights Reserved |
|---|
| 5 |
* http://www.digitalmars.com |
|---|
| 6 |
* Written by Walter Bright |
|---|
| 7 |
* |
|---|
| 8 |
* This source file is made available for personal use |
|---|
| 9 |
* only. The license is in /dmd/src/dmd/backendlicense.txt |
|---|
| 10 |
* For any other uses, please contact Digital Mars. |
|---|
| 11 |
*/ |
|---|
| 12 |
|
|---|
| 13 |
// Compiler implementation of the D programming language |
|---|
| 14 |
|
|---|
| 15 |
#include <stdio.h> |
|---|
| 16 |
#include <stdlib.h> |
|---|
| 17 |
#include <assert.h> |
|---|
| 18 |
|
|---|
| 19 |
#include "rmem.h" |
|---|
| 20 |
#include "root.h" |
|---|
| 21 |
#include "stringtable.h" |
|---|
| 22 |
|
|---|
| 23 |
#include "mars.h" |
|---|
| 24 |
#include "lib.h" |
|---|
| 25 |
|
|---|
| 26 |
#define LOG 0 |
|---|
| 27 |
|
|---|
| 28 |
Library::Library() |
|---|
| 29 |
{ |
|---|
| 30 |
libfile = NULL; |
|---|
| 31 |
} |
|---|
| 32 |
|
|---|
| 33 |
/*********************************** |
|---|
| 34 |
* Set the library file name based on the output directory |
|---|
| 35 |
* and the filename. |
|---|
| 36 |
* Add default library file name extension. |
|---|
| 37 |
*/ |
|---|
| 38 |
|
|---|
| 39 |
void Library::setFilename(char *dir, char *filename) |
|---|
| 40 |
{ |
|---|
| 41 |
char *arg = filename; |
|---|
| 42 |
if (!arg || !*arg) |
|---|
| 43 |
{ // Generate lib file name from first obj name |
|---|
| 44 |
char *n = (char *)global.params.objfiles->data[0]; |
|---|
| 45 |
|
|---|
| 46 |
n = FileName::name(n); |
|---|
| 47 |
FileName *fn = FileName::forceExt(n, global.lib_ext); |
|---|
| 48 |
arg = fn->toChars(); |
|---|
| 49 |
} |
|---|
| 50 |
if (!FileName::absolute(arg)) |
|---|
| 51 |
arg = FileName::combine(dir, arg); |
|---|
| 52 |
FileName *libfilename = FileName::defaultExt(arg, global.lib_ext); |
|---|
| 53 |
|
|---|
| 54 |
libfile = new File(libfilename); |
|---|
| 55 |
} |
|---|
| 56 |
|
|---|
| 57 |
void Library::write() |
|---|
| 58 |
{ |
|---|
| 59 |
if (global.params.verbose) |
|---|
| 60 |
printf("library %s\n", libfile->name->toChars()); |
|---|
| 61 |
|
|---|
| 62 |
OutBuffer libbuf; |
|---|
| 63 |
WriteLibToBuffer(&libbuf); |
|---|
| 64 |
|
|---|
| 65 |
// Transfer image to file |
|---|
| 66 |
libfile->setbuffer(libbuf.data, libbuf.offset); |
|---|
| 67 |
libbuf.extractData(); |
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 |
char *p = FileName::path(libfile->name->toChars()); |
|---|
| 71 |
FileName::ensurePathExists(p); |
|---|
| 72 |
//mem.free(p); |
|---|
| 73 |
|
|---|
| 74 |
libfile->writev(); |
|---|
| 75 |
} |
|---|
| 76 |
|
|---|
| 77 |
/*****************************************************************************/ |
|---|
| 78 |
|
|---|
| 79 |
void Library::addLibrary(void *buf, size_t buflen) |
|---|
| 80 |
{ |
|---|
| 81 |
addObject(NULL, buf, buflen); |
|---|
| 82 |
} |
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 |
/*****************************************************************************/ |
|---|
| 86 |
/*****************************************************************************/ |
|---|
| 87 |
|
|---|
| 88 |
/************************** |
|---|
| 89 |
* Record types: |
|---|
| 90 |
*/ |
|---|
| 91 |
|
|---|
| 92 |
#define RHEADR 0x6E |
|---|
| 93 |
#define REGINT 0x70 |
|---|
| 94 |
#define REDATA 0x72 |
|---|
| 95 |
#define RIDATA 0x74 |
|---|
| 96 |
#define OVLDEF 0x76 |
|---|
| 97 |
#define ENDREC 0x78 |
|---|
| 98 |
#define BLKDEF 0x7A |
|---|
| 99 |
#define BLKEND 0x7C |
|---|
| 100 |
#define DEBSYM 0x7E |
|---|
| 101 |
#define THEADR 0x80 |
|---|
| 102 |
#define LHEADR 0x82 |
|---|
| 103 |
#define PEDATA 0x84 |
|---|
| 104 |
#define PIDATA 0x86 |
|---|
| 105 |
#define COMENT 0x88 |
|---|
| 106 |
#define MODEND 0x8A |
|---|
| 107 |
#define M386END 0x8B /* 32 bit module end record */ |
|---|
| 108 |
#define EXTDEF 0x8C |
|---|
| 109 |
#define TYPDEF 0x8E |
|---|
| 110 |
#define PUBDEF 0x90 |
|---|
| 111 |
#define PUB386 0x91 |
|---|
| 112 |
#define LOCSYM 0x92 |
|---|
| 113 |
#define LINNUM 0x94 |
|---|
| 114 |
#define LNAMES 0x96 |
|---|
| 115 |
#define SEGDEF 0x98 |
|---|
| 116 |
#define GRPDEF 0x9A |
|---|
| 117 |
#define FIXUPP 0x9C |
|---|
| 118 |
/*#define (none) 0x9E */ |
|---|
| 119 |
#define LEDATA 0xA0 |
|---|
| 120 |
#define LIDATA 0xA2 |
|---|
| 121 |
#define LIBHED 0xA4 |
|---|
| 122 |
#define LIBNAM 0xA6 |
|---|
| 123 |
#define LIBLOC 0xA8 |
|---|
| 124 |
#define LIBDIC 0xAA |
|---|
| 125 |
#define COMDEF 0xB0 |
|---|
| 126 |
#define LEXTDEF 0xB4 |
|---|
| 127 |
#define LPUBDEF 0xB6 |
|---|
| 128 |
#define LCOMDEF 0xB8 |
|---|
| 129 |
#define CEXTDEF 0xBC |
|---|
| 130 |
#define COMDAT 0xC2 |
|---|
| 131 |
#define LINSYM 0xC4 |
|---|
| 132 |
#define ALIAS 0xC6 |
|---|
| 133 |
#define LLNAMES 0xCA |
|---|
| 134 |
|
|---|
| 135 |
|
|---|
| 136 |
#define LIBIDMAX (512 - 0x25 - 3 - 4) // max size that will fit in dictionary |
|---|
| 137 |
|
|---|
| 138 |
|
|---|
| 139 |
struct ObjModule |
|---|
| 140 |
{ |
|---|
| 141 |
unsigned char *base; // where are we holding it in memory |
|---|
| 142 |
unsigned length; // in bytes |
|---|
| 143 |
unsigned short page; // page module starts in output file |
|---|
| 144 |
unsigned char flags; |
|---|
| 145 |
#define MFgentheadr 1 // generate THEADR record |
|---|
| 146 |
#define MFtheadr 2 // module name comes from THEADR record |
|---|
| 147 |
char *name; // module name |
|---|
| 148 |
}; |
|---|
| 149 |
|
|---|
| 150 |
static void parseName(unsigned char **pp, char *name) |
|---|
| 151 |
{ |
|---|
| 152 |
unsigned char *p = *pp; |
|---|
| 153 |
unsigned len = *p++; |
|---|
| 154 |
if (len == 0xFF && *p == 0) // if long name |
|---|
| 155 |
{ |
|---|
| 156 |
len = p[1] & 0xFF; |
|---|
| 157 |
len |= (unsigned)p[2] << 8; |
|---|
| 158 |
p += 3; |
|---|
| 159 |
assert(len <= LIBIDMAX); |
|---|
| 160 |
} |
|---|
| 161 |
memcpy(name, p, len); |
|---|
| 162 |
name[len] = 0; |
|---|
| 163 |
*pp = p + len; |
|---|
| 164 |
} |
|---|
| 165 |
|
|---|
| 166 |
static unsigned short parseIdx(unsigned char **pp) |
|---|
| 167 |
{ |
|---|
| 168 |
unsigned char *p = *pp; |
|---|
| 169 |
unsigned char c = *p++; |
|---|
| 170 |
|
|---|
| 171 |
unsigned short idx = (0x80 & c) ? ((0x7F & c) << 8) + *p++ : c; |
|---|
| 172 |
*pp = p; |
|---|
| 173 |
return idx; |
|---|
| 174 |
} |
|---|
| 175 |
|
|---|
| 176 |
void Library::addSymbol(ObjModule *om, char *name, int pickAny) |
|---|
| 177 |
{ |
|---|
| 178 |
#if LOG |
|---|
| 179 |
printf("Library::addSymbol(%s, %s, %d)\n", om->name, name, pickAny); |
|---|
| 180 |
#endif |
|---|
| 181 |
StringValue *s = tab.insert(name, strlen(name)); |
|---|
| 182 |
if (!s) |
|---|
| 183 |
{ // already in table |
|---|
| 184 |
if (!pickAny) |
|---|
| 185 |
{ s = tab.lookup(name, strlen(name)); |
|---|
| 186 |
assert(s); |
|---|
| 187 |
ObjSymbol *os = (ObjSymbol *)s->ptrvalue; |
|---|
| 188 |
error("multiple definition of %s: %s and %s: %s", |
|---|
| 189 |
om->name, name, os->om->name, os->name); |
|---|
| 190 |
} |
|---|
| 191 |
} |
|---|
| 192 |
else |
|---|
| 193 |
{ |
|---|
| 194 |
ObjSymbol *os = new ObjSymbol(); |
|---|
| 195 |
os->name = strdup(name); |
|---|
| 196 |
os->om = om; |
|---|
| 197 |
s->ptrvalue = (void *)os; |
|---|
| 198 |
|
|---|
| 199 |
objsymbols.push(os); |
|---|
| 200 |
} |
|---|
| 201 |
} |
|---|
| 202 |
|
|---|
| 203 |
/************************************ |
|---|
| 204 |
* Scan single object module for dictionary symbols. |
|---|
| 205 |
* Send those symbols to Library::addSymbol(). |
|---|
| 206 |
*/ |
|---|
| 207 |
|
|---|
| 208 |
void Library::scanObjModule(ObjModule *om) |
|---|
| 209 |
{ int easyomf; |
|---|
| 210 |
unsigned u; |
|---|
| 211 |
unsigned char result = 0; |
|---|
| 212 |
char name[LIBIDMAX + 1]; |
|---|
| 213 |
|
|---|
| 214 |
Array names; |
|---|
| 215 |
names.push(NULL); // don't use index 0 |
|---|
| 216 |
|
|---|
| 217 |
assert(om); |
|---|
| 218 |
easyomf = 0; // assume not EASY-OMF |
|---|
| 219 |
unsigned char *pend = om->base + om->length; |
|---|
| 220 |
|
|---|
| 221 |
unsigned char *pnext; |
|---|
| 222 |
for (unsigned char *p = om->base; 1; p = pnext) |
|---|
| 223 |
{ |
|---|
| 224 |
assert(p < pend); |
|---|
| 225 |
unsigned char recTyp = *p++; |
|---|
| 226 |
unsigned short recLen = *(unsigned short *)p; |
|---|
| 227 |
p += 2; |
|---|
| 228 |
pnext = p + recLen; |
|---|
| 229 |
recLen--; // forget the checksum |
|---|
| 230 |
|
|---|
| 231 |
switch (recTyp) |
|---|
| 232 |
{ |
|---|
| 233 |
case LNAMES: |
|---|
| 234 |
case LLNAMES: |
|---|
| 235 |
while (p + 1 < pnext) |
|---|
| 236 |
{ |
|---|
| 237 |
parseName(&p, name); |
|---|
| 238 |
names.push(strdup(name)); |
|---|
| 239 |
} |
|---|
| 240 |
break; |
|---|
| 241 |
|
|---|
| 242 |
case PUBDEF: |
|---|
| 243 |
if (easyomf) |
|---|
| 244 |
recTyp = PUB386; // convert to MS format |
|---|
| 245 |
case PUB386: |
|---|
| 246 |
if (!(parseIdx(&p) | parseIdx(&p))) |
|---|
| 247 |
p += 2; // skip seg, grp, frame |
|---|
| 248 |
while (p + 1 < pnext) |
|---|
| 249 |
{ |
|---|
| 250 |
parseName(&p, name); |
|---|
| 251 |
p += (recTyp == PUBDEF) ? 2 : 4; // skip offset |
|---|
| 252 |
parseIdx(&p); // skip type index |
|---|
| 253 |
addSymbol(om, name); |
|---|
| 254 |
} |
|---|
| 255 |
break; |
|---|
| 256 |
|
|---|
| 257 |
case COMDAT: |
|---|
| 258 |
if (easyomf) |
|---|
| 259 |
recTyp = COMDAT+1; // convert to MS format |
|---|
| 260 |
case COMDAT+1: |
|---|
| 261 |
int pickAny = 0; |
|---|
| 262 |
|
|---|
| 263 |
if (*p++ & 5) // if continuation or local comdat |
|---|
| 264 |
break; |
|---|
| 265 |
|
|---|
| 266 |
unsigned char attr = *p++; |
|---|
| 267 |
if (attr & 0xF0) // attr: if multiple instances allowed |
|---|
| 268 |
pickAny = 1; |
|---|
| 269 |
p++; // align |
|---|
| 270 |
|
|---|
| 271 |
p += 2; // enum data offset |
|---|
| 272 |
if (recTyp == COMDAT+1) |
|---|
| 273 |
p += 2; // enum data offset |
|---|
| 274 |
|
|---|
| 275 |
parseIdx(&p); // type index |
|---|
| 276 |
|
|---|
| 277 |
if ((attr & 0x0F) == 0) // if explicit allocation |
|---|
| 278 |
{ parseIdx(&p); // base group |
|---|
| 279 |
parseIdx(&p); // base segment |
|---|
| 280 |
} |
|---|
| 281 |
|
|---|
| 282 |
unsigned idx = parseIdx(&p); // public name index |
|---|
| 283 |
if( idx == 0 || idx >= names.dim) |
|---|
| 284 |
{ |
|---|
| 285 |
//debug(printf("[s] name idx=%d, uCntNames=%d\n", idx, uCntNames)); |
|---|
| 286 |
error("corrupt COMDAT"); |
|---|
| 287 |
return; |
|---|
| 288 |
} |
|---|
| 289 |
|
|---|
| 290 |
//printf("[s] name='%s'\n",name); |
|---|
| 291 |
addSymbol(om, (char *)names.data[idx],pickAny); |
|---|
| 292 |
break; |
|---|
| 293 |
|
|---|
| 294 |
case ALIAS: |
|---|
| 295 |
while (p + 1 < pnext) |
|---|
| 296 |
{ |
|---|
| 297 |
parseName(&p, name); |
|---|
| 298 |
addSymbol(om, name); |
|---|
| 299 |
parseName(&p, name); |
|---|
| 300 |
} |
|---|
| 301 |
break; |
|---|
| 302 |
|
|---|
| 303 |
case MODEND: |
|---|
| 304 |
case M386END: |
|---|
| 305 |
result = 1; |
|---|
| 306 |
goto Ret; |
|---|
| 307 |
|
|---|
| 308 |
case COMENT: |
|---|
| 309 |
// Recognize Phar Lap EASY-OMF format |
|---|
| 310 |
{ static unsigned char omfstr[7] = |
|---|
| 311 |
{0x80,0xAA,'8','0','3','8','6'}; |
|---|
| 312 |
|
|---|
| 313 |
if (recLen == sizeof(omfstr)) |
|---|
| 314 |
{ |
|---|
| 315 |
for (unsigned i = 0; i < sizeof(omfstr); i++) |
|---|
| 316 |
if (*p++ != omfstr[i]) |
|---|
| 317 |
goto L1; |
|---|
| 318 |
easyomf = 1; |
|---|
| 319 |
break; |
|---|
| 320 |
L1: ; |
|---|
| 321 |
} |
|---|
| 322 |
} |
|---|
| 323 |
// Recognize .IMPDEF Import Definition Records |
|---|
| 324 |
{ static unsigned char omfstr[] = |
|---|
| 325 |
{0,0xA0,1}; |
|---|
| 326 |
|
|---|
| 327 |
if (recLen >= 7) |
|---|
| 328 |
{ |
|---|
| 329 |
p++; |
|---|
| 330 |
for (unsigned i = 1; i < sizeof(omfstr); i++) |
|---|
| 331 |
if (*p++ != omfstr[i]) |
|---|
| 332 |
goto L2; |
|---|
| 333 |
p++; // skip OrdFlag field |
|---|
| 334 |
parseName(&p, name); |
|---|
| 335 |
addSymbol(om, name); |
|---|
| 336 |
break; |
|---|
| 337 |
L2: ; |
|---|
| 338 |
} |
|---|
| 339 |
} |
|---|
| 340 |
break; |
|---|
| 341 |
|
|---|
| 342 |
default: |
|---|
| 343 |
// ignore |
|---|
| 344 |
; |
|---|
| 345 |
} |
|---|
| 346 |
} |
|---|
| 347 |
Ret: |
|---|
| 348 |
for (u = 1; u < names.dim; u++) |
|---|
| 349 |
free(names.data[u]); |
|---|
| 350 |
} |
|---|
| 351 |
|
|---|
| 352 |
/*************************************** |
|---|
| 353 |
* Add object module or library to the library. |
|---|
| 354 |
* Examine the buffer to see which it is. |
|---|
| 355 |
* If the buffer is NULL, use module_name as the file name |
|---|
| 356 |
* and load the file. |
|---|
| 357 |
*/ |
|---|
| 358 |
|
|---|
| 359 |
void Library::addObject(const char *module_name, void *buf, size_t buflen) |
|---|
| 360 |
{ |
|---|
| 361 |
#if LOG |
|---|
| 362 |
printf("Library::addObject(%s)\n", module_name ? module_name : ""); |
|---|
| 363 |
#endif |
|---|
| 364 |
if (!buf) |
|---|
| 365 |
{ assert(module_name); |
|---|
| 366 |
FileName f((char *)module_name, 0); |
|---|
| 367 |
File file(&f); |
|---|
| 368 |
file.readv(); |
|---|
| 369 |
buf = file.buffer; |
|---|
| 370 |
buflen = file.len; |
|---|
| 371 |
file.ref = 1; |
|---|
| 372 |
} |
|---|
| 373 |
|
|---|
| 374 |
unsigned g_page_size; |
|---|
| 375 |
unsigned char *pstart = (unsigned char *)buf; |
|---|
| 376 |
int islibrary = 0; |
|---|
| 377 |
|
|---|
| 378 |
/* See if it's an OMF library. |
|---|
| 379 |
* Don't go by file extension. |
|---|
| 380 |
*/ |
|---|
| 381 |
|
|---|
| 382 |
#pragma pack(1) |
|---|
| 383 |
struct LibHeader |
|---|
| 384 |
{ |
|---|
| 385 |
unsigned char recTyp; // 0xF0 |
|---|
| 386 |
unsigned short pagesize; |
|---|
| 387 |
long lSymSeek; |
|---|
| 388 |
unsigned short ndicpages; |
|---|
| 389 |
unsigned char flags; |
|---|
| 390 |
}; |
|---|
| 391 |
#pragma pack() |
|---|
| 392 |
|
|---|
| 393 |
/* Determine if it is an OMF library, an OMF object module, |
|---|
| 394 |
* or something else. |
|---|
| 395 |
*/ |
|---|
| 396 |
if (buflen < sizeof(LibHeader)) |
|---|
| 397 |
{ |
|---|
| 398 |
Lcorrupt: |
|---|
| 399 |
error("corrupt object module"); |
|---|
| 400 |
} |
|---|
| 401 |
LibHeader *lh = (LibHeader *)buf; |
|---|
| 402 |
if (lh->recTyp == 0xF0) |
|---|
| 403 |
{ /* OMF library |
|---|
| 404 |
* The modules are all at buf[g_page_size .. lh->lSymSeek] |
|---|
| 405 |
*/ |
|---|
| 406 |
islibrary = 1; |
|---|
| 407 |
g_page_size = lh->pagesize + 3; |
|---|
| 408 |
buf = (void *)(pstart + g_page_size); |
|---|
| 409 |
if (lh->lSymSeek > buflen || |
|---|
| 410 |
g_page_size > buflen) |
|---|
| 411 |
goto Lcorrupt; |
|---|
| 412 |
buflen = lh->lSymSeek - g_page_size; |
|---|
| 413 |
} |
|---|
| 414 |
else if (lh->recTyp == '!' && memcmp(lh, "!<arch>\n", 8) == 0) |
|---|
| 415 |
{ |
|---|
| 416 |
error("COFF libraries not supported"); |
|---|
| 417 |
return; |
|---|
| 418 |
} |
|---|
| 419 |
else |
|---|
| 420 |
{ // Not a library, assume OMF object module |
|---|
| 421 |
g_page_size = 16; |
|---|
| 422 |
} |
|---|
| 423 |
|
|---|
| 424 |
/* Split up the buffer buf[0..buflen] into multiple object modules, |
|---|
| 425 |
* each aligned on a g_page_size boundary. |
|---|
| 426 |
*/ |
|---|
| 427 |
|
|---|
| 428 |
ObjModule *om = NULL; |
|---|
| 429 |
int first_module = 1; |
|---|
| 430 |
|
|---|
| 431 |
unsigned char *p = (unsigned char *)buf; |
|---|
| 432 |
unsigned char *pend = p + buflen; |
|---|
| 433 |
unsigned char *pnext; |
|---|
| 434 |
for (; p < pend; p = pnext) // for each OMF record |
|---|
| 435 |
{ |
|---|
| 436 |
if (p + 3 >= pend) |
|---|
| 437 |
goto Lcorrupt; |
|---|
| 438 |
unsigned char recTyp = *p; |
|---|
| 439 |
unsigned short recLen = *(unsigned short *)(p + 1); |
|---|
| 440 |
pnext = p + 3 + recLen; |
|---|
| 441 |
if (pnext > pend) |
|---|
| 442 |
goto Lcorrupt; |
|---|
| 443 |
recLen--; /* forget the checksum */ |
|---|
| 444 |
|
|---|
| 445 |
switch (recTyp) |
|---|
| 446 |
{ |
|---|
| 447 |
case LHEADR : |
|---|
| 448 |
case THEADR : |
|---|
| 449 |
if (!om) |
|---|
| 450 |
{ char name[LIBIDMAX + 1]; |
|---|
| 451 |
om = new ObjModule(); |
|---|
| 452 |
om->flags = 0; |
|---|
| 453 |
om->base = p; |
|---|
| 454 |
p += 3; |
|---|
| 455 |
parseName(&p, name); |
|---|
| 456 |
if (first_module && module_name && !islibrary) |
|---|
| 457 |
{ // Remove path and extension |
|---|
| 458 |
om->name = strdup(FileName::name(module_name)); |
|---|
| 459 |
char *ext = FileName::ext(om->name); |
|---|
| 460 |
if (ext) |
|---|
| 461 |
ext[-1] = 0; |
|---|
| 462 |
} |
|---|
| 463 |
else |
|---|
| 464 |
{ /* Use THEADR name as module name, |
|---|
| 465 |
* removing path and extension. |
|---|
| 466 |
*/ |
|---|
| 467 |
om->name = strdup(FileName::name(name)); |
|---|
| 468 |
char *ext = FileName::ext(om->name); |
|---|
| 469 |
if (ext) |
|---|
| 470 |
ext[-1] = 0; |
|---|
| 471 |
|
|---|
| 472 |
om->flags |= MFtheadr; |
|---|
| 473 |
} |
|---|
| 474 |
if (strcmp(name, "C") == 0) // old C compilers did this |
|---|
| 475 |
{ om->flags |= MFgentheadr; // generate our own THEADR |
|---|
| 476 |
om->base = pnext; // skip past THEADR |
|---|
| 477 |
} |
|---|
| 478 |
objmodules.push(om); |
|---|
| 479 |
first_module = 0; |
|---|
| 480 |
} |
|---|
| 481 |
break; |
|---|
| 482 |
|
|---|
| 483 |
case MODEND : |
|---|
| 484 |
case M386END: |
|---|
| 485 |
if (om) |
|---|
| 486 |
{ om->page = (om->base - pstart) / g_page_size; |
|---|
| 487 |
om->length = pnext - om->base; |
|---|
| 488 |
om = NULL; |
|---|
| 489 |
} |
|---|
| 490 |
// Round up to next page |
|---|
| 491 |
unsigned t = pnext - pstart; |
|---|
| 492 |
t = (t + g_page_size - 1) & ~(unsigned)(g_page_size - 1); |
|---|
| 493 |
pnext = pstart + t; |
|---|
| 494 |
break; |
|---|
| 495 |
|
|---|
| 496 |
default: |
|---|
| 497 |
// ignore |
|---|
| 498 |
; |
|---|
| 499 |
} |
|---|
| 500 |
} |
|---|
| 501 |
|
|---|
| 502 |
if (om) |
|---|
| 503 |
goto Lcorrupt; // missing MODEND record |
|---|
| 504 |
} |
|---|
| 505 |
|
|---|
| 506 |
|
|---|
| 507 |
/*****************************************************************************/ |
|---|
| 508 |
/*****************************************************************************/ |
|---|
| 509 |
|
|---|
| 510 |
typedef int (__cdecl * cmpfunc_t)(const void *,const void *); |
|---|
| 511 |
|
|---|
| 512 |
extern "C" int NameCompare(ObjSymbol **p1, ObjSymbol **p2) |
|---|
| 513 |
{ |
|---|
| 514 |
return strcmp((*p1)->name, (*p2)->name); |
|---|
| 515 |
} |
|---|
| 516 |
|
|---|
| 517 |
#define HASHMOD 0x25 |
|---|
| 518 |
#define BUCKETPAGE 512 |
|---|
| 519 |
#define BUCKETSIZE (BUCKETPAGE - HASHMOD - 1) |
|---|
| 520 |
|
|---|
| 521 |
|
|---|
| 522 |
/*********************************** |
|---|
| 523 |
* Calculates number of pages needed for dictionary |
|---|
| 524 |
* Returns: |
|---|
| 525 |
* number of pages |
|---|
| 526 |
*/ |
|---|
| 527 |
|
|---|
| 528 |
unsigned short Library::numDictPages(unsigned padding) |
|---|
| 529 |
{ |
|---|
| 530 |
unsigned short ndicpages; |
|---|
| 531 |
unsigned short bucksForHash; |
|---|
| 532 |
unsigned short bucksForSize; |
|---|
| 533 |
unsigned symSize = 0; |
|---|
| 534 |
|
|---|
| 535 |
for (int i = 0; i < objsymbols.dim; i++) |
|---|
| 536 |
{ ObjSymbol *s = (ObjSymbol *)objsymbols.data[i]; |
|---|
| 537 |
|
|---|
| 538 |
symSize += ( strlen(s->name) + 4 ) & ~1; |
|---|
| 539 |
} |
|---|
| 540 |
|
|---|
| 541 |
for (int i = 0; i < objmodules.dim; i++) |
|---|
| 542 |
{ ObjModule *om = (ObjModule *)objmodules.data[i]; |
|---|
| 543 |
|
|---|
| 544 |
size_t len = strlen(om->name); |
|---|
| 545 |
if (len > 0xFF) |
|---|
| 546 |
len += 2; // Digital Mars long name extension |
|---|
| 547 |
symSize += ( len + 4 + 1 ) & ~1; |
|---|
| 548 |
} |
|---|
| 549 |
|
|---|
| 550 |
bucksForHash = (objsymbols.dim + objmodules.dim + HASHMOD - 3) / |
|---|
| 551 |
(HASHMOD - 2); |
|---|
| 552 |
bucksForSize = (symSize + BUCKETSIZE - padding - padding - 1) / |
|---|
| 553 |
(BUCKETSIZE - padding); |
|---|
| 554 |
|
|---|
| 555 |
ndicpages = (bucksForHash > bucksForSize ) ? bucksForHash : bucksForSize; |
|---|
| 556 |
//printf("ndicpages = %u\n",ndicpages); |
|---|
| 557 |
|
|---|
| 558 |
// Find prime number greater than ndicpages |
|---|
| 559 |
static unsigned primes[] = |
|---|
| 560 |
{ 1,2,3,5,7,11,13,17,19,23,29,31,37,41,43, |
|---|
| 561 |
47,53,59,61,67,71,73,79,83,89,97,101,103, |
|---|
| 562 |
107,109,113,127,131,137,139,149,151,157, |
|---|
| 563 |
163,167,173,179,181,191,193,197,199,211, |
|---|
| 564 |
223,227,229,233,239,241,251,257,263,269, |
|---|
| 565 |
271,277,281,283,293,307,311,313,317,331, |
|---|
| 566 |
337,347,349,353,359,367,373,379,383,389, |
|---|
| 567 |
397,401,409,419,421,431,433,439,443,449, |
|---|
| 568 |
457,461,463,467,479,487,491,499,503,509, |
|---|
| 569 |
//521,523,541,547, |
|---|
| 570 |
0 |
|---|
| 571 |
}; |
|---|
| 572 |
|
|---|
| 573 |
for (int i = 0; 1; i++) |
|---|
| 574 |
{ |
|---|
| 575 |
if ( primes[i] == 0 ) |
|---|
| 576 |
{ // Quick and easy way is out. |
|---|
| 577 |
// Now try and find first prime number > ndicpages |
|---|
| 578 |
unsigned prime; |
|---|
| 579 |
|
|---|
| 580 |
for (prime = (ndicpages + 1) | 1; 1; prime += 2) |
|---|
| 581 |
{ // Determine if prime is prime |
|---|
| 582 |
for (unsigned u = 3; u < prime / 2; u += 2) |
|---|
| 583 |
{ |
|---|
| 584 |
if ((prime / u) * u == prime) |
|---|
| 585 |
goto L1; |
|---|
| 586 |
} |
|---|
| 587 |
break; |
|---|
| 588 |
|
|---|
| 589 |
L1: ; |
|---|
| 590 |
} |
|---|
| 591 |
ndicpages = prime; |
|---|
| 592 |
break; |
|---|
| 593 |
} |
|---|
| 594 |
|
|---|
| 595 |
if (primes[i] > ndicpages) |
|---|
| 596 |
{ |
|---|
| 597 |
ndicpages = primes[i]; |
|---|
| 598 |
break; |
|---|
| 599 |
} |
|---|
| 600 |
} |
|---|
| 601 |
|
|---|
| 602 |
return ndicpages; |
|---|
| 603 |
} |
|---|
| 604 |
|
|---|
| 605 |
|
|---|
| 606 |
/******************************************* |
|---|
| 607 |
* Write a single entry into dictionary. |
|---|
| 608 |
* Returns: |
|---|
| 609 |
* 0 failure |
|---|
| 610 |
*/ |
|---|
| 611 |
|
|---|
| 612 |
static int EnterDict( unsigned char *bucketsP, unsigned short ndicpages, unsigned char *entry, unsigned entrylen ) |
|---|
| 613 |
{ |
|---|
| 614 |
unsigned short uStartIndex; |
|---|
| 615 |
unsigned short uStep; |
|---|
| 616 |
unsigned short uStartPage; |
|---|
| 617 |
unsigned short uPageStep; |
|---|
| 618 |
unsigned short uIndex; |
|---|
| 619 |
unsigned short uPage; |
|---|
| 620 |
unsigned short n; |
|---|
| 621 |
unsigned u; |
|---|
| 622 |
unsigned nbytes; |
|---|
| 623 |
unsigned char *aP; |
|---|
| 624 |
unsigned char *zP; |
|---|
| 625 |
|
|---|
| 626 |
aP = entry; |
|---|
| 627 |
zP = aP + entrylen; // point at last char in identifier |
|---|
| 628 |
|
|---|
| 629 |
uStartPage = 0; |
|---|
| 630 |
uPageStep = 0; |
|---|
| 631 |
uStartIndex = 0; |
|---|
| 632 |
uStep = 0; |
|---|
| 633 |
|
|---|
| 634 |
u = entrylen; |
|---|
| 635 |
while ( u-- ) |
|---|
| 636 |
{ |
|---|
| 637 |
uStartPage = _rotl( uStartPage, 2 ) ^ ( *aP | 0x20 ); |
|---|
| 638 |
uStep = _rotr( uStep, 2 ) ^ ( *aP++ | 0x20 ); |
|---|
| 639 |
uStartIndex = _rotr( uStartIndex, 2 ) ^ ( *zP | 0x20 ); |
|---|
| 640 |
uPageStep = _rotl( uPageStep, 2 ) ^ ( *zP-- | 0x20 ); |
|---|
| 641 |
} |
|---|
| 642 |
|
|---|
| 643 |
uStartPage %= ndicpages; |
|---|
| 644 |
uPageStep %= ndicpages; |
|---|
| 645 |
if ( uPageStep == 0 ) |
|---|
| 646 |
uPageStep++; |
|---|
| 647 |
uStartIndex %= HASHMOD; |
|---|
| 648 |
uStep %= HASHMOD; |
|---|
| 649 |
if ( uStep == 0 ) |
|---|
| 650 |
uStep++; |
|---|
| 651 |
|
|---|
| 652 |
uPage = uStartPage; |
|---|
| 653 |
uIndex = uStartIndex; |
|---|
| 654 |
|
|---|
| 655 |
// number of bytes in entry |
|---|
| 656 |
nbytes = 1 + entrylen + 2; |
|---|
| 657 |
if (entrylen > 255) |
|---|
| 658 |
nbytes += 2; |
|---|
| 659 |
|
|---|
| 660 |
while (1) |
|---|
| 661 |
{ |
|---|
| 662 |
aP = &bucketsP[uPage * BUCKETPAGE]; |
|---|
| 663 |
uStartIndex = uIndex; |
|---|
| 664 |
while (1) |
|---|
| 665 |
{ |
|---|
| 666 |
if ( 0 == aP[ uIndex ] ) |
|---|
| 667 |
{ |
|---|
| 668 |
// n = next available position in this page |
|---|
| 669 |
n = aP[ HASHMOD ] << 1; |
|---|
| 670 |
assert(n > HASHMOD); |
|---|
| 671 |
|
|---|
| 672 |
// if off end of this page |
|---|
| 673 |
if (n + nbytes > BUCKETPAGE ) |
|---|
| 674 |
{ aP[ HASHMOD ] = 0xFF; |
|---|
| 675 |
break; // next page |
|---|
| 676 |
} |
|---|
| 677 |
else |
|---|
| 678 |
{ |
|---|
| 679 |
aP[ uIndex ] = n >> 1; |
|---|
| 680 |
memcpy( (aP + n), entry, nbytes ); |
|---|
| 681 |
aP[ HASHMOD ] += (nbytes + 1) >> 1; |
|---|
| 682 |
if (aP[HASHMOD] == 0) |
|---|
| 683 |
aP[HASHMOD] = 0xFF; |
|---|
| 684 |
return 1; |
|---|
| 685 |
} |
|---|
| 686 |
} |
|---|
| 687 |
uIndex += uStep; |
|---|
| 688 |
uIndex %= 0x25; |
|---|
| 689 |
/*if (uIndex > 0x25) |
|---|
| 690 |
uIndex -= 0x25;*/ |
|---|
| 691 |
if( uIndex == uStartIndex ) |
|---|
| 692 |
break; |
|---|
| 693 |
} |
|---|
| 694 |
uPage += uPageStep; |
|---|
| 695 |
if (uPage >= ndicpages) |
|---|
| 696 |
uPage -= ndicpages; |
|---|
| 697 |
if( uPage == uStartPage ) |
|---|
| 698 |
break; |
|---|
| 699 |
} |
|---|
| 700 |
|
|---|
| 701 |
return 0; |
|---|
| 702 |
} |
|---|
| 703 |
|
|---|
| 704 |
/******************************************* |
|---|
| 705 |
* Write the module and symbol names to the dictionary. |
|---|
| 706 |
* Returns: |
|---|
| 707 |
* 0 failure |
|---|
| 708 |
*/ |
|---|
| 709 |
|
|---|
| 710 |
int Library::FillDict(unsigned char *bucketsP, unsigned short ndicpages) |
|---|
| 711 |
{ |
|---|
| 712 |
unsigned char entry[4 + LIBIDMAX + 2 + 1]; |
|---|
| 713 |
|
|---|
| 714 |
//printf("FillDict()\n"); |
|---|
| 715 |
|
|---|
| 716 |
// Add each of the module names |
|---|
| 717 |
for (int i = 0; i < objmodules.dim; i++) |
|---|
| 718 |
{ ObjModule *om = (ObjModule *)objmodules.data[i]; |
|---|
| 719 |
|
|---|
| 720 |
unsigned short n = strlen( om->name ); |
|---|
| 721 |
if (n > 255) |
|---|
| 722 |
{ entry[0] = 0xFF; |
|---|
| 723 |
entry[1] = 0; |
|---|
| 724 |
*(unsigned short *)(entry + 2) = n + 1; |
|---|
| 725 |
memcpy(entry + 4, om->name, n); |
|---|
| 726 |
n += 3; |
|---|
| 727 |
} |
|---|
| 728 |
else |
|---|
| 729 |
{ entry[ 0 ] = 1 + n; |
|---|
| 730 |
memcpy(entry + 1, om->name, n ); |
|---|
| 731 |
} |
|---|
| 732 |
entry[ n + 1 ] = '!'; |
|---|
| 733 |
*((unsigned short *)( n + 2 + entry )) = om->page; |
|---|
| 734 |
if ( n & 1 ) |
|---|
| 735 |
entry[ n + 2 + 2 ] = 0; |
|---|
| 736 |
if ( !EnterDict( bucketsP, ndicpages, entry, n + 1 ) ) |
|---|
| 737 |
return 0; |
|---|
| 738 |
} |
|---|
| 739 |
|
|---|
| 740 |
// Sort the symbols |
|---|
| 741 |
qsort( objsymbols.data, objsymbols.dim, 4, (cmpfunc_t)NameCompare ); |
|---|
| 742 |
|
|---|
| 743 |
// Add each of the symbols |
|---|
| 744 |
for (int i = 0; i < objsymbols.dim; i++) |
|---|
| 745 |
{ ObjSymbol *os = (ObjSymbol *)objsymbols.data[i]; |
|---|
| 746 |
|
|---|
| 747 |
unsigned short n = strlen( os->name ); |
|---|
| 748 |
if (n > 255) |
|---|
| 749 |
{ entry[0] = 0xFF; |
|---|
| 750 |
entry[1] = 0; |
|---|
| 751 |
*(unsigned short *)(entry + 2) = n; |
|---|
| 752 |
memcpy(entry + 4, os->name, n); |
|---|
| 753 |
n += 3; |
|---|
| 754 |
} |
|---|
| 755 |
else |
|---|
| 756 |
{ entry[ 0 ] = n; |
|---|
| 757 |
memcpy( entry + 1, os->name, n ); |
|---|
| 758 |
} |
|---|
| 759 |
*((unsigned short *)( n + 1 + entry )) = os->om->page; |
|---|
| 760 |
if ( (n & 1) == 0 ) |
|---|
| 761 |
entry[ n + 3] = 0; |
|---|
| 762 |
if ( !EnterDict( bucketsP, ndicpages, entry, n ) ) |
|---|
| 763 |
{ |
|---|
| 764 |
return 0; |
|---|
| 765 |
} |
|---|
| 766 |
} |
|---|
| 767 |
return 1; |
|---|
| 768 |
} |
|---|
| 769 |
|
|---|
| 770 |
|
|---|
| 771 |
/********************************************** |
|---|
| 772 |
* Create and write library to libbuf. |
|---|
| 773 |
* The library consists of: |
|---|
| 774 |
* library header |
|---|
| 775 |
* object modules... |
|---|
| 776 |
* dictionary header |
|---|
| 777 |
* dictionary pages... |
|---|
| 778 |
*/ |
|---|
| 779 |
|
|---|
| 780 |
void Library::WriteLibToBuffer(OutBuffer *libbuf) |
|---|
| 781 |
{ |
|---|
| 782 |
/* Scan each of the object modules for symbols |
|---|
| 783 |
* to go into the dictionary |
|---|
| 784 |
*/ |
|---|
| 785 |
for (int i = 0; i < objmodules.dim; i++) |
|---|
| 786 |
{ ObjModule *om = (ObjModule *)objmodules.data[i]; |
|---|
| 787 |
|
|---|
| 788 |
scanObjModule(om); |
|---|
| 789 |
} |
|---|
| 790 |
|
|---|
| 791 |
unsigned g_page_size = 16; |
|---|
| 792 |
|
|---|
| 793 |
/* Calculate page size so that the number of pages |
|---|
| 794 |
* fits in 16 bits. This is because object modules |
|---|
| 795 |
* are indexed by page number, stored as an unsigned short. |
|---|
| 796 |
*/ |
|---|
| 797 |
while (1) |
|---|
| 798 |
{ |
|---|
| 799 |
Lagain: |
|---|
| 800 |
#if LOG |
|---|
| 801 |
printf("g_page_size = %d\n", g_page_size); |
|---|
| 802 |
#endif |
|---|
| 803 |
unsigned offset = g_page_size; |
|---|
| 804 |
|
|---|
| 805 |
for (int i = 0; i < objmodules.dim; i++) |
|---|
| 806 |
{ ObjModule *om = (ObjModule *)objmodules.data[i]; |
|---|
| 807 |
|
|---|
| 808 |
unsigned page = offset / g_page_size; |
|---|
| 809 |
if (page > 0xFFFF) |
|---|
| 810 |
{ // Page size is too small, double it and try again |
|---|
| 811 |
g_page_size *= 2; |
|---|
| 812 |
goto Lagain; |
|---|
| 813 |
} |
|---|
| 814 |
|
|---|
| 815 |
// Write out the object module m |
|---|
| 816 |
if (om->flags & MFgentheadr) // if generate THEADR record |
|---|
| 817 |
{ |
|---|
| 818 |
size_t size = strlen(om->name); |
|---|
| 819 |
assert(size <= LIBIDMAX); |
|---|
| 820 |
|
|---|
| 821 |
offset += size + 5; |
|---|
| 822 |
//offset += om->length - (size + 5); |
|---|
| 823 |
offset += om->length; |
|---|
| 824 |
} |
|---|
| 825 |
else |
|---|
| 826 |
offset += om->length; |
|---|
| 827 |
|
|---|
| 828 |
// Round the size of the file up to the next page size |
|---|
| 829 |
// by filling with 0s |
|---|
| 830 |
unsigned n = (g_page_size - 1) & offset; |
|---|
| 831 |
if (n) |
|---|
| 832 |
offset += g_page_size - n; |
|---|
| 833 |
} |
|---|
| 834 |
break; |
|---|
| 835 |
} |
|---|
| 836 |
|
|---|
| 837 |
|
|---|
| 838 |
/* Leave one page of 0s at start as a dummy library header. |
|---|
| 839 |
* Fill it in later with the real data. |
|---|
| 840 |
*/ |
|---|
| 841 |
libbuf->fill0(g_page_size); |
|---|
| 842 |
|
|---|
| 843 |
/* Write each object module into the library |
|---|
| 844 |
*/ |
|---|
| 845 |
for (int i = 0; i < objmodules.dim; i++) |
|---|
| 846 |
{ ObjModule *om = (ObjModule *)objmodules.data[i]; |
|---|
| 847 |
|
|---|
| 848 |
unsigned page = libbuf->offset / g_page_size; |
|---|
| 849 |
assert(page <= 0xFFFF); |
|---|
| 850 |
om->page = page; |
|---|
| 851 |
|
|---|
| 852 |
// Write out the object module om |
|---|
| 853 |
if (om->flags & MFgentheadr) // if generate THEADR record |
|---|
| 854 |
{ |
|---|
| 855 |
unsigned size = strlen(om->name); |
|---|
| 856 |
unsigned char header[4 + LIBIDMAX + 1]; |
|---|
| 857 |
|
|---|
| 858 |
header [0] = THEADR; |
|---|
| 859 |
header [1] = 2 + size; |
|---|
| 860 |
header [2] = 0; |
|---|
| 861 |
header [3] = size; |
|---|
| 862 |
assert(size <= 0xFF - 2); |
|---|
| 863 |
|
|---|
| 864 |
memcpy(4 + header, om->name, size); |
|---|
| 865 |
|
|---|
| 866 |
// Compute and store record checksum |
|---|
| 867 |
unsigned n = size + 4; |
|---|
| 868 |
unsigned char checksum = 0; |
|---|
| 869 |
unsigned char *p = header; |
|---|
| 870 |
while (n--) |
|---|
| 871 |
{ checksum -= *p; |
|---|
| 872 |
p++; |
|---|
| 873 |
} |
|---|
| 874 |
*p = checksum; |
|---|
| 875 |
|
|---|
| 876 |
libbuf->write(header, size + 5); |
|---|
| 877 |
//libbuf->write(om->base, om->length - (size + 5)); |
|---|
| 878 |
libbuf->write(om->base, om->length); |
|---|
| 879 |
} |
|---|
| 880 |
else |
|---|
| 881 |
libbuf->write(om->base, om->length); |
|---|
| 882 |
|
|---|
| 883 |
// Round the size of the file up to the next page size |
|---|
| 884 |
// by filling with 0s |
|---|
| 885 |
unsigned n = (g_page_size - 1) & libbuf->offset; |
|---|
| 886 |
if (n) |
|---|
| 887 |
libbuf->fill0(g_page_size - n); |
|---|
| 888 |
} |
|---|
| 889 |
|
|---|
| 890 |
// File offset of start of dictionary |
|---|
| 891 |
unsigned offset = libbuf->offset; |
|---|
| 892 |
|
|---|
| 893 |
// Write dictionary header, then round it to a BUCKETPAGE boundary |
|---|
| 894 |
unsigned short size = (BUCKETPAGE - ((short)offset + 3)) & (BUCKETPAGE - 1); |
|---|
| 895 |
libbuf->writeByte(0xF1); |
|---|
| 896 |
libbuf->writeword(size); |
|---|
| 897 |
libbuf->fill0(size); |
|---|
| 898 |
|
|---|
| 899 |
// Create dictionary |
|---|
| 900 |
unsigned char *bucketsP = NULL; |
|---|
| 901 |
unsigned short ndicpages; |
|---|
| 902 |
unsigned short padding = 32; |
|---|
| 903 |
for (;;) |
|---|
| 904 |
{ |
|---|
| 905 |
ndicpages = numDictPages(padding); |
|---|
| 906 |
|
|---|
| 907 |
#if LOG |
|---|
| 908 |
printf("ndicpages = %d\n", ndicpages); |
|---|
| 909 |
#endif |
|---|
| 910 |
// Allocate dictionary |
|---|
| 911 |
if (bucketsP) |
|---|
| 912 |
bucketsP = (unsigned char *)realloc(bucketsP, ndicpages * BUCKETPAGE); |
|---|
| 913 |
else |
|---|
| 914 |
bucketsP = (unsigned char *)malloc(ndicpages * BUCKETPAGE); |
|---|
| 915 |
assert(bucketsP); |
|---|
| 916 |
memset(bucketsP, 0, ndicpages * BUCKETPAGE); |
|---|
| 917 |
for (unsigned u = 0; u < ndicpages; u++) |
|---|
| 918 |
{ |
|---|
| 919 |
// 'next available' slot |
|---|
| 920 |
bucketsP[u * BUCKETPAGE + HASHMOD] = (HASHMOD + 1) >> 1; |
|---|
| 921 |
} |
|---|
| 922 |
|
|---|
| 923 |
if (FillDict(bucketsP, ndicpages)) |
|---|
| 924 |
break; |
|---|
| 925 |
padding += 16; // try again with more margins |
|---|
| 926 |
} |
|---|
| 927 |
|
|---|
| 928 |
// Write dictionary |
|---|
| 929 |
libbuf->write(bucketsP, ndicpages * BUCKETPAGE); |
|---|
| 930 |
if (bucketsP) |
|---|
| 931 |
free(bucketsP); |
|---|
| 932 |
|
|---|
| 933 |
// Create library header |
|---|
| 934 |
#pragma pack(1) |
|---|
| 935 |
struct Libheader |
|---|
| 936 |
{ |
|---|
| 937 |
unsigned char recTyp; |
|---|
| 938 |
unsigned short recLen; |
|---|
| 939 |
long trailerPosn; |
|---|
| 940 |
unsigned short ndicpages; |
|---|
| 941 |
unsigned char flags; |
|---|
| 942 |
char filler[ 6 ]; |
|---|
| 943 |
}; |
|---|
| 944 |
#pragma pack() |
|---|
| 945 |
|
|---|
| 946 |
Libheader libHeader; |
|---|
| 947 |
memset(&libHeader, 0, sizeof(Libheader)); |
|---|
| 948 |
libHeader.recTyp = 0xF0; |
|---|
| 949 |
libHeader.recLen = 0x0D; |
|---|
| 950 |
libHeader.trailerPosn = offset + (3 + size); |
|---|
| 951 |
libHeader.recLen = g_page_size - 3; |
|---|
| 952 |
libHeader.ndicpages = ndicpages; |
|---|
| 953 |
libHeader.flags = 1; // always case sensitive |
|---|
| 954 |
|
|---|
| 955 |
// Write library header at start of buffer |
|---|
| 956 |
memcpy(libbuf->data, &libHeader, sizeof(libHeader)); |
|---|
| 957 |
} |
|---|