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