1/* ----------------------------------------------------------------------- *
2 *
3 * Copyright 1996-2017 The NASM Authors - All Rights Reserved
4 * See the file AUTHORS included with the NASM distribution for
5 * the specific copyright holders.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following
9 * conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * ----------------------------------------------------------------------- */
33
34/*
35 * codeview.c Codeview Debug Format support for COFF
36 */
37
38#include "version.h"
39#include "compiler.h"
40
41#include <stdio.h>
42#include <stddef.h>
43#include <stdlib.h>
44
45#include "nasm.h"
46#include "nasmlib.h"
47#include "error.h"
48#include "preproc.h"
49#include "saa.h"
50#include "hashtbl.h"
51#include "outlib.h"
52#include "pecoff.h"
53#include "md5.h"
54
55static void cv8_init(void);
56static void cv8_linenum(const char *filename, int32_t linenumber,
57 int32_t segto);
58static void cv8_deflabel(char *name, int32_t segment, int64_t offset,
59 int is_global, char *special);
60static void cv8_typevalue(int32_t type);
61static void cv8_output(int type, void *param);
62static void cv8_cleanup(void);
63
64const struct dfmt df_cv8 = {
65 "Codeview 8", /* .fullname */
66 "cv8", /* .shortname */
67 cv8_init, /* .init */
68 cv8_linenum, /* .linenum */
69 cv8_deflabel, /* .debug_deflabel */
70 null_debug_directive, /* .debug_directive */
71 cv8_typevalue, /* .debug_typevalue */
72 cv8_output, /* .debug_output */
73 cv8_cleanup, /* .cleanup */
74 NULL /* pragma list */
75};
76
77/*******************************************************************************
78 * dfmt callbacks
79 ******************************************************************************/
80struct source_file;
81
82struct source_file {
83 const char *filename;
84 char *fullname;
85 uint32_t fullnamelen;
86
87 struct source_file *next;
88
89 uint32_t filetbl_off;
90 uint32_t sourcetbl_off;
91
92 struct SAA *lines;
93 uint32_t num_lines;
94
95 unsigned char md5sum[MD5_HASHBYTES];
96};
97
98struct linepair {
99 uint32_t file_offset;
100 uint32_t linenumber;
101};
102
103enum symbol_type {
104 SYMTYPE_CODE,
105 SYMTYPE_PROC,
106 SYMTYPE_LDATA,
107 SYMTYPE_GDATA,
108
109 SYMTYPE_MAX
110};
111
112struct cv8_symbol {
113 enum symbol_type type;
114 char *name;
115
116 uint32_t secrel;
117 uint16_t section;
118 uint32_t size;
119 uint32_t typeindex;
120
121 enum symtype {
122 TYPE_UNREGISTERED = 0x0000, /* T_NOTYPE */
123 TYPE_BYTE = 0x0020,
124 TYPE_WORD = 0x0021,
125 TYPE_DWORD= 0x0022,
126 TYPE_QUAD = 0x0023,
127
128 TYPE_REAL32 = 0x0040,
129 TYPE_REAL64 = 0x0041,
130 TYPE_REAL80 = 0x0042,
131 TYPE_REAL128= 0x0043,
132 TYPE_REAL256= 0x0044,
133 TYPE_REAL512= 0x0045
134 } symtype;
135};
136
137struct cv8_state {
138 int symbol_sect;
139 int type_sect;
140
141 uint32_t text_offset;
142
143 struct source_file *source_files, **source_files_tail;
144 const char *last_filename;
145 struct source_file *last_source_file;
146 struct hash_table file_hash;
147 unsigned num_files;
148 uint32_t total_filename_len;
149
150
151 unsigned total_lines;
152
153 struct SAA *symbols;
154 struct cv8_symbol *last_sym;
155 unsigned num_syms[SYMTYPE_MAX];
156 unsigned symbol_lengths;
157 unsigned total_syms;
158
159 struct {
160 char *name;
161 size_t namebytes;
162 } outfile;
163};
164struct cv8_state cv8_state;
165
166static void cv8_init(void)
167{
168 const uint32_t sect_flags = IMAGE_SCN_MEM_READ |
169 IMAGE_SCN_MEM_DISCARDABLE |
170 IMAGE_SCN_CNT_INITIALIZED_DATA |
171 IMAGE_SCN_ALIGN_1BYTES;
172
173 cv8_state.symbol_sect = coff_make_section(".debug$S", sect_flags);
174 cv8_state.type_sect = coff_make_section(".debug$T", sect_flags);
175
176 cv8_state.text_offset = 0;
177
178 cv8_state.source_files = NULL;
179 cv8_state.source_files_tail = &cv8_state.source_files;
180 hash_init(&cv8_state.file_hash, HASH_MEDIUM);
181
182 cv8_state.num_files = 0;
183 cv8_state.total_filename_len = 0;
184
185 cv8_state.total_lines = 0;
186
187 cv8_state.symbols = saa_init(sizeof(struct cv8_symbol));
188 cv8_state.last_sym = NULL;
189}
190
191static struct source_file *register_file(const char *filename);
192static struct coff_Section *find_section(int32_t segto);
193
194static void cv8_linenum(const char *filename, int32_t linenumber,
195 int32_t segto)
196{
197 struct coff_Section *s;
198 struct linepair *li;
199 struct source_file *file;
200
201 file = register_file(filename);
202
203 s = find_section(segto);
204 if (s == NULL)
205 return;
206
207 if ((s->flags & IMAGE_SCN_MEM_EXECUTE) == 0)
208 return;
209
210 li = saa_wstruct(file->lines);
211 li->file_offset = cv8_state.text_offset;
212 li->linenumber = linenumber;
213
214 file->num_lines++;
215 cv8_state.total_lines++;
216}
217
218static void cv8_deflabel(char *name, int32_t segment, int64_t offset,
219 int is_global, char *special)
220{
221 struct cv8_symbol *sym;
222 struct coff_Section *s;
223
224 (void)special;
225
226 s = find_section(segment);
227 if (s == NULL)
228 return;
229
230 sym = saa_wstruct(cv8_state.symbols);
231
232 if (s->flags & IMAGE_SCN_MEM_EXECUTE)
233 sym->type = is_global ? SYMTYPE_PROC : SYMTYPE_CODE;
234 else
235 sym->type = is_global ? SYMTYPE_GDATA : SYMTYPE_LDATA;
236 cv8_state.num_syms[sym->type]++;
237 cv8_state.total_syms++;
238
239 sym->section = segment;
240 sym->secrel = offset;
241 sym->symtype = TYPE_UNREGISTERED;
242 sym->size = 0;
243 sym->typeindex = 0;
244
245 sym->name = nasm_strdup(name);
246 cv8_state.symbol_lengths += strlen(sym->name) + 1;
247
248 if (cv8_state.last_sym && cv8_state.last_sym->section == segment)
249 cv8_state.last_sym->size = offset - cv8_state.last_sym->secrel;
250 cv8_state.last_sym = sym;
251}
252
253static void cv8_typevalue(int32_t type)
254{
255 if (!cv8_state.last_sym)
256 return;
257 if (cv8_state.last_sym->symtype != TYPE_UNREGISTERED)
258 return;
259
260 switch (TYM_TYPE(type)) {
261 case TY_BYTE:
262 cv8_state.last_sym->symtype = TYPE_BYTE;
263 break;
264 case TY_WORD:
265 cv8_state.last_sym->symtype = TYPE_WORD;
266 break;
267 case TY_DWORD:
268 cv8_state.last_sym->symtype = TYPE_DWORD;
269 break;
270 case TY_QWORD:
271 cv8_state.last_sym->symtype = TYPE_QUAD;
272 break;
273 case TY_FLOAT:
274 cv8_state.last_sym->symtype = TYPE_REAL32;
275 break;
276 case TY_TBYTE:
277 cv8_state.last_sym->symtype = TYPE_REAL80;
278 break;
279 case TY_OWORD:
280 cv8_state.last_sym->symtype = TYPE_REAL128;
281 break;
282 case TY_YWORD:
283 cv8_state.last_sym->symtype = TYPE_REAL256;
284 break;
285 case TY_ZWORD:
286 cv8_state.last_sym->symtype = TYPE_REAL512;
287 break;
288 case TY_UNKNOWN:
289 break;
290 case TY_LABEL:
291 break;
292 }
293}
294
295static void cv8_output(int type, void *param)
296{
297 struct coff_DebugInfo *dinfo = param;
298
299 (void)type;
300
301 if (dinfo->section && dinfo->section->name &&
302 !strncmp(dinfo->section->name, ".text", 5))
303 cv8_state.text_offset += dinfo->size;
304}
305
306static void build_symbol_table(struct coff_Section *const sect);
307static void build_type_table(struct coff_Section *const sect);
308
309static void cv8_cleanup(void)
310{
311 struct cv8_symbol *sym;
312 struct source_file *file;
313
314 struct coff_Section *symbol_sect = coff_sects[cv8_state.symbol_sect];
315 struct coff_Section *type_sect = coff_sects[cv8_state.type_sect];
316
317 cv8_state.outfile.name = nasm_realpath(outname);
318 cv8_state.outfile.namebytes = strlen(cv8_state.outfile.name) + 1;
319
320 build_symbol_table(symbol_sect);
321 build_type_table(type_sect);
322
323 list_for_each(file, cv8_state.source_files) {
324 nasm_free(file->fullname);
325 saa_free(file->lines);
326 free(file);
327 }
328 hash_free(&cv8_state.file_hash);
329
330 saa_rewind(cv8_state.symbols);
331 while ((sym = saa_rstruct(cv8_state.symbols)))
332 nasm_free(sym->name);
333 saa_free(cv8_state.symbols);
334
335 nasm_free(cv8_state.outfile.name);
336}
337
338/*******************************************************************************
339 * implementation
340 ******************************************************************************/
341static void calc_md5(const char *const filename,
342 unsigned char sum[MD5_HASHBYTES])
343{
344 int success = 0;
345 unsigned char *file_buf;
346 FILE *f;
347 MD5_CTX ctx;
348
349 f = pp_input_fopen(filename, NF_BINARY);
350 if (!f)
351 goto done;
352
353 file_buf = nasm_zalloc(BUFSIZ);
354
355 MD5Init(&ctx);
356 while (!feof(f)) {
357 size_t i = fread(file_buf, 1, BUFSIZ, f);
358 if (ferror(f))
359 goto done_0;
360 else if (i == 0)
361 break;
362 MD5Update(&ctx, file_buf, i);
363 }
364 MD5Final(sum, &ctx);
365
366 success = 1;
367done_0:
368 nasm_free(file_buf);
369 fclose(f);
370done:
371 if (!success) {
372 nasm_error(ERR_NONFATAL, "unable to hash file %s. "
373 "Debug information may be unavailable.\n",
374 filename);
375 }
376 return;
377}
378
379static struct source_file *register_file(const char *filename)
380{
381 struct source_file *file;
382 void **filep;
383 char *fullpath;
384 struct hash_insert hi;
385
386 /*
387 * The common case is that we are invoked with the same filename
388 * as we were last time. Make this a pointer comparison: this is
389 * safe because the NASM core code allocates each filename once
390 * and never frees it.
391 */
392 if (likely(cv8_state.last_filename == filename))
393 return cv8_state.last_source_file;
394
395 cv8_state.last_filename = filename;
396
397 filep = hash_find(&cv8_state.file_hash, filename, &hi);
398 if (likely(filep)) {
399 file = *filep;
400 } else {
401 /* New filename encounter */
402
403 fullpath = nasm_realpath(filename);
404
405 file = nasm_zalloc(sizeof(*file));
406
407 file->filename = filename;
408 file->fullname = fullpath;
409 file->fullnamelen = strlen(fullpath);
410 file->lines = saa_init(sizeof(struct linepair));
411 *cv8_state.source_files_tail = file;
412 cv8_state.source_files_tail = &file->next;
413 calc_md5(fullpath, file->md5sum);
414
415 hash_add(&hi, filename, file);
416
417 cv8_state.num_files++;
418 cv8_state.total_filename_len += file->fullnamelen + 1;
419 }
420
421 cv8_state.last_source_file = file;
422 return file;
423}
424
425static struct coff_Section *find_section(int32_t segto)
426{
427 int i;
428
429 for (i = 0; i < coff_nsects; i++) {
430 struct coff_Section *sec;
431
432 sec = coff_sects[i];
433 if (segto == sec->index)
434 return sec;
435 }
436 return NULL;
437}
438
439static void register_reloc(struct coff_Section *const sect,
440 char *sym, uint32_t addr, uint16_t type)
441{
442 struct coff_Reloc *r;
443 struct coff_Section *sec;
444 uint32_t i;
445
446 r = *sect->tail = nasm_malloc(sizeof(struct coff_Reloc));
447 sect->tail = &r->next;
448 r->next = NULL;
449 sect->nrelocs++;
450
451 r->address = addr;
452 r->symbase = SECT_SYMBOLS;
453 r->type = type;
454
455 r->symbol = 0;
456 for (i = 0; i < (uint32_t)coff_nsects; i++) {
457 sec = coff_sects[i];
458 if (!strcmp(sym, sec->name)) {
459 return;
460 }
461 r->symbol += 2;
462 }
463
464 saa_rewind(coff_syms);
465 for (i = 0; i < coff_nsyms; i++) {
466 struct coff_Symbol *s = saa_rstruct(coff_syms);
467 r->symbol++;
468 if (s->strpos == -1 && !strcmp(sym, s->name)) {
469 return;
470 } else if (s->strpos != -1) {
471 int res;
472 char *symname;
473
474 symname = nasm_malloc(s->namlen + 1);
475 saa_fread(coff_strs, s->strpos-4, symname, s->namlen);
476 symname[s->namlen] = '\0';
477 res = strcmp(sym, symname);
478 nasm_free(symname);
479 if (!res)
480 return;
481 }
482 }
483 nasm_panic(0, "codeview: relocation for unregistered symbol: %s", sym);
484}
485
486static inline void section_write32(struct coff_Section *sect, uint32_t val)
487{
488 saa_write32(sect->data, val);
489 sect->len += 4;
490}
491
492static inline void section_write16(struct coff_Section *sect, uint16_t val)
493{
494 saa_write16(sect->data, val);
495 sect->len += 2;
496}
497
498static inline void section_write8(struct coff_Section *sect, uint8_t val)
499{
500 saa_write8(sect->data, val);
501 sect->len++;
502}
503
504static inline void section_wbytes(struct coff_Section *sect, const void *buf,
505 size_t len)
506{
507 saa_wbytes(sect->data, buf, len);
508 sect->len += len;
509}
510
511static void write_filename_table(struct coff_Section *const sect)
512{
513 uint32_t field_length;
514 uint32_t tbl_off = 1; /* offset starts at 1 to skip NULL entry */
515 struct source_file *file;
516
517 nasm_assert(cv8_state.source_files != NULL);
518 nasm_assert(cv8_state.num_files > 0);
519 nasm_assert(cv8_state.total_filename_len > 0);
520
521 field_length = 1 + cv8_state.total_filename_len;
522
523 section_write32(sect, 0x000000F3);
524 section_write32(sect, field_length);
525
526 section_write8(sect, 0);
527
528 list_for_each(file, cv8_state.source_files) {
529 section_wbytes(sect, file->fullname, file->fullnamelen + 1);
530 file->filetbl_off = tbl_off;
531 tbl_off += file->fullnamelen + 1;
532 }
533}
534
535static void write_sourcefile_table(struct coff_Section *const sect)
536{
537 const uint32_t entry_size = 4 + 2 + MD5_HASHBYTES + 2;
538
539 uint32_t field_length = 0;
540 uint32_t tbl_off = 0;
541 struct source_file *file;
542
543 field_length = entry_size * cv8_state.num_files;
544
545 section_write32(sect, 0x000000F4);
546 section_write32(sect, field_length);
547
548 list_for_each(file, cv8_state.source_files) {
549 nasm_assert(file->filetbl_off > 0);
550 section_write32(sect, file->filetbl_off);
551 section_write16(sect, 0x0110);
552 section_wbytes(sect, file->md5sum, MD5_HASHBYTES);
553 section_write16(sect, 0);
554
555 file->sourcetbl_off = tbl_off;
556 tbl_off += entry_size;
557 }
558}
559
560static void write_linenumber_table(struct coff_Section *const sect)
561{
562 const uint32_t file_field_len = 12;
563 const uint32_t line_field_len = 8;
564
565 int i;
566 uint32_t field_length = 0;
567 size_t field_base;
568 struct source_file *file;
569 struct coff_Section *s;
570
571 for (i = 0; i < coff_nsects; i++) {
572 if (!strncmp(coff_sects[i]->name, ".text", 5))
573 break;
574 }
575
576 if (i == coff_nsects)
577 return;
578 s = coff_sects[i];
579
580 field_length = 12;
581 field_length += (cv8_state.num_files * file_field_len);
582 field_length += (cv8_state.total_lines * line_field_len);
583
584 section_write32(sect, 0x000000F2);
585 section_write32(sect, field_length);
586
587 field_base = sect->len;
588 section_write32(sect, 0); /* SECREL, updated by relocation */
589 section_write16(sect, 0); /* SECTION, updated by relocation*/
590 section_write16(sect, 0); /* pad */
591 section_write32(sect, s->len);
592
593 register_reloc(sect, ".text", field_base,
594 win64 ? IMAGE_REL_AMD64_SECREL : IMAGE_REL_I386_SECREL);
595
596 register_reloc(sect, ".text", field_base + 4,
597 win64 ? IMAGE_REL_AMD64_SECTION : IMAGE_REL_I386_SECTION);
598
599 list_for_each(file, cv8_state.source_files) {
600 struct linepair *li;
601
602 /* source mapping */
603 section_write32(sect, file->sourcetbl_off);
604 section_write32(sect, file->num_lines);
605 section_write32(sect, file_field_len + (file->num_lines * line_field_len));
606
607 /* the pairs */
608 saa_rewind(file->lines);
609 while ((li = saa_rstruct(file->lines))) {
610 section_write32(sect, li->file_offset);
611 section_write32(sect, li->linenumber |= 0x80000000);
612 }
613 }
614}
615
616static uint16_t write_symbolinfo_obj(struct coff_Section *sect)
617{
618 uint16_t obj_len;
619
620 obj_len = 2 + 4 + cv8_state.outfile.namebytes;
621
622 section_write16(sect, obj_len);
623 section_write16(sect, 0x1101);
624 section_write32(sect, 0); /* ASM language */
625 section_wbytes(sect, cv8_state.outfile.name, cv8_state.outfile.namebytes);
626
627 return obj_len;
628}
629
630static uint16_t write_symbolinfo_properties(struct coff_Section *sect,
631 const char *const creator_str)
632{
633 /* https://github.com/Microsoft/microsoft-pdb/blob/1d60e041/include/cvinfo.h#L3313 */
634 uint16_t creator_len;
635
636 creator_len = 2 + 4 + 2 + 3*2 + 3*2 + strlen(creator_str)+1 + 2;
637
638 /*
639 * We used to use a language ID of 3 for "MASM", since it's closest of the
640 * options available; however, BinScope from WACK (the Windows Application
641 * Certification Kit) tests for specific minimum MASM versions and trying to
642 * match an increasing sequence of random MASM version/build numbers seems
643 * like a fool's errand.
644 *
645 * Instead, use a different language ID (NASM is, after all, not MASM
646 * syntax) and just write the actual NASM version number. BinScope appears
647 * to be happy with that.
648 */
649
650 section_write16(sect, creator_len);
651 section_write16(sect, 0x1116);
652 section_write32(sect, 'N'); /* language: 'N' (0x4e) for "NASM"; flags are 0 */
653 if (win64)
654 section_write16(sect, 0x00D0); /* machine */
655 else if (win32)
656 section_write16(sect, 0x0006); /* machine */
657 else
658 nasm_assert(!"neither win32 nor win64 are set!");
659 section_write16(sect, 0); /* verFEMajor */
660 section_write16(sect, 0); /* verFEMinor */
661 section_write16(sect, 0); /* verFEBuild */
662
663 /* BinScope/WACK insist on version >= 8.0.50727 */
664 section_write16(sect, NASM_MAJOR_VER); /* verMajor */
665 section_write16(sect, NASM_MINOR_VER); /* verMinor */
666 section_write16(sect, NASM_SUBMINOR_VER*100 + NASM_PATCHLEVEL_VER); /* verBuild */
667
668 section_wbytes(sect, creator_str, strlen(creator_str)+1); /* verSt */
669 /*
670 * normally there would be key/value pairs here, but they aren't
671 * necessary. They are terminated by 2B
672 */
673 section_write16(sect, 0);
674
675 return creator_len;
676}
677
678static uint16_t write_symbolinfo_symbols(struct coff_Section *sect)
679{
680 uint16_t len = 0, field_len;
681 uint32_t field_base;
682 struct cv8_symbol *sym;
683
684 saa_rewind(cv8_state.symbols);
685 while ((sym = saa_rstruct(cv8_state.symbols))) {
686 switch (sym->type) {
687 case SYMTYPE_LDATA:
688 case SYMTYPE_GDATA:
689 field_len = 12 + strlen(sym->name) + 1;
690 len += field_len - 2;
691 section_write16(sect, field_len);
692 if (sym->type == SYMTYPE_LDATA)
693 section_write16(sect, 0x110C);
694 else
695 section_write16(sect, 0x110D);
696 section_write32(sect, sym->symtype);
697
698 field_base = sect->len;
699 section_write32(sect, 0); /* SECREL */
700 section_write16(sect, 0); /* SECTION */
701 break;
702 case SYMTYPE_PROC:
703 case SYMTYPE_CODE:
704 field_len = 9 + strlen(sym->name) + 1;
705 len += field_len - 2;
706 section_write16(sect, field_len);
707 section_write16(sect, 0x1105);
708
709 field_base = sect->len;
710 section_write32(sect, 0); /* SECREL */
711 section_write16(sect, 0); /* SECTION */
712 section_write8(sect, 0); /* FLAG */
713 break;
714 default:
715 nasm_assert(!"unknown symbol type");
716 }
717
718 section_wbytes(sect, sym->name, strlen(sym->name) + 1);
719
720 register_reloc(sect, sym->name, field_base,
721 win64 ? IMAGE_REL_AMD64_SECREL :
722 IMAGE_REL_I386_SECREL);
723 register_reloc(sect, sym->name, field_base + 4,
724 win64 ? IMAGE_REL_AMD64_SECTION :
725 IMAGE_REL_I386_SECTION);
726 }
727
728 return len;
729}
730
731static void write_symbolinfo_table(struct coff_Section *const sect)
732{
733 static const char creator_str[] = "The Netwide Assembler " NASM_VER;
734 uint16_t obj_length, creator_length, sym_length;
735 uint32_t field_length = 0, out_len;
736
737 nasm_assert(cv8_state.outfile.namebytes);
738
739 /* signature, language, outfile NULL */
740 obj_length = 2 + 4 + cv8_state.outfile.namebytes;
741 creator_length = 2 + 4 + 2 + 3*2 + 3*2 + strlen(creator_str)+1 + 2;
742
743 sym_length = ( cv8_state.num_syms[SYMTYPE_CODE] * 7) +
744 ( cv8_state.num_syms[SYMTYPE_PROC] * 7) +
745 ( cv8_state.num_syms[SYMTYPE_LDATA] * 10) +
746 ( cv8_state.num_syms[SYMTYPE_GDATA] * 10) +
747 cv8_state.symbol_lengths;
748
749 field_length = 2 + obj_length +
750 2 + creator_length +
751 (4 * cv8_state.total_syms) + sym_length;
752
753 section_write32(sect, 0x000000F1);
754 section_write32(sect, field_length);
755
756 /* for sub fields, length preceeds type */
757
758 out_len = write_symbolinfo_obj(sect);
759 nasm_assert(out_len == obj_length);
760
761 out_len = write_symbolinfo_properties(sect, creator_str);
762 nasm_assert(out_len == creator_length);
763
764 out_len = write_symbolinfo_symbols(sect);
765 nasm_assert(out_len == sym_length);
766}
767
768static inline void align4_table(struct coff_Section *const sect)
769{
770 unsigned diff;
771 uint32_t zero = 0;
772 struct SAA *data = sect->data;
773
774 if (data->wptr % 4 == 0)
775 return;
776
777 diff = 4 - (data->wptr % 4);
778 if (diff)
779 section_wbytes(sect, &zero, diff);
780}
781
782static void build_symbol_table(struct coff_Section *const sect)
783{
784 section_write32(sect, 0x00000004);
785
786 write_filename_table(sect);
787 align4_table(sect);
788 write_sourcefile_table(sect);
789 align4_table(sect);
790 write_linenumber_table(sect);
791 align4_table(sect);
792 write_symbolinfo_table(sect);
793 align4_table(sect);
794}
795
796static void build_type_table(struct coff_Section *const sect)
797{
798 uint16_t field_len;
799 struct cv8_symbol *sym;
800
801 section_write32(sect, 0x00000004);
802
803 saa_rewind(cv8_state.symbols);
804 while ((sym = saa_rstruct(cv8_state.symbols))) {
805 if (sym->type != SYMTYPE_PROC)
806 continue;
807
808 /* proc leaf */
809
810 field_len = 2 + 4 + 4 + 4 + 2;
811 section_write16(sect, field_len);
812 section_write16(sect, 0x1008); /* PROC type */
813
814 section_write32(sect, 0x00000003); /* return type */
815 section_write32(sect, 0); /* calling convention (default) */
816 section_write32(sect, sym->typeindex);
817 section_write16(sect, 0); /* # params */
818
819 /* arglist */
820
821 field_len = 2 + 4;
822 section_write16(sect, field_len);
823 section_write16(sect, 0x1201); /* ARGLIST */
824 section_write32(sect, 0); /*num params */
825 }
826}
827