1 /** 2 * A library in the COFF format, used on 32-bit and 64-bit Windows targets. 3 * 4 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/libmscoff.d, _libmscoff.d) 8 * Documentation: https://dlang.org/phobos/dmd_libmscoff.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/libmscoff.d 10 */ 11 12 module dmd.libmscoff; 13 14 import core.stdc.stdlib; 15 import core.stdc.string; 16 import core.stdc.time; 17 import core.stdc.stdio; 18 import core.stdc.string; 19 20 version (Posix) 21 { 22 import core.sys.posix.sys.stat; 23 import core.sys.posix.unistd; 24 } 25 version (Windows) 26 { 27 import core.sys.windows.stat; 28 } 29 30 import dmd.globals; 31 import dmd.lib; 32 import dmd.location; 33 import dmd.utils; 34 35 import dmd.root.array; 36 import dmd.root.file; 37 import dmd.root.filename; 38 import dmd.common.outbuffer; 39 import dmd.root.port; 40 import dmd.root.rmem; 41 import dmd.root.string; 42 import dmd.root.stringtable; 43 44 import dmd.scanmscoff; 45 46 // Entry point (only public symbol in this module). 47 public extern (C++) Library LibMSCoff_factory() 48 { 49 return new LibMSCoff(); 50 } 51 52 private: // for the remainder of this module 53 54 enum LOG = false; 55 56 struct MSCoffObjSymbol 57 { 58 const(char)[] name; // still has a terminating 0 59 MSCoffObjModule* om; 60 61 /// Predicate for `Array.sort`for name comparison 62 static int name_pred (scope const MSCoffObjSymbol** ppe1, scope const MSCoffObjSymbol** ppe2) nothrow @nogc pure 63 { 64 return dstrcmp((**ppe1).name, (**ppe2).name); 65 } 66 67 /// Predicate for `Array.sort`for offset comparison 68 static int offset_pred (scope const MSCoffObjSymbol** ppe1, scope const MSCoffObjSymbol** ppe2) nothrow @nogc pure 69 { 70 return (**ppe1).om.offset - (**ppe2).om.offset; 71 } 72 } 73 74 alias MSCoffObjModules = Array!(MSCoffObjModule*); 75 alias MSCoffObjSymbols = Array!(MSCoffObjSymbol*); 76 77 final class LibMSCoff : Library 78 { 79 MSCoffObjModules objmodules; // MSCoffObjModule[] 80 MSCoffObjSymbols objsymbols; // MSCoffObjSymbol[] 81 82 /*************************************** 83 * Add object module or library to the library. 84 * Examine the buffer to see which it is. 85 * If the buffer is NULL, use module_name as the file name 86 * and load the file. 87 */ 88 override void addObject(const(char)[] module_name, const ubyte[] buffer) 89 { 90 static if (LOG) 91 { 92 printf("LibMSCoff::addObject(%.*s)\n", cast(int)module_name.length, 93 module_name.ptr); 94 } 95 96 void corrupt(int reason) 97 { 98 error("corrupt MS Coff object module %.*s %d", 99 cast(int)module_name.length, module_name.ptr, reason); 100 } 101 102 int fromfile = 0; 103 auto buf = buffer.ptr; 104 auto buflen = buffer.length; 105 if (!buf) 106 { 107 assert(module_name.length, "No module nor buffer provided to `addObject`"); 108 // read file and take buffer ownership 109 auto data = readFile(Loc.initial, module_name).extractSlice(); 110 buf = data.ptr; 111 buflen = data.length; 112 fromfile = 1; 113 } 114 if (buflen < 16) 115 { 116 static if (LOG) 117 { 118 printf("buf = %p, buflen = %d\n", buf, buflen); 119 } 120 return corrupt(__LINE__); 121 } 122 if (memcmp(buf, "!<arch>\n".ptr, 8) == 0) 123 { 124 /* It's a library file. 125 * Pull each object module out of the library and add it 126 * to the object module array. 127 */ 128 static if (LOG) 129 { 130 printf("archive, buf = %p, buflen = %d\n", buf, buflen); 131 } 132 MSCoffLibHeader* flm = null; // first linker member 133 MSCoffLibHeader* slm = null; // second linker member 134 uint number_of_members = 0; 135 uint* member_file_offsets = null; 136 uint number_of_symbols = 0; 137 ushort* indices = null; 138 char* string_table = null; 139 size_t string_table_length = 0; 140 MSCoffLibHeader* lnm = null; // longname member 141 char* longnames = null; 142 size_t longnames_length = 0; 143 size_t offset = 8; 144 size_t mstart = objmodules.length; 145 while (1) 146 { 147 offset = (offset + 1) & ~1; // round to even boundary 148 if (offset >= buflen) 149 break; 150 if (offset + MSCoffLibHeader.sizeof >= buflen) 151 return corrupt(__LINE__); 152 MSCoffLibHeader* header = cast(MSCoffLibHeader*)(cast(ubyte*)buf + offset); 153 offset += MSCoffLibHeader.sizeof; 154 char* endptr = null; 155 uint size = cast(uint)strtoul(cast(char*)header.file_size, &endptr, 10); 156 if (endptr >= header.file_size.ptr + 10 || *endptr != ' ') 157 return corrupt(__LINE__); 158 if (offset + size > buflen) 159 return corrupt(__LINE__); 160 //printf("header.object_name = '%.*s'\n", cast(int)MSCOFF_OBJECT_NAME_SIZE, header.object_name.ptr); 161 if (memcmp(cast(char*)header.object_name, cast(char*)"/ ", MSCOFF_OBJECT_NAME_SIZE) == 0) 162 { 163 if (!flm) 164 { 165 // First Linker Member, which is ignored 166 flm = header; 167 } 168 else if (!slm) 169 { 170 // Second Linker Member, which we require even though the format doesn't require it 171 slm = header; 172 if (size < 4 + 4) 173 return corrupt(__LINE__); 174 number_of_members = Port.readlongLE(cast(char*)buf + offset); 175 member_file_offsets = cast(uint*)(cast(char*)buf + offset + 4); 176 if (size < 4 + number_of_members * 4 + 4) 177 return corrupt(__LINE__); 178 number_of_symbols = Port.readlongLE(cast(char*)buf + offset + 4 + number_of_members * 4); 179 indices = cast(ushort*)(cast(char*)buf + offset + 4 + number_of_members * 4 + 4); 180 string_table = cast(char*)(cast(char*)buf + offset + 4 + number_of_members * 4 + 4 + number_of_symbols * 2); 181 if (size <= (4 + number_of_members * 4 + 4 + number_of_symbols * 2)) 182 return corrupt(__LINE__); 183 string_table_length = size - (4 + number_of_members * 4 + 4 + number_of_symbols * 2); 184 /* The number of strings in the string_table must be number_of_symbols; check it 185 * The strings must also be in ascending lexical order; not checked. 186 */ 187 size_t i = 0; 188 for (uint n = 0; n < number_of_symbols; n++) 189 { 190 while (1) 191 { 192 if (i >= string_table_length) 193 return corrupt(__LINE__); 194 if (!string_table[i++]) 195 break; 196 } 197 } 198 if (i != string_table_length) 199 return corrupt(__LINE__); 200 } 201 } 202 else if (memcmp(cast(char*)header.object_name, cast(char*)"// ", MSCOFF_OBJECT_NAME_SIZE) == 0) 203 { 204 if (!lnm) 205 { 206 lnm = header; 207 longnames = cast(char*)buf + offset; 208 longnames_length = size; 209 } 210 } 211 else 212 { 213 if (!slm) 214 return corrupt(__LINE__); 215 version (none) 216 { 217 // Microsoft Spec says longnames member must appear, but Microsoft Lib says otherwise 218 if (!lnm) 219 return corrupt(__LINE__); 220 } 221 auto om = new MSCoffObjModule(); 222 // Include MSCoffLibHeader in base[0..length], so we don't have to repro it 223 om.base = cast(ubyte*)buf + offset - MSCoffLibHeader.sizeof; 224 om.length = cast(uint)(size + MSCoffLibHeader.sizeof); 225 om.offset = 0; 226 if (header.object_name[0] == '/') 227 { 228 /* Pick long name out of longnames[] 229 */ 230 uint foff = cast(uint)strtoul(cast(char*)header.object_name + 1, &endptr, 10); 231 uint i; 232 for (i = 0; 1; i++) 233 { 234 if (foff + i >= longnames_length) 235 return corrupt(__LINE__); 236 char c = longnames[foff + i]; 237 if (c == 0) 238 break; 239 } 240 char* oname = cast(char*)Mem.check(malloc(i + 1)); 241 memcpy(oname, longnames + foff, i); 242 oname[i] = 0; 243 om.name = oname[0 .. i]; 244 //printf("\tname = '%s'\n", om.name.ptr); 245 } 246 else 247 { 248 /* Pick short name out of header 249 */ 250 char* oname = cast(char*)Mem.check(malloc(MSCOFF_OBJECT_NAME_SIZE)); 251 int i; 252 for (i = 0; 1; i++) 253 { 254 if (i == MSCOFF_OBJECT_NAME_SIZE) 255 return corrupt(__LINE__); 256 char c = header.object_name[i]; 257 if (c == '/') 258 { 259 oname[i] = 0; 260 break; 261 } 262 oname[i] = c; 263 } 264 om.name = oname[0 .. i]; 265 } 266 om.file_time = strtoul(cast(char*)header.file_time, &endptr, 10); 267 om.user_id = cast(uint)strtoul(cast(char*)header.user_id, &endptr, 10); 268 om.group_id = cast(uint)strtoul(cast(char*)header.group_id, &endptr, 10); 269 om.file_mode = cast(uint)strtoul(cast(char*)header.file_mode, &endptr, 8); 270 om.scan = 0; // don't scan object module for symbols 271 objmodules.push(om); 272 } 273 offset += size; 274 } 275 if (offset != buflen) 276 return corrupt(__LINE__); 277 /* Scan the library's symbol table, and insert it into our own. 278 * We use this instead of rescanning the object module, because 279 * the library's creator may have a different idea of what symbols 280 * go into the symbol table than we do. 281 * This is also probably faster. 282 */ 283 if (!slm) 284 return corrupt(__LINE__); 285 char* s = string_table; 286 for (uint i = 0; i < number_of_symbols; i++) 287 { 288 const(char)[] name = s.toDString(); 289 s += name.length + 1; 290 uint memi = indices[i] - 1; 291 if (memi >= number_of_members) 292 return corrupt(__LINE__); 293 uint moff = member_file_offsets[memi]; 294 for (size_t m = mstart; 1; m++) 295 { 296 if (m == objmodules.length) 297 return corrupt(__LINE__); // didn't find it 298 MSCoffObjModule* om = objmodules[m]; 299 //printf("\tom offset = x%x\n", cast(char *)om.base - cast(char *)buf); 300 if (moff == cast(char*)om.base - cast(char*)buf) 301 { 302 addSymbol(om, name, 1); 303 //if (mstart == m) 304 // mstart++; 305 break; 306 } 307 } 308 } 309 return; 310 } 311 /* It's an object module 312 */ 313 auto om = new MSCoffObjModule(); 314 om.base = cast(ubyte*)buf; 315 om.length = cast(uint)buflen; 316 om.offset = 0; 317 // remove path, but not extension 318 om.name = global.params.preservePaths ? module_name : FileName.name(module_name); 319 om.scan = 1; 320 if (fromfile) 321 { 322 version (Posix) 323 stat_t statbuf; 324 version (Windows) 325 struct_stat statbuf; 326 int i = module_name.toCStringThen!(name => stat(name.ptr, &statbuf)); 327 if (i == -1) // error, errno is set 328 return corrupt(__LINE__); 329 om.file_time = statbuf.st_ctime; 330 om.user_id = statbuf.st_uid; 331 om.group_id = statbuf.st_gid; 332 om.file_mode = statbuf.st_mode; 333 } 334 else 335 { 336 /* Mock things up for the object module file that never was 337 * actually written out. 338 */ 339 version (Posix) 340 { 341 __gshared uid_t uid; 342 __gshared gid_t gid; 343 __gshared int _init; 344 if (!_init) 345 { 346 _init = 1; 347 uid = getuid(); 348 gid = getgid(); 349 } 350 om.user_id = uid; 351 om.group_id = gid; 352 } 353 version (Windows) 354 { 355 om.user_id = 0; // meaningless on Windows 356 om.group_id = 0; // meaningless on Windows 357 } 358 time_t file_time = 0; 359 time(&file_time); 360 om.file_time = cast(long)file_time; 361 om.file_mode = (1 << 15) | (6 << 6) | (4 << 3) | (4 << 0); // 0100644 362 } 363 objmodules.push(om); 364 } 365 366 /*****************************************************************************/ 367 368 void addSymbol(MSCoffObjModule* om, const(char)[] name, int pickAny = 0) 369 { 370 static if (LOG) 371 { 372 printf("LibMSCoff::addSymbol(%s, %s, %d)\n", om.name.ptr, name, pickAny); 373 } 374 auto os = new MSCoffObjSymbol(); 375 os.name = xarraydup(name); 376 os.om = om; 377 objsymbols.push(os); 378 } 379 380 private: 381 /************************************ 382 * Scan single object module for dictionary symbols. 383 * Send those symbols to LibMSCoff::addSymbol(). 384 */ 385 void scanObjModule(MSCoffObjModule* om) 386 { 387 static if (LOG) 388 { 389 printf("LibMSCoff::scanObjModule(%s)\n", om.name.ptr); 390 } 391 392 extern (D) void addSymbol(const(char)[] name, int pickAny) 393 { 394 this.addSymbol(om, name, pickAny); 395 } 396 397 scanMSCoffObjModule(&addSymbol, om.base[0 .. om.length], om.name.ptr, loc); 398 } 399 400 /*****************************************************************************/ 401 /*****************************************************************************/ 402 /********************************************** 403 * Create and write library to libbuf. 404 * The library consists of: 405 * !<arch>\n 406 * header 407 * 1st Linker Member 408 * Header 409 * 2nd Linker Member 410 * Header 411 * Longnames Member 412 * object modules... 413 */ 414 protected override void WriteLibToBuffer(OutBuffer* libbuf) 415 { 416 static if (LOG) 417 { 418 printf("LibElf::WriteLibToBuffer()\n"); 419 } 420 assert(MSCoffLibHeader.sizeof == 60); 421 /************* Scan Object Modules for Symbols ******************/ 422 for (size_t i = 0; i < objmodules.length; i++) 423 { 424 MSCoffObjModule* om = objmodules[i]; 425 if (om.scan) 426 { 427 scanObjModule(om); 428 } 429 } 430 /************* Determine longnames size ******************/ 431 /* The longnames section is where we store long file names. 432 */ 433 uint noffset = 0; 434 for (size_t i = 0; i < objmodules.length; i++) 435 { 436 MSCoffObjModule* om = objmodules[i]; 437 size_t len = om.name.length; 438 if (len >= MSCOFF_OBJECT_NAME_SIZE) 439 { 440 om.name_offset = noffset; 441 noffset += len + 1; 442 } 443 else 444 om.name_offset = -1; 445 } 446 static if (LOG) 447 { 448 printf("\tnoffset = x%x\n", noffset); 449 } 450 /************* Determine string table length ******************/ 451 size_t slength = 0; 452 for (size_t i = 0; i < objsymbols.length; i++) 453 { 454 MSCoffObjSymbol* os = objsymbols[i]; 455 slength += os.name.length + 1; 456 } 457 /************* Offset of first module ***********************/ 458 size_t moffset = 8; // signature 459 size_t firstLinkerMemberOffset = moffset; 460 moffset += MSCoffLibHeader.sizeof + 4 + objsymbols.length * 4 + slength; // 1st Linker Member 461 moffset += moffset & 1; 462 size_t secondLinkerMemberOffset = moffset; 463 moffset += MSCoffLibHeader.sizeof + 4 + objmodules.length * 4 + 4 + objsymbols.length * 2 + slength; 464 moffset += moffset & 1; 465 size_t LongnamesMemberOffset = moffset; 466 moffset += MSCoffLibHeader.sizeof + noffset; // Longnames Member size 467 static if (LOG) 468 { 469 printf("\tmoffset = x%x\n", moffset); 470 } 471 /************* Offset of each module *************************/ 472 for (size_t i = 0; i < objmodules.length; i++) 473 { 474 MSCoffObjModule* om = objmodules[i]; 475 moffset += moffset & 1; 476 om.offset = cast(uint)moffset; 477 if (om.scan) 478 moffset += MSCoffLibHeader.sizeof + om.length; 479 else 480 moffset += om.length; 481 } 482 libbuf.reserve(moffset); 483 /************* Write the library ******************/ 484 libbuf.write("!<arch>\n"); 485 MSCoffObjModule om; 486 om.name_offset = -1; 487 om.base = null; 488 om.length = cast(uint)(4 + objsymbols.length * 4 + slength); 489 om.offset = 8; 490 om.name = ""; 491 time_t file_time = 0; 492 .time(&file_time); 493 om.file_time = cast(long)file_time; 494 om.user_id = 0; 495 om.group_id = 0; 496 om.file_mode = 0; 497 /*** Write out First Linker Member ***/ 498 assert(libbuf.length == firstLinkerMemberOffset); 499 MSCoffLibHeader h; 500 MSCoffOmToHeader(&h, &om); 501 libbuf.write((&h)[0 .. 1]); 502 char[4] buf; 503 Port.writelongBE(cast(uint)objsymbols.length, buf.ptr); 504 libbuf.write(buf[0 .. 4]); 505 // Sort objsymbols[] in module offset order 506 objsymbols.sort!(MSCoffObjSymbol.offset_pred); 507 uint lastoffset; 508 for (size_t i = 0; i < objsymbols.length; i++) 509 { 510 MSCoffObjSymbol* os = objsymbols[i]; 511 //printf("objsymbols[%d] = '%s', offset = %u\n", cast(int) i, os.name.ptr, os.om.offset); 512 if (i) 513 { 514 // Should be sorted in module order 515 assert(lastoffset <= os.om.offset); 516 } 517 lastoffset = os.om.offset; 518 Port.writelongBE(lastoffset, buf.ptr); 519 libbuf.write(buf[0 .. 4]); 520 } 521 for (size_t i = 0; i < objsymbols.length; i++) 522 { 523 MSCoffObjSymbol* os = objsymbols[i]; 524 libbuf.writestring(os.name); 525 libbuf.writeByte(0); 526 } 527 /*** Write out Second Linker Member ***/ 528 if (libbuf.length & 1) 529 libbuf.writeByte('\n'); 530 assert(libbuf.length == secondLinkerMemberOffset); 531 om.length = cast(uint)(4 + objmodules.length * 4 + 4 + objsymbols.length * 2 + slength); 532 MSCoffOmToHeader(&h, &om); 533 libbuf.write((&h)[0 .. 1]); 534 Port.writelongLE(cast(uint)objmodules.length, buf.ptr); 535 libbuf.write(buf[0 .. 4]); 536 for (size_t i = 0; i < objmodules.length; i++) 537 { 538 MSCoffObjModule* om2 = objmodules[i]; 539 om2.index = cast(ushort)i; 540 Port.writelongLE(om2.offset, buf.ptr); 541 libbuf.write(buf[0 .. 4]); 542 } 543 Port.writelongLE(cast(uint)objsymbols.length, buf.ptr); 544 libbuf.write(buf[0 .. 4]); 545 // Sort objsymbols[] in lexical order 546 objsymbols.sort!(MSCoffObjSymbol.name_pred); 547 for (size_t i = 0; i < objsymbols.length; i++) 548 { 549 MSCoffObjSymbol* os = objsymbols[i]; 550 Port.writelongLE(os.om.index + 1, buf.ptr); 551 libbuf.write(buf[0 .. 2]); 552 } 553 for (size_t i = 0; i < objsymbols.length; i++) 554 { 555 MSCoffObjSymbol* os = objsymbols[i]; 556 libbuf.writestring(os.name); 557 libbuf.writeByte(0); 558 } 559 /*** Write out longnames Member ***/ 560 if (libbuf.length & 1) 561 libbuf.writeByte('\n'); 562 //printf("libbuf %x longnames %x\n", cast(int)libbuf.length, cast(int)LongnamesMemberOffset); 563 assert(libbuf.length == LongnamesMemberOffset); 564 // header 565 memset(&h, ' ', MSCoffLibHeader.sizeof); 566 h.object_name[0] = '/'; 567 h.object_name[1] = '/'; 568 size_t len = snprintf(h.file_size.ptr, MSCOFF_FILE_SIZE_SIZE, "%u", noffset); 569 assert(len < 10); 570 h.file_size[len] = ' '; 571 h.trailer[0] = '`'; 572 h.trailer[1] = '\n'; 573 libbuf.write((&h)[0 .. 1]); 574 for (size_t i = 0; i < objmodules.length; i++) 575 { 576 MSCoffObjModule* om2 = objmodules[i]; 577 if (om2.name_offset >= 0) 578 { 579 libbuf.writestring(om2.name); 580 libbuf.writeByte(0); 581 } 582 } 583 /* Write out each of the object modules 584 */ 585 for (size_t i = 0; i < objmodules.length; i++) 586 { 587 MSCoffObjModule* om2 = objmodules[i]; 588 if (libbuf.length & 1) 589 libbuf.writeByte('\n'); // module alignment 590 //printf("libbuf %x om %x\n", cast(int)libbuf.length, cast(int)om2.offset); 591 assert(libbuf.length == om2.offset); 592 if (om2.scan) 593 { 594 MSCoffOmToHeader(&h, om2); 595 libbuf.write((&h)[0 .. 1]); // module header 596 libbuf.write(om2.base[0 .. om2.length]); // module contents 597 } 598 else 599 { 600 // Header is included in om.base[0..length] 601 libbuf.write(om2.base[0 .. om2.length]); // module contents 602 } 603 } 604 static if (LOG) 605 { 606 printf("moffset = x%x, libbuf.length = x%x\n", cast(uint)moffset, cast(uint)libbuf.length); 607 } 608 assert(libbuf.length == moffset); 609 } 610 } 611 612 /*****************************************************************************/ 613 /*****************************************************************************/ 614 struct MSCoffObjModule 615 { 616 ubyte* base; // where are we holding it in memory 617 uint length; // in bytes 618 uint offset; // offset from start of library 619 ushort index; // index in Second Linker Member 620 const(char)[] name; // module name (file name) terminated with 0 621 int name_offset; // if not -1, offset into string table of name 622 long file_time; // file time 623 uint user_id; 624 uint group_id; 625 uint file_mode; 626 int scan; // 1 means scan for symbols 627 } 628 629 enum MSCOFF_OBJECT_NAME_SIZE = 16; 630 enum MSCOFF_FILE_TIME_SIZE = 12; 631 enum MSCOFF_USER_ID_SIZE = 6; 632 enum MSCOFF_GROUP_ID_SIZE = 6; 633 enum MSCOFF_FILE_MODE_SIZE = 8; 634 enum MSCOFF_FILE_SIZE_SIZE = 10; 635 enum MSCOFF_TRAILER_SIZE = 2; 636 637 struct MSCoffLibHeader 638 { 639 char[MSCOFF_OBJECT_NAME_SIZE] object_name; 640 char[MSCOFF_FILE_TIME_SIZE] file_time; 641 char[MSCOFF_USER_ID_SIZE] user_id; 642 char[MSCOFF_GROUP_ID_SIZE] group_id; 643 char[MSCOFF_FILE_MODE_SIZE] file_mode; // in octal 644 char[MSCOFF_FILE_SIZE_SIZE] file_size; 645 char[MSCOFF_TRAILER_SIZE] trailer; 646 } 647 648 extern (C++) void MSCoffOmToHeader(MSCoffLibHeader* h, MSCoffObjModule* om) 649 { 650 size_t len; 651 if (om.name_offset == -1) 652 { 653 len = om.name.length; 654 memcpy(h.object_name.ptr, om.name.ptr, len); 655 h.object_name[len] = '/'; 656 } 657 else 658 { 659 len = snprintf(h.object_name.ptr, MSCOFF_OBJECT_NAME_SIZE, "/%d", om.name_offset); 660 h.object_name[len] = ' '; 661 } 662 assert(len < MSCOFF_OBJECT_NAME_SIZE); 663 memset(h.object_name.ptr + len + 1, ' ', MSCOFF_OBJECT_NAME_SIZE - (len + 1)); 664 len = snprintf(h.file_time.ptr, MSCOFF_FILE_TIME_SIZE, "%llu", cast(long)om.file_time); 665 assert(len <= 12); 666 memset(h.file_time.ptr + len, ' ', 12 - len); 667 // Match what MS tools do (set to all blanks) 668 memset(h.user_id.ptr, ' ', (h.user_id).sizeof); 669 memset(h.group_id.ptr, ' ', (h.group_id).sizeof); 670 len = snprintf(h.file_mode.ptr, MSCOFF_FILE_MODE_SIZE, "%o", om.file_mode); 671 assert(len <= 8); 672 memset(h.file_mode.ptr + len, ' ', 8 - len); 673 len = snprintf(h.file_size.ptr, MSCOFF_FILE_SIZE_SIZE, "%u", om.length); 674 assert(len <= 10); 675 memset(h.file_size.ptr + len, ' ', 10 - len); 676 h.trailer[0] = '`'; 677 h.trailer[1] = '\n'; 678 }