mirror of
				https://github.com/netwide-assembler/nasm.git
				synced 2025-10-10 00:25:06 -04:00 
			
		
		
		
	NASM 2.15.04 Conflicts: asm/listing.h asm/pptok.pl asm/preproc.c version This doesn't pass travis test 3392711, which is using an extremely odd construct of %?? in the middle of an argument sequence for an smacro while not being in a macro itself, and expecting it to expand to the macro name. This seems to *really* confuse the master branch. Resolve this later...
		
			
				
	
	
		
			821 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			821 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* ----------------------------------------------------------------------- *
 | |
|  *
 | |
|  *   Copyright 1996-2020 The NASM Authors - All Rights Reserved
 | |
|  *   See the file AUTHORS included with the NASM distribution for
 | |
|  *   the specific copyright holders.
 | |
|  *
 | |
|  *   Redistribution and use in source and binary forms, with or without
 | |
|  *   modification, are permitted provided that the following
 | |
|  *   conditions are met:
 | |
|  *
 | |
|  *   * Redistributions of source code must retain the above copyright
 | |
|  *     notice, this list of conditions and the following disclaimer.
 | |
|  *   * Redistributions in binary form must reproduce the above
 | |
|  *     copyright notice, this list of conditions and the following
 | |
|  *     disclaimer in the documentation and/or other materials provided
 | |
|  *     with the distribution.
 | |
|  *
 | |
|  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 | |
|  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 | |
|  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | |
|  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | |
|  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | |
|  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
|  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | |
|  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | |
|  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | |
|  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | |
|  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 | |
|  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 | |
|  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  * ----------------------------------------------------------------------- */
 | |
| 
 | |
| /*
 | |
|  * codeview.c Codeview Debug Format support for COFF
 | |
|  */
 | |
| 
 | |
| #include "version.h"
 | |
| #include "compiler.h"
 | |
| 
 | |
| 
 | |
| #include "nasm.h"
 | |
| #include "nasmlib.h"
 | |
| #include "error.h"
 | |
| #include "preproc.h"
 | |
| #include "saa.h"
 | |
| #include "hashtbl.h"
 | |
| #include "outlib.h"
 | |
| #include "pecoff.h"
 | |
| #include "md5.h"
 | |
| 
 | |
| static void cv8_init(void);
 | |
| static void cv8_linenum(const char *filename, int32_t linenumber,
 | |
|         int32_t segto);
 | |
| static void cv8_deflabel(char *name, int32_t segment, int64_t offset,
 | |
|         int is_global, char *special);
 | |
| static void cv8_typevalue(int32_t type);
 | |
| static void cv8_output(int type, void *param);
 | |
| static void cv8_cleanup(void);
 | |
| 
 | |
| const struct dfmt df_cv8 = {
 | |
|     "Codeview 8+",              /* .fullname */
 | |
|     "cv8",                      /* .shortname */
 | |
|     cv8_init,                   /* .init */
 | |
|     cv8_linenum,                /* .linenum */
 | |
|     cv8_deflabel,               /* .debug_deflabel */
 | |
|     NULL,                       /* .debug_smacros */
 | |
|     NULL,                       /* .debug_include */
 | |
|     NULL,                       /* .debug_mmacros */
 | |
|     null_debug_directive,       /* .debug_directive */
 | |
|     cv8_typevalue,              /* .debug_typevalue */
 | |
|     cv8_output,                 /* .debug_output */
 | |
|     cv8_cleanup,                /* .cleanup */
 | |
|     NULL                        /* pragma list */
 | |
| };
 | |
| 
 | |
| /*******************************************************************************
 | |
|  * dfmt callbacks
 | |
|  ******************************************************************************/
 | |
| struct source_file;
 | |
| 
 | |
| struct source_file {
 | |
|     const char *filename;
 | |
|     char *fullname;
 | |
|     uint32_t fullnamelen;
 | |
| 
 | |
|     struct source_file *next;
 | |
| 
 | |
|     uint32_t filetbl_off;
 | |
|     uint32_t sourcetbl_off;
 | |
| 
 | |
|     struct SAA *lines;
 | |
|     uint32_t num_lines;
 | |
| 
 | |
|     unsigned char md5sum[MD5_HASHBYTES];
 | |
| };
 | |
| 
 | |
| struct linepair {
 | |
|     uint32_t file_offset;
 | |
|     uint32_t linenumber;
 | |
| };
 | |
| 
 | |
| enum symbol_type {
 | |
|     SYMTYPE_CODE,
 | |
|     SYMTYPE_PROC,
 | |
|     SYMTYPE_LDATA,
 | |
|     SYMTYPE_GDATA,
 | |
| 
 | |
|     SYMTYPE_MAX
 | |
| };
 | |
| 
 | |
| struct cv8_symbol {
 | |
|     enum symbol_type type;
 | |
|     char *name;
 | |
| 
 | |
|     uint32_t secrel;
 | |
|     uint16_t section;
 | |
|     uint32_t size;
 | |
|     uint32_t typeindex;
 | |
| 
 | |
|     enum symtype {
 | |
|         TYPE_UNREGISTERED = 0x0000, /* T_NOTYPE */
 | |
|         TYPE_BYTE = 0x0020,
 | |
|         TYPE_WORD = 0x0021,
 | |
|         TYPE_DWORD= 0x0022,
 | |
|         TYPE_QUAD = 0x0023,
 | |
| 
 | |
|         TYPE_REAL32 = 0x0040,
 | |
|         TYPE_REAL64 = 0x0041,
 | |
|         TYPE_REAL80 = 0x0042,
 | |
|         TYPE_REAL128= 0x0043,
 | |
|         TYPE_REAL256= 0x0044,
 | |
|         TYPE_REAL512= 0x0045
 | |
|     } symtype;
 | |
| };
 | |
| 
 | |
| struct cv8_state {
 | |
|     int symbol_sect;
 | |
|     int type_sect;
 | |
| 
 | |
|     uint32_t text_offset;
 | |
| 
 | |
|     struct source_file *source_files, **source_files_tail;
 | |
|     const char *last_filename;
 | |
|     struct source_file *last_source_file;
 | |
|     struct hash_table file_hash;
 | |
|     unsigned num_files;
 | |
|     uint32_t total_filename_len;
 | |
| 
 | |
| 
 | |
|     unsigned total_lines;
 | |
| 
 | |
|     struct SAA *symbols;
 | |
|     struct cv8_symbol *last_sym;
 | |
|     unsigned num_syms[SYMTYPE_MAX];
 | |
|     unsigned symbol_lengths;
 | |
|     unsigned total_syms;
 | |
| 
 | |
|     struct {
 | |
|         char *name;
 | |
|         size_t namebytes;
 | |
|     } outfile;
 | |
| };
 | |
| struct cv8_state cv8_state;
 | |
| 
 | |
| static void cv8_init(void)
 | |
| {
 | |
|     const uint32_t sect_flags =     IMAGE_SCN_MEM_READ |
 | |
|                     IMAGE_SCN_MEM_DISCARDABLE |
 | |
|                     IMAGE_SCN_CNT_INITIALIZED_DATA |
 | |
|                     IMAGE_SCN_ALIGN_1BYTES;
 | |
| 
 | |
|     cv8_state.symbol_sect = coff_make_section(".debug$S", sect_flags);
 | |
|     cv8_state.type_sect = coff_make_section(".debug$T", sect_flags);
 | |
| 
 | |
|     cv8_state.text_offset = 0;
 | |
| 
 | |
|     cv8_state.source_files = NULL;
 | |
|     cv8_state.source_files_tail = &cv8_state.source_files;
 | |
| 
 | |
|     cv8_state.num_files = 0;
 | |
|     cv8_state.total_filename_len = 0;
 | |
| 
 | |
|     cv8_state.total_lines = 0;
 | |
| 
 | |
|     cv8_state.symbols = saa_init(sizeof(struct cv8_symbol));
 | |
|     cv8_state.last_sym = NULL;
 | |
| }
 | |
| 
 | |
| static struct source_file *register_file(const char *filename);
 | |
| static struct coff_Section *find_section(int32_t segto);
 | |
| 
 | |
| static void cv8_linenum(const char *filename, int32_t linenumber,
 | |
|         int32_t segto)
 | |
| {
 | |
|     struct coff_Section *s;
 | |
|     struct linepair *li;
 | |
|     struct source_file *file;
 | |
| 
 | |
|     file = register_file(filename);
 | |
| 
 | |
|     s = find_section(segto);
 | |
|     if (s == NULL)
 | |
|         return;
 | |
| 
 | |
|     if ((s->flags & IMAGE_SCN_MEM_EXECUTE) == 0)
 | |
|         return;
 | |
| 
 | |
|     li = saa_wstruct(file->lines);
 | |
|     li->file_offset = cv8_state.text_offset;
 | |
|     li->linenumber = linenumber;
 | |
| 
 | |
|     file->num_lines++;
 | |
|     cv8_state.total_lines++;
 | |
| }
 | |
| 
 | |
| static void cv8_deflabel(char *name, int32_t segment, int64_t offset,
 | |
|         int is_global, char *special)
 | |
| {
 | |
|     struct cv8_symbol *sym;
 | |
|     struct coff_Section *s;
 | |
| 
 | |
|     (void)special;
 | |
| 
 | |
|     s = find_section(segment);
 | |
|     if (s == NULL)
 | |
|         return;
 | |
| 
 | |
|     sym = saa_wstruct(cv8_state.symbols);
 | |
| 
 | |
|     if (s->flags & IMAGE_SCN_MEM_EXECUTE)
 | |
|         sym->type = is_global ? SYMTYPE_PROC : SYMTYPE_CODE;
 | |
|     else
 | |
|         sym->type = is_global ?  SYMTYPE_GDATA : SYMTYPE_LDATA;
 | |
|     cv8_state.num_syms[sym->type]++;
 | |
|     cv8_state.total_syms++;
 | |
| 
 | |
|     sym->section = segment;
 | |
|     sym->secrel = offset;
 | |
|     sym->symtype = TYPE_UNREGISTERED;
 | |
|     sym->size = 0;
 | |
|     sym->typeindex = 0;
 | |
| 
 | |
|     sym->name = nasm_strdup(name);
 | |
|     cv8_state.symbol_lengths += strlen(sym->name) + 1;
 | |
| 
 | |
|     if (cv8_state.last_sym && cv8_state.last_sym->section == segment)
 | |
|         cv8_state.last_sym->size = offset - cv8_state.last_sym->secrel;
 | |
|     cv8_state.last_sym = sym;
 | |
| }
 | |
| 
 | |
| static void cv8_typevalue(int32_t type)
 | |
| {
 | |
|     if (!cv8_state.last_sym)
 | |
|         return;
 | |
|     if (cv8_state.last_sym->symtype != TYPE_UNREGISTERED)
 | |
|         return;
 | |
| 
 | |
|     switch (TYM_TYPE(type)) {
 | |
|     case TY_BYTE:
 | |
|         cv8_state.last_sym->symtype = TYPE_BYTE;
 | |
|         break;
 | |
|     case TY_WORD:
 | |
|         cv8_state.last_sym->symtype = TYPE_WORD;
 | |
|         break;
 | |
|     case TY_DWORD:
 | |
|         cv8_state.last_sym->symtype = TYPE_DWORD;
 | |
|         break;
 | |
|     case TY_QWORD:
 | |
|         cv8_state.last_sym->symtype = TYPE_QUAD;
 | |
|         break;
 | |
|     case TY_FLOAT:
 | |
|         cv8_state.last_sym->symtype = TYPE_REAL32;
 | |
|         break;
 | |
|     case TY_TBYTE:
 | |
|         cv8_state.last_sym->symtype = TYPE_REAL80;
 | |
|         break;
 | |
|     case TY_OWORD:
 | |
|         cv8_state.last_sym->symtype = TYPE_REAL128;
 | |
|         break;
 | |
|     case TY_YWORD:
 | |
|         cv8_state.last_sym->symtype = TYPE_REAL256;
 | |
|         break;
 | |
|     case TY_ZWORD:
 | |
|         cv8_state.last_sym->symtype = TYPE_REAL512;
 | |
|         break;
 | |
|     case TY_UNKNOWN:
 | |
|         break;
 | |
|     case TY_LABEL:
 | |
|         break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void cv8_output(int type, void *param)
 | |
| {
 | |
|     struct coff_DebugInfo *dinfo = param;
 | |
| 
 | |
|     (void)type;
 | |
| 
 | |
|     if (dinfo->section && dinfo->section->name &&
 | |
|     !strncmp(dinfo->section->name, ".text", 5))
 | |
|         cv8_state.text_offset += dinfo->size;
 | |
| }
 | |
| 
 | |
| static void build_symbol_table(struct coff_Section *const sect);
 | |
| static void build_type_table(struct coff_Section *const sect);
 | |
| 
 | |
| static void cv8_cleanup(void)
 | |
| {
 | |
|     struct cv8_symbol *sym;
 | |
|     struct source_file *file, *ftmp;
 | |
| 
 | |
|     struct coff_Section *symbol_sect = coff_sects[cv8_state.symbol_sect];
 | |
|     struct coff_Section *type_sect = coff_sects[cv8_state.type_sect];
 | |
| 
 | |
|     cv8_state.outfile.name = nasm_realpath(outname);
 | |
|     cv8_state.outfile.namebytes = strlen(cv8_state.outfile.name) + 1;
 | |
| 
 | |
|     build_symbol_table(symbol_sect);
 | |
|     build_type_table(type_sect);
 | |
| 
 | |
|     list_for_each_safe(file, ftmp, cv8_state.source_files) {
 | |
|         nasm_free(file->fullname);
 | |
|         saa_free(file->lines);
 | |
|         nasm_free(file);
 | |
|     }
 | |
|     hash_free(&cv8_state.file_hash);
 | |
| 
 | |
|     saa_rewind(cv8_state.symbols);
 | |
|     while ((sym = saa_rstruct(cv8_state.symbols)))
 | |
|         nasm_free(sym->name);
 | |
|     saa_free(cv8_state.symbols);
 | |
| 
 | |
|     nasm_free(cv8_state.outfile.name);
 | |
| }
 | |
| 
 | |
| /*******************************************************************************
 | |
|  * implementation
 | |
|  ******************************************************************************/
 | |
| static void calc_md5(const char *const filename,
 | |
|         unsigned char sum[MD5_HASHBYTES])
 | |
| {
 | |
|     int success = 0;
 | |
|     unsigned char *file_buf;
 | |
|     FILE *f;
 | |
|     MD5_CTX ctx;
 | |
| 
 | |
|     f = pp_input_fopen(filename, NF_BINARY);
 | |
|     if (!f)
 | |
|         goto done;
 | |
| 
 | |
|     file_buf = nasm_zalloc(BUFSIZ);
 | |
| 
 | |
|     MD5Init(&ctx);
 | |
|     while (!feof(f)) {
 | |
|         size_t i = fread(file_buf, 1, BUFSIZ, f);
 | |
|         if (ferror(f))
 | |
|             goto done_0;
 | |
|         else if (i == 0)
 | |
|             break;
 | |
|         MD5Update(&ctx, file_buf, i);
 | |
|     }
 | |
|     MD5Final(sum, &ctx);
 | |
| 
 | |
|     success = 1;
 | |
| done_0:
 | |
|     nasm_free(file_buf);
 | |
|     fclose(f);
 | |
| done:
 | |
|     if (!success) {
 | |
|         nasm_nonfatal("unable to hash file %s. "
 | |
|                       "Debug information may be unavailable.",
 | |
|                       filename);
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| 
 | |
| static struct source_file *register_file(const char *filename)
 | |
| {
 | |
|     struct source_file *file;
 | |
|     void **filep;
 | |
|     char *fullpath;
 | |
|     struct hash_insert hi;
 | |
| 
 | |
|     /*
 | |
|      * The common case is that we are invoked with the same filename
 | |
|      * as we were last time.  Make this a pointer comparison: this is
 | |
|      * safe because the NASM core code allocates each filename once
 | |
|      * and never frees it.
 | |
|      */
 | |
|     if (likely(cv8_state.last_filename == filename))
 | |
|         return cv8_state.last_source_file;
 | |
| 
 | |
|     cv8_state.last_filename = filename;
 | |
| 
 | |
|     filep = hash_find(&cv8_state.file_hash, filename, &hi);
 | |
|     if (likely(filep)) {
 | |
|         file = *filep;
 | |
|     } else {
 | |
|         /* New filename encounter */
 | |
| 
 | |
|         fullpath = nasm_realpath(filename);
 | |
| 
 | |
|         nasm_new(file);
 | |
|         file->filename = filename;
 | |
|         file->fullname = fullpath;
 | |
|         file->fullnamelen = strlen(fullpath);
 | |
|         file->lines = saa_init(sizeof(struct linepair));
 | |
|         *cv8_state.source_files_tail = file;
 | |
|         cv8_state.source_files_tail = &file->next;
 | |
|         calc_md5(fullpath, file->md5sum);
 | |
| 
 | |
|         hash_add(&hi, filename, file);
 | |
| 
 | |
|         cv8_state.num_files++;
 | |
|         cv8_state.total_filename_len += file->fullnamelen + 1;
 | |
|     }
 | |
| 
 | |
|     cv8_state.last_source_file = file;
 | |
|     return file;
 | |
| }
 | |
| 
 | |
| static struct coff_Section *find_section(int32_t segto)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < coff_nsects; i++) {
 | |
|         struct coff_Section *sec;
 | |
| 
 | |
|         sec = coff_sects[i];
 | |
|         if (segto == sec->index)
 | |
|             return sec;
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static void register_reloc(struct coff_Section *const sect,
 | |
|         char *sym, uint32_t addr, uint16_t type)
 | |
| {
 | |
|     struct coff_Reloc *r;
 | |
|     struct coff_Section *sec;
 | |
|     uint32_t i;
 | |
| 
 | |
|     r = *sect->tail = nasm_malloc(sizeof(struct coff_Reloc));
 | |
|     sect->tail = &r->next;
 | |
|     r->next = NULL;
 | |
|     sect->nrelocs++;
 | |
| 
 | |
|     r->address = addr;
 | |
|     r->symbase = SECT_SYMBOLS;
 | |
|     r->type = type;
 | |
| 
 | |
|     r->symbol = 0;
 | |
|     for (i = 0; i < (uint32_t)coff_nsects; i++) {
 | |
|         sec = coff_sects[i];
 | |
|         if (!strcmp(sym, sec->name)) {
 | |
|             return;
 | |
|         }
 | |
|         r->symbol += 2;
 | |
|     }
 | |
| 
 | |
|     saa_rewind(coff_syms);
 | |
|     for (i = 0; i < coff_nsyms; i++) {
 | |
|         struct coff_Symbol *s = saa_rstruct(coff_syms);
 | |
|         r->symbol++;
 | |
|         if (s->strpos == -1 && !strcmp(sym, s->name)) {
 | |
|             return;
 | |
|         } else if (s->strpos != -1) {
 | |
|             int res;
 | |
|             char *symname;
 | |
| 
 | |
|             symname = nasm_malloc(s->namlen + 1);
 | |
|             saa_fread(coff_strs, s->strpos-4, symname, s->namlen);
 | |
|             symname[s->namlen] = '\0';
 | |
|             res = strcmp(sym, symname);
 | |
|             nasm_free(symname);
 | |
|             if (!res)
 | |
|                 return;
 | |
|         }
 | |
|     }
 | |
|     nasm_panic("codeview: relocation for unregistered symbol: %s", sym);
 | |
| }
 | |
| 
 | |
| static inline void section_write32(struct coff_Section *sect, uint32_t val)
 | |
| {
 | |
|     saa_write32(sect->data, val);
 | |
|     sect->len += 4;
 | |
| }
 | |
| 
 | |
| static inline void section_write16(struct coff_Section *sect, uint16_t val)
 | |
| {
 | |
|     saa_write16(sect->data, val);
 | |
|     sect->len += 2;
 | |
| }
 | |
| 
 | |
| static inline void section_write8(struct coff_Section *sect, uint8_t val)
 | |
| {
 | |
|     saa_write8(sect->data, val);
 | |
|     sect->len++;
 | |
| }
 | |
| 
 | |
| static inline void section_wbytes(struct coff_Section *sect, const void *buf,
 | |
|         size_t len)
 | |
| {
 | |
|     saa_wbytes(sect->data, buf, len);
 | |
|     sect->len += len;
 | |
| }
 | |
| 
 | |
| static void write_filename_table(struct coff_Section *const sect)
 | |
| {
 | |
|     uint32_t field_length;
 | |
|     uint32_t tbl_off = 1;    /* offset starts at 1 to skip NULL entry */
 | |
|     struct source_file *file;
 | |
| 
 | |
|     nasm_assert(cv8_state.source_files != NULL);
 | |
|     nasm_assert(cv8_state.num_files > 0);
 | |
|     nasm_assert(cv8_state.total_filename_len > 0);
 | |
| 
 | |
|     field_length = 1 + cv8_state.total_filename_len;
 | |
| 
 | |
|     section_write32(sect, 0x000000F3);
 | |
|     section_write32(sect, field_length);
 | |
| 
 | |
|     section_write8(sect, 0);
 | |
| 
 | |
|     list_for_each(file, cv8_state.source_files) {
 | |
|         section_wbytes(sect, file->fullname, file->fullnamelen + 1);
 | |
|         file->filetbl_off = tbl_off;
 | |
|         tbl_off += file->fullnamelen + 1;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void write_sourcefile_table(struct coff_Section *const sect)
 | |
| {
 | |
|     const uint32_t entry_size = 4 + 2 + MD5_HASHBYTES + 2;
 | |
| 
 | |
|     uint32_t field_length = 0;
 | |
|     uint32_t tbl_off = 0;
 | |
|     struct source_file *file;
 | |
| 
 | |
|     field_length = entry_size * cv8_state.num_files;
 | |
| 
 | |
|     section_write32(sect, 0x000000F4);
 | |
|     section_write32(sect, field_length);
 | |
| 
 | |
|     list_for_each(file, cv8_state.source_files) {
 | |
|         nasm_assert(file->filetbl_off > 0);
 | |
|         section_write32(sect, file->filetbl_off);
 | |
|         section_write16(sect, 0x0110);
 | |
|         section_wbytes(sect, file->md5sum, MD5_HASHBYTES);
 | |
|         section_write16(sect, 0);
 | |
| 
 | |
|         file->sourcetbl_off = tbl_off;
 | |
|         tbl_off += entry_size;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void write_linenumber_table(struct coff_Section *const sect)
 | |
| {
 | |
|     const uint32_t file_field_len = 12;
 | |
|     const uint32_t line_field_len = 8;
 | |
| 
 | |
|     int i;
 | |
|     uint32_t field_length = 0;
 | |
|     size_t field_base;
 | |
|     struct source_file *file;
 | |
|     struct coff_Section *s;
 | |
| 
 | |
|     for (i = 0; i < coff_nsects; i++) {
 | |
|         if (!strncmp(coff_sects[i]->name, ".text", 5))
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     if (i == coff_nsects)
 | |
|         return;
 | |
|     s = coff_sects[i];
 | |
| 
 | |
|     field_length = 12;
 | |
|     field_length += (cv8_state.num_files * file_field_len);
 | |
|     field_length += (cv8_state.total_lines * line_field_len);
 | |
| 
 | |
|     section_write32(sect, 0x000000F2);
 | |
|     section_write32(sect, field_length);
 | |
| 
 | |
|     field_base = sect->len;
 | |
|     section_write32(sect, 0); /* SECREL, updated by relocation */
 | |
|     section_write16(sect, 0); /* SECTION, updated by relocation*/
 | |
|     section_write16(sect, 0); /* pad */
 | |
|     section_write32(sect, s->len);
 | |
| 
 | |
|     register_reloc(sect, ".text", field_base,
 | |
|         win64 ? IMAGE_REL_AMD64_SECREL : IMAGE_REL_I386_SECREL);
 | |
| 
 | |
|     register_reloc(sect, ".text", field_base + 4,
 | |
|         win64 ? IMAGE_REL_AMD64_SECTION : IMAGE_REL_I386_SECTION);
 | |
| 
 | |
|     list_for_each(file, cv8_state.source_files) {
 | |
|         struct linepair *li;
 | |
| 
 | |
|         /* source mapping */
 | |
|         section_write32(sect, file->sourcetbl_off);
 | |
|         section_write32(sect, file->num_lines);
 | |
|         section_write32(sect, file_field_len + (file->num_lines * line_field_len));
 | |
| 
 | |
|         /* the pairs */
 | |
|         saa_rewind(file->lines);
 | |
|         while ((li = saa_rstruct(file->lines))) {
 | |
|             section_write32(sect, li->file_offset);
 | |
|             section_write32(sect, li->linenumber |= 0x80000000);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static uint32_t write_symbolinfo_obj(struct coff_Section *sect)
 | |
| {
 | |
|     uint32_t obj_len;
 | |
| 
 | |
|     obj_len = 2 + 4 + cv8_state.outfile.namebytes;
 | |
| 
 | |
|     section_write16(sect, obj_len);
 | |
|     section_write16(sect, 0x1101);
 | |
|     section_write32(sect, 0); /* ASM language */
 | |
|     section_wbytes(sect, cv8_state.outfile.name, cv8_state.outfile.namebytes);
 | |
| 
 | |
|     return obj_len;
 | |
| }
 | |
| 
 | |
| static uint32_t write_symbolinfo_properties(struct coff_Section *sect,
 | |
|         const char *const creator_str)
 | |
| {
 | |
|     /* https://github.com/Microsoft/microsoft-pdb/blob/1d60e041/include/cvinfo.h#L3313 */
 | |
|     uint32_t creator_len;
 | |
| 
 | |
|     creator_len = 2 + 4 + 2 + 3*2 + 3*2 + strlen(creator_str)+1 + 2;
 | |
| 
 | |
|     /*
 | |
|      * We used to use a language ID of 3 for "MASM", since it's closest of the
 | |
|      * options available; however, BinScope from WACK (the Windows Application
 | |
|      * Certification Kit) tests for specific minimum MASM versions and trying to
 | |
|      * match an increasing sequence of random MASM version/build numbers seems
 | |
|      * like a fool's errand.
 | |
|      *
 | |
|      * Instead, use a different language ID (NASM is, after all, not MASM
 | |
|      * syntax) and just write the actual NASM version number. BinScope appears
 | |
|      * to be happy with that.
 | |
|      */
 | |
| 
 | |
|     section_write16(sect, creator_len);
 | |
|     section_write16(sect, 0x1116);
 | |
|     section_write32(sect, 'N'); /* language: 'N' (0x4e) for "NASM"; flags are 0 */
 | |
|     if (win64)
 | |
|         section_write16(sect, 0x00D0); /* machine */
 | |
|     else if (win32)
 | |
|         section_write16(sect, 0x0006); /* machine */
 | |
|     else
 | |
|         nasm_panic("neither win32 nor win64 are set!");
 | |
|     section_write16(sect, 0); /* verFEMajor */
 | |
|     section_write16(sect, 0); /* verFEMinor */
 | |
|     section_write16(sect, 0); /* verFEBuild */
 | |
| 
 | |
|     /* BinScope/WACK insist on version >= 8.0.50727 */
 | |
|     section_write16(sect, NASM_MAJOR_VER); /* verMajor */
 | |
|     section_write16(sect, NASM_MINOR_VER); /* verMinor */
 | |
|     section_write16(sect, NASM_SUBMINOR_VER*100 + NASM_PATCHLEVEL_VER); /* verBuild */
 | |
| 
 | |
|     section_wbytes(sect, creator_str, strlen(creator_str)+1); /* verSt */
 | |
|     /*
 | |
|      * normally there would be key/value pairs here, but they aren't
 | |
|      * necessary. They are terminated by 2B
 | |
|      */
 | |
|     section_write16(sect, 0);
 | |
| 
 | |
|     return creator_len;
 | |
| }
 | |
| 
 | |
| static uint32_t write_symbolinfo_symbols(struct coff_Section *sect)
 | |
| {
 | |
|     uint32_t len = 0, field_len;
 | |
|     uint32_t field_base;
 | |
|     struct cv8_symbol *sym;
 | |
| 
 | |
|     saa_rewind(cv8_state.symbols);
 | |
|     while ((sym = saa_rstruct(cv8_state.symbols))) {
 | |
|         switch (sym->type) {
 | |
|         case SYMTYPE_LDATA:
 | |
|         case SYMTYPE_GDATA:
 | |
|             field_len = 12 + strlen(sym->name) + 1;
 | |
|             len += field_len - 2;
 | |
|             section_write16(sect, field_len);
 | |
|             if (sym->type == SYMTYPE_LDATA)
 | |
|                 section_write16(sect, 0x110C);
 | |
|             else
 | |
|                 section_write16(sect, 0x110D);
 | |
|             section_write32(sect, sym->symtype);
 | |
| 
 | |
|             field_base = sect->len;
 | |
|             section_write32(sect, 0); /* SECREL */
 | |
|             section_write16(sect, 0); /* SECTION */
 | |
|             break;
 | |
|         case SYMTYPE_PROC:
 | |
|         case SYMTYPE_CODE:
 | |
|             field_len = 9 + strlen(sym->name) + 1;
 | |
|             len += field_len - 2;
 | |
|             section_write16(sect, field_len);
 | |
|             section_write16(sect, 0x1105);
 | |
| 
 | |
|             field_base = sect->len;
 | |
|             section_write32(sect, 0); /* SECREL */
 | |
|             section_write16(sect, 0); /* SECTION */
 | |
|             section_write8(sect, 0); /* FLAG */
 | |
|             break;
 | |
|         default:
 | |
|             nasm_panic("unknown symbol type");
 | |
|         }
 | |
| 
 | |
|         section_wbytes(sect, sym->name, strlen(sym->name) + 1);
 | |
| 
 | |
|         register_reloc(sect, sym->name, field_base,
 | |
|             win64 ? IMAGE_REL_AMD64_SECREL :
 | |
|                 IMAGE_REL_I386_SECREL);
 | |
|         register_reloc(sect, sym->name, field_base + 4,
 | |
|             win64 ? IMAGE_REL_AMD64_SECTION :
 | |
|                 IMAGE_REL_I386_SECTION);
 | |
|     }
 | |
| 
 | |
|     return len;
 | |
| }
 | |
| 
 | |
| static void write_symbolinfo_table(struct coff_Section *const sect)
 | |
| {
 | |
|     static const char creator_str[] = "The Netwide Assembler " NASM_VER;
 | |
|     uint32_t obj_length, creator_length, sym_length;
 | |
|     uint32_t field_length = 0, out_len;
 | |
| 
 | |
|     nasm_assert(cv8_state.outfile.namebytes);
 | |
| 
 | |
|     /* signature, language, outfile NULL */
 | |
|     obj_length = 2 + 4 + cv8_state.outfile.namebytes;
 | |
|     creator_length = 2 + 4 + 2 + 3*2 + 3*2 + strlen(creator_str)+1 + 2;
 | |
| 
 | |
|     sym_length =    ( cv8_state.num_syms[SYMTYPE_CODE] *  7) +
 | |
|             ( cv8_state.num_syms[SYMTYPE_PROC] *  7) +
 | |
|             ( cv8_state.num_syms[SYMTYPE_LDATA] * 10) +
 | |
|             ( cv8_state.num_syms[SYMTYPE_GDATA] * 10) +
 | |
|             cv8_state.symbol_lengths;
 | |
| 
 | |
|     field_length = 2 + obj_length +
 | |
|                2 + creator_length +
 | |
|                (4 * cv8_state.total_syms) + sym_length;
 | |
| 
 | |
|     section_write32(sect, 0x000000F1);
 | |
|     section_write32(sect, field_length);
 | |
| 
 | |
|     /* for sub fields, length preceeds type */
 | |
| 
 | |
|     out_len = write_symbolinfo_obj(sect);
 | |
|     nasm_assert(out_len == obj_length);
 | |
| 
 | |
|     out_len = write_symbolinfo_properties(sect, creator_str);
 | |
|     nasm_assert(out_len == creator_length);
 | |
| 
 | |
|     out_len = write_symbolinfo_symbols(sect);
 | |
|     nasm_assert(out_len == sym_length);
 | |
| }
 | |
| 
 | |
| static inline void align4_table(struct coff_Section *const sect)
 | |
| {
 | |
|     unsigned diff;
 | |
|     uint32_t zero = 0;
 | |
|     struct SAA *data = sect->data;
 | |
| 
 | |
|     if (data->wptr % 4 == 0)
 | |
|         return;
 | |
| 
 | |
|     diff = 4 - (data->wptr % 4);
 | |
|     if (diff)
 | |
|         section_wbytes(sect, &zero, diff);
 | |
| }
 | |
| 
 | |
| static void build_symbol_table(struct coff_Section *const sect)
 | |
| {
 | |
|     section_write32(sect, 0x00000004);
 | |
| 
 | |
|     write_filename_table(sect);
 | |
|     align4_table(sect);
 | |
|     write_sourcefile_table(sect);
 | |
|     align4_table(sect);
 | |
|     write_linenumber_table(sect);
 | |
|     align4_table(sect);
 | |
|     write_symbolinfo_table(sect);
 | |
|     align4_table(sect);
 | |
| }
 | |
| 
 | |
| static void build_type_table(struct coff_Section *const sect)
 | |
| {
 | |
|     uint32_t field_len;
 | |
|     uint32_t typeindex = 0x1000;
 | |
|     uint32_t idx_arglist;
 | |
| 
 | |
|     section_write32(sect, 0x00000004);
 | |
| 
 | |
|     /* empty argument list type */
 | |
|     field_len = 2 + 4;
 | |
|     section_write16(sect, field_len);
 | |
|     section_write16(sect, 0x1201); /* ARGLIST */
 | |
|     section_write32(sect, 0); /* num params */
 | |
|     idx_arglist = typeindex++;
 | |
| 
 | |
|     /* procedure type: void proc(void) */
 | |
|     field_len = 2 + 4 + 1 + 1 + 2 + 4;
 | |
|     section_write16(sect, field_len);
 | |
|     section_write16(sect, 0x1008); /* PROC type */
 | |
| 
 | |
|     section_write32(sect, 0x00000003); /* return type VOID */
 | |
|     section_write8(sect, 0);  /* calling convention (default) */
 | |
|     section_write8(sect, 0);  /* function attributes */
 | |
|     section_write16(sect, 0); /* # params */
 | |
|     section_write32(sect, idx_arglist); /* argument list type */
 | |
|     /* idx_voidfunc = typeindex++; */
 | |
| }
 |