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