mirror of
https://github.com/netwide-assembler/nasm.git
synced 2024-11-21 03:14:19 +08:00
713fd1ffc8
Windows Store and Xbox One apps need to pass WACK, the Windows App Certification Kit, and part of that process involves a tool named BinScope that checks the debug info of all object files making up the final executable against a list of minimum versions. These minimum versions get increased periodically as new SDKs and compilers are released. In a patch 2 years ago, I made NASM pretend it was MASM and output a then-current MASM version number. Well, the minimum version number has increased again, and periodically hardcoding a new random MASM version to keep BinScope happy doesn't seem like the way to go. It turns out that BinScope does not impose any minimum version requirements on object files listing a source language BinScope doesn't know about. I have no idea how to officially request a new CodeView language ID (or whether there even is a way to do so for someone outside MS). But experimentally, using 'N' (0x4e) for NASM seems to be working just fine and is far away from the range of currently allocated language IDs (which stop at 0x10). Long story short, make NASM emit a source language ID of 0x4e, with the actual NASM version in the version number fields. BinScope is happy to accept that, and since the language ID field is purely an informational field in an optional debug info record that (as far as I can tell) is not used for anything else, this seems reasonably safe and unlikely to cause trouble. Signed-off-by: Fabian Giesen <fabiang@radgametools.com> Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
827 lines
23 KiB
C
827 lines
23 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* Copyright 1996-2017 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 <stdio.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.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_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;
|
|
hash_init(&cv8_state.file_hash, HASH_MEDIUM);
|
|
|
|
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;
|
|
|
|
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(file, cv8_state.source_files) {
|
|
nasm_free(file->fullname);
|
|
saa_free(file->lines);
|
|
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_error(ERR_NONFATAL, "unable to hash file %s. "
|
|
"Debug information may be unavailable.\n",
|
|
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);
|
|
|
|
file = nasm_zalloc(sizeof(*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(0, "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 uint16_t write_symbolinfo_obj(struct coff_Section *sect)
|
|
{
|
|
uint16_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 uint16_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 */
|
|
uint16_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_assert(!"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 uint16_t write_symbolinfo_symbols(struct coff_Section *sect)
|
|
{
|
|
uint16_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_assert(!"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;
|
|
uint16_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)
|
|
{
|
|
uint16_t field_len;
|
|
struct cv8_symbol *sym;
|
|
|
|
section_write32(sect, 0x00000004);
|
|
|
|
saa_rewind(cv8_state.symbols);
|
|
while ((sym = saa_rstruct(cv8_state.symbols))) {
|
|
if (sym->type != SYMTYPE_PROC)
|
|
continue;
|
|
|
|
/* proc leaf */
|
|
|
|
field_len = 2 + 4 + 4 + 4 + 2;
|
|
section_write16(sect, field_len);
|
|
section_write16(sect, 0x1008); /* PROC type */
|
|
|
|
section_write32(sect, 0x00000003); /* return type */
|
|
section_write32(sect, 0); /* calling convention (default) */
|
|
section_write32(sect, sym->typeindex);
|
|
section_write16(sect, 0); /* # params */
|
|
|
|
/* arglist */
|
|
|
|
field_len = 2 + 4;
|
|
section_write16(sect, field_len);
|
|
section_write16(sect, 0x1201); /* ARGLIST */
|
|
section_write32(sect, 0); /*num params */
|
|
}
|
|
}
|