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 }