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 | |
55 | static void cv8_init(void); |
56 | static void cv8_linenum(const char *filename, int32_t linenumber, |
57 | int32_t segto); |
58 | static void cv8_deflabel(char *name, int32_t segment, int64_t offset, |
59 | int is_global, char *special); |
60 | static void cv8_typevalue(int32_t type); |
61 | static void cv8_output(int type, void *param); |
62 | static void cv8_cleanup(void); |
63 | |
64 | const 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 | ******************************************************************************/ |
80 | struct source_file; |
81 | |
82 | struct 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 | |
98 | struct linepair { |
99 | uint32_t file_offset; |
100 | uint32_t linenumber; |
101 | }; |
102 | |
103 | enum symbol_type { |
104 | SYMTYPE_CODE, |
105 | SYMTYPE_PROC, |
106 | SYMTYPE_LDATA, |
107 | SYMTYPE_GDATA, |
108 | |
109 | SYMTYPE_MAX |
110 | }; |
111 | |
112 | struct 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 | |
137 | struct 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 | }; |
164 | struct cv8_state cv8_state; |
165 | |
166 | static 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 | |
191 | static struct source_file *register_file(const char *filename); |
192 | static struct coff_Section *find_section(int32_t segto); |
193 | |
194 | static 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 | |
218 | static 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 | |
253 | static 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 | |
295 | static 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 | |
306 | static void build_symbol_table(struct coff_Section *const sect); |
307 | static void build_type_table(struct coff_Section *const sect); |
308 | |
309 | static 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 | ******************************************************************************/ |
341 | static 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; |
367 | done_0: |
368 | nasm_free(file_buf); |
369 | fclose(f); |
370 | done: |
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 | |
379 | static 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 | |
425 | static 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 | |
439 | static 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 | |
486 | static inline void section_write32(struct coff_Section *sect, uint32_t val) |
487 | { |
488 | saa_write32(sect->data, val); |
489 | sect->len += 4; |
490 | } |
491 | |
492 | static inline void section_write16(struct coff_Section *sect, uint16_t val) |
493 | { |
494 | saa_write16(sect->data, val); |
495 | sect->len += 2; |
496 | } |
497 | |
498 | static inline void section_write8(struct coff_Section *sect, uint8_t val) |
499 | { |
500 | saa_write8(sect->data, val); |
501 | sect->len++; |
502 | } |
503 | |
504 | static 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 | |
511 | static 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 | |
535 | static 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 | |
560 | static 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 | |
616 | static 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 | |
630 | static 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 | |
678 | static 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 | |
731 | static 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 | |
768 | static 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 | |
782 | static 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 | |
796 | static 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 | |