diff options
author | somov <somov@yandex-team.ru> | 2022-02-10 16:45:47 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:47 +0300 |
commit | a5950576e397b1909261050b8c7da16db58f10b1 (patch) | |
tree | 7ba7677f6a4c3e19e2cefab34d16df2c8963b4d4 /contrib/tools/yasm/modules/objfmts/coff | |
parent | 81eddc8c0b55990194e112b02d127b87d54164a9 (diff) | |
download | ydb-a5950576e397b1909261050b8c7da16db58f10b1.tar.gz |
Restoring authorship annotation for <somov@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/tools/yasm/modules/objfmts/coff')
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c | 5032 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h | 154 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/coff/win64-except.c | 1118 |
3 files changed, 3152 insertions, 3152 deletions
diff --git a/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c b/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c index 388b09af32..80d9567e05 100644 --- a/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c +++ b/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c @@ -1,2522 +1,2522 @@ -/* - * COFF (DJGPP) object format - * - * Copyright (C) 2002-2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 AUTHOR AND OTHER 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 AUTHOR OR OTHER 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. - */ -#include <util.h> -#include <time.h> - -#include <libyasm.h> - -#include "coff-objfmt.h" - - -#define REGULAR_OUTBUF_SIZE 1024 - -/* Defining this to 0 sets all section VMA's to 0 rather than as the same as - * the LMA. According to the DJGPP COFF Spec, this should be set to 1 - * (VMA=LMA), and indeed DJGPP's GCC output shows VMA=LMA. However, NASM - * outputs VMA=0 (as if this was 0), and GNU objdump output looks a lot nicer - * with VMA=0. Who's right? This is #defined as changing this setting affects - * several places in the code. - */ -#define COFF_SET_VMA (!objfmt_coff->win32) - -#define COFF_MACHINE_I386 0x014C -#define COFF_MACHINE_AMD64 0x8664 - -#define COFF_F_LNNO 0x0004 /* line number info NOT present */ -#define COFF_F_LSYMS 0x0008 /* local symbols NOT present */ -#define COFF_F_AR32WR 0x0100 /* 32-bit little endian file */ - -typedef struct coff_reloc { - yasm_reloc reloc; - enum { - COFF_RELOC_ABSOLUTE = 0, /* absolute, no reloc needed */ - - /* I386 relocations */ - COFF_RELOC_I386_ADDR16 = 0x1, /* 16-bit absolute reference */ - COFF_RELOC_I386_REL16 = 0x2, /* 16-bit PC-relative reference */ - COFF_RELOC_I386_ADDR32 = 0x6, /* 32-bit absolute reference */ - COFF_RELOC_I386_ADDR32NB = 0x7, /* 32-bit absolute ref w/o base */ - COFF_RELOC_I386_SEG12 = 0x9, /* 16-bit absolute segment ref */ - COFF_RELOC_I386_SECTION = 0xA, /* section index */ - COFF_RELOC_I386_SECREL = 0xB, /* offset from start of segment */ - COFF_RELOC_I386_TOKEN = 0xC, /* CLR metadata token */ - COFF_RELOC_I386_SECREL7 = 0xD, /* 7-bit offset from base of sect */ - COFF_RELOC_I386_REL32 = 0x14, /* 32-bit PC-relative reference */ - - /* AMD64 relocations */ - COFF_RELOC_AMD64_ADDR64 = 0x1, /* 64-bit address (VA) */ - COFF_RELOC_AMD64_ADDR32 = 0x2, /* 32-bit address (VA) */ - COFF_RELOC_AMD64_ADDR32NB = 0x3, /* 32-bit address w/o base (RVA) */ - COFF_RELOC_AMD64_REL32 = 0x4, /* 32-bit relative (0 byte dist) */ - COFF_RELOC_AMD64_REL32_1 = 0x5, /* 32-bit relative (1 byte dist) */ - COFF_RELOC_AMD64_REL32_2 = 0x6, /* 32-bit relative (2 byte dist) */ - COFF_RELOC_AMD64_REL32_3 = 0x7, /* 32-bit relative (3 byte dist) */ - COFF_RELOC_AMD64_REL32_4 = 0x8, /* 32-bit relative (4 byte dist) */ - COFF_RELOC_AMD64_REL32_5 = 0x9, /* 32-bit relative (5 byte dist) */ - COFF_RELOC_AMD64_SECTION = 0xA, /* section index */ - COFF_RELOC_AMD64_SECREL = 0xB, /* 32-bit offset from base of sect */ - COFF_RELOC_AMD64_SECREL7 = 0xC, /* 7-bit offset from base of sect */ - COFF_RELOC_AMD64_TOKEN = 0xD /* CLR metadata token */ - } type; /* type of relocation */ -} coff_reloc; - -#define COFF_STYP_TEXT 0x00000020UL -#define COFF_STYP_DATA 0x00000040UL -#define COFF_STYP_BSS 0x00000080UL -#define COFF_STYP_INFO 0x00000200UL -#define COFF_STYP_STD_MASK 0x000003FFUL -#define COFF_STYP_ALIGN_MASK 0x00F00000UL -#define COFF_STYP_ALIGN_SHIFT 20 -#define COFF_STYP_NRELOC_OVFL 0x01000000UL -#define COFF_STYP_DISCARD 0x02000000UL -#define COFF_STYP_NOCACHE 0x04000000UL -#define COFF_STYP_NOPAGE 0x08000000UL -#define COFF_STYP_SHARED 0x10000000UL -#define COFF_STYP_EXECUTE 0x20000000UL -#define COFF_STYP_READ 0x40000000UL -#define COFF_STYP_WRITE 0x80000000UL -#define COFF_STYP_WIN32_MASK 0xFF000000UL - -#define COFF_FLAG_NOBASE (1UL<<0) /* Use no-base (NB) relocs */ - -typedef struct coff_section_data { - /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ - unsigned int scnum; /* section number (1=first section) */ - unsigned long flags; /* section flags (see COFF_STYP_* above) */ - unsigned long addr; /* starting memory address (first section -> 0) */ - unsigned long scnptr; /* file ptr to raw data */ - unsigned long size; /* size of raw data (section data) in bytes */ - unsigned long relptr; /* file ptr to relocation */ - unsigned long nreloc; /* number of relocation entries >64k -> error */ - unsigned long flags2; /* internal flags (see COFF_FLAG_* above) */ - unsigned long strtab_name; /* strtab offset of name if name > 8 chars */ - int isdebug; /* is a debug section? */ -} coff_section_data; - -typedef enum coff_symrec_sclass { - COFF_SCL_EFCN = 0xff, /* physical end of function */ - COFF_SCL_NULL = 0, - COFF_SCL_AUTO = 1, /* automatic variable */ - COFF_SCL_EXT = 2, /* external symbol */ - COFF_SCL_STAT = 3, /* static */ - COFF_SCL_REG = 4, /* register variable */ - COFF_SCL_EXTDEF = 5, /* external definition */ - COFF_SCL_LABEL = 6, /* label */ - COFF_SCL_ULABEL = 7, /* undefined label */ - COFF_SCL_MOS = 8, /* member of structure */ - COFF_SCL_ARG = 9, /* function argument */ - COFF_SCL_STRTAG = 10, /* structure tag */ - COFF_SCL_MOU = 11, /* member of union */ - COFF_SCL_UNTAG = 12, /* union tag */ - COFF_SCL_TPDEF = 13, /* type definition */ - COFF_SCL_USTATIC = 14, /* undefined static */ - COFF_SCL_ENTAG = 15, /* enumeration tag */ - COFF_SCL_MOE = 16, /* member of enumeration */ - COFF_SCL_REGPARM = 17, /* register parameter */ - COFF_SCL_FIELD = 18, /* bit field */ - COFF_SCL_AUTOARG = 19, /* auto argument */ - COFF_SCL_LASTENT = 20, /* dummy entry (end of block) */ - COFF_SCL_BLOCK = 100, /* ".bb" or ".eb" */ - COFF_SCL_FCN = 101, /* ".bf" or ".ef" */ - COFF_SCL_EOS = 102, /* end of structure */ - COFF_SCL_FILE = 103, /* file name */ - COFF_SCL_LINE = 104, /* line # reformatted as symbol table entry */ - COFF_SCL_ALIAS = 105, /* duplicate tag */ - COFF_SCL_HIDDEN = 106 /* ext symbol in dmert public lib */ -} coff_symrec_sclass; - -typedef union coff_symtab_auxent { - /* no data needed for section symbol auxent, all info avail from sym */ - /*@owned@*/ char *fname; /* filename aux entry */ -} coff_symtab_auxent; - -typedef enum coff_symtab_auxtype { - COFF_SYMTAB_AUX_NONE = 0, - COFF_SYMTAB_AUX_SECT, - COFF_SYMTAB_AUX_FILE -} coff_symtab_auxtype; - -typedef struct coff_symrec_data { - int forcevis; /* force visibility in symbol table */ - unsigned long index; /* assigned COFF symbol table index */ - unsigned int type; /* type */ - coff_symrec_sclass sclass; /* storage class */ - - int numaux; /* number of auxiliary entries */ - coff_symtab_auxtype auxtype; /* type of aux entries */ - coff_symtab_auxent aux[1]; /* actually may be any size (including 0) */ -} coff_symrec_data; - -typedef struct yasm_objfmt_coff { - yasm_objfmt_base objfmt; /* base structure */ - - unsigned int parse_scnum; /* sect numbering in parser */ - int win32; /* nonzero for win32/64 output */ - int win64; /* nonzero for win64 output */ - - unsigned int machine; /* COFF machine to use */ - - coff_symrec_data *filesym_data; /* Data for .file symbol */ - - /* data for .def/.endef and related directives */ - coff_symrec_data *def_sym; /* symbol specified by .def */ - - /* data for win64 proc_frame and related directives */ - unsigned long proc_frame; /* Line number of start of proc, or 0 */ - unsigned long done_prolog; /* Line number of end of prologue, or 0 */ - /*@null@*/ coff_unwind_info *unwind; /* Unwind info */ - - yasm_symrec *ssym_imagebase; /* ..imagebase symbol for win64 */ -} yasm_objfmt_coff; - -typedef struct coff_objfmt_output_info { - yasm_object *object; - yasm_objfmt_coff *objfmt_coff; - yasm_errwarns *errwarns; - /*@dependent@*/ FILE *f; - /*@only@*/ unsigned char *buf; - yasm_section *sect; - /*@dependent@*/ coff_section_data *csd; - unsigned long addr; /* start of next section */ - - unsigned long indx; /* current symbol index */ - int all_syms; /* outputting all symbols? */ - unsigned long strtab_offset; /* current string table offset */ -} coff_objfmt_output_info; - -static void coff_section_data_destroy(/*@only@*/ void *d); -static void coff_section_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback coff_section_data_cb = { - coff_section_data_destroy, - coff_section_data_print -}; - -static void coff_symrec_data_destroy(/*@only@*/ void *d); -static void coff_symrec_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback coff_symrec_data_cb = { - coff_symrec_data_destroy, - coff_symrec_data_print -}; - -/* Bytecode callback function prototypes */ -static void win32_sxdata_bc_destroy(void *contents); -static void win32_sxdata_bc_print(const void *contents, FILE *f, - int indent_level); -static int win32_sxdata_bc_calc_len - (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); -static int win32_sxdata_bc_tobytes - (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - /*@null@*/ yasm_output_reloc_func output_reloc); - -/* Bytecode callback structures */ -static const yasm_bytecode_callback win32_sxdata_bc_callback = { - win32_sxdata_bc_destroy, - win32_sxdata_bc_print, - yasm_bc_finalize_common, - NULL, - win32_sxdata_bc_calc_len, - yasm_bc_expand_common, - win32_sxdata_bc_tobytes, - 0 -}; - -yasm_objfmt_module yasm_coff_LTX_objfmt; -yasm_objfmt_module yasm_win32_LTX_objfmt; -yasm_objfmt_module yasm_win64_LTX_objfmt; - - -static /*@dependent@*/ coff_symrec_data * -coff_objfmt_sym_set_data(yasm_symrec *sym, coff_symrec_sclass sclass, - int numaux, coff_symtab_auxtype auxtype) -{ - coff_symrec_data *sym_data; - - sym_data = yasm_xmalloc(sizeof(coff_symrec_data) + - (numaux-1)*sizeof(coff_symtab_auxent)); - sym_data->forcevis = 0; - sym_data->index = 0; - sym_data->type = 0; - sym_data->sclass = sclass; - sym_data->numaux = numaux; - sym_data->auxtype = auxtype; - - yasm_symrec_add_data(sym, &coff_symrec_data_cb, sym_data); - - return sym_data; -} - -static yasm_objfmt_coff * -coff_common_create(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = yasm_xmalloc(sizeof(yasm_objfmt_coff)); - yasm_symrec *filesym; - - /* Only support x86 arch */ - if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") != 0) { - yasm_xfree(objfmt_coff); - return NULL; - } - - objfmt_coff->parse_scnum = 1; /* section numbering starts at 1 */ - - /* FIXME: misuse of NULL bytecode here; it works, but only barely. */ - filesym = yasm_symtab_define_special(object->symtab, ".file", - YASM_SYM_GLOBAL); - objfmt_coff->filesym_data = - coff_objfmt_sym_set_data(filesym, COFF_SCL_FILE, 1, - COFF_SYMTAB_AUX_FILE); - /* Filename is set in coff_objfmt_output */ - objfmt_coff->filesym_data->aux[0].fname = NULL; - - objfmt_coff->proc_frame = 0; - objfmt_coff->done_prolog = 0; - objfmt_coff->unwind = NULL; - objfmt_coff->ssym_imagebase = NULL; - - return objfmt_coff; -} - -static yasm_objfmt * -coff_objfmt_create(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = coff_common_create(object); - - if (objfmt_coff) { - /* Support x86 and amd64 machines of x86 arch */ - if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), "x86") == 0) - objfmt_coff->machine = COFF_MACHINE_I386; - else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), - "amd64") == 0) - objfmt_coff->machine = COFF_MACHINE_AMD64; - else { - yasm_xfree(objfmt_coff); - return NULL; - } - - objfmt_coff->objfmt.module = &yasm_coff_LTX_objfmt; - objfmt_coff->win32 = 0; - objfmt_coff->win64 = 0; - } - return (yasm_objfmt *)objfmt_coff; -} - -static yasm_objfmt * -win32_objfmt_create(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = coff_common_create(object); - - if (objfmt_coff) { - /* Support x86 and amd64 machines of x86 arch. - * (amd64 machine supported for backwards compatibility) - */ - if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), - "x86") == 0) { - objfmt_coff->machine = COFF_MACHINE_I386; - objfmt_coff->objfmt.module = &yasm_win32_LTX_objfmt; - objfmt_coff->win64 = 0; - } else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), - "amd64") == 0) { - objfmt_coff->machine = COFF_MACHINE_AMD64; - objfmt_coff->objfmt.module = &yasm_win64_LTX_objfmt; - objfmt_coff->win64 = 1; - } else { - yasm_xfree(objfmt_coff); - return NULL; - } - - objfmt_coff->win32 = 1; - /* Define a @feat.00 symbol for win32 safeseh handling */ - if (!objfmt_coff->win64) { - yasm_symrec *feat00; - coff_symrec_data *sym_data; - feat00 = yasm_symtab_define_equ(object->symtab, "@feat.00", - yasm_expr_create_ident(yasm_expr_int( - yasm_intnum_create_uint(1)), 0), 0); - sym_data = coff_objfmt_sym_set_data(feat00, COFF_SCL_STAT, 0, - COFF_SYMTAB_AUX_NONE); - sym_data->forcevis = 1; - } - } - return (yasm_objfmt *)objfmt_coff; -} - -static yasm_objfmt * -win64_objfmt_create(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = coff_common_create(object); - - if (objfmt_coff) { - /* Support amd64 machine of x86 arch */ - if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), - "amd64") == 0) { - objfmt_coff->machine = COFF_MACHINE_AMD64; - } else { - yasm_xfree(objfmt_coff); - return NULL; - } - - objfmt_coff->objfmt.module = &yasm_win64_LTX_objfmt; - objfmt_coff->win32 = 1; - objfmt_coff->win64 = 1; - objfmt_coff->ssym_imagebase = - yasm_symtab_define_label(object->symtab, "..imagebase", NULL, 0, 0); - } - return (yasm_objfmt *)objfmt_coff; -} - -static void -coff_objfmt_init_new_section(yasm_section *sect, unsigned long line) -{ - yasm_object *object = yasm_section_get_object(sect); - const char *sectname = yasm_section_get_name(sect); - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - coff_section_data *data; - yasm_symrec *sym; - - data = yasm_xmalloc(sizeof(coff_section_data)); - data->scnum = objfmt_coff->parse_scnum++; - data->flags = 0; - data->addr = 0; - data->scnptr = 0; - data->size = 0; - data->relptr = 0; - data->nreloc = 0; - data->flags2 = 0; - data->strtab_name = 0; - data->isdebug = 0; - - if (yasm__strncasecmp(sectname, ".debug", 6)==0) { - data->flags = COFF_STYP_DATA; - if (objfmt_coff->win32) - data->flags |= COFF_STYP_DISCARD|COFF_STYP_READ; - data->isdebug = 1; - } else - data->flags = COFF_STYP_TEXT; - - yasm_section_add_data(sect, &coff_section_data_cb, data); - - sym = yasm_symtab_define_label(object->symtab, sectname, - yasm_section_bcs_first(sect), 1, line); - yasm_symrec_declare(sym, YASM_SYM_GLOBAL, line); - coff_objfmt_sym_set_data(sym, COFF_SCL_STAT, 1, COFF_SYMTAB_AUX_SECT); - data->sym = sym; -} - -static int -coff_objfmt_set_section_addr(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ coff_section_data *csd; - - assert(info != NULL); - csd = yasm_section_get_data(sect, &coff_section_data_cb); - assert(csd != NULL); - - csd->addr = info->addr; - info->addr += yasm_bc_next_offset(yasm_section_bcs_last(sect)); - - return 0; -} - -static int -coff_objfmt_output_value(yasm_value *value, unsigned char *buf, - unsigned int destsize, unsigned long offset, - yasm_bytecode *bc, int warn, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_objfmt_coff *objfmt_coff; - /*@only@*/ /*@null@*/ yasm_intnum *dist = NULL; - /*@dependent@*/ /*@null@*/ yasm_intnum *intn; - unsigned long intn_val, intn_minus; - int retval; - unsigned int valsize = value->size; - - assert(info != NULL); - objfmt_coff = info->objfmt_coff; - - if (value->abs) - value->abs = yasm_expr_simplify(value->abs, 1); - - /* Try to output constant and PC-relative section-local first. - * Note this does NOT output any value with a SEG, WRT, external, - * cross-section, or non-PC-relative reference (those are handled below). - */ - switch (yasm_value_output_basic(value, buf, destsize, bc, warn, - info->object->arch)) { - case -1: - return 1; - case 0: - break; - default: - return 0; - } - - /* Handle other expressions, with relocation if necessary */ - if (value->rshift > 0 - || (value->seg_of && (value->wrt || value->curpos_rel)) - || (value->section_rel && (value->wrt || value->curpos_rel))) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: relocation too complex")); - return 1; - } - - intn_val = 0; - intn_minus = 0; - if (value->rel) { - yasm_sym_vis vis = yasm_symrec_get_visibility(value->rel); - /*@dependent@*/ /*@null@*/ yasm_symrec *sym = value->rel; - unsigned long addr; - coff_reloc *reloc; - int nobase = info->csd->flags2 & COFF_FLAG_NOBASE; - - /* Sometimes we want the relocation to be generated against one - * symbol but the value generated correspond to a different symbol. - * This is done through (sym being referenced) WRT (sym used for - * reloc). Note both syms need to be in the same section! - */ - if (value->wrt && value->wrt == objfmt_coff->ssym_imagebase) - nobase = 1; - else if (value->wrt) { - /*@dependent@*/ /*@null@*/ yasm_bytecode *rel_precbc, *wrt_precbc; - - if (!yasm_symrec_get_label(sym, &rel_precbc) - || !yasm_symrec_get_label(value->wrt, &wrt_precbc)) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: wrt expression too complex")); - return 1; - } - dist = yasm_calc_bc_dist(wrt_precbc, rel_precbc); - if (!dist) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: cannot wrt across sections")); - return 1; - } - sym = value->wrt; - } - - if (vis & YASM_SYM_COMMON) { - /* In standard COFF, COMMON symbols have their length added in */ - if (!objfmt_coff->win32) { - /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; - /*@dependent@*/ /*@null@*/ yasm_intnum *common_size; - - csize_expr = yasm_symrec_get_common_size(sym); - assert(csize_expr != NULL); - common_size = yasm_expr_get_intnum(csize_expr, 1); - if (!common_size) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: common size too complex")); - return 1; - } - - if (yasm_intnum_sign(common_size) < 0) { - yasm_error_set(YASM_ERROR_VALUE, - N_("coff: common size is negative")); - return 1; - } - - intn_val += yasm_intnum_get_uint(common_size); - } - } else if (!(vis & YASM_SYM_EXTERN) && !objfmt_coff->win64) { - /*@dependent@*/ /*@null@*/ yasm_bytecode *sym_precbc; - - /* Local symbols need relocation to their section's start */ - if (yasm_symrec_get_label(sym, &sym_precbc)) { - yasm_section *sym_sect = yasm_bc_get_section(sym_precbc); - /*@null@*/ coff_section_data *sym_csd; - sym_csd = yasm_section_get_data(sym_sect, - &coff_section_data_cb); - assert(sym_csd != NULL); - sym = sym_csd->sym; - intn_val = yasm_bc_next_offset(sym_precbc); - if (COFF_SET_VMA) - intn_val += sym_csd->addr; - } - } - - if (value->curpos_rel) { - /* For standard COFF, need to adjust to start of section, e.g. - * subtract out the bytecode offset. - * For Win32 COFF, need to adjust based on value size and position. - * For Win64 COFF that's IP-relative, adjust to next bytecode; - * the difference between the offset+destsize and BC length is - * taken care of by special relocation types. - */ - if (objfmt_coff->win64 && value->ip_rel) - intn_val += bc->len*bc->mult_int; - else if (objfmt_coff->win32) - intn_val += offset+destsize; - else - intn_minus = bc->offset; - } - - if (value->seg_of) { - /* Segment generation; zero value. */ - intn_val = 0; - intn_minus = 0; - } - - /* Generate reloc */ - reloc = yasm_xmalloc(sizeof(coff_reloc)); - addr = bc->offset + offset; - if (COFF_SET_VMA) - addr += info->addr; - reloc->reloc.addr = yasm_intnum_create_uint(addr); - reloc->reloc.sym = sym; - - if (value->curpos_rel) { - if (objfmt_coff->machine == COFF_MACHINE_I386) { - if (valsize == 32) - reloc->type = COFF_RELOC_I386_REL32; - else { - yasm_error_set(YASM_ERROR_TYPE, - N_("coff: invalid relocation size")); - return 1; - } - } else if (objfmt_coff->machine == COFF_MACHINE_AMD64) { - if (valsize != 32) { - yasm_error_set(YASM_ERROR_TYPE, - N_("coff: invalid relocation size")); - return 1; - } - if (!value->ip_rel) - reloc->type = COFF_RELOC_AMD64_REL32; - else switch (bc->len*bc->mult_int - (offset+destsize)) { - case 0: - reloc->type = COFF_RELOC_AMD64_REL32; - break; - case 1: - reloc->type = COFF_RELOC_AMD64_REL32_1; - break; - case 2: - reloc->type = COFF_RELOC_AMD64_REL32_2; - break; - case 3: - reloc->type = COFF_RELOC_AMD64_REL32_3; - break; - case 4: - reloc->type = COFF_RELOC_AMD64_REL32_4; - break; - case 5: - reloc->type = COFF_RELOC_AMD64_REL32_5; - break; - default: - yasm_error_set(YASM_ERROR_TYPE, - N_("coff: invalid relocation size")); - return 1; - } - } else - yasm_internal_error(N_("coff objfmt: unrecognized machine")); - } else if (value->seg_of) { - if (objfmt_coff->machine == COFF_MACHINE_I386) - reloc->type = COFF_RELOC_I386_SECTION; - else if (objfmt_coff->machine == COFF_MACHINE_AMD64) - reloc->type = COFF_RELOC_AMD64_SECTION; - else - yasm_internal_error(N_("coff objfmt: unrecognized machine")); - } else if (value->section_rel) { - if (objfmt_coff->machine == COFF_MACHINE_I386) - reloc->type = COFF_RELOC_I386_SECREL; - else if (objfmt_coff->machine == COFF_MACHINE_AMD64) - reloc->type = COFF_RELOC_AMD64_SECREL; - else - yasm_internal_error(N_("coff objfmt: unrecognized machine")); - } else { - if (objfmt_coff->machine == COFF_MACHINE_I386) { - if (nobase) - reloc->type = COFF_RELOC_I386_ADDR32NB; - else - reloc->type = COFF_RELOC_I386_ADDR32; - } else if (objfmt_coff->machine == COFF_MACHINE_AMD64) { - if (valsize == 32) { - if (nobase) - reloc->type = COFF_RELOC_AMD64_ADDR32NB; - else - reloc->type = COFF_RELOC_AMD64_ADDR32; - } else if (valsize == 64) - reloc->type = COFF_RELOC_AMD64_ADDR64; - else { - yasm_error_set(YASM_ERROR_TYPE, - N_("coff: invalid relocation size")); - return 1; - } - } else - yasm_internal_error(N_("coff objfmt: unrecognized machine")); - } - info->csd->nreloc++; - yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); - } - - /* Build up final integer output from intn_val, intn_minus, value->abs, - * and dist. We do all this at the end to avoid creating temporary - * intnums above (except for dist). - */ - if (intn_minus <= intn_val) - intn = yasm_intnum_create_uint(intn_val-intn_minus); - else { - intn = yasm_intnum_create_uint(intn_minus-intn_val); - yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); - } - - if (value->abs) { - yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); - if (!intn2) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: relocation too complex")); - yasm_intnum_destroy(intn); - if (dist) - yasm_intnum_destroy(dist); - return 1; - } - yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); - } - - if (dist) { - yasm_intnum_calc(intn, YASM_EXPR_ADD, dist); - yasm_intnum_destroy(dist); - } - - retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, - valsize, 0, bc, warn); - yasm_intnum_destroy(intn); - return retval; -} - -static int -coff_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - /*@null@*/ /*@only@*/ unsigned char *bigbuf; - unsigned long size = REGULAR_OUTBUF_SIZE; - int gap; - - assert(info != NULL); - - bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, - coff_objfmt_output_value, NULL); - - /* Don't bother doing anything else if size ended up being 0. */ - if (size == 0) { - if (bigbuf) - yasm_xfree(bigbuf); - return 0; - } - - info->csd->size += size; - - /* Warn that gaps are converted to 0 and write out the 0's. */ - if (gap) { - unsigned long left; - yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, - N_("uninitialized space declared in code/data section: zeroing")); - /* Write out in chunks */ - memset(info->buf, 0, REGULAR_OUTBUF_SIZE); - left = size; - while (left > REGULAR_OUTBUF_SIZE) { - fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); - left -= REGULAR_OUTBUF_SIZE; - } - fwrite(info->buf, left, 1, info->f); - } else { - /* Output buf (or bigbuf if non-NULL) to file */ - fwrite(bigbuf ? bigbuf : info->buf, (size_t)size, 1, info->f); - } - - /* If bigbuf was allocated, free it */ - if (bigbuf) - yasm_xfree(bigbuf); - - return 0; -} - -static int -coff_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ coff_section_data *csd; - long pos; - coff_reloc *reloc; - unsigned char *localbuf; - - assert(info != NULL); - csd = yasm_section_get_data(sect, &coff_section_data_cb); - assert(csd != NULL); - - /* Add to strtab if in win32 format and name > 8 chars */ - if (info->objfmt_coff->win32) { - size_t namelen = strlen(yasm_section_get_name(sect)); - if (namelen > 8) { - csd->strtab_name = info->strtab_offset; - info->strtab_offset += (unsigned long)(namelen + 1); - } - } - - if (!csd->isdebug) - csd->addr = info->addr; - - if ((csd->flags & COFF_STYP_STD_MASK) == COFF_STYP_BSS) { - /* Don't output BSS sections. - * TODO: Check for non-reserve bytecodes? - */ - pos = 0; /* position = 0 because it's not in the file */ - csd->size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); - } else { - pos = ftell(info->f); - if (pos == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return 1; - } - - info->sect = sect; - info->csd = csd; - yasm_section_bcs_traverse(sect, info->errwarns, info, - coff_objfmt_output_bytecode); - - /* Sanity check final section size */ - if (yasm_errwarns_num_errors(info->errwarns, 0) == 0 && - csd->size != yasm_bc_next_offset(yasm_section_bcs_last(sect))) - yasm_internal_error( - N_("coff: section computed size did not match actual size")); - } - - /* Empty? Go on to next section */ - if (csd->size == 0) - return 0; - - if (!csd->isdebug) - info->addr += csd->size; - csd->scnptr = (unsigned long)pos; - - /* No relocations to output? Go on to next section */ - if (csd->nreloc == 0) - return 0; - - pos = ftell(info->f); - if (pos == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return 1; - } - csd->relptr = (unsigned long)pos; - - /* If >=64K relocs (for Win32/64), we set a flag in the section header - * (NRELOC_OVFL) and the first relocation contains the number of relocs. - */ - if (csd->nreloc >= 64*1024 && info->objfmt_coff->win32) { - localbuf = info->buf; - YASM_WRITE_32_L(localbuf, csd->nreloc+1); /* address of relocation */ - YASM_WRITE_32_L(localbuf, 0); /* relocated symbol */ - YASM_WRITE_16_L(localbuf, 0); /* type of relocation */ - fwrite(info->buf, 10, 1, info->f); - } - - reloc = (coff_reloc *)yasm_section_relocs_first(sect); - while (reloc) { - /*@null@*/ coff_symrec_data *csymd; - localbuf = info->buf; - - csymd = yasm_symrec_get_data(reloc->reloc.sym, &coff_symrec_data_cb); - if (!csymd) - yasm_internal_error( - N_("coff: no symbol data for relocated symbol")); - - yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); - localbuf += 4; /* address of relocation */ - YASM_WRITE_32_L(localbuf, csymd->index); /* relocated symbol */ - YASM_WRITE_16_L(localbuf, reloc->type); /* type of relocation */ - fwrite(info->buf, 10, 1, info->f); - - reloc = (coff_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); - } - - return 0; -} - -static int -coff_objfmt_output_sectstr(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - const char *name; - size_t len; - - /* Add to strtab if in win32 format and name > 8 chars */ - if (!info->objfmt_coff->win32) - return 0; - - name = yasm_section_get_name(sect); - len = strlen(name); - if (len > 8) - fwrite(name, len+1, 1, info->f); - return 0; -} - -static int -coff_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_objfmt_coff *objfmt_coff; - /*@dependent@*/ /*@null@*/ coff_section_data *csd; - unsigned char *localbuf; - unsigned long align = yasm_section_get_align(sect); - - assert(info != NULL); - objfmt_coff = info->objfmt_coff; - csd = yasm_section_get_data(sect, &coff_section_data_cb); - assert(csd != NULL); - - /* Check to see if alignment is supported size */ - if (align > 8192) - align = 8192; - - /* Convert alignment into flags setting */ - csd->flags &= ~COFF_STYP_ALIGN_MASK; - while (align != 0) { - csd->flags += 1<<COFF_STYP_ALIGN_SHIFT; - align >>= 1; - } - - /* section name */ - localbuf = info->buf; - if (strlen(yasm_section_get_name(sect)) > 8) { - char namenum[30]; - sprintf(namenum, "/%ld", csd->strtab_name); - strncpy((char *)localbuf, namenum, 8); - } else - strncpy((char *)localbuf, yasm_section_get_name(sect), 8); - localbuf += 8; - if (csd->isdebug) { - YASM_WRITE_32_L(localbuf, 0); /* physical address */ - YASM_WRITE_32_L(localbuf, 0); /* virtual address */ - } else { - YASM_WRITE_32_L(localbuf, csd->addr); /* physical address */ - if (COFF_SET_VMA) - YASM_WRITE_32_L(localbuf, csd->addr);/* virtual address */ - else - YASM_WRITE_32_L(localbuf, 0); /* virtual address */ - } - YASM_WRITE_32_L(localbuf, csd->size); /* section size */ - YASM_WRITE_32_L(localbuf, csd->scnptr); /* file ptr to data */ - YASM_WRITE_32_L(localbuf, csd->relptr); /* file ptr to relocs */ - YASM_WRITE_32_L(localbuf, 0); /* file ptr to line nums */ - if (csd->nreloc >= 64*1024) { - /* Win32/64 has special handling for this case. */ - if (objfmt_coff->win32) - csd->flags |= COFF_STYP_NRELOC_OVFL; - else { - yasm_warn_set(YASM_WARN_GENERAL, - N_("too many relocations in section `%s'"), - yasm_section_get_name(sect)); - yasm_errwarn_propagate(info->errwarns, 0); - } - YASM_WRITE_16_L(localbuf, 0xFFFF); /* max out */ - } else - YASM_WRITE_16_L(localbuf, csd->nreloc); /* num of relocation entries */ - YASM_WRITE_16_L(localbuf, 0); /* num of line number entries */ - YASM_WRITE_32_L(localbuf, csd->flags); /* flags */ - fwrite(info->buf, 40, 1, info->f); - - return 0; -} - -static int -coff_objfmt_count_sym(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - coff_symrec_data *sym_data; - - assert(info != NULL); - - sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - - if (info->all_syms || vis != YASM_SYM_LOCAL || yasm_symrec_is_abs(sym) || - (sym_data && sym_data->forcevis)) { - /* Save index in symrec data */ - if (!sym_data) - sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, - COFF_SYMTAB_AUX_NONE); - /* Set storage class based on visibility if not already set */ - if (sym_data->sclass == COFF_SCL_NULL) { - if (vis & (YASM_SYM_EXTERN|YASM_SYM_GLOBAL|YASM_SYM_COMMON)) - sym_data->sclass = COFF_SCL_EXT; - else - sym_data->sclass = COFF_SCL_STAT; - } - - sym_data->index = info->indx; - - info->indx += sym_data->numaux + 1; - } - return 0; -} - -static int -coff_objfmt_output_sym(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - int is_abs = yasm_symrec_is_abs(sym); - /*@dependent@*/ /*@null@*/ coff_symrec_data *csymd; - yasm_valparamhead *objext_valparams = - yasm_symrec_get_objext_valparams(sym); - csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - - assert(info != NULL); - - /* Look for "function" flag on global syms */ - if (csymd && csymd->type == 0 && (vis & YASM_SYM_GLOBAL) != 0) { - if (objext_valparams) { - const char *id = yasm_vp_id(yasm_vps_first(objext_valparams)); - if (yasm__strcasecmp(id, "function") == 0) - csymd->type = 0x20; - } - } - - /* Don't output local syms unless outputting all syms */ - if (info->all_syms || vis != YASM_SYM_LOCAL || is_abs || - (csymd && csymd->forcevis)) { - /*@only*/ char *name; - const yasm_expr *equ_val; - const yasm_intnum *intn; - unsigned char *localbuf; - size_t len; - int aux; - unsigned long value = 0; - unsigned int scnum = 0xfffe; /* -2 = debugging symbol */ - /*@dependent@*/ /*@null@*/ yasm_section *sect; - /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; - unsigned long scnlen = 0; /* for sect auxent */ - unsigned long nreloc = 0; /* for sect auxent */ - yasm_objfmt_coff *objfmt_coff = info->objfmt_coff; - - if (is_abs) - name = yasm__xstrdup(".absolut"); - else - name = yasm_symrec_get_global_name(sym, info->object); - len = strlen(name); - - /* Get symrec's of_data (needed for storage class) */ - if (!csymd) - yasm_internal_error(N_("coff: expected sym data to be present")); - - /* Look at symrec for value/scnum/etc. */ - if (yasm_symrec_get_label(sym, &precbc)) { - if (precbc) - sect = yasm_bc_get_section(precbc); - else - sect = NULL; - /* it's a label: get value and offset. - * If there is not a section, leave as debugging symbol. - */ - if (sect) { - /*@dependent@*/ /*@null@*/ coff_section_data *csectd; - csectd = yasm_section_get_data(sect, &coff_section_data_cb); - if (csectd) { - scnum = csectd->scnum; - scnlen = csectd->size; - nreloc = csectd->nreloc; - if (COFF_SET_VMA) - value = csectd->addr; - } else - yasm_internal_error(N_("didn't understand section")); - if (precbc) - value += yasm_bc_next_offset(precbc); - } - } else if ((equ_val = yasm_symrec_get_equ(sym))) { - yasm_expr *equ_val_copy = yasm_expr_copy(equ_val); - intn = yasm_expr_get_intnum(&equ_val_copy, 1); - if (!intn) { - if (vis & YASM_SYM_GLOBAL) { - yasm_error_set(YASM_ERROR_NOT_CONSTANT, - N_("global EQU value not an integer expression")); - yasm_errwarn_propagate(info->errwarns, equ_val->line); - } - } else - value = yasm_intnum_get_uint(intn); - yasm_expr_destroy(equ_val_copy); - - scnum = 0xffff; /* -1 = absolute symbol */ - } else { - if (vis & YASM_SYM_COMMON) { - /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; - csize_expr = yasm_symrec_get_common_size(sym); - assert(csize_expr != NULL); - intn = yasm_expr_get_intnum(csize_expr, 1); - if (!intn) { - yasm_error_set(YASM_ERROR_NOT_CONSTANT, - N_("COMMON data size not an integer expression")); - yasm_errwarn_propagate(info->errwarns, - (*csize_expr)->line); - } else - value = yasm_intnum_get_uint(intn); - scnum = 0; - } - if (vis & YASM_SYM_EXTERN) - scnum = 0; - } - - localbuf = info->buf; - if (len > 8) { - YASM_WRITE_32_L(localbuf, 0); /* "zeros" field */ - YASM_WRITE_32_L(localbuf, info->strtab_offset); /* strtab offset */ - info->strtab_offset += (unsigned long)(len+1); - } else { - /* <8 chars, so no string table entry needed */ - strncpy((char *)localbuf, name, 8); - localbuf += 8; - } - YASM_WRITE_32_L(localbuf, value); /* value */ - YASM_WRITE_16_L(localbuf, scnum); /* section number */ - YASM_WRITE_16_L(localbuf, csymd->type); /* type */ - YASM_WRITE_8(localbuf, csymd->sclass); /* storage class */ - YASM_WRITE_8(localbuf, csymd->numaux); /* number of aux entries */ - fwrite(info->buf, 18, 1, info->f); - for (aux=0; aux<csymd->numaux; aux++) { - localbuf = info->buf; - memset(localbuf, 0, 18); - switch (csymd->auxtype) { - case COFF_SYMTAB_AUX_NONE: - break; - case COFF_SYMTAB_AUX_SECT: - YASM_WRITE_32_L(localbuf, scnlen); /* section length */ - YASM_WRITE_16_L(localbuf, nreloc); /* number relocs */ - YASM_WRITE_16_L(localbuf, 0); /* number line nums */ - break; - case COFF_SYMTAB_AUX_FILE: - len = strlen(csymd->aux[0].fname); - if (len > 14) { - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, info->strtab_offset); - info->strtab_offset += (unsigned long)(len+1); - } else - strncpy((char *)localbuf, csymd->aux[0].fname, 14); - break; - default: - yasm_internal_error( - N_("coff: unrecognized aux symtab type")); - } - fwrite(info->buf, 18, 1, info->f); - } - yasm_xfree(name); - } - return 0; -} - -static int -coff_objfmt_output_str(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - /*@dependent@*/ /*@null@*/ coff_symrec_data *csymd; - csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - - assert(info != NULL); - - /* Don't output local syms unless outputting all syms */ - if (info->all_syms || vis != YASM_SYM_LOCAL || - (csymd && csymd->forcevis)) { - /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); - size_t len = strlen(name); - int aux; - - if (!csymd) - yasm_internal_error(N_("coff: expected sym data to be present")); - - if (len > 8) - fwrite(name, len+1, 1, info->f); - for (aux=0; aux<csymd->numaux; aux++) { - switch (csymd->auxtype) { - case COFF_SYMTAB_AUX_FILE: - len = strlen(csymd->aux[0].fname); - if (len > 14) - fwrite(csymd->aux[0].fname, len+1, 1, info->f); - break; - default: - break; - } - } - yasm_xfree(name); - } - return 0; -} - -static void -coff_objfmt_output(yasm_object *object, FILE *f, int all_syms, - yasm_errwarns *errwarns) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - coff_objfmt_output_info info; - unsigned char *localbuf; - long pos; - unsigned long symtab_pos; - unsigned long symtab_count; - unsigned int flags; - unsigned long ts; - - if (objfmt_coff->proc_frame) { - yasm_error_set_xref(objfmt_coff->proc_frame, - N_("procedure started here")); - yasm_error_set(YASM_ERROR_GENERAL, - N_("end of file in procedure frame")); - yasm_errwarn_propagate(errwarns, 0); - return; - } - - if (objfmt_coff->filesym_data->aux[0].fname) - yasm_xfree(objfmt_coff->filesym_data->aux[0].fname); +/* + * COFF (DJGPP) object format + * + * Copyright (C) 2002-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR AND OTHER 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 AUTHOR OR OTHER 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. + */ +#include <util.h> +#include <time.h> + +#include <libyasm.h> + +#include "coff-objfmt.h" + + +#define REGULAR_OUTBUF_SIZE 1024 + +/* Defining this to 0 sets all section VMA's to 0 rather than as the same as + * the LMA. According to the DJGPP COFF Spec, this should be set to 1 + * (VMA=LMA), and indeed DJGPP's GCC output shows VMA=LMA. However, NASM + * outputs VMA=0 (as if this was 0), and GNU objdump output looks a lot nicer + * with VMA=0. Who's right? This is #defined as changing this setting affects + * several places in the code. + */ +#define COFF_SET_VMA (!objfmt_coff->win32) + +#define COFF_MACHINE_I386 0x014C +#define COFF_MACHINE_AMD64 0x8664 + +#define COFF_F_LNNO 0x0004 /* line number info NOT present */ +#define COFF_F_LSYMS 0x0008 /* local symbols NOT present */ +#define COFF_F_AR32WR 0x0100 /* 32-bit little endian file */ + +typedef struct coff_reloc { + yasm_reloc reloc; + enum { + COFF_RELOC_ABSOLUTE = 0, /* absolute, no reloc needed */ + + /* I386 relocations */ + COFF_RELOC_I386_ADDR16 = 0x1, /* 16-bit absolute reference */ + COFF_RELOC_I386_REL16 = 0x2, /* 16-bit PC-relative reference */ + COFF_RELOC_I386_ADDR32 = 0x6, /* 32-bit absolute reference */ + COFF_RELOC_I386_ADDR32NB = 0x7, /* 32-bit absolute ref w/o base */ + COFF_RELOC_I386_SEG12 = 0x9, /* 16-bit absolute segment ref */ + COFF_RELOC_I386_SECTION = 0xA, /* section index */ + COFF_RELOC_I386_SECREL = 0xB, /* offset from start of segment */ + COFF_RELOC_I386_TOKEN = 0xC, /* CLR metadata token */ + COFF_RELOC_I386_SECREL7 = 0xD, /* 7-bit offset from base of sect */ + COFF_RELOC_I386_REL32 = 0x14, /* 32-bit PC-relative reference */ + + /* AMD64 relocations */ + COFF_RELOC_AMD64_ADDR64 = 0x1, /* 64-bit address (VA) */ + COFF_RELOC_AMD64_ADDR32 = 0x2, /* 32-bit address (VA) */ + COFF_RELOC_AMD64_ADDR32NB = 0x3, /* 32-bit address w/o base (RVA) */ + COFF_RELOC_AMD64_REL32 = 0x4, /* 32-bit relative (0 byte dist) */ + COFF_RELOC_AMD64_REL32_1 = 0x5, /* 32-bit relative (1 byte dist) */ + COFF_RELOC_AMD64_REL32_2 = 0x6, /* 32-bit relative (2 byte dist) */ + COFF_RELOC_AMD64_REL32_3 = 0x7, /* 32-bit relative (3 byte dist) */ + COFF_RELOC_AMD64_REL32_4 = 0x8, /* 32-bit relative (4 byte dist) */ + COFF_RELOC_AMD64_REL32_5 = 0x9, /* 32-bit relative (5 byte dist) */ + COFF_RELOC_AMD64_SECTION = 0xA, /* section index */ + COFF_RELOC_AMD64_SECREL = 0xB, /* 32-bit offset from base of sect */ + COFF_RELOC_AMD64_SECREL7 = 0xC, /* 7-bit offset from base of sect */ + COFF_RELOC_AMD64_TOKEN = 0xD /* CLR metadata token */ + } type; /* type of relocation */ +} coff_reloc; + +#define COFF_STYP_TEXT 0x00000020UL +#define COFF_STYP_DATA 0x00000040UL +#define COFF_STYP_BSS 0x00000080UL +#define COFF_STYP_INFO 0x00000200UL +#define COFF_STYP_STD_MASK 0x000003FFUL +#define COFF_STYP_ALIGN_MASK 0x00F00000UL +#define COFF_STYP_ALIGN_SHIFT 20 +#define COFF_STYP_NRELOC_OVFL 0x01000000UL +#define COFF_STYP_DISCARD 0x02000000UL +#define COFF_STYP_NOCACHE 0x04000000UL +#define COFF_STYP_NOPAGE 0x08000000UL +#define COFF_STYP_SHARED 0x10000000UL +#define COFF_STYP_EXECUTE 0x20000000UL +#define COFF_STYP_READ 0x40000000UL +#define COFF_STYP_WRITE 0x80000000UL +#define COFF_STYP_WIN32_MASK 0xFF000000UL + +#define COFF_FLAG_NOBASE (1UL<<0) /* Use no-base (NB) relocs */ + +typedef struct coff_section_data { + /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ + unsigned int scnum; /* section number (1=first section) */ + unsigned long flags; /* section flags (see COFF_STYP_* above) */ + unsigned long addr; /* starting memory address (first section -> 0) */ + unsigned long scnptr; /* file ptr to raw data */ + unsigned long size; /* size of raw data (section data) in bytes */ + unsigned long relptr; /* file ptr to relocation */ + unsigned long nreloc; /* number of relocation entries >64k -> error */ + unsigned long flags2; /* internal flags (see COFF_FLAG_* above) */ + unsigned long strtab_name; /* strtab offset of name if name > 8 chars */ + int isdebug; /* is a debug section? */ +} coff_section_data; + +typedef enum coff_symrec_sclass { + COFF_SCL_EFCN = 0xff, /* physical end of function */ + COFF_SCL_NULL = 0, + COFF_SCL_AUTO = 1, /* automatic variable */ + COFF_SCL_EXT = 2, /* external symbol */ + COFF_SCL_STAT = 3, /* static */ + COFF_SCL_REG = 4, /* register variable */ + COFF_SCL_EXTDEF = 5, /* external definition */ + COFF_SCL_LABEL = 6, /* label */ + COFF_SCL_ULABEL = 7, /* undefined label */ + COFF_SCL_MOS = 8, /* member of structure */ + COFF_SCL_ARG = 9, /* function argument */ + COFF_SCL_STRTAG = 10, /* structure tag */ + COFF_SCL_MOU = 11, /* member of union */ + COFF_SCL_UNTAG = 12, /* union tag */ + COFF_SCL_TPDEF = 13, /* type definition */ + COFF_SCL_USTATIC = 14, /* undefined static */ + COFF_SCL_ENTAG = 15, /* enumeration tag */ + COFF_SCL_MOE = 16, /* member of enumeration */ + COFF_SCL_REGPARM = 17, /* register parameter */ + COFF_SCL_FIELD = 18, /* bit field */ + COFF_SCL_AUTOARG = 19, /* auto argument */ + COFF_SCL_LASTENT = 20, /* dummy entry (end of block) */ + COFF_SCL_BLOCK = 100, /* ".bb" or ".eb" */ + COFF_SCL_FCN = 101, /* ".bf" or ".ef" */ + COFF_SCL_EOS = 102, /* end of structure */ + COFF_SCL_FILE = 103, /* file name */ + COFF_SCL_LINE = 104, /* line # reformatted as symbol table entry */ + COFF_SCL_ALIAS = 105, /* duplicate tag */ + COFF_SCL_HIDDEN = 106 /* ext symbol in dmert public lib */ +} coff_symrec_sclass; + +typedef union coff_symtab_auxent { + /* no data needed for section symbol auxent, all info avail from sym */ + /*@owned@*/ char *fname; /* filename aux entry */ +} coff_symtab_auxent; + +typedef enum coff_symtab_auxtype { + COFF_SYMTAB_AUX_NONE = 0, + COFF_SYMTAB_AUX_SECT, + COFF_SYMTAB_AUX_FILE +} coff_symtab_auxtype; + +typedef struct coff_symrec_data { + int forcevis; /* force visibility in symbol table */ + unsigned long index; /* assigned COFF symbol table index */ + unsigned int type; /* type */ + coff_symrec_sclass sclass; /* storage class */ + + int numaux; /* number of auxiliary entries */ + coff_symtab_auxtype auxtype; /* type of aux entries */ + coff_symtab_auxent aux[1]; /* actually may be any size (including 0) */ +} coff_symrec_data; + +typedef struct yasm_objfmt_coff { + yasm_objfmt_base objfmt; /* base structure */ + + unsigned int parse_scnum; /* sect numbering in parser */ + int win32; /* nonzero for win32/64 output */ + int win64; /* nonzero for win64 output */ + + unsigned int machine; /* COFF machine to use */ + + coff_symrec_data *filesym_data; /* Data for .file symbol */ + + /* data for .def/.endef and related directives */ + coff_symrec_data *def_sym; /* symbol specified by .def */ + + /* data for win64 proc_frame and related directives */ + unsigned long proc_frame; /* Line number of start of proc, or 0 */ + unsigned long done_prolog; /* Line number of end of prologue, or 0 */ + /*@null@*/ coff_unwind_info *unwind; /* Unwind info */ + + yasm_symrec *ssym_imagebase; /* ..imagebase symbol for win64 */ +} yasm_objfmt_coff; + +typedef struct coff_objfmt_output_info { + yasm_object *object; + yasm_objfmt_coff *objfmt_coff; + yasm_errwarns *errwarns; + /*@dependent@*/ FILE *f; + /*@only@*/ unsigned char *buf; + yasm_section *sect; + /*@dependent@*/ coff_section_data *csd; + unsigned long addr; /* start of next section */ + + unsigned long indx; /* current symbol index */ + int all_syms; /* outputting all symbols? */ + unsigned long strtab_offset; /* current string table offset */ +} coff_objfmt_output_info; + +static void coff_section_data_destroy(/*@only@*/ void *d); +static void coff_section_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback coff_section_data_cb = { + coff_section_data_destroy, + coff_section_data_print +}; + +static void coff_symrec_data_destroy(/*@only@*/ void *d); +static void coff_symrec_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback coff_symrec_data_cb = { + coff_symrec_data_destroy, + coff_symrec_data_print +}; + +/* Bytecode callback function prototypes */ +static void win32_sxdata_bc_destroy(void *contents); +static void win32_sxdata_bc_print(const void *contents, FILE *f, + int indent_level); +static int win32_sxdata_bc_calc_len + (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); +static int win32_sxdata_bc_tobytes + (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +/* Bytecode callback structures */ +static const yasm_bytecode_callback win32_sxdata_bc_callback = { + win32_sxdata_bc_destroy, + win32_sxdata_bc_print, + yasm_bc_finalize_common, + NULL, + win32_sxdata_bc_calc_len, + yasm_bc_expand_common, + win32_sxdata_bc_tobytes, + 0 +}; + +yasm_objfmt_module yasm_coff_LTX_objfmt; +yasm_objfmt_module yasm_win32_LTX_objfmt; +yasm_objfmt_module yasm_win64_LTX_objfmt; + + +static /*@dependent@*/ coff_symrec_data * +coff_objfmt_sym_set_data(yasm_symrec *sym, coff_symrec_sclass sclass, + int numaux, coff_symtab_auxtype auxtype) +{ + coff_symrec_data *sym_data; + + sym_data = yasm_xmalloc(sizeof(coff_symrec_data) + + (numaux-1)*sizeof(coff_symtab_auxent)); + sym_data->forcevis = 0; + sym_data->index = 0; + sym_data->type = 0; + sym_data->sclass = sclass; + sym_data->numaux = numaux; + sym_data->auxtype = auxtype; + + yasm_symrec_add_data(sym, &coff_symrec_data_cb, sym_data); + + return sym_data; +} + +static yasm_objfmt_coff * +coff_common_create(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = yasm_xmalloc(sizeof(yasm_objfmt_coff)); + yasm_symrec *filesym; + + /* Only support x86 arch */ + if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") != 0) { + yasm_xfree(objfmt_coff); + return NULL; + } + + objfmt_coff->parse_scnum = 1; /* section numbering starts at 1 */ + + /* FIXME: misuse of NULL bytecode here; it works, but only barely. */ + filesym = yasm_symtab_define_special(object->symtab, ".file", + YASM_SYM_GLOBAL); + objfmt_coff->filesym_data = + coff_objfmt_sym_set_data(filesym, COFF_SCL_FILE, 1, + COFF_SYMTAB_AUX_FILE); + /* Filename is set in coff_objfmt_output */ + objfmt_coff->filesym_data->aux[0].fname = NULL; + + objfmt_coff->proc_frame = 0; + objfmt_coff->done_prolog = 0; + objfmt_coff->unwind = NULL; + objfmt_coff->ssym_imagebase = NULL; + + return objfmt_coff; +} + +static yasm_objfmt * +coff_objfmt_create(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = coff_common_create(object); + + if (objfmt_coff) { + /* Support x86 and amd64 machines of x86 arch */ + if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), "x86") == 0) + objfmt_coff->machine = COFF_MACHINE_I386; + else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), + "amd64") == 0) + objfmt_coff->machine = COFF_MACHINE_AMD64; + else { + yasm_xfree(objfmt_coff); + return NULL; + } + + objfmt_coff->objfmt.module = &yasm_coff_LTX_objfmt; + objfmt_coff->win32 = 0; + objfmt_coff->win64 = 0; + } + return (yasm_objfmt *)objfmt_coff; +} + +static yasm_objfmt * +win32_objfmt_create(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = coff_common_create(object); + + if (objfmt_coff) { + /* Support x86 and amd64 machines of x86 arch. + * (amd64 machine supported for backwards compatibility) + */ + if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), + "x86") == 0) { + objfmt_coff->machine = COFF_MACHINE_I386; + objfmt_coff->objfmt.module = &yasm_win32_LTX_objfmt; + objfmt_coff->win64 = 0; + } else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), + "amd64") == 0) { + objfmt_coff->machine = COFF_MACHINE_AMD64; + objfmt_coff->objfmt.module = &yasm_win64_LTX_objfmt; + objfmt_coff->win64 = 1; + } else { + yasm_xfree(objfmt_coff); + return NULL; + } + + objfmt_coff->win32 = 1; + /* Define a @feat.00 symbol for win32 safeseh handling */ + if (!objfmt_coff->win64) { + yasm_symrec *feat00; + coff_symrec_data *sym_data; + feat00 = yasm_symtab_define_equ(object->symtab, "@feat.00", + yasm_expr_create_ident(yasm_expr_int( + yasm_intnum_create_uint(1)), 0), 0); + sym_data = coff_objfmt_sym_set_data(feat00, COFF_SCL_STAT, 0, + COFF_SYMTAB_AUX_NONE); + sym_data->forcevis = 1; + } + } + return (yasm_objfmt *)objfmt_coff; +} + +static yasm_objfmt * +win64_objfmt_create(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = coff_common_create(object); + + if (objfmt_coff) { + /* Support amd64 machine of x86 arch */ + if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), + "amd64") == 0) { + objfmt_coff->machine = COFF_MACHINE_AMD64; + } else { + yasm_xfree(objfmt_coff); + return NULL; + } + + objfmt_coff->objfmt.module = &yasm_win64_LTX_objfmt; + objfmt_coff->win32 = 1; + objfmt_coff->win64 = 1; + objfmt_coff->ssym_imagebase = + yasm_symtab_define_label(object->symtab, "..imagebase", NULL, 0, 0); + } + return (yasm_objfmt *)objfmt_coff; +} + +static void +coff_objfmt_init_new_section(yasm_section *sect, unsigned long line) +{ + yasm_object *object = yasm_section_get_object(sect); + const char *sectname = yasm_section_get_name(sect); + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + coff_section_data *data; + yasm_symrec *sym; + + data = yasm_xmalloc(sizeof(coff_section_data)); + data->scnum = objfmt_coff->parse_scnum++; + data->flags = 0; + data->addr = 0; + data->scnptr = 0; + data->size = 0; + data->relptr = 0; + data->nreloc = 0; + data->flags2 = 0; + data->strtab_name = 0; + data->isdebug = 0; + + if (yasm__strncasecmp(sectname, ".debug", 6)==0) { + data->flags = COFF_STYP_DATA; + if (objfmt_coff->win32) + data->flags |= COFF_STYP_DISCARD|COFF_STYP_READ; + data->isdebug = 1; + } else + data->flags = COFF_STYP_TEXT; + + yasm_section_add_data(sect, &coff_section_data_cb, data); + + sym = yasm_symtab_define_label(object->symtab, sectname, + yasm_section_bcs_first(sect), 1, line); + yasm_symrec_declare(sym, YASM_SYM_GLOBAL, line); + coff_objfmt_sym_set_data(sym, COFF_SCL_STAT, 1, COFF_SYMTAB_AUX_SECT); + data->sym = sym; +} + +static int +coff_objfmt_set_section_addr(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ coff_section_data *csd; + + assert(info != NULL); + csd = yasm_section_get_data(sect, &coff_section_data_cb); + assert(csd != NULL); + + csd->addr = info->addr; + info->addr += yasm_bc_next_offset(yasm_section_bcs_last(sect)); + + return 0; +} + +static int +coff_objfmt_output_value(yasm_value *value, unsigned char *buf, + unsigned int destsize, unsigned long offset, + yasm_bytecode *bc, int warn, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_objfmt_coff *objfmt_coff; + /*@only@*/ /*@null@*/ yasm_intnum *dist = NULL; + /*@dependent@*/ /*@null@*/ yasm_intnum *intn; + unsigned long intn_val, intn_minus; + int retval; + unsigned int valsize = value->size; + + assert(info != NULL); + objfmt_coff = info->objfmt_coff; + + if (value->abs) + value->abs = yasm_expr_simplify(value->abs, 1); + + /* Try to output constant and PC-relative section-local first. + * Note this does NOT output any value with a SEG, WRT, external, + * cross-section, or non-PC-relative reference (those are handled below). + */ + switch (yasm_value_output_basic(value, buf, destsize, bc, warn, + info->object->arch)) { + case -1: + return 1; + case 0: + break; + default: + return 0; + } + + /* Handle other expressions, with relocation if necessary */ + if (value->rshift > 0 + || (value->seg_of && (value->wrt || value->curpos_rel)) + || (value->section_rel && (value->wrt || value->curpos_rel))) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: relocation too complex")); + return 1; + } + + intn_val = 0; + intn_minus = 0; + if (value->rel) { + yasm_sym_vis vis = yasm_symrec_get_visibility(value->rel); + /*@dependent@*/ /*@null@*/ yasm_symrec *sym = value->rel; + unsigned long addr; + coff_reloc *reloc; + int nobase = info->csd->flags2 & COFF_FLAG_NOBASE; + + /* Sometimes we want the relocation to be generated against one + * symbol but the value generated correspond to a different symbol. + * This is done through (sym being referenced) WRT (sym used for + * reloc). Note both syms need to be in the same section! + */ + if (value->wrt && value->wrt == objfmt_coff->ssym_imagebase) + nobase = 1; + else if (value->wrt) { + /*@dependent@*/ /*@null@*/ yasm_bytecode *rel_precbc, *wrt_precbc; + + if (!yasm_symrec_get_label(sym, &rel_precbc) + || !yasm_symrec_get_label(value->wrt, &wrt_precbc)) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: wrt expression too complex")); + return 1; + } + dist = yasm_calc_bc_dist(wrt_precbc, rel_precbc); + if (!dist) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: cannot wrt across sections")); + return 1; + } + sym = value->wrt; + } + + if (vis & YASM_SYM_COMMON) { + /* In standard COFF, COMMON symbols have their length added in */ + if (!objfmt_coff->win32) { + /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; + /*@dependent@*/ /*@null@*/ yasm_intnum *common_size; + + csize_expr = yasm_symrec_get_common_size(sym); + assert(csize_expr != NULL); + common_size = yasm_expr_get_intnum(csize_expr, 1); + if (!common_size) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: common size too complex")); + return 1; + } + + if (yasm_intnum_sign(common_size) < 0) { + yasm_error_set(YASM_ERROR_VALUE, + N_("coff: common size is negative")); + return 1; + } + + intn_val += yasm_intnum_get_uint(common_size); + } + } else if (!(vis & YASM_SYM_EXTERN) && !objfmt_coff->win64) { + /*@dependent@*/ /*@null@*/ yasm_bytecode *sym_precbc; + + /* Local symbols need relocation to their section's start */ + if (yasm_symrec_get_label(sym, &sym_precbc)) { + yasm_section *sym_sect = yasm_bc_get_section(sym_precbc); + /*@null@*/ coff_section_data *sym_csd; + sym_csd = yasm_section_get_data(sym_sect, + &coff_section_data_cb); + assert(sym_csd != NULL); + sym = sym_csd->sym; + intn_val = yasm_bc_next_offset(sym_precbc); + if (COFF_SET_VMA) + intn_val += sym_csd->addr; + } + } + + if (value->curpos_rel) { + /* For standard COFF, need to adjust to start of section, e.g. + * subtract out the bytecode offset. + * For Win32 COFF, need to adjust based on value size and position. + * For Win64 COFF that's IP-relative, adjust to next bytecode; + * the difference between the offset+destsize and BC length is + * taken care of by special relocation types. + */ + if (objfmt_coff->win64 && value->ip_rel) + intn_val += bc->len*bc->mult_int; + else if (objfmt_coff->win32) + intn_val += offset+destsize; + else + intn_minus = bc->offset; + } + + if (value->seg_of) { + /* Segment generation; zero value. */ + intn_val = 0; + intn_minus = 0; + } + + /* Generate reloc */ + reloc = yasm_xmalloc(sizeof(coff_reloc)); + addr = bc->offset + offset; + if (COFF_SET_VMA) + addr += info->addr; + reloc->reloc.addr = yasm_intnum_create_uint(addr); + reloc->reloc.sym = sym; + + if (value->curpos_rel) { + if (objfmt_coff->machine == COFF_MACHINE_I386) { + if (valsize == 32) + reloc->type = COFF_RELOC_I386_REL32; + else { + yasm_error_set(YASM_ERROR_TYPE, + N_("coff: invalid relocation size")); + return 1; + } + } else if (objfmt_coff->machine == COFF_MACHINE_AMD64) { + if (valsize != 32) { + yasm_error_set(YASM_ERROR_TYPE, + N_("coff: invalid relocation size")); + return 1; + } + if (!value->ip_rel) + reloc->type = COFF_RELOC_AMD64_REL32; + else switch (bc->len*bc->mult_int - (offset+destsize)) { + case 0: + reloc->type = COFF_RELOC_AMD64_REL32; + break; + case 1: + reloc->type = COFF_RELOC_AMD64_REL32_1; + break; + case 2: + reloc->type = COFF_RELOC_AMD64_REL32_2; + break; + case 3: + reloc->type = COFF_RELOC_AMD64_REL32_3; + break; + case 4: + reloc->type = COFF_RELOC_AMD64_REL32_4; + break; + case 5: + reloc->type = COFF_RELOC_AMD64_REL32_5; + break; + default: + yasm_error_set(YASM_ERROR_TYPE, + N_("coff: invalid relocation size")); + return 1; + } + } else + yasm_internal_error(N_("coff objfmt: unrecognized machine")); + } else if (value->seg_of) { + if (objfmt_coff->machine == COFF_MACHINE_I386) + reloc->type = COFF_RELOC_I386_SECTION; + else if (objfmt_coff->machine == COFF_MACHINE_AMD64) + reloc->type = COFF_RELOC_AMD64_SECTION; + else + yasm_internal_error(N_("coff objfmt: unrecognized machine")); + } else if (value->section_rel) { + if (objfmt_coff->machine == COFF_MACHINE_I386) + reloc->type = COFF_RELOC_I386_SECREL; + else if (objfmt_coff->machine == COFF_MACHINE_AMD64) + reloc->type = COFF_RELOC_AMD64_SECREL; + else + yasm_internal_error(N_("coff objfmt: unrecognized machine")); + } else { + if (objfmt_coff->machine == COFF_MACHINE_I386) { + if (nobase) + reloc->type = COFF_RELOC_I386_ADDR32NB; + else + reloc->type = COFF_RELOC_I386_ADDR32; + } else if (objfmt_coff->machine == COFF_MACHINE_AMD64) { + if (valsize == 32) { + if (nobase) + reloc->type = COFF_RELOC_AMD64_ADDR32NB; + else + reloc->type = COFF_RELOC_AMD64_ADDR32; + } else if (valsize == 64) + reloc->type = COFF_RELOC_AMD64_ADDR64; + else { + yasm_error_set(YASM_ERROR_TYPE, + N_("coff: invalid relocation size")); + return 1; + } + } else + yasm_internal_error(N_("coff objfmt: unrecognized machine")); + } + info->csd->nreloc++; + yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); + } + + /* Build up final integer output from intn_val, intn_minus, value->abs, + * and dist. We do all this at the end to avoid creating temporary + * intnums above (except for dist). + */ + if (intn_minus <= intn_val) + intn = yasm_intnum_create_uint(intn_val-intn_minus); + else { + intn = yasm_intnum_create_uint(intn_minus-intn_val); + yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); + } + + if (value->abs) { + yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); + if (!intn2) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: relocation too complex")); + yasm_intnum_destroy(intn); + if (dist) + yasm_intnum_destroy(dist); + return 1; + } + yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); + } + + if (dist) { + yasm_intnum_calc(intn, YASM_EXPR_ADD, dist); + yasm_intnum_destroy(dist); + } + + retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, + valsize, 0, bc, warn); + yasm_intnum_destroy(intn); + return retval; +} + +static int +coff_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + /*@null@*/ /*@only@*/ unsigned char *bigbuf; + unsigned long size = REGULAR_OUTBUF_SIZE; + int gap; + + assert(info != NULL); + + bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, + coff_objfmt_output_value, NULL); + + /* Don't bother doing anything else if size ended up being 0. */ + if (size == 0) { + if (bigbuf) + yasm_xfree(bigbuf); + return 0; + } + + info->csd->size += size; + + /* Warn that gaps are converted to 0 and write out the 0's. */ + if (gap) { + unsigned long left; + yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, + N_("uninitialized space declared in code/data section: zeroing")); + /* Write out in chunks */ + memset(info->buf, 0, REGULAR_OUTBUF_SIZE); + left = size; + while (left > REGULAR_OUTBUF_SIZE) { + fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); + left -= REGULAR_OUTBUF_SIZE; + } + fwrite(info->buf, left, 1, info->f); + } else { + /* Output buf (or bigbuf if non-NULL) to file */ + fwrite(bigbuf ? bigbuf : info->buf, (size_t)size, 1, info->f); + } + + /* If bigbuf was allocated, free it */ + if (bigbuf) + yasm_xfree(bigbuf); + + return 0; +} + +static int +coff_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ coff_section_data *csd; + long pos; + coff_reloc *reloc; + unsigned char *localbuf; + + assert(info != NULL); + csd = yasm_section_get_data(sect, &coff_section_data_cb); + assert(csd != NULL); + + /* Add to strtab if in win32 format and name > 8 chars */ + if (info->objfmt_coff->win32) { + size_t namelen = strlen(yasm_section_get_name(sect)); + if (namelen > 8) { + csd->strtab_name = info->strtab_offset; + info->strtab_offset += (unsigned long)(namelen + 1); + } + } + + if (!csd->isdebug) + csd->addr = info->addr; + + if ((csd->flags & COFF_STYP_STD_MASK) == COFF_STYP_BSS) { + /* Don't output BSS sections. + * TODO: Check for non-reserve bytecodes? + */ + pos = 0; /* position = 0 because it's not in the file */ + csd->size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); + } else { + pos = ftell(info->f); + if (pos == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return 1; + } + + info->sect = sect; + info->csd = csd; + yasm_section_bcs_traverse(sect, info->errwarns, info, + coff_objfmt_output_bytecode); + + /* Sanity check final section size */ + if (yasm_errwarns_num_errors(info->errwarns, 0) == 0 && + csd->size != yasm_bc_next_offset(yasm_section_bcs_last(sect))) + yasm_internal_error( + N_("coff: section computed size did not match actual size")); + } + + /* Empty? Go on to next section */ + if (csd->size == 0) + return 0; + + if (!csd->isdebug) + info->addr += csd->size; + csd->scnptr = (unsigned long)pos; + + /* No relocations to output? Go on to next section */ + if (csd->nreloc == 0) + return 0; + + pos = ftell(info->f); + if (pos == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return 1; + } + csd->relptr = (unsigned long)pos; + + /* If >=64K relocs (for Win32/64), we set a flag in the section header + * (NRELOC_OVFL) and the first relocation contains the number of relocs. + */ + if (csd->nreloc >= 64*1024 && info->objfmt_coff->win32) { + localbuf = info->buf; + YASM_WRITE_32_L(localbuf, csd->nreloc+1); /* address of relocation */ + YASM_WRITE_32_L(localbuf, 0); /* relocated symbol */ + YASM_WRITE_16_L(localbuf, 0); /* type of relocation */ + fwrite(info->buf, 10, 1, info->f); + } + + reloc = (coff_reloc *)yasm_section_relocs_first(sect); + while (reloc) { + /*@null@*/ coff_symrec_data *csymd; + localbuf = info->buf; + + csymd = yasm_symrec_get_data(reloc->reloc.sym, &coff_symrec_data_cb); + if (!csymd) + yasm_internal_error( + N_("coff: no symbol data for relocated symbol")); + + yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); + localbuf += 4; /* address of relocation */ + YASM_WRITE_32_L(localbuf, csymd->index); /* relocated symbol */ + YASM_WRITE_16_L(localbuf, reloc->type); /* type of relocation */ + fwrite(info->buf, 10, 1, info->f); + + reloc = (coff_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); + } + + return 0; +} + +static int +coff_objfmt_output_sectstr(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + const char *name; + size_t len; + + /* Add to strtab if in win32 format and name > 8 chars */ + if (!info->objfmt_coff->win32) + return 0; + + name = yasm_section_get_name(sect); + len = strlen(name); + if (len > 8) + fwrite(name, len+1, 1, info->f); + return 0; +} + +static int +coff_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_objfmt_coff *objfmt_coff; + /*@dependent@*/ /*@null@*/ coff_section_data *csd; + unsigned char *localbuf; + unsigned long align = yasm_section_get_align(sect); + + assert(info != NULL); + objfmt_coff = info->objfmt_coff; + csd = yasm_section_get_data(sect, &coff_section_data_cb); + assert(csd != NULL); + + /* Check to see if alignment is supported size */ + if (align > 8192) + align = 8192; + + /* Convert alignment into flags setting */ + csd->flags &= ~COFF_STYP_ALIGN_MASK; + while (align != 0) { + csd->flags += 1<<COFF_STYP_ALIGN_SHIFT; + align >>= 1; + } + + /* section name */ + localbuf = info->buf; + if (strlen(yasm_section_get_name(sect)) > 8) { + char namenum[30]; + sprintf(namenum, "/%ld", csd->strtab_name); + strncpy((char *)localbuf, namenum, 8); + } else + strncpy((char *)localbuf, yasm_section_get_name(sect), 8); + localbuf += 8; + if (csd->isdebug) { + YASM_WRITE_32_L(localbuf, 0); /* physical address */ + YASM_WRITE_32_L(localbuf, 0); /* virtual address */ + } else { + YASM_WRITE_32_L(localbuf, csd->addr); /* physical address */ + if (COFF_SET_VMA) + YASM_WRITE_32_L(localbuf, csd->addr);/* virtual address */ + else + YASM_WRITE_32_L(localbuf, 0); /* virtual address */ + } + YASM_WRITE_32_L(localbuf, csd->size); /* section size */ + YASM_WRITE_32_L(localbuf, csd->scnptr); /* file ptr to data */ + YASM_WRITE_32_L(localbuf, csd->relptr); /* file ptr to relocs */ + YASM_WRITE_32_L(localbuf, 0); /* file ptr to line nums */ + if (csd->nreloc >= 64*1024) { + /* Win32/64 has special handling for this case. */ + if (objfmt_coff->win32) + csd->flags |= COFF_STYP_NRELOC_OVFL; + else { + yasm_warn_set(YASM_WARN_GENERAL, + N_("too many relocations in section `%s'"), + yasm_section_get_name(sect)); + yasm_errwarn_propagate(info->errwarns, 0); + } + YASM_WRITE_16_L(localbuf, 0xFFFF); /* max out */ + } else + YASM_WRITE_16_L(localbuf, csd->nreloc); /* num of relocation entries */ + YASM_WRITE_16_L(localbuf, 0); /* num of line number entries */ + YASM_WRITE_32_L(localbuf, csd->flags); /* flags */ + fwrite(info->buf, 40, 1, info->f); + + return 0; +} + +static int +coff_objfmt_count_sym(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + coff_symrec_data *sym_data; + + assert(info != NULL); + + sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + + if (info->all_syms || vis != YASM_SYM_LOCAL || yasm_symrec_is_abs(sym) || + (sym_data && sym_data->forcevis)) { + /* Save index in symrec data */ + if (!sym_data) + sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, + COFF_SYMTAB_AUX_NONE); + /* Set storage class based on visibility if not already set */ + if (sym_data->sclass == COFF_SCL_NULL) { + if (vis & (YASM_SYM_EXTERN|YASM_SYM_GLOBAL|YASM_SYM_COMMON)) + sym_data->sclass = COFF_SCL_EXT; + else + sym_data->sclass = COFF_SCL_STAT; + } + + sym_data->index = info->indx; + + info->indx += sym_data->numaux + 1; + } + return 0; +} + +static int +coff_objfmt_output_sym(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + int is_abs = yasm_symrec_is_abs(sym); + /*@dependent@*/ /*@null@*/ coff_symrec_data *csymd; + yasm_valparamhead *objext_valparams = + yasm_symrec_get_objext_valparams(sym); + csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + + assert(info != NULL); + + /* Look for "function" flag on global syms */ + if (csymd && csymd->type == 0 && (vis & YASM_SYM_GLOBAL) != 0) { + if (objext_valparams) { + const char *id = yasm_vp_id(yasm_vps_first(objext_valparams)); + if (yasm__strcasecmp(id, "function") == 0) + csymd->type = 0x20; + } + } + + /* Don't output local syms unless outputting all syms */ + if (info->all_syms || vis != YASM_SYM_LOCAL || is_abs || + (csymd && csymd->forcevis)) { + /*@only*/ char *name; + const yasm_expr *equ_val; + const yasm_intnum *intn; + unsigned char *localbuf; + size_t len; + int aux; + unsigned long value = 0; + unsigned int scnum = 0xfffe; /* -2 = debugging symbol */ + /*@dependent@*/ /*@null@*/ yasm_section *sect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + unsigned long scnlen = 0; /* for sect auxent */ + unsigned long nreloc = 0; /* for sect auxent */ + yasm_objfmt_coff *objfmt_coff = info->objfmt_coff; + + if (is_abs) + name = yasm__xstrdup(".absolut"); + else + name = yasm_symrec_get_global_name(sym, info->object); + len = strlen(name); + + /* Get symrec's of_data (needed for storage class) */ + if (!csymd) + yasm_internal_error(N_("coff: expected sym data to be present")); + + /* Look at symrec for value/scnum/etc. */ + if (yasm_symrec_get_label(sym, &precbc)) { + if (precbc) + sect = yasm_bc_get_section(precbc); + else + sect = NULL; + /* it's a label: get value and offset. + * If there is not a section, leave as debugging symbol. + */ + if (sect) { + /*@dependent@*/ /*@null@*/ coff_section_data *csectd; + csectd = yasm_section_get_data(sect, &coff_section_data_cb); + if (csectd) { + scnum = csectd->scnum; + scnlen = csectd->size; + nreloc = csectd->nreloc; + if (COFF_SET_VMA) + value = csectd->addr; + } else + yasm_internal_error(N_("didn't understand section")); + if (precbc) + value += yasm_bc_next_offset(precbc); + } + } else if ((equ_val = yasm_symrec_get_equ(sym))) { + yasm_expr *equ_val_copy = yasm_expr_copy(equ_val); + intn = yasm_expr_get_intnum(&equ_val_copy, 1); + if (!intn) { + if (vis & YASM_SYM_GLOBAL) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("global EQU value not an integer expression")); + yasm_errwarn_propagate(info->errwarns, equ_val->line); + } + } else + value = yasm_intnum_get_uint(intn); + yasm_expr_destroy(equ_val_copy); + + scnum = 0xffff; /* -1 = absolute symbol */ + } else { + if (vis & YASM_SYM_COMMON) { + /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; + csize_expr = yasm_symrec_get_common_size(sym); + assert(csize_expr != NULL); + intn = yasm_expr_get_intnum(csize_expr, 1); + if (!intn) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("COMMON data size not an integer expression")); + yasm_errwarn_propagate(info->errwarns, + (*csize_expr)->line); + } else + value = yasm_intnum_get_uint(intn); + scnum = 0; + } + if (vis & YASM_SYM_EXTERN) + scnum = 0; + } + + localbuf = info->buf; + if (len > 8) { + YASM_WRITE_32_L(localbuf, 0); /* "zeros" field */ + YASM_WRITE_32_L(localbuf, info->strtab_offset); /* strtab offset */ + info->strtab_offset += (unsigned long)(len+1); + } else { + /* <8 chars, so no string table entry needed */ + strncpy((char *)localbuf, name, 8); + localbuf += 8; + } + YASM_WRITE_32_L(localbuf, value); /* value */ + YASM_WRITE_16_L(localbuf, scnum); /* section number */ + YASM_WRITE_16_L(localbuf, csymd->type); /* type */ + YASM_WRITE_8(localbuf, csymd->sclass); /* storage class */ + YASM_WRITE_8(localbuf, csymd->numaux); /* number of aux entries */ + fwrite(info->buf, 18, 1, info->f); + for (aux=0; aux<csymd->numaux; aux++) { + localbuf = info->buf; + memset(localbuf, 0, 18); + switch (csymd->auxtype) { + case COFF_SYMTAB_AUX_NONE: + break; + case COFF_SYMTAB_AUX_SECT: + YASM_WRITE_32_L(localbuf, scnlen); /* section length */ + YASM_WRITE_16_L(localbuf, nreloc); /* number relocs */ + YASM_WRITE_16_L(localbuf, 0); /* number line nums */ + break; + case COFF_SYMTAB_AUX_FILE: + len = strlen(csymd->aux[0].fname); + if (len > 14) { + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, info->strtab_offset); + info->strtab_offset += (unsigned long)(len+1); + } else + strncpy((char *)localbuf, csymd->aux[0].fname, 14); + break; + default: + yasm_internal_error( + N_("coff: unrecognized aux symtab type")); + } + fwrite(info->buf, 18, 1, info->f); + } + yasm_xfree(name); + } + return 0; +} + +static int +coff_objfmt_output_str(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + /*@dependent@*/ /*@null@*/ coff_symrec_data *csymd; + csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + + assert(info != NULL); + + /* Don't output local syms unless outputting all syms */ + if (info->all_syms || vis != YASM_SYM_LOCAL || + (csymd && csymd->forcevis)) { + /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); + size_t len = strlen(name); + int aux; + + if (!csymd) + yasm_internal_error(N_("coff: expected sym data to be present")); + + if (len > 8) + fwrite(name, len+1, 1, info->f); + for (aux=0; aux<csymd->numaux; aux++) { + switch (csymd->auxtype) { + case COFF_SYMTAB_AUX_FILE: + len = strlen(csymd->aux[0].fname); + if (len > 14) + fwrite(csymd->aux[0].fname, len+1, 1, info->f); + break; + default: + break; + } + } + yasm_xfree(name); + } + return 0; +} + +static void +coff_objfmt_output(yasm_object *object, FILE *f, int all_syms, + yasm_errwarns *errwarns) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + coff_objfmt_output_info info; + unsigned char *localbuf; + long pos; + unsigned long symtab_pos; + unsigned long symtab_count; + unsigned int flags; + unsigned long ts; + + if (objfmt_coff->proc_frame) { + yasm_error_set_xref(objfmt_coff->proc_frame, + N_("procedure started here")); + yasm_error_set(YASM_ERROR_GENERAL, + N_("end of file in procedure frame")); + yasm_errwarn_propagate(errwarns, 0); + return; + } + + if (objfmt_coff->filesym_data->aux[0].fname) + yasm_xfree(objfmt_coff->filesym_data->aux[0].fname); if (!object->deb_filename) { object->deb_filename = yasm_replace_path( objfmt_coff->objfmt.module->replace_map, objfmt_coff->objfmt.module->replace_map_size, object->src_filename, strlen(object->src_filename)); } - objfmt_coff->filesym_data->aux[0].fname = + objfmt_coff->filesym_data->aux[0].fname = yasm__xstrdup(object->deb_filename); - - /* Force all syms for win64 because they're needed for relocations. - * FIXME: Not *all* syms need to be output, only the ones needed for - * relocation. Find a way to do that someday. - */ - all_syms |= objfmt_coff->win64; - - info.strtab_offset = 4; - info.object = object; - info.objfmt_coff = objfmt_coff; - info.errwarns = errwarns; - info.f = f; - info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); - - /* Allocate space for headers by seeking forward */ - if (fseek(f, (long)(20+40*(objfmt_coff->parse_scnum-1)), SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - /* Finalize symbol table (assign index to each symbol) */ - info.indx = 0; - info.all_syms = all_syms; - yasm_symtab_traverse(object->symtab, &info, coff_objfmt_count_sym); - symtab_count = info.indx; - - /* Section data/relocs */ - if (COFF_SET_VMA) { - /* If we're setting the VMA, we need to do a first section pass to - * determine each section's addr value before actually outputting - * relocations, as a relocation's section address is added into the - * addends in the generated code. - */ - info.addr = 0; - if (yasm_object_sections_traverse(object, &info, - coff_objfmt_set_section_addr)) - return; - } - info.addr = 0; - if (yasm_object_sections_traverse(object, &info, - coff_objfmt_output_section)) - return; - - /* Symbol table */ - pos = ftell(f); - if (pos == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return; - } - symtab_pos = (unsigned long)pos; - yasm_symtab_traverse(object->symtab, &info, coff_objfmt_output_sym); - - /* String table */ - yasm_fwrite_32_l(info.strtab_offset, f); /* total length */ - yasm_object_sections_traverse(object, &info, coff_objfmt_output_sectstr); - yasm_symtab_traverse(object->symtab, &info, coff_objfmt_output_str); - - /* Write headers */ - if (fseek(f, 0, SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - localbuf = info.buf; - YASM_WRITE_16_L(localbuf, objfmt_coff->machine); /* magic number */ - YASM_WRITE_16_L(localbuf, objfmt_coff->parse_scnum-1);/* number of sects */ - if (getenv("YASM_TEST_SUITE")) - ts = 0; - else - ts = (unsigned long)time(NULL); - YASM_WRITE_32_L(localbuf, ts); /* time/date stamp */ - YASM_WRITE_32_L(localbuf, symtab_pos); /* file ptr to symtab */ - YASM_WRITE_32_L(localbuf, symtab_count); /* number of symtabs */ - YASM_WRITE_16_L(localbuf, 0); /* size of optional header (none) */ - /* flags */ - flags = 0; - if (strcmp(yasm_dbgfmt_keyword(object->dbgfmt), "null")==0) - flags = COFF_F_LNNO; - if (!all_syms) - flags |= COFF_F_LSYMS; - if (objfmt_coff->machine != COFF_MACHINE_AMD64) - flags |= COFF_F_AR32WR; - YASM_WRITE_16_L(localbuf, flags); - fwrite(info.buf, 20, 1, f); - - yasm_object_sections_traverse(object, &info, coff_objfmt_output_secthead); - - yasm_xfree(info.buf); -} - -static void -coff_objfmt_destroy(yasm_objfmt *objfmt) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)objfmt; - if (objfmt_coff->filesym_data->aux[0].fname) - yasm_xfree(objfmt_coff->filesym_data->aux[0].fname); - if (objfmt_coff->unwind) - yasm_win64__uwinfo_destroy(objfmt_coff->unwind); - yasm_xfree(objfmt); -} - -static yasm_section * -coff_objfmt_add_default_section(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_section *retval; - coff_section_data *csd; - int isnew; - - retval = yasm_object_get_general(object, ".text", 16, 1, 0, &isnew, 0); - if (isnew) { - csd = yasm_section_get_data(retval, &coff_section_data_cb); - csd->flags = COFF_STYP_TEXT; - if (objfmt_coff->win32) - csd->flags |= COFF_STYP_EXECUTE | COFF_STYP_READ; - yasm_section_set_default(retval, 1); - } - return retval; -} - -struct coff_section_switch_data { - int isdefault; - int gasflags; - unsigned long flags; - unsigned long flags2; - /*@only@*/ /*@null@*/ yasm_intnum *align_intn; -}; - -/* GAS-style flags */ -static int -coff_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d, - /*@unused@*/ uintptr_t arg) -{ - struct coff_section_switch_data *data = - (struct coff_section_switch_data *)d; - int alloc = 0, load = 0, readonly = 0, code = 0, datasect = 0; - int shared = 0; - const char *s = yasm_vp_string(vp); - size_t i; - - if (!s) { - yasm_error_set(YASM_ERROR_VALUE, N_("non-string section attribute")); - return -1; - } - - /* For GAS, default to read/write data */ - if (data->isdefault) - data->flags = COFF_STYP_TEXT | COFF_STYP_READ | COFF_STYP_WRITE; - - for (i=0; i<strlen(s); i++) { - switch (s[i]) { - case 'a': - break; - case 'b': - alloc = 1; - load = 0; - break; - case 'n': - load = 0; - break; - case 's': - shared = 1; - /*@fallthrough@*/ - case 'd': - datasect = 1; - load = 1; - readonly = 0; - break; - case 'x': - code = 1; - load = 1; - break; - case 'r': - datasect = 1; - load = 1; - readonly = 1; - break; - case 'w': - readonly = 0; - break; - default: - yasm_warn_set(YASM_WARN_GENERAL, - N_("unrecognized section attribute: `%c'"), - s[i]); - } - } - - if (code) - data->flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; - else if (datasect) - data->flags = COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE; - else if (readonly) - data->flags = COFF_STYP_DATA | COFF_STYP_READ; - else if (load) - data->flags = COFF_STYP_TEXT; - else if (alloc) - data->flags = COFF_STYP_BSS; - - if (shared) - data->flags |= COFF_STYP_SHARED; - - data->gasflags = 1; - return 0; -} - -static /*@observer@*/ /*@null@*/ yasm_section * -coff_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, - /*@unused@*/ /*@null@*/ - yasm_valparamhead *objext_valparams, - unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp; - yasm_section *retval; - int isnew; - int iscode = 0; - int flags_override; - const char *sectname; - char *realname; - int resonly = 0; - unsigned long align = 0; - coff_section_data *csd; - - struct coff_section_switch_data data; - - static const yasm_dir_help help[] = { - { "code", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ }, - { "text", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ }, - { "data", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE }, - { "rdata", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_DATA | COFF_STYP_READ }, - { "bss", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_BSS | COFF_STYP_READ | COFF_STYP_WRITE }, - { "info", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ }, - { "gasflags", 1, coff_helper_gasflags, 0, 0 }, - /* Win32 only below this point */ - { "discard", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_DISCARD}, - { "nodiscard", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_DISCARD}, - { "cache", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOCACHE}, - { "nocache", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOCACHE}, - { "page", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOPAGE }, - { "nopage", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOPAGE }, - { "share", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_SHARED }, - { "noshare", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_SHARED }, - { "execute", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_EXECUTE}, - { "noexecute", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_EXECUTE}, - { "read", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_READ }, - { "noread", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_READ }, - { "write", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_WRITE }, - { "nowrite", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_WRITE }, - { "base", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags2), COFF_FLAG_NOBASE}, - { "nobase", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags2), COFF_FLAG_NOBASE}, - { "align", 1, yasm_dir_helper_intn, - offsetof(struct coff_section_switch_data, align_intn), 0 } - }; - - vp = yasm_vps_first(valparams); - sectname = yasm_vp_string(vp); - if (!sectname) - return NULL; - vp = yasm_vps_next(vp); - - data.isdefault = 0; - data.gasflags = 0; - data.flags = 0; - data.flags2 = 0; - data.align_intn = NULL; - - if (strcmp(sectname, ".data") == 0) { - data.flags = COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE; - if (objfmt_coff->win32) { - if (objfmt_coff->machine == COFF_MACHINE_AMD64) - align = 16; - else - align = 4; - } - } else if (strcmp(sectname, ".bss") == 0) { - data.flags = COFF_STYP_BSS | COFF_STYP_READ | COFF_STYP_WRITE; - if (objfmt_coff->win32) { - if (objfmt_coff->machine == COFF_MACHINE_AMD64) - align = 16; - else - align = 4; - } - resonly = 1; - } else if (strcmp(sectname, ".text") == 0) { - data.flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; - if (objfmt_coff->win32) - align = 16; - } else if (strcmp(sectname, ".rdata") == 0 - || strncmp(sectname, ".rodata", 7) == 0 - || strncmp(sectname, ".rdata$", 7) == 0) { - data.flags = COFF_STYP_DATA | COFF_STYP_READ; - if (objfmt_coff->win32) - align = 8; - else - yasm_warn_set(YASM_WARN_GENERAL, - N_("Standard COFF does not support read-only data sections")); - } else if (strcmp(sectname, ".drectve") == 0) { - data.flags = COFF_STYP_INFO; - if (objfmt_coff->win32) - data.flags |= COFF_STYP_DISCARD | COFF_STYP_READ; - } else if (objfmt_coff->win64 && strcmp(sectname, ".pdata") == 0) { - data.flags = COFF_STYP_DATA | COFF_STYP_READ; - align = 4; - data.flags2 = COFF_FLAG_NOBASE; - } else if (objfmt_coff->win64 && strcmp(sectname, ".xdata") == 0) { - data.flags = COFF_STYP_DATA | COFF_STYP_READ; - align = 8; - data.flags2 = COFF_FLAG_NOBASE; - } else if (objfmt_coff->win32 && strcmp(sectname, ".sxdata") == 0) { - data.flags = COFF_STYP_INFO; - } else if (strcmp(sectname, ".comment") == 0) { - data.flags = COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ; - } else if (yasm__strncasecmp(sectname, ".debug", 6)==0) { - data.flags = COFF_STYP_DATA | COFF_STYP_DISCARD | COFF_STYP_READ; - align = 1; - } else { - /* Default to code, but set a flag so if we get gasflags we can - * change it (NASM and GAS have different defaults). - */ - data.isdefault = 1; - data.flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; - } - - flags_override = yasm_dir_helper(object, vp, line, help, - objfmt_coff->win32 ? NELEMS(help) : 7, - &data, yasm_dir_helper_valparam_warn); - if (flags_override < 0) - return NULL; /* error occurred */ - - if (data.flags & COFF_STYP_EXECUTE) - iscode = 1; - - if (!objfmt_coff->win32) - data.flags &= ~COFF_STYP_WIN32_MASK; - - if (data.align_intn) { - align = yasm_intnum_get_uint(data.align_intn); - yasm_intnum_destroy(data.align_intn); - - /* Alignments must be a power of two. */ - if (!is_exp2(align)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("argument to `%s' is not a power of two"), - "align"); - return NULL; - } - - /* Check to see if alignment is supported size */ - if (align > 8192) { - yasm_error_set(YASM_ERROR_VALUE, - N_("Win32 does not support alignments > 8192")); - return NULL; - } - } - - realname = yasm__xstrdup(sectname); - if (strlen(sectname) > 8 && !objfmt_coff->win32) { - /* win32 format supports >8 character section names in object - * files via "/nnnn" (where nnnn is decimal offset into string table), - * so only warn for regular COFF. - */ - yasm_warn_set(YASM_WARN_GENERAL, - N_("COFF section names limited to 8 characters: truncating")); - realname[8] = '\0'; - } - - retval = yasm_object_get_general(object, realname, align, iscode, - resonly, &isnew, line); - yasm_xfree(realname); - - csd = yasm_section_get_data(retval, &coff_section_data_cb); - - if (isnew || yasm_section_is_default(retval)) { - yasm_section_set_default(retval, 0); - csd->flags = data.flags; - csd->flags2 = data.flags2; - yasm_section_set_align(retval, align, line); - } else if (flags_override && !data.gasflags) - yasm_warn_set(YASM_WARN_GENERAL, - N_("section flags ignored on section redeclaration")); - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -coff_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - return NULL; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -win64_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - if (yasm__strcasecmp(name, "imagebase") == 0) { - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - return objfmt_coff->ssym_imagebase; - } - return NULL; -} - -static void -coff_section_data_destroy(void *data) -{ - yasm_xfree(data); -} - -static void -coff_section_data_print(void *data, FILE *f, int indent_level) -{ - coff_section_data *csd = (coff_section_data *)data; - - fprintf(f, "%*ssym=\n", indent_level, ""); - yasm_symrec_print(csd->sym, f, indent_level+1); - fprintf(f, "%*sscnum=%d\n", indent_level, "", csd->scnum); - fprintf(f, "%*sflags=", indent_level, ""); - switch (csd->flags & COFF_STYP_STD_MASK) { - case COFF_STYP_TEXT: - fprintf(f, "TEXT"); - break; - case COFF_STYP_DATA: - fprintf(f, "DATA"); - break; - case COFF_STYP_BSS: - fprintf(f, "BSS"); - break; - default: - fprintf(f, "UNKNOWN"); - break; - } - fprintf(f, "\n%*saddr=0x%lx\n", indent_level, "", csd->addr); - fprintf(f, "%*sscnptr=0x%lx\n", indent_level, "", csd->scnptr); - fprintf(f, "%*ssize=%ld\n", indent_level, "", csd->size); - fprintf(f, "%*srelptr=0x%lx\n", indent_level, "", csd->relptr); - fprintf(f, "%*snreloc=%ld\n", indent_level, "", csd->nreloc); - fprintf(f, "%*srelocs:\n", indent_level, ""); -} - -static void -coff_symrec_data_destroy(void *data) -{ - yasm_xfree(data); -} - -static void -coff_symrec_data_print(void *data, FILE *f, int indent_level) -{ - coff_symrec_data *csd = (coff_symrec_data *)data; - - fprintf(f, "%*ssymtab index=%lu\n", indent_level, "", csd->index); - fprintf(f, "%*ssclass=%d\n", indent_level, "", csd->sclass); -} - -static void -dir_export(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_valparam *vp; - /*@null@*/ const char *symname; - int isnew; - yasm_section *sect; - yasm_datavalhead dvs; - - /* Reference exported symbol (to generate error if not declared) */ - vp = yasm_vps_first(valparams); - symname = yasm_vp_id(vp); - if (symname) - yasm_symtab_use(object->symtab, symname, line); - else { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("argument to EXPORT must be symbol name")); - return; - } - - /* Add to end of linker directives */ - sect = yasm_object_get_general(object, ".drectve", 0, 0, 0, &isnew, line); - - /* Initialize directive section if needed */ - if (isnew) { - coff_section_data *csd; - csd = yasm_section_get_data(sect, &coff_section_data_cb); - csd->flags = COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ; - } - - /* Add text as data bytecode */ - yasm_dvs_initialize(&dvs); - yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup("-export:"), - strlen("-export:"))); - yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup(symname), - strlen(symname))); - yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup(" "), 1)); - yasm_section_bcs_append(sect, yasm_bc_create_data(&dvs, 1, 0, NULL, line)); -} - -static void -dir_safeseh(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_valparam *vp; - /*@null@*/ const char *symname; - yasm_symrec *sym; - int isnew; - yasm_section *sect; - - /* Reference symbol (to generate error if not declared). - * Also, symbol must be externally visible, so force it. - */ - vp = yasm_vps_first(valparams); - symname = yasm_vp_id(vp); - if (symname) { - coff_symrec_data *sym_data; - sym = yasm_symtab_use(object->symtab, symname, line); - sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - if (!sym_data) { - sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, - COFF_SYMTAB_AUX_NONE); - } - sym_data->forcevis = 1; - sym_data->type = 0x20; /* function */ - } else { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("argument to SAFESEH must be symbol name")); - return; - } - - /* - * Add symbol number to end of .sxdata section. - */ - - sect = yasm_object_get_general(object, ".sxdata", 0, 0, 0, &isnew, line); - - /* Initialize sxdata section if needed */ - if (isnew) { - coff_section_data *csd; - csd = yasm_section_get_data(sect, &coff_section_data_cb); - csd->flags = COFF_STYP_INFO; - } - - /* Add as sxdata bytecode */ - yasm_section_bcs_append(sect, - yasm_bc_create_common(&win32_sxdata_bc_callback, - sym, line)); -} - -static void -win32_sxdata_bc_destroy(void *contents) -{ - /* Contents is just the symbol pointer, so no need to delete */ -} - -static void -win32_sxdata_bc_print(const void *contents, FILE *f, int indent_level) -{ - /* TODO */ -} - -static int -win32_sxdata_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, - void *add_span_data) -{ - bc->len += 4; - return 0; -} - -static int -win32_sxdata_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, - unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - yasm_output_reloc_func output_reloc) -{ - yasm_symrec *sym = (yasm_symrec *)bc->contents; - unsigned char *buf = *bufp; - coff_symrec_data *csymd; - - csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - if (!csymd) - yasm_internal_error(N_("coff: no symbol data for SAFESEH symbol")); - - YASM_WRITE_32_L(buf, csymd->index); - - *bufp = buf; - return 0; -} - -static void -dir_ident(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparamhead sect_vps; - yasm_datavalhead dvs; - yasm_section *comment; - const char *sectname; - yasm_valparam *vp, *vp2; - - /* Accept, but do nothing with empty ident */ - if (!valparams) - return; - - vp = yasm_vps_first(valparams); - if (!vp) - return; - - if (objfmt_coff->win32) { - /* Put ident data into .comment section for COFF, or .rdata$zzz - * to be compatible with the GNU linker, which doesn't ignore - * .comment (see binutils/gas/config/obj-coff.c:476-502). - */ - sectname = ".rdata$zzz"; - } else { - sectname = ".comment"; - } - yasm_vps_initialize(§_vps); - vp2 = yasm_vp_create_id(NULL, yasm__xstrdup(sectname), '\0'); - yasm_vps_append(§_vps, vp2); - comment = coff_objfmt_section_switch(object, §_vps, NULL, line); - yasm_vps_delete(§_vps); - - /* To match GAS output, if the comment section is empty, put an - * initial 0 byte in the section. - */ - if (yasm_section_bcs_first(comment) == yasm_section_bcs_last(comment)) { - yasm_dvs_initialize(&dvs); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)), - line))); - yasm_section_bcs_append(comment, - yasm_bc_create_data(&dvs, 1, 0, object->arch, line)); - } - - yasm_dvs_initialize(&dvs); - do { - const char *s = yasm_vp_string(vp); - if (!s) { - yasm_error_set(YASM_ERROR_VALUE, - N_(".comment requires string parameters")); - yasm_dvs_delete(&dvs); - return; - } - yasm_dvs_append(&dvs, - yasm_dv_create_string(yasm__xstrdup(s), strlen(s))); - } while ((vp = yasm_vps_next(vp))); - - yasm_section_bcs_append(comment, - yasm_bc_create_data(&dvs, 1, 1, object->arch, line)); -} - -static void -dir_secrel32(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_datavalhead dvs; - yasm_valparam *vp; - - if (!object->cur_section) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_(".secrel32 can only be used inside of a section")); - return; - } - - vp = yasm_vps_first(valparams); - yasm_dvs_initialize(&dvs); - do { - yasm_expr *e = yasm_vp_expr(vp, object->symtab, line); - yasm_dataval *dv; - if (!e) { - yasm_error_set(YASM_ERROR_VALUE, - N_(".secrel32 requires expressions")); - yasm_dvs_delete(&dvs); - return; - } - dv = yasm_dv_create_expr(e); - yasm_dv_get_value(dv)->section_rel = 1; - yasm_dvs_append(&dvs, dv); - } while ((vp = yasm_vps_next(vp))); - - yasm_section_bcs_append(object->cur_section, - yasm_bc_create_data(&dvs, 4, 0, object->arch, line)); -} - -static void -dir_def(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp; - const char *symname; - yasm_symrec *sym; - coff_symrec_data *sym_data; - - if (objfmt_coff->def_sym) { - yasm_warn_set(YASM_WARN_GENERAL, - N_(".def pseudo-op used inside of .def/.endef; ignored")); - return; - } - - vp = yasm_vps_first(valparams); - symname = yasm_vp_id(vp); - if (!symname) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("argument to SAFESEH must be symbol name")); - return; - } - - sym = yasm_symtab_use(object->symtab, symname, line); - sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - if (!sym_data) { - sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, - COFF_SYMTAB_AUX_NONE); - } - objfmt_coff->def_sym = sym_data; -} - -static void -dir_scl(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_intnum *intn = NULL; - - if (!objfmt_coff->def_sym) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("%s pseudo-op used outside of .def/.endef; ignored"), - ".scl"); - return; - } - - if (yasm_dir_helper_intn(object, yasm_vps_first(valparams), line, - &intn, 0) < 0) - return; - if (!intn) - return; - objfmt_coff->def_sym->sclass = yasm_intnum_get_uint(intn); - yasm_intnum_destroy(intn); -} - -static void -dir_type(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_intnum *intn = NULL; - - if (!objfmt_coff->def_sym) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("%s pseudo-op used outside of .def/.endef; ignored"), - ".type"); - return; - } - - if (yasm_dir_helper_intn(object, yasm_vps_first(valparams), line, - &intn, 0) < 0) - return; - if (!intn) - return; - objfmt_coff->def_sym->type = yasm_intnum_get_uint(intn); - yasm_intnum_destroy(intn); -} - -static void -dir_endef(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - if (!objfmt_coff->def_sym) { - yasm_warn_set(YASM_WARN_GENERAL, - N_(".endef pseudo-op used before .def; ignored")); - return; - } - objfmt_coff->def_sym = NULL; -} - -static void -dir_proc_frame(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - const char *name = yasm_vp_id(vp); - - if (objfmt_coff->proc_frame) { - yasm_error_set_xref(objfmt_coff->proc_frame, - N_("previous procedure started here")); - yasm_error_set(YASM_ERROR_SYNTAX, - N_("nested procedures not supported (didn't use [ENDPROC_FRAME]?)")); - return; - } - objfmt_coff->proc_frame = line; - objfmt_coff->done_prolog = 0; - objfmt_coff->unwind = yasm_win64__uwinfo_create(); - objfmt_coff->unwind->proc = yasm_symtab_use(object->symtab, name, line); - - /* Optional error handler */ - vp = yasm_vps_next(vp); - if (!vp || !(name = yasm_vp_id(vp))) - return; - objfmt_coff->unwind->ehandler = - yasm_symtab_use(object->symtab, name, line); -} - -static int -procframe_checkstate(yasm_objfmt_coff *objfmt_coff, const char *dirname) -{ - if (!objfmt_coff->proc_frame) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] without preceding [PROC_FRAME]"), dirname); - return 0; - } - if (objfmt_coff->done_prolog) { - yasm_error_set_xref(objfmt_coff->done_prolog, - N_("prologue ended here")); - yasm_error_set(YASM_ERROR_SYNTAX, N_("[%s] after end of prologue"), - dirname); - return 0; - } - if (!objfmt_coff->unwind) - yasm_internal_error(N_("unwind info not present")); - return 1; -} - -/* Get current assembly position. - * XXX: There should be a better way to do this. - */ -static yasm_symrec * -get_curpos(yasm_object *object, const char *dirname, unsigned long line) -{ - if (!object->cur_section) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] can only be used inside of a section"), - dirname); - return NULL; - } - return yasm_symtab_define_curpos(object->symtab, "$", - yasm_section_bcs_last(object->cur_section), line); -} - -static void -dir_pushreg(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - coff_unwind_code *code; - const uintptr_t *reg; - - if (!procframe_checkstate(objfmt_coff, "PUSHREG")) - return; - - if (vp->type != YASM_PARAM_EXPR || - !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] requires a register as the first parameter"), - "PUSHREG"); - return; - } - - /* Generate a PUSH_NONVOL unwind code. */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, "PUSHREG", line); - code->opcode = UWOP_PUSH_NONVOL; - code->info = (unsigned int)(*reg & 0xF); - yasm_value_initialize(&code->off, NULL, 0); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_setframe(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - coff_unwind_code *code; - const uintptr_t *reg; - yasm_expr *off = NULL; - - if (!procframe_checkstate(objfmt_coff, "SETFRAME")) - return; - - if (vp->type != YASM_PARAM_EXPR || - !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] requires a register as the first parameter"), - "SETFRAME"); - return; - } - - vp = yasm_vps_next(vp); - if (vp) - off = yasm_vp_expr(vp, object->symtab, line); - - /* Set the frame fields in the unwind info */ - objfmt_coff->unwind->framereg = (unsigned long)(*reg); - yasm_value_initialize(&objfmt_coff->unwind->frameoff, off, 8); - - /* Generate a SET_FPREG unwind code */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, "SETFRAME", line); - code->opcode = UWOP_SET_FPREG; - code->info = (unsigned int)(*reg & 0xF); - yasm_value_initialize(&code->off, off ? yasm_expr_copy(off) : NULL, 8); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_allocstack(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - /*@null@*/ /*@only@*/ yasm_expr *size; - coff_unwind_code *code; - - if (!procframe_checkstate(objfmt_coff, "ALLOCSTACK")) - return; - - size = yasm_vp_expr(vp, object->symtab, line); - if (!size) { - yasm_error_set(YASM_ERROR_SYNTAX, N_("[%s] requires a size"), - "ALLOCSTACK"); - return; - } - - /* Generate an ALLOC_SMALL unwind code; this will get enlarged to an - * ALLOC_LARGE if necessary. - */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, "ALLOCSTACK", line); - code->opcode = UWOP_ALLOC_SMALL; - code->info = 0; - yasm_value_initialize(&code->off, size, 7); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_save_common(yasm_object *object, yasm_valparamhead *valparams, - unsigned long line, const char *name, int op) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - coff_unwind_code *code; - const uintptr_t *reg; - /*@only@*/ /*@null@*/ yasm_expr *offset; - - if (!procframe_checkstate(objfmt_coff, name)) - return; - - if (vp->type != YASM_PARAM_EXPR || - !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] requires a register as the first parameter"), - name); - return; - } - - vp = yasm_vps_next(vp); - offset = yasm_vp_expr(vp, object->symtab, line); - if (!offset) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] requires an offset as the second parameter"), - name); - return; - } - - /* Generate a SAVE_XXX unwind code; this will get enlarged to a - * SAVE_XXX_FAR if necessary. - */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, name, line); - code->opcode = op; - code->info = (unsigned int)(*reg & 0xF); - yasm_value_initialize(&code->off, offset, 16); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_savereg(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - dir_save_common(object, valparams, line, "SAVEREG", UWOP_SAVE_NONVOL); -} - -static void -dir_savexmm128(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - dir_save_common(object, valparams, line, "SAVEXMM128", UWOP_SAVE_XMM128); -} - -static void -dir_pushframe(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - coff_unwind_code *code; - - if (!procframe_checkstate(objfmt_coff, "PUSHFRAME")) - return; - - /* Generate a PUSH_MACHFRAME unwind code. If there's any parameter, - * we set info to 1. Otherwise we set info to 0. - */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, "PUSHFRAME", line); - code->opcode = UWOP_PUSH_MACHFRAME; - code->info = vp != NULL; - yasm_value_initialize(&code->off, NULL, 0); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_endprolog(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - if (!procframe_checkstate(objfmt_coff, "ENDPROLOG")) - return; - objfmt_coff->done_prolog = line; - - objfmt_coff->unwind->prolog = get_curpos(object, "ENDPROLOG", line); -} - -static void -dir_endproc_frame(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_section *sect; - coff_section_data *csd; - yasm_datavalhead dvs; - int isnew; - /*@dependent@*/ yasm_symrec *curpos, *unwindpos, *proc_sym, *xdata_sym; - - if (!objfmt_coff->proc_frame) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] without preceding [PROC_FRAME]"), - "ENDPROC_FRAME"); - return; - } - if (!objfmt_coff->done_prolog) { - yasm_error_set_xref(objfmt_coff->proc_frame, - N_("procedure started here")); - yasm_error_set(YASM_ERROR_SYNTAX, - N_("ended procedure without ending prologue"), - "ENDPROC_FRAME"); - objfmt_coff->proc_frame = 0; - yasm_win64__uwinfo_destroy(objfmt_coff->unwind); - objfmt_coff->unwind = NULL; - return; - } - if (!objfmt_coff->unwind) - yasm_internal_error(N_("unwind info not present")); - - proc_sym = objfmt_coff->unwind->proc; - - curpos = get_curpos(object, "ENDPROC_FRAME", line); - - /* - * Add unwind info to end of .xdata section. - */ - - sect = yasm_object_get_general(object, ".xdata", 0, 0, 0, &isnew, line); - - /* Initialize xdata section if needed */ - if (isnew) { - csd = yasm_section_get_data(sect, &coff_section_data_cb); - csd->flags = COFF_STYP_DATA | COFF_STYP_READ; - yasm_section_set_align(sect, 8, line); - } - - /* Get current position in .xdata section */ - unwindpos = yasm_symtab_define_curpos(object->symtab, "$", - yasm_section_bcs_last(sect), line); - /* Get symbol for .xdata as we'll want to reference it with WRT */ - csd = yasm_section_get_data(sect, &coff_section_data_cb); - xdata_sym = csd->sym; - - /* Add unwind info. Use line number of start of procedure. */ - yasm_win64__unwind_generate(sect, objfmt_coff->unwind, - objfmt_coff->proc_frame); - objfmt_coff->unwind = NULL; /* generate keeps the unwind pointer */ - - /* - * Add function lookup to end of .pdata section. - */ - - sect = yasm_object_get_general(object, ".pdata", 0, 0, 0, &isnew, line); - - /* Initialize pdata section if needed */ - if (isnew) { - csd = yasm_section_get_data(sect, &coff_section_data_cb); - csd->flags = COFF_STYP_DATA | COFF_STYP_READ; - csd->flags2 = COFF_FLAG_NOBASE; - yasm_section_set_align(sect, 4, line); - } - - /* Add function structure as data bytecode */ - yasm_dvs_initialize(&dvs); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create_ident(yasm_expr_sym(proc_sym), line))); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create(YASM_EXPR_WRT, yasm_expr_sym(curpos), - yasm_expr_sym(proc_sym), line))); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create(YASM_EXPR_WRT, yasm_expr_sym(unwindpos), - yasm_expr_sym(xdata_sym), line))); - yasm_section_bcs_append(sect, yasm_bc_create_data(&dvs, 4, 0, NULL, line)); - - objfmt_coff->proc_frame = 0; - objfmt_coff->done_prolog = 0; -} - -/* Define valid debug formats to use with this object format */ -static const char *coff_objfmt_dbgfmt_keywords[] = { - "null", - "dwarf2", - NULL -}; - -static const yasm_directive coff_objfmt_directives[] = { - { ".ident", "gas", dir_ident, YASM_DIR_ANY }, - { "ident", "nasm", dir_ident, YASM_DIR_ANY }, - { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, - { ".endef", "gas", dir_endef, YASM_DIR_ANY }, - { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, - { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, - { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, - { NULL, NULL, NULL, 0 } -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_coff_LTX_objfmt = { - "COFF (DJGPP)", - "coff", - "o", - 32, - 0, - coff_objfmt_dbgfmt_keywords, - "null", - coff_objfmt_directives, - NULL, /* no standard macros */ - coff_objfmt_create, - coff_objfmt_output, - coff_objfmt_destroy, - coff_objfmt_add_default_section, - coff_objfmt_init_new_section, - coff_objfmt_section_switch, - coff_objfmt_get_special_sym -}; - -/* Define valid debug formats to use with this object format */ -static const char *winXX_objfmt_dbgfmt_keywords[] = { - "null", - "dwarf2", - "cv8", - NULL -}; - -static const yasm_directive win32_objfmt_directives[] = { - { ".ident", "gas", dir_ident, YASM_DIR_ANY }, - { "ident", "nasm", dir_ident, YASM_DIR_ANY }, - { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, - { ".endef", "gas", dir_endef, YASM_DIR_ANY }, - { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, - { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, - { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, - { ".export", "gas", dir_export, YASM_DIR_ID_REQUIRED }, - { "export", "nasm", dir_export, YASM_DIR_ID_REQUIRED }, - { ".safeseh", "gas", dir_safeseh, YASM_DIR_ID_REQUIRED }, - { "safeseh", "nasm", dir_safeseh, YASM_DIR_ID_REQUIRED }, - { NULL, NULL, NULL, 0 } -}; - -static const char *win32_nasm_stdmac[] = { - "%imacro export 1+.nolist", - "[export %1]", - "%endmacro", - "%imacro safeseh 1+.nolist", - "[safeseh %1]", - "%endmacro", - NULL -}; - -static const yasm_stdmac win32_objfmt_stdmacs[] = { - { "nasm", "nasm", win32_nasm_stdmac }, - { NULL, NULL, NULL } -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_win32_LTX_objfmt = { - "Win32", - "win32", - "obj", - 32, - 1, - winXX_objfmt_dbgfmt_keywords, - "null", - win32_objfmt_directives, - win32_objfmt_stdmacs, - win32_objfmt_create, - coff_objfmt_output, - coff_objfmt_destroy, - coff_objfmt_add_default_section, - coff_objfmt_init_new_section, - coff_objfmt_section_switch, - coff_objfmt_get_special_sym -}; - -static const yasm_directive win64_objfmt_directives[] = { - { ".ident", "gas", dir_ident, YASM_DIR_ANY }, - { "ident", "nasm", dir_ident, YASM_DIR_ANY }, - { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, - { ".endef", "gas", dir_endef, YASM_DIR_ANY }, - { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, - { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, - { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, - { ".export", "gas", dir_export, YASM_DIR_ID_REQUIRED }, - { "export", "nasm", dir_export, YASM_DIR_ID_REQUIRED }, - { ".proc_frame", "gas", dir_proc_frame, YASM_DIR_ID_REQUIRED }, - { "proc_frame", "nasm", dir_proc_frame, YASM_DIR_ID_REQUIRED }, - { ".pushreg", "gas", dir_pushreg, YASM_DIR_ARG_REQUIRED }, - { "pushreg", "nasm", dir_pushreg, YASM_DIR_ARG_REQUIRED }, - { ".setframe", "gas", dir_setframe, YASM_DIR_ARG_REQUIRED }, - { "setframe", "nasm", dir_setframe, YASM_DIR_ARG_REQUIRED }, - { ".allocstack", "gas", dir_allocstack, YASM_DIR_ARG_REQUIRED }, - { "allocstack", "nasm", dir_allocstack, YASM_DIR_ARG_REQUIRED }, - { ".savereg", "gas", dir_savereg, YASM_DIR_ARG_REQUIRED }, - { "savereg", "nasm", dir_savereg, YASM_DIR_ARG_REQUIRED }, - { ".savexmm128", "gas", dir_savexmm128, YASM_DIR_ARG_REQUIRED }, - { "savexmm128", "nasm", dir_savexmm128, YASM_DIR_ARG_REQUIRED }, - { ".pushframe", "gas", dir_pushframe, YASM_DIR_ANY }, - { "pushframe", "nasm", dir_pushframe, YASM_DIR_ANY }, - { ".endprolog", "gas", dir_endprolog, YASM_DIR_ANY }, - { "endprolog", "nasm", dir_endprolog, YASM_DIR_ANY }, - { ".endproc_frame", "gas", dir_endproc_frame, YASM_DIR_ANY }, - { "endproc_frame", "nasm", dir_endproc_frame, YASM_DIR_ANY }, - { NULL, NULL, NULL, 0 } -}; - -#include "win64-nasm.c" -#include "win64-gas.c" - -static const yasm_stdmac win64_objfmt_stdmacs[] = { - { "nasm", "nasm", win64_nasm_stdmac }, - { "gas", "nasm", win64_gas_stdmac }, - { NULL, NULL, NULL } -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_win64_LTX_objfmt = { - "Win64", - "win64", - "obj", - 64, - 1, - winXX_objfmt_dbgfmt_keywords, - "null", - win64_objfmt_directives, - win64_objfmt_stdmacs, - win64_objfmt_create, - coff_objfmt_output, - coff_objfmt_destroy, - coff_objfmt_add_default_section, - coff_objfmt_init_new_section, - coff_objfmt_section_switch, - win64_objfmt_get_special_sym -}; -yasm_objfmt_module yasm_x64_LTX_objfmt = { - "Win64", - "x64", - "obj", - 64, - 1, - winXX_objfmt_dbgfmt_keywords, - "null", - win64_objfmt_directives, - win64_objfmt_stdmacs, - win64_objfmt_create, - coff_objfmt_output, - coff_objfmt_destroy, - coff_objfmt_add_default_section, - coff_objfmt_init_new_section, - coff_objfmt_section_switch, - win64_objfmt_get_special_sym -}; + + /* Force all syms for win64 because they're needed for relocations. + * FIXME: Not *all* syms need to be output, only the ones needed for + * relocation. Find a way to do that someday. + */ + all_syms |= objfmt_coff->win64; + + info.strtab_offset = 4; + info.object = object; + info.objfmt_coff = objfmt_coff; + info.errwarns = errwarns; + info.f = f; + info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); + + /* Allocate space for headers by seeking forward */ + if (fseek(f, (long)(20+40*(objfmt_coff->parse_scnum-1)), SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + /* Finalize symbol table (assign index to each symbol) */ + info.indx = 0; + info.all_syms = all_syms; + yasm_symtab_traverse(object->symtab, &info, coff_objfmt_count_sym); + symtab_count = info.indx; + + /* Section data/relocs */ + if (COFF_SET_VMA) { + /* If we're setting the VMA, we need to do a first section pass to + * determine each section's addr value before actually outputting + * relocations, as a relocation's section address is added into the + * addends in the generated code. + */ + info.addr = 0; + if (yasm_object_sections_traverse(object, &info, + coff_objfmt_set_section_addr)) + return; + } + info.addr = 0; + if (yasm_object_sections_traverse(object, &info, + coff_objfmt_output_section)) + return; + + /* Symbol table */ + pos = ftell(f); + if (pos == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return; + } + symtab_pos = (unsigned long)pos; + yasm_symtab_traverse(object->symtab, &info, coff_objfmt_output_sym); + + /* String table */ + yasm_fwrite_32_l(info.strtab_offset, f); /* total length */ + yasm_object_sections_traverse(object, &info, coff_objfmt_output_sectstr); + yasm_symtab_traverse(object->symtab, &info, coff_objfmt_output_str); + + /* Write headers */ + if (fseek(f, 0, SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + localbuf = info.buf; + YASM_WRITE_16_L(localbuf, objfmt_coff->machine); /* magic number */ + YASM_WRITE_16_L(localbuf, objfmt_coff->parse_scnum-1);/* number of sects */ + if (getenv("YASM_TEST_SUITE")) + ts = 0; + else + ts = (unsigned long)time(NULL); + YASM_WRITE_32_L(localbuf, ts); /* time/date stamp */ + YASM_WRITE_32_L(localbuf, symtab_pos); /* file ptr to symtab */ + YASM_WRITE_32_L(localbuf, symtab_count); /* number of symtabs */ + YASM_WRITE_16_L(localbuf, 0); /* size of optional header (none) */ + /* flags */ + flags = 0; + if (strcmp(yasm_dbgfmt_keyword(object->dbgfmt), "null")==0) + flags = COFF_F_LNNO; + if (!all_syms) + flags |= COFF_F_LSYMS; + if (objfmt_coff->machine != COFF_MACHINE_AMD64) + flags |= COFF_F_AR32WR; + YASM_WRITE_16_L(localbuf, flags); + fwrite(info.buf, 20, 1, f); + + yasm_object_sections_traverse(object, &info, coff_objfmt_output_secthead); + + yasm_xfree(info.buf); +} + +static void +coff_objfmt_destroy(yasm_objfmt *objfmt) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)objfmt; + if (objfmt_coff->filesym_data->aux[0].fname) + yasm_xfree(objfmt_coff->filesym_data->aux[0].fname); + if (objfmt_coff->unwind) + yasm_win64__uwinfo_destroy(objfmt_coff->unwind); + yasm_xfree(objfmt); +} + +static yasm_section * +coff_objfmt_add_default_section(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_section *retval; + coff_section_data *csd; + int isnew; + + retval = yasm_object_get_general(object, ".text", 16, 1, 0, &isnew, 0); + if (isnew) { + csd = yasm_section_get_data(retval, &coff_section_data_cb); + csd->flags = COFF_STYP_TEXT; + if (objfmt_coff->win32) + csd->flags |= COFF_STYP_EXECUTE | COFF_STYP_READ; + yasm_section_set_default(retval, 1); + } + return retval; +} + +struct coff_section_switch_data { + int isdefault; + int gasflags; + unsigned long flags; + unsigned long flags2; + /*@only@*/ /*@null@*/ yasm_intnum *align_intn; +}; + +/* GAS-style flags */ +static int +coff_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d, + /*@unused@*/ uintptr_t arg) +{ + struct coff_section_switch_data *data = + (struct coff_section_switch_data *)d; + int alloc = 0, load = 0, readonly = 0, code = 0, datasect = 0; + int shared = 0; + const char *s = yasm_vp_string(vp); + size_t i; + + if (!s) { + yasm_error_set(YASM_ERROR_VALUE, N_("non-string section attribute")); + return -1; + } + + /* For GAS, default to read/write data */ + if (data->isdefault) + data->flags = COFF_STYP_TEXT | COFF_STYP_READ | COFF_STYP_WRITE; + + for (i=0; i<strlen(s); i++) { + switch (s[i]) { + case 'a': + break; + case 'b': + alloc = 1; + load = 0; + break; + case 'n': + load = 0; + break; + case 's': + shared = 1; + /*@fallthrough@*/ + case 'd': + datasect = 1; + load = 1; + readonly = 0; + break; + case 'x': + code = 1; + load = 1; + break; + case 'r': + datasect = 1; + load = 1; + readonly = 1; + break; + case 'w': + readonly = 0; + break; + default: + yasm_warn_set(YASM_WARN_GENERAL, + N_("unrecognized section attribute: `%c'"), + s[i]); + } + } + + if (code) + data->flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; + else if (datasect) + data->flags = COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE; + else if (readonly) + data->flags = COFF_STYP_DATA | COFF_STYP_READ; + else if (load) + data->flags = COFF_STYP_TEXT; + else if (alloc) + data->flags = COFF_STYP_BSS; + + if (shared) + data->flags |= COFF_STYP_SHARED; + + data->gasflags = 1; + return 0; +} + +static /*@observer@*/ /*@null@*/ yasm_section * +coff_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, + /*@unused@*/ /*@null@*/ + yasm_valparamhead *objext_valparams, + unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp; + yasm_section *retval; + int isnew; + int iscode = 0; + int flags_override; + const char *sectname; + char *realname; + int resonly = 0; + unsigned long align = 0; + coff_section_data *csd; + + struct coff_section_switch_data data; + + static const yasm_dir_help help[] = { + { "code", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ }, + { "text", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ }, + { "data", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE }, + { "rdata", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_DATA | COFF_STYP_READ }, + { "bss", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_BSS | COFF_STYP_READ | COFF_STYP_WRITE }, + { "info", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ }, + { "gasflags", 1, coff_helper_gasflags, 0, 0 }, + /* Win32 only below this point */ + { "discard", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_DISCARD}, + { "nodiscard", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_DISCARD}, + { "cache", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOCACHE}, + { "nocache", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOCACHE}, + { "page", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOPAGE }, + { "nopage", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOPAGE }, + { "share", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_SHARED }, + { "noshare", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_SHARED }, + { "execute", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_EXECUTE}, + { "noexecute", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_EXECUTE}, + { "read", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_READ }, + { "noread", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_READ }, + { "write", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_WRITE }, + { "nowrite", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_WRITE }, + { "base", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags2), COFF_FLAG_NOBASE}, + { "nobase", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags2), COFF_FLAG_NOBASE}, + { "align", 1, yasm_dir_helper_intn, + offsetof(struct coff_section_switch_data, align_intn), 0 } + }; + + vp = yasm_vps_first(valparams); + sectname = yasm_vp_string(vp); + if (!sectname) + return NULL; + vp = yasm_vps_next(vp); + + data.isdefault = 0; + data.gasflags = 0; + data.flags = 0; + data.flags2 = 0; + data.align_intn = NULL; + + if (strcmp(sectname, ".data") == 0) { + data.flags = COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE; + if (objfmt_coff->win32) { + if (objfmt_coff->machine == COFF_MACHINE_AMD64) + align = 16; + else + align = 4; + } + } else if (strcmp(sectname, ".bss") == 0) { + data.flags = COFF_STYP_BSS | COFF_STYP_READ | COFF_STYP_WRITE; + if (objfmt_coff->win32) { + if (objfmt_coff->machine == COFF_MACHINE_AMD64) + align = 16; + else + align = 4; + } + resonly = 1; + } else if (strcmp(sectname, ".text") == 0) { + data.flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; + if (objfmt_coff->win32) + align = 16; + } else if (strcmp(sectname, ".rdata") == 0 + || strncmp(sectname, ".rodata", 7) == 0 + || strncmp(sectname, ".rdata$", 7) == 0) { + data.flags = COFF_STYP_DATA | COFF_STYP_READ; + if (objfmt_coff->win32) + align = 8; + else + yasm_warn_set(YASM_WARN_GENERAL, + N_("Standard COFF does not support read-only data sections")); + } else if (strcmp(sectname, ".drectve") == 0) { + data.flags = COFF_STYP_INFO; + if (objfmt_coff->win32) + data.flags |= COFF_STYP_DISCARD | COFF_STYP_READ; + } else if (objfmt_coff->win64 && strcmp(sectname, ".pdata") == 0) { + data.flags = COFF_STYP_DATA | COFF_STYP_READ; + align = 4; + data.flags2 = COFF_FLAG_NOBASE; + } else if (objfmt_coff->win64 && strcmp(sectname, ".xdata") == 0) { + data.flags = COFF_STYP_DATA | COFF_STYP_READ; + align = 8; + data.flags2 = COFF_FLAG_NOBASE; + } else if (objfmt_coff->win32 && strcmp(sectname, ".sxdata") == 0) { + data.flags = COFF_STYP_INFO; + } else if (strcmp(sectname, ".comment") == 0) { + data.flags = COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ; + } else if (yasm__strncasecmp(sectname, ".debug", 6)==0) { + data.flags = COFF_STYP_DATA | COFF_STYP_DISCARD | COFF_STYP_READ; + align = 1; + } else { + /* Default to code, but set a flag so if we get gasflags we can + * change it (NASM and GAS have different defaults). + */ + data.isdefault = 1; + data.flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; + } + + flags_override = yasm_dir_helper(object, vp, line, help, + objfmt_coff->win32 ? NELEMS(help) : 7, + &data, yasm_dir_helper_valparam_warn); + if (flags_override < 0) + return NULL; /* error occurred */ + + if (data.flags & COFF_STYP_EXECUTE) + iscode = 1; + + if (!objfmt_coff->win32) + data.flags &= ~COFF_STYP_WIN32_MASK; + + if (data.align_intn) { + align = yasm_intnum_get_uint(data.align_intn); + yasm_intnum_destroy(data.align_intn); + + /* Alignments must be a power of two. */ + if (!is_exp2(align)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("argument to `%s' is not a power of two"), + "align"); + return NULL; + } + + /* Check to see if alignment is supported size */ + if (align > 8192) { + yasm_error_set(YASM_ERROR_VALUE, + N_("Win32 does not support alignments > 8192")); + return NULL; + } + } + + realname = yasm__xstrdup(sectname); + if (strlen(sectname) > 8 && !objfmt_coff->win32) { + /* win32 format supports >8 character section names in object + * files via "/nnnn" (where nnnn is decimal offset into string table), + * so only warn for regular COFF. + */ + yasm_warn_set(YASM_WARN_GENERAL, + N_("COFF section names limited to 8 characters: truncating")); + realname[8] = '\0'; + } + + retval = yasm_object_get_general(object, realname, align, iscode, + resonly, &isnew, line); + yasm_xfree(realname); + + csd = yasm_section_get_data(retval, &coff_section_data_cb); + + if (isnew || yasm_section_is_default(retval)) { + yasm_section_set_default(retval, 0); + csd->flags = data.flags; + csd->flags2 = data.flags2; + yasm_section_set_align(retval, align, line); + } else if (flags_override && !data.gasflags) + yasm_warn_set(YASM_WARN_GENERAL, + N_("section flags ignored on section redeclaration")); + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +coff_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + return NULL; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +win64_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + if (yasm__strcasecmp(name, "imagebase") == 0) { + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + return objfmt_coff->ssym_imagebase; + } + return NULL; +} + +static void +coff_section_data_destroy(void *data) +{ + yasm_xfree(data); +} + +static void +coff_section_data_print(void *data, FILE *f, int indent_level) +{ + coff_section_data *csd = (coff_section_data *)data; + + fprintf(f, "%*ssym=\n", indent_level, ""); + yasm_symrec_print(csd->sym, f, indent_level+1); + fprintf(f, "%*sscnum=%d\n", indent_level, "", csd->scnum); + fprintf(f, "%*sflags=", indent_level, ""); + switch (csd->flags & COFF_STYP_STD_MASK) { + case COFF_STYP_TEXT: + fprintf(f, "TEXT"); + break; + case COFF_STYP_DATA: + fprintf(f, "DATA"); + break; + case COFF_STYP_BSS: + fprintf(f, "BSS"); + break; + default: + fprintf(f, "UNKNOWN"); + break; + } + fprintf(f, "\n%*saddr=0x%lx\n", indent_level, "", csd->addr); + fprintf(f, "%*sscnptr=0x%lx\n", indent_level, "", csd->scnptr); + fprintf(f, "%*ssize=%ld\n", indent_level, "", csd->size); + fprintf(f, "%*srelptr=0x%lx\n", indent_level, "", csd->relptr); + fprintf(f, "%*snreloc=%ld\n", indent_level, "", csd->nreloc); + fprintf(f, "%*srelocs:\n", indent_level, ""); +} + +static void +coff_symrec_data_destroy(void *data) +{ + yasm_xfree(data); +} + +static void +coff_symrec_data_print(void *data, FILE *f, int indent_level) +{ + coff_symrec_data *csd = (coff_symrec_data *)data; + + fprintf(f, "%*ssymtab index=%lu\n", indent_level, "", csd->index); + fprintf(f, "%*ssclass=%d\n", indent_level, "", csd->sclass); +} + +static void +dir_export(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp; + /*@null@*/ const char *symname; + int isnew; + yasm_section *sect; + yasm_datavalhead dvs; + + /* Reference exported symbol (to generate error if not declared) */ + vp = yasm_vps_first(valparams); + symname = yasm_vp_id(vp); + if (symname) + yasm_symtab_use(object->symtab, symname, line); + else { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("argument to EXPORT must be symbol name")); + return; + } + + /* Add to end of linker directives */ + sect = yasm_object_get_general(object, ".drectve", 0, 0, 0, &isnew, line); + + /* Initialize directive section if needed */ + if (isnew) { + coff_section_data *csd; + csd = yasm_section_get_data(sect, &coff_section_data_cb); + csd->flags = COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ; + } + + /* Add text as data bytecode */ + yasm_dvs_initialize(&dvs); + yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup("-export:"), + strlen("-export:"))); + yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup(symname), + strlen(symname))); + yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup(" "), 1)); + yasm_section_bcs_append(sect, yasm_bc_create_data(&dvs, 1, 0, NULL, line)); +} + +static void +dir_safeseh(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp; + /*@null@*/ const char *symname; + yasm_symrec *sym; + int isnew; + yasm_section *sect; + + /* Reference symbol (to generate error if not declared). + * Also, symbol must be externally visible, so force it. + */ + vp = yasm_vps_first(valparams); + symname = yasm_vp_id(vp); + if (symname) { + coff_symrec_data *sym_data; + sym = yasm_symtab_use(object->symtab, symname, line); + sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + if (!sym_data) { + sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, + COFF_SYMTAB_AUX_NONE); + } + sym_data->forcevis = 1; + sym_data->type = 0x20; /* function */ + } else { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("argument to SAFESEH must be symbol name")); + return; + } + + /* + * Add symbol number to end of .sxdata section. + */ + + sect = yasm_object_get_general(object, ".sxdata", 0, 0, 0, &isnew, line); + + /* Initialize sxdata section if needed */ + if (isnew) { + coff_section_data *csd; + csd = yasm_section_get_data(sect, &coff_section_data_cb); + csd->flags = COFF_STYP_INFO; + } + + /* Add as sxdata bytecode */ + yasm_section_bcs_append(sect, + yasm_bc_create_common(&win32_sxdata_bc_callback, + sym, line)); +} + +static void +win32_sxdata_bc_destroy(void *contents) +{ + /* Contents is just the symbol pointer, so no need to delete */ +} + +static void +win32_sxdata_bc_print(const void *contents, FILE *f, int indent_level) +{ + /* TODO */ +} + +static int +win32_sxdata_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + bc->len += 4; + return 0; +} + +static int +win32_sxdata_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + yasm_output_reloc_func output_reloc) +{ + yasm_symrec *sym = (yasm_symrec *)bc->contents; + unsigned char *buf = *bufp; + coff_symrec_data *csymd; + + csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + if (!csymd) + yasm_internal_error(N_("coff: no symbol data for SAFESEH symbol")); + + YASM_WRITE_32_L(buf, csymd->index); + + *bufp = buf; + return 0; +} + +static void +dir_ident(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparamhead sect_vps; + yasm_datavalhead dvs; + yasm_section *comment; + const char *sectname; + yasm_valparam *vp, *vp2; + + /* Accept, but do nothing with empty ident */ + if (!valparams) + return; + + vp = yasm_vps_first(valparams); + if (!vp) + return; + + if (objfmt_coff->win32) { + /* Put ident data into .comment section for COFF, or .rdata$zzz + * to be compatible with the GNU linker, which doesn't ignore + * .comment (see binutils/gas/config/obj-coff.c:476-502). + */ + sectname = ".rdata$zzz"; + } else { + sectname = ".comment"; + } + yasm_vps_initialize(§_vps); + vp2 = yasm_vp_create_id(NULL, yasm__xstrdup(sectname), '\0'); + yasm_vps_append(§_vps, vp2); + comment = coff_objfmt_section_switch(object, §_vps, NULL, line); + yasm_vps_delete(§_vps); + + /* To match GAS output, if the comment section is empty, put an + * initial 0 byte in the section. + */ + if (yasm_section_bcs_first(comment) == yasm_section_bcs_last(comment)) { + yasm_dvs_initialize(&dvs); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)), + line))); + yasm_section_bcs_append(comment, + yasm_bc_create_data(&dvs, 1, 0, object->arch, line)); + } + + yasm_dvs_initialize(&dvs); + do { + const char *s = yasm_vp_string(vp); + if (!s) { + yasm_error_set(YASM_ERROR_VALUE, + N_(".comment requires string parameters")); + yasm_dvs_delete(&dvs); + return; + } + yasm_dvs_append(&dvs, + yasm_dv_create_string(yasm__xstrdup(s), strlen(s))); + } while ((vp = yasm_vps_next(vp))); + + yasm_section_bcs_append(comment, + yasm_bc_create_data(&dvs, 1, 1, object->arch, line)); +} + +static void +dir_secrel32(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_datavalhead dvs; + yasm_valparam *vp; + + if (!object->cur_section) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_(".secrel32 can only be used inside of a section")); + return; + } + + vp = yasm_vps_first(valparams); + yasm_dvs_initialize(&dvs); + do { + yasm_expr *e = yasm_vp_expr(vp, object->symtab, line); + yasm_dataval *dv; + if (!e) { + yasm_error_set(YASM_ERROR_VALUE, + N_(".secrel32 requires expressions")); + yasm_dvs_delete(&dvs); + return; + } + dv = yasm_dv_create_expr(e); + yasm_dv_get_value(dv)->section_rel = 1; + yasm_dvs_append(&dvs, dv); + } while ((vp = yasm_vps_next(vp))); + + yasm_section_bcs_append(object->cur_section, + yasm_bc_create_data(&dvs, 4, 0, object->arch, line)); +} + +static void +dir_def(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp; + const char *symname; + yasm_symrec *sym; + coff_symrec_data *sym_data; + + if (objfmt_coff->def_sym) { + yasm_warn_set(YASM_WARN_GENERAL, + N_(".def pseudo-op used inside of .def/.endef; ignored")); + return; + } + + vp = yasm_vps_first(valparams); + symname = yasm_vp_id(vp); + if (!symname) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("argument to SAFESEH must be symbol name")); + return; + } + + sym = yasm_symtab_use(object->symtab, symname, line); + sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + if (!sym_data) { + sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, + COFF_SYMTAB_AUX_NONE); + } + objfmt_coff->def_sym = sym_data; +} + +static void +dir_scl(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_intnum *intn = NULL; + + if (!objfmt_coff->def_sym) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("%s pseudo-op used outside of .def/.endef; ignored"), + ".scl"); + return; + } + + if (yasm_dir_helper_intn(object, yasm_vps_first(valparams), line, + &intn, 0) < 0) + return; + if (!intn) + return; + objfmt_coff->def_sym->sclass = yasm_intnum_get_uint(intn); + yasm_intnum_destroy(intn); +} + +static void +dir_type(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_intnum *intn = NULL; + + if (!objfmt_coff->def_sym) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("%s pseudo-op used outside of .def/.endef; ignored"), + ".type"); + return; + } + + if (yasm_dir_helper_intn(object, yasm_vps_first(valparams), line, + &intn, 0) < 0) + return; + if (!intn) + return; + objfmt_coff->def_sym->type = yasm_intnum_get_uint(intn); + yasm_intnum_destroy(intn); +} + +static void +dir_endef(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + if (!objfmt_coff->def_sym) { + yasm_warn_set(YASM_WARN_GENERAL, + N_(".endef pseudo-op used before .def; ignored")); + return; + } + objfmt_coff->def_sym = NULL; +} + +static void +dir_proc_frame(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + const char *name = yasm_vp_id(vp); + + if (objfmt_coff->proc_frame) { + yasm_error_set_xref(objfmt_coff->proc_frame, + N_("previous procedure started here")); + yasm_error_set(YASM_ERROR_SYNTAX, + N_("nested procedures not supported (didn't use [ENDPROC_FRAME]?)")); + return; + } + objfmt_coff->proc_frame = line; + objfmt_coff->done_prolog = 0; + objfmt_coff->unwind = yasm_win64__uwinfo_create(); + objfmt_coff->unwind->proc = yasm_symtab_use(object->symtab, name, line); + + /* Optional error handler */ + vp = yasm_vps_next(vp); + if (!vp || !(name = yasm_vp_id(vp))) + return; + objfmt_coff->unwind->ehandler = + yasm_symtab_use(object->symtab, name, line); +} + +static int +procframe_checkstate(yasm_objfmt_coff *objfmt_coff, const char *dirname) +{ + if (!objfmt_coff->proc_frame) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] without preceding [PROC_FRAME]"), dirname); + return 0; + } + if (objfmt_coff->done_prolog) { + yasm_error_set_xref(objfmt_coff->done_prolog, + N_("prologue ended here")); + yasm_error_set(YASM_ERROR_SYNTAX, N_("[%s] after end of prologue"), + dirname); + return 0; + } + if (!objfmt_coff->unwind) + yasm_internal_error(N_("unwind info not present")); + return 1; +} + +/* Get current assembly position. + * XXX: There should be a better way to do this. + */ +static yasm_symrec * +get_curpos(yasm_object *object, const char *dirname, unsigned long line) +{ + if (!object->cur_section) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] can only be used inside of a section"), + dirname); + return NULL; + } + return yasm_symtab_define_curpos(object->symtab, "$", + yasm_section_bcs_last(object->cur_section), line); +} + +static void +dir_pushreg(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + coff_unwind_code *code; + const uintptr_t *reg; + + if (!procframe_checkstate(objfmt_coff, "PUSHREG")) + return; + + if (vp->type != YASM_PARAM_EXPR || + !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] requires a register as the first parameter"), + "PUSHREG"); + return; + } + + /* Generate a PUSH_NONVOL unwind code. */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, "PUSHREG", line); + code->opcode = UWOP_PUSH_NONVOL; + code->info = (unsigned int)(*reg & 0xF); + yasm_value_initialize(&code->off, NULL, 0); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_setframe(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + coff_unwind_code *code; + const uintptr_t *reg; + yasm_expr *off = NULL; + + if (!procframe_checkstate(objfmt_coff, "SETFRAME")) + return; + + if (vp->type != YASM_PARAM_EXPR || + !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] requires a register as the first parameter"), + "SETFRAME"); + return; + } + + vp = yasm_vps_next(vp); + if (vp) + off = yasm_vp_expr(vp, object->symtab, line); + + /* Set the frame fields in the unwind info */ + objfmt_coff->unwind->framereg = (unsigned long)(*reg); + yasm_value_initialize(&objfmt_coff->unwind->frameoff, off, 8); + + /* Generate a SET_FPREG unwind code */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, "SETFRAME", line); + code->opcode = UWOP_SET_FPREG; + code->info = (unsigned int)(*reg & 0xF); + yasm_value_initialize(&code->off, off ? yasm_expr_copy(off) : NULL, 8); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_allocstack(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + /*@null@*/ /*@only@*/ yasm_expr *size; + coff_unwind_code *code; + + if (!procframe_checkstate(objfmt_coff, "ALLOCSTACK")) + return; + + size = yasm_vp_expr(vp, object->symtab, line); + if (!size) { + yasm_error_set(YASM_ERROR_SYNTAX, N_("[%s] requires a size"), + "ALLOCSTACK"); + return; + } + + /* Generate an ALLOC_SMALL unwind code; this will get enlarged to an + * ALLOC_LARGE if necessary. + */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, "ALLOCSTACK", line); + code->opcode = UWOP_ALLOC_SMALL; + code->info = 0; + yasm_value_initialize(&code->off, size, 7); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_save_common(yasm_object *object, yasm_valparamhead *valparams, + unsigned long line, const char *name, int op) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + coff_unwind_code *code; + const uintptr_t *reg; + /*@only@*/ /*@null@*/ yasm_expr *offset; + + if (!procframe_checkstate(objfmt_coff, name)) + return; + + if (vp->type != YASM_PARAM_EXPR || + !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] requires a register as the first parameter"), + name); + return; + } + + vp = yasm_vps_next(vp); + offset = yasm_vp_expr(vp, object->symtab, line); + if (!offset) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] requires an offset as the second parameter"), + name); + return; + } + + /* Generate a SAVE_XXX unwind code; this will get enlarged to a + * SAVE_XXX_FAR if necessary. + */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, name, line); + code->opcode = op; + code->info = (unsigned int)(*reg & 0xF); + yasm_value_initialize(&code->off, offset, 16); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_savereg(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + dir_save_common(object, valparams, line, "SAVEREG", UWOP_SAVE_NONVOL); +} + +static void +dir_savexmm128(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + dir_save_common(object, valparams, line, "SAVEXMM128", UWOP_SAVE_XMM128); +} + +static void +dir_pushframe(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + coff_unwind_code *code; + + if (!procframe_checkstate(objfmt_coff, "PUSHFRAME")) + return; + + /* Generate a PUSH_MACHFRAME unwind code. If there's any parameter, + * we set info to 1. Otherwise we set info to 0. + */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, "PUSHFRAME", line); + code->opcode = UWOP_PUSH_MACHFRAME; + code->info = vp != NULL; + yasm_value_initialize(&code->off, NULL, 0); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_endprolog(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + if (!procframe_checkstate(objfmt_coff, "ENDPROLOG")) + return; + objfmt_coff->done_prolog = line; + + objfmt_coff->unwind->prolog = get_curpos(object, "ENDPROLOG", line); +} + +static void +dir_endproc_frame(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_section *sect; + coff_section_data *csd; + yasm_datavalhead dvs; + int isnew; + /*@dependent@*/ yasm_symrec *curpos, *unwindpos, *proc_sym, *xdata_sym; + + if (!objfmt_coff->proc_frame) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] without preceding [PROC_FRAME]"), + "ENDPROC_FRAME"); + return; + } + if (!objfmt_coff->done_prolog) { + yasm_error_set_xref(objfmt_coff->proc_frame, + N_("procedure started here")); + yasm_error_set(YASM_ERROR_SYNTAX, + N_("ended procedure without ending prologue"), + "ENDPROC_FRAME"); + objfmt_coff->proc_frame = 0; + yasm_win64__uwinfo_destroy(objfmt_coff->unwind); + objfmt_coff->unwind = NULL; + return; + } + if (!objfmt_coff->unwind) + yasm_internal_error(N_("unwind info not present")); + + proc_sym = objfmt_coff->unwind->proc; + + curpos = get_curpos(object, "ENDPROC_FRAME", line); + + /* + * Add unwind info to end of .xdata section. + */ + + sect = yasm_object_get_general(object, ".xdata", 0, 0, 0, &isnew, line); + + /* Initialize xdata section if needed */ + if (isnew) { + csd = yasm_section_get_data(sect, &coff_section_data_cb); + csd->flags = COFF_STYP_DATA | COFF_STYP_READ; + yasm_section_set_align(sect, 8, line); + } + + /* Get current position in .xdata section */ + unwindpos = yasm_symtab_define_curpos(object->symtab, "$", + yasm_section_bcs_last(sect), line); + /* Get symbol for .xdata as we'll want to reference it with WRT */ + csd = yasm_section_get_data(sect, &coff_section_data_cb); + xdata_sym = csd->sym; + + /* Add unwind info. Use line number of start of procedure. */ + yasm_win64__unwind_generate(sect, objfmt_coff->unwind, + objfmt_coff->proc_frame); + objfmt_coff->unwind = NULL; /* generate keeps the unwind pointer */ + + /* + * Add function lookup to end of .pdata section. + */ + + sect = yasm_object_get_general(object, ".pdata", 0, 0, 0, &isnew, line); + + /* Initialize pdata section if needed */ + if (isnew) { + csd = yasm_section_get_data(sect, &coff_section_data_cb); + csd->flags = COFF_STYP_DATA | COFF_STYP_READ; + csd->flags2 = COFF_FLAG_NOBASE; + yasm_section_set_align(sect, 4, line); + } + + /* Add function structure as data bytecode */ + yasm_dvs_initialize(&dvs); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create_ident(yasm_expr_sym(proc_sym), line))); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create(YASM_EXPR_WRT, yasm_expr_sym(curpos), + yasm_expr_sym(proc_sym), line))); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create(YASM_EXPR_WRT, yasm_expr_sym(unwindpos), + yasm_expr_sym(xdata_sym), line))); + yasm_section_bcs_append(sect, yasm_bc_create_data(&dvs, 4, 0, NULL, line)); + + objfmt_coff->proc_frame = 0; + objfmt_coff->done_prolog = 0; +} + +/* Define valid debug formats to use with this object format */ +static const char *coff_objfmt_dbgfmt_keywords[] = { + "null", + "dwarf2", + NULL +}; + +static const yasm_directive coff_objfmt_directives[] = { + { ".ident", "gas", dir_ident, YASM_DIR_ANY }, + { "ident", "nasm", dir_ident, YASM_DIR_ANY }, + { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, + { ".endef", "gas", dir_endef, YASM_DIR_ANY }, + { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, + { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, + { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, + { NULL, NULL, NULL, 0 } +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_coff_LTX_objfmt = { + "COFF (DJGPP)", + "coff", + "o", + 32, + 0, + coff_objfmt_dbgfmt_keywords, + "null", + coff_objfmt_directives, + NULL, /* no standard macros */ + coff_objfmt_create, + coff_objfmt_output, + coff_objfmt_destroy, + coff_objfmt_add_default_section, + coff_objfmt_init_new_section, + coff_objfmt_section_switch, + coff_objfmt_get_special_sym +}; + +/* Define valid debug formats to use with this object format */ +static const char *winXX_objfmt_dbgfmt_keywords[] = { + "null", + "dwarf2", + "cv8", + NULL +}; + +static const yasm_directive win32_objfmt_directives[] = { + { ".ident", "gas", dir_ident, YASM_DIR_ANY }, + { "ident", "nasm", dir_ident, YASM_DIR_ANY }, + { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, + { ".endef", "gas", dir_endef, YASM_DIR_ANY }, + { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, + { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, + { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, + { ".export", "gas", dir_export, YASM_DIR_ID_REQUIRED }, + { "export", "nasm", dir_export, YASM_DIR_ID_REQUIRED }, + { ".safeseh", "gas", dir_safeseh, YASM_DIR_ID_REQUIRED }, + { "safeseh", "nasm", dir_safeseh, YASM_DIR_ID_REQUIRED }, + { NULL, NULL, NULL, 0 } +}; + +static const char *win32_nasm_stdmac[] = { + "%imacro export 1+.nolist", + "[export %1]", + "%endmacro", + "%imacro safeseh 1+.nolist", + "[safeseh %1]", + "%endmacro", + NULL +}; + +static const yasm_stdmac win32_objfmt_stdmacs[] = { + { "nasm", "nasm", win32_nasm_stdmac }, + { NULL, NULL, NULL } +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_win32_LTX_objfmt = { + "Win32", + "win32", + "obj", + 32, + 1, + winXX_objfmt_dbgfmt_keywords, + "null", + win32_objfmt_directives, + win32_objfmt_stdmacs, + win32_objfmt_create, + coff_objfmt_output, + coff_objfmt_destroy, + coff_objfmt_add_default_section, + coff_objfmt_init_new_section, + coff_objfmt_section_switch, + coff_objfmt_get_special_sym +}; + +static const yasm_directive win64_objfmt_directives[] = { + { ".ident", "gas", dir_ident, YASM_DIR_ANY }, + { "ident", "nasm", dir_ident, YASM_DIR_ANY }, + { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, + { ".endef", "gas", dir_endef, YASM_DIR_ANY }, + { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, + { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, + { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, + { ".export", "gas", dir_export, YASM_DIR_ID_REQUIRED }, + { "export", "nasm", dir_export, YASM_DIR_ID_REQUIRED }, + { ".proc_frame", "gas", dir_proc_frame, YASM_DIR_ID_REQUIRED }, + { "proc_frame", "nasm", dir_proc_frame, YASM_DIR_ID_REQUIRED }, + { ".pushreg", "gas", dir_pushreg, YASM_DIR_ARG_REQUIRED }, + { "pushreg", "nasm", dir_pushreg, YASM_DIR_ARG_REQUIRED }, + { ".setframe", "gas", dir_setframe, YASM_DIR_ARG_REQUIRED }, + { "setframe", "nasm", dir_setframe, YASM_DIR_ARG_REQUIRED }, + { ".allocstack", "gas", dir_allocstack, YASM_DIR_ARG_REQUIRED }, + { "allocstack", "nasm", dir_allocstack, YASM_DIR_ARG_REQUIRED }, + { ".savereg", "gas", dir_savereg, YASM_DIR_ARG_REQUIRED }, + { "savereg", "nasm", dir_savereg, YASM_DIR_ARG_REQUIRED }, + { ".savexmm128", "gas", dir_savexmm128, YASM_DIR_ARG_REQUIRED }, + { "savexmm128", "nasm", dir_savexmm128, YASM_DIR_ARG_REQUIRED }, + { ".pushframe", "gas", dir_pushframe, YASM_DIR_ANY }, + { "pushframe", "nasm", dir_pushframe, YASM_DIR_ANY }, + { ".endprolog", "gas", dir_endprolog, YASM_DIR_ANY }, + { "endprolog", "nasm", dir_endprolog, YASM_DIR_ANY }, + { ".endproc_frame", "gas", dir_endproc_frame, YASM_DIR_ANY }, + { "endproc_frame", "nasm", dir_endproc_frame, YASM_DIR_ANY }, + { NULL, NULL, NULL, 0 } +}; + +#include "win64-nasm.c" +#include "win64-gas.c" + +static const yasm_stdmac win64_objfmt_stdmacs[] = { + { "nasm", "nasm", win64_nasm_stdmac }, + { "gas", "nasm", win64_gas_stdmac }, + { NULL, NULL, NULL } +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_win64_LTX_objfmt = { + "Win64", + "win64", + "obj", + 64, + 1, + winXX_objfmt_dbgfmt_keywords, + "null", + win64_objfmt_directives, + win64_objfmt_stdmacs, + win64_objfmt_create, + coff_objfmt_output, + coff_objfmt_destroy, + coff_objfmt_add_default_section, + coff_objfmt_init_new_section, + coff_objfmt_section_switch, + win64_objfmt_get_special_sym +}; +yasm_objfmt_module yasm_x64_LTX_objfmt = { + "Win64", + "x64", + "obj", + 64, + 1, + winXX_objfmt_dbgfmt_keywords, + "null", + win64_objfmt_directives, + win64_objfmt_stdmacs, + win64_objfmt_create, + coff_objfmt_output, + coff_objfmt_destroy, + coff_objfmt_add_default_section, + coff_objfmt_init_new_section, + coff_objfmt_section_switch, + win64_objfmt_get_special_sym +}; diff --git a/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h b/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h index 10d88a0982..cdd581acb2 100644 --- a/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h +++ b/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h @@ -1,77 +1,77 @@ -/* - * COFF (DJGPP) object format - * - * Copyright (C) 2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 AUTHOR AND OTHER 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 AUTHOR OR OTHER 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. - */ -#ifndef COFF_OBJFMT_H -#define COFF_OBJFMT_H - -typedef struct coff_unwind_code { - SLIST_ENTRY(coff_unwind_code) link; - - /*@dependent@*/ yasm_symrec *proc; /* Start of procedure */ - /*@dependent@*/ yasm_symrec *loc; /* Location of operation */ - /* Unwind operation code */ - enum { - UWOP_PUSH_NONVOL = 0, - UWOP_ALLOC_LARGE = 1, - UWOP_ALLOC_SMALL = 2, - UWOP_SET_FPREG = 3, - UWOP_SAVE_NONVOL = 4, - UWOP_SAVE_NONVOL_FAR = 5, - UWOP_SAVE_XMM128 = 8, - UWOP_SAVE_XMM128_FAR = 9, - UWOP_PUSH_MACHFRAME = 10 - } opcode; - unsigned int info; /* Operation info */ - yasm_value off; /* Offset expression (used for some codes) */ -} coff_unwind_code; - -typedef struct coff_unwind_info { - /*@dependent@*/ yasm_symrec *proc; /* Start of procedure */ - /*@dependent@*/ yasm_symrec *prolog; /* End of prologue */ - - /*@null@*/ /*@dependent@*/ yasm_symrec *ehandler; /* Error handler */ - - unsigned long framereg; /* Frame register */ - yasm_value frameoff; /* Frame offset */ - - /* Linked list of codes, in decreasing location offset order. - * Inserting at the head of this list during assembly naturally results - * in this sorting. - */ - SLIST_HEAD(coff_unwind_code_head, coff_unwind_code) codes; - - /* These aren't used until inside of generate. */ - yasm_value prolog_size; - yasm_value codes_count; -} coff_unwind_info; - -coff_unwind_info *yasm_win64__uwinfo_create(void); -void yasm_win64__uwinfo_destroy(coff_unwind_info *info); -void yasm_win64__unwind_generate(yasm_section *xdata, - /*@only@*/ coff_unwind_info *info, - unsigned long line); - -#endif +/* + * COFF (DJGPP) object format + * + * Copyright (C) 2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR AND OTHER 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 AUTHOR OR OTHER 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. + */ +#ifndef COFF_OBJFMT_H +#define COFF_OBJFMT_H + +typedef struct coff_unwind_code { + SLIST_ENTRY(coff_unwind_code) link; + + /*@dependent@*/ yasm_symrec *proc; /* Start of procedure */ + /*@dependent@*/ yasm_symrec *loc; /* Location of operation */ + /* Unwind operation code */ + enum { + UWOP_PUSH_NONVOL = 0, + UWOP_ALLOC_LARGE = 1, + UWOP_ALLOC_SMALL = 2, + UWOP_SET_FPREG = 3, + UWOP_SAVE_NONVOL = 4, + UWOP_SAVE_NONVOL_FAR = 5, + UWOP_SAVE_XMM128 = 8, + UWOP_SAVE_XMM128_FAR = 9, + UWOP_PUSH_MACHFRAME = 10 + } opcode; + unsigned int info; /* Operation info */ + yasm_value off; /* Offset expression (used for some codes) */ +} coff_unwind_code; + +typedef struct coff_unwind_info { + /*@dependent@*/ yasm_symrec *proc; /* Start of procedure */ + /*@dependent@*/ yasm_symrec *prolog; /* End of prologue */ + + /*@null@*/ /*@dependent@*/ yasm_symrec *ehandler; /* Error handler */ + + unsigned long framereg; /* Frame register */ + yasm_value frameoff; /* Frame offset */ + + /* Linked list of codes, in decreasing location offset order. + * Inserting at the head of this list during assembly naturally results + * in this sorting. + */ + SLIST_HEAD(coff_unwind_code_head, coff_unwind_code) codes; + + /* These aren't used until inside of generate. */ + yasm_value prolog_size; + yasm_value codes_count; +} coff_unwind_info; + +coff_unwind_info *yasm_win64__uwinfo_create(void); +void yasm_win64__uwinfo_destroy(coff_unwind_info *info); +void yasm_win64__unwind_generate(yasm_section *xdata, + /*@only@*/ coff_unwind_info *info, + unsigned long line); + +#endif diff --git a/contrib/tools/yasm/modules/objfmts/coff/win64-except.c b/contrib/tools/yasm/modules/objfmts/coff/win64-except.c index 8c8ecbbc49..8fe585df8c 100644 --- a/contrib/tools/yasm/modules/objfmts/coff/win64-except.c +++ b/contrib/tools/yasm/modules/objfmts/coff/win64-except.c @@ -1,559 +1,559 @@ -/* - * Win64 structured exception handling support - * - * Copyright (C) 2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 AUTHOR AND OTHER 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 AUTHOR OR OTHER 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. - */ -#include <util.h> - -#include <libyasm.h> - -#include "coff-objfmt.h" - - -#define UNW_FLAG_EHANDLER 0x01 -#define UNW_FLAG_UHANDLER 0x02 -#define UNW_FLAG_CHAININFO 0x04 - -/* Bytecode callback function prototypes */ -static void win64_uwinfo_bc_destroy(void *contents); -static void win64_uwinfo_bc_print(const void *contents, FILE *f, - int indent_level); -static void win64_uwinfo_bc_finalize(yasm_bytecode *bc, - yasm_bytecode *prev_bc); -static int win64_uwinfo_bc_calc_len - (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); -static int win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, - long new_val, /*@out@*/ long *neg_thres, - /*@out@*/ long *pos_thres); -static int win64_uwinfo_bc_tobytes - (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - /*@null@*/ yasm_output_reloc_func output_reloc); - -static void win64_uwcode_bc_destroy(void *contents); -static void win64_uwcode_bc_print(const void *contents, FILE *f, - int indent_level); -static void win64_uwcode_bc_finalize(yasm_bytecode *bc, - yasm_bytecode *prev_bc); -static int win64_uwcode_bc_calc_len - (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); -static int win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, - long new_val, /*@out@*/ long *neg_thres, - /*@out@*/ long *pos_thres); -static int win64_uwcode_bc_tobytes - (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - /*@null@*/ yasm_output_reloc_func output_reloc); - -/* Bytecode callback structures */ -static const yasm_bytecode_callback win64_uwinfo_bc_callback = { - win64_uwinfo_bc_destroy, - win64_uwinfo_bc_print, - win64_uwinfo_bc_finalize, - NULL, - win64_uwinfo_bc_calc_len, - win64_uwinfo_bc_expand, - win64_uwinfo_bc_tobytes, - 0 -}; - -static const yasm_bytecode_callback win64_uwcode_bc_callback = { - win64_uwcode_bc_destroy, - win64_uwcode_bc_print, - win64_uwcode_bc_finalize, - NULL, - win64_uwcode_bc_calc_len, - win64_uwcode_bc_expand, - win64_uwcode_bc_tobytes, - 0 -}; - - -coff_unwind_info * -yasm_win64__uwinfo_create(void) -{ - coff_unwind_info *info = yasm_xmalloc(sizeof(coff_unwind_info)); - info->proc = NULL; - info->prolog = NULL; - info->ehandler = NULL; - info->framereg = 0; - /* Frameoff is really a 4-bit value, scaled by 16 */ - yasm_value_initialize(&info->frameoff, NULL, 8); - SLIST_INIT(&info->codes); - yasm_value_initialize(&info->prolog_size, NULL, 8); - yasm_value_initialize(&info->codes_count, NULL, 8); - return info; -} - -void -yasm_win64__uwinfo_destroy(coff_unwind_info *info) -{ - coff_unwind_code *code; - - yasm_value_delete(&info->frameoff); - yasm_value_delete(&info->prolog_size); - yasm_value_delete(&info->codes_count); - - while (!SLIST_EMPTY(&info->codes)) { - code = SLIST_FIRST(&info->codes); - SLIST_REMOVE_HEAD(&info->codes, link); - yasm_value_delete(&code->off); - yasm_xfree(code); - } - yasm_xfree(info); -} - -void -yasm_win64__unwind_generate(yasm_section *xdata, coff_unwind_info *info, - unsigned long line) -{ - yasm_bytecode *infobc, *codebc = NULL; - coff_unwind_code *code; - - /* 4-byte align the start of unwind info */ - yasm_section_bcs_append(xdata, yasm_bc_create_align( - yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)), - line), - NULL, NULL, NULL, line)); - - /* Prolog size = end of prolog - start of procedure */ - yasm_value_initialize(&info->prolog_size, - yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(info->prolog), - yasm_expr_sym(info->proc), line), - 8); - - /* Unwind info */ - infobc = yasm_bc_create_common(&win64_uwinfo_bc_callback, info, line); - yasm_section_bcs_append(xdata, infobc); - - /* Code array */ - SLIST_FOREACH(code, &info->codes, link) { - codebc = yasm_bc_create_common(&win64_uwcode_bc_callback, code, - yasm_symrec_get_def_line(code->loc)); - yasm_section_bcs_append(xdata, codebc); - } - - /* Avoid double-free (by code destroy and uwinfo destroy). */ - SLIST_INIT(&info->codes); - - /* Number of codes = (Last code - end of info) >> 1 */ - if (!codebc) { - yasm_value_initialize(&info->codes_count, - yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)), - line), - 8); - } else { - yasm_value_initialize(&info->codes_count, - yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr( - yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(codebc), - yasm_expr_precbc(infobc), line)), - yasm_expr_int(yasm_intnum_create_uint(1)), line), - 8); - } - - /* 4-byte align */ - yasm_section_bcs_append(xdata, yasm_bc_create_align( - yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)), - line), - NULL, NULL, NULL, line)); - - /* Exception handler, if present. Use data bytecode. */ - if (info->ehandler) { - yasm_datavalhead dvs; - - yasm_dvs_initialize(&dvs); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create_ident(yasm_expr_sym(info->ehandler), line))); - yasm_section_bcs_append(xdata, - yasm_bc_create_data(&dvs, 4, 0, NULL, line)); - } -} - -static void -win64_uwinfo_bc_destroy(void *contents) -{ - yasm_win64__uwinfo_destroy((coff_unwind_info *)contents); -} - -static void -win64_uwinfo_bc_print(const void *contents, FILE *f, int indent_level) -{ - /* TODO */ -} - -static void -win64_uwinfo_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) -{ - coff_unwind_info *info = (coff_unwind_info *)bc->contents; - - if (yasm_value_finalize(&info->prolog_size, prev_bc)) - yasm_internal_error(N_("prolog size expression too complex")); - - if (yasm_value_finalize(&info->codes_count, prev_bc)) - yasm_internal_error(N_("codes count expression too complex")); - - if (yasm_value_finalize(&info->frameoff, prev_bc)) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset expression too complex")); -} - -static int -win64_uwinfo_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, - void *add_span_data) -{ - coff_unwind_info *info = (coff_unwind_info *)bc->contents; - /*@only@*/ /*@null@*/ yasm_intnum *intn; - long intv; - - /* Want to make sure prolog size and codes count doesn't exceed - * byte-size, and scaled frame offset doesn't exceed 4 bits. - */ - add_span(add_span_data, bc, 1, &info->prolog_size, 0, 255); - add_span(add_span_data, bc, 2, &info->codes_count, 0, 255); - - intn = yasm_value_get_intnum(&info->frameoff, bc, 0); - if (intn) { - intv = yasm_intnum_get_int(intn); - if (intv < 0 || intv > 240) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld bytes, must be between 0 and 240"), - intv); - else if ((intv & 0xF) != 0) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld is not a multiple of 16"), intv); - yasm_intnum_destroy(intn); - } else - add_span(add_span_data, bc, 3, &info->frameoff, 0, 240); - - bc->len += 4; - return 0; -} - -static int -win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, - /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) -{ - coff_unwind_info *info = (coff_unwind_info *)bc->contents; - switch (span) { - case 1: - yasm_error_set_xref(yasm_symrec_get_def_line(info->prolog), - N_("prologue ended here")); - yasm_error_set(YASM_ERROR_VALUE, - N_("prologue %ld bytes, must be <256"), new_val); - return -1; - case 2: - yasm_error_set(YASM_ERROR_VALUE, - N_("%ld unwind codes, maximum of 255"), new_val); - return -1; - case 3: - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld bytes, must be between 0 and 240"), - new_val); - return -1; - default: - yasm_internal_error(N_("unrecognized span id")); - } - return 0; -} - -static int -win64_uwinfo_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, - unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - yasm_output_reloc_func output_reloc) -{ - coff_unwind_info *info = (coff_unwind_info *)bc->contents; - unsigned char *buf = *bufp; - /*@only@*/ /*@null@*/ yasm_intnum *frameoff; - long intv; - - /* Version and flags */ - if (info->ehandler) - YASM_WRITE_8(buf, 1 | (UNW_FLAG_EHANDLER << 3)); - else - YASM_WRITE_8(buf, 1); - - /* Size of prolog */ - output_value(&info->prolog_size, buf, 1, (unsigned long)(buf-bufstart), - bc, 1, d); - buf += 1; - - /* Count of codes */ - output_value(&info->codes_count, buf, 1, (unsigned long)(buf-bufstart), - bc, 1, d); - buf += 1; - - /* Frame register and offset */ - frameoff = yasm_value_get_intnum(&info->frameoff, bc, 1); - if (!frameoff) { - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset expression too complex")); - return 1; - } - intv = yasm_intnum_get_int(frameoff); - if (intv < 0 || intv > 240) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld bytes, must be between 0 and 240"), intv); - else if ((intv & 0xF) != 0) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld is not a multiple of 16"), intv); - - YASM_WRITE_8(buf, ((unsigned long)intv & 0xF0) | (info->framereg & 0x0F)); - yasm_intnum_destroy(frameoff); - - *bufp = buf; - return 0; -} - -static void -win64_uwcode_bc_destroy(void *contents) -{ - coff_unwind_code *code = (coff_unwind_code *)contents; - yasm_value_delete(&code->off); - yasm_xfree(contents); -} - -static void -win64_uwcode_bc_print(const void *contents, FILE *f, int indent_level) -{ - /* TODO */ -} - -static void -win64_uwcode_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) -{ - coff_unwind_code *code = (coff_unwind_code *)bc->contents; - if (yasm_value_finalize(&code->off, prev_bc)) - yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex")); -} - -static int -win64_uwcode_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, - void *add_span_data) -{ - coff_unwind_code *code = (coff_unwind_code *)bc->contents; - int span = 0; - /*@only@*/ /*@null@*/ yasm_intnum *intn; - long intv; - long low, high, mask; - - bc->len += 2; /* Prolog offset, code, and info */ - - switch (code->opcode) { - case UWOP_PUSH_NONVOL: - case UWOP_SET_FPREG: - case UWOP_PUSH_MACHFRAME: - /* always 1 node */ - return 0; - case UWOP_ALLOC_SMALL: - case UWOP_ALLOC_LARGE: - /* Start with smallest, then work our way up as necessary */ - code->opcode = UWOP_ALLOC_SMALL; - code->info = 0; - span = 1; low = 8; high = 128; mask = 0x7; - break; - case UWOP_SAVE_NONVOL: - case UWOP_SAVE_NONVOL_FAR: - /* Start with smallest, then work our way up as necessary */ - code->opcode = UWOP_SAVE_NONVOL; - bc->len += 2; /* Scaled offset */ - span = 2; - low = 0; - high = 8*64*1024-8; /* 16-bit field, *8 scaling */ - mask = 0x7; - break; - case UWOP_SAVE_XMM128: - case UWOP_SAVE_XMM128_FAR: - /* Start with smallest, then work our way up as necessary */ - code->opcode = UWOP_SAVE_XMM128; - bc->len += 2; /* Scaled offset */ - span = 3; - low = 0; - high = 16*64*1024-16; /* 16-bit field, *16 scaling */ - mask = 0xF; - break; - default: - yasm_internal_error(N_("unrecognied unwind opcode")); - /*@unreached@*/ - return 0; - } - - intn = yasm_value_get_intnum(&code->off, bc, 0); - if (intn) { - intv = yasm_intnum_get_int(intn); - if (intv > high) { - /* Expand it ourselves here if we can and we're already larger */ - if (win64_uwcode_bc_expand(bc, span, intv, intv, &low, &high) > 0) - add_span(add_span_data, bc, span, &code->off, low, high); - } - if (intv < low) - yasm_error_set(YASM_ERROR_VALUE, - N_("negative offset not allowed")); - if ((intv & mask) != 0) - yasm_error_set(YASM_ERROR_VALUE, - N_("offset of %ld is not a multiple of %ld"), intv, mask+1); - yasm_intnum_destroy(intn); - } else - add_span(add_span_data, bc, span, &code->off, low, high); - return 0; -} - -static int -win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, - /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) -{ - coff_unwind_code *code = (coff_unwind_code *)bc->contents; - - if (new_val < 0) { - yasm_error_set(YASM_ERROR_VALUE, N_("negative offset not allowed")); - return -1; - } - - if (span == 1) { - /* 3 stages: SMALL, LARGE and info=0, LARGE and info=1 */ - if (code->opcode == UWOP_ALLOC_LARGE && code->info == 1) - yasm_internal_error(N_("expansion on already largest alloc")); - - if (code->opcode == UWOP_ALLOC_SMALL && new_val > 128) { - /* Overflowed small size */ - code->opcode = UWOP_ALLOC_LARGE; - bc->len += 2; - } - if (new_val <= 8*64*1024-8) { - /* Still can grow one more size */ - *pos_thres = 8*64*1024-8; - return 1; - } - /* We're into the largest size */ - code->info = 1; - bc->len += 2; - } else if (code->opcode == UWOP_SAVE_NONVOL && span == 2) { - code->opcode = UWOP_SAVE_NONVOL_FAR; - bc->len += 2; - } else if (code->opcode == UWOP_SAVE_XMM128 && span == 3) { - code->opcode = UWOP_SAVE_XMM128_FAR; - bc->len += 2; - } - return 0; -} - -static int -win64_uwcode_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, - unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - yasm_output_reloc_func output_reloc) -{ - coff_unwind_code *code = (coff_unwind_code *)bc->contents; - unsigned char *buf = *bufp; - yasm_value val; - unsigned int size; - int shift; - long intv, low, high, mask; - yasm_intnum *intn; - - /* Offset in prolog */ - yasm_value_initialize(&val, - yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(code->loc), - yasm_expr_sym(code->proc), bc->line), - 8); - output_value(&val, buf, 1, (unsigned long)(buf-bufstart), bc, 1, d); - buf += 1; - yasm_value_delete(&val); - - /* Offset value */ - switch (code->opcode) { - case UWOP_PUSH_NONVOL: - case UWOP_SET_FPREG: - case UWOP_PUSH_MACHFRAME: - /* just 1 node, no offset; write opcode and info and we're done */ - YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF)); - *bufp = buf; - return 0; - case UWOP_ALLOC_SMALL: - /* 1 node, but offset stored in info */ - size = 0; low = 8; high = 128; shift = 3; mask = 0x7; - break; - case UWOP_ALLOC_LARGE: - if (code->info == 0) { - size = 2; low = 136; high = 8*64*1024-8; shift = 3; - } else { - size = 4; low = high = 0; shift = 0; - } - mask = 0x7; - break; - case UWOP_SAVE_NONVOL: - size = 2; low = 0; high = 8*64*1024-8; shift = 3; mask = 0x7; - break; - case UWOP_SAVE_XMM128: - size = 2; low = 0; high = 16*64*1024-16; shift = 4; mask = 0xF; - break; - case UWOP_SAVE_NONVOL_FAR: - size = 4; low = high = 0; shift = 0; mask = 0x7; - break; - case UWOP_SAVE_XMM128_FAR: - size = 4; low = high = 0; shift = 0; mask = 0xF; - break; - default: - yasm_internal_error(N_("unrecognied unwind opcode")); - /*@unreached@*/ - return 1; - } - - /* Check for overflow */ - intn = yasm_value_get_intnum(&code->off, bc, 1); - if (!intn) { - yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex")); - return 1; - } - intv = yasm_intnum_get_int(intn); - if (size != 4 && (intv < low || intv > high)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("offset of %ld bytes, must be between %ld and %ld"), - intv, low, high); - return 1; - } - if ((intv & mask) != 0) { - yasm_error_set(YASM_ERROR_VALUE, - N_("offset of %ld is not a multiple of %ld"), - intv, mask+1); - return 1; - } - - /* Stored value in info instead of extra code space */ - if (size == 0) - code->info = (yasm_intnum_get_uint(intn) >> shift)-1; - - /* Opcode and info */ - YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF)); - - if (size != 0) { - yasm_intnum_get_sized(intn, buf, size, size*8, -shift, 0, 1); - buf += size; - } - - yasm_intnum_destroy(intn); - - *bufp = buf; - return 0; -} +/* + * Win64 structured exception handling support + * + * Copyright (C) 2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR AND OTHER 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 AUTHOR OR OTHER 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. + */ +#include <util.h> + +#include <libyasm.h> + +#include "coff-objfmt.h" + + +#define UNW_FLAG_EHANDLER 0x01 +#define UNW_FLAG_UHANDLER 0x02 +#define UNW_FLAG_CHAININFO 0x04 + +/* Bytecode callback function prototypes */ +static void win64_uwinfo_bc_destroy(void *contents); +static void win64_uwinfo_bc_print(const void *contents, FILE *f, + int indent_level); +static void win64_uwinfo_bc_finalize(yasm_bytecode *bc, + yasm_bytecode *prev_bc); +static int win64_uwinfo_bc_calc_len + (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); +static int win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, + long new_val, /*@out@*/ long *neg_thres, + /*@out@*/ long *pos_thres); +static int win64_uwinfo_bc_tobytes + (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +static void win64_uwcode_bc_destroy(void *contents); +static void win64_uwcode_bc_print(const void *contents, FILE *f, + int indent_level); +static void win64_uwcode_bc_finalize(yasm_bytecode *bc, + yasm_bytecode *prev_bc); +static int win64_uwcode_bc_calc_len + (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); +static int win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, + long new_val, /*@out@*/ long *neg_thres, + /*@out@*/ long *pos_thres); +static int win64_uwcode_bc_tobytes + (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +/* Bytecode callback structures */ +static const yasm_bytecode_callback win64_uwinfo_bc_callback = { + win64_uwinfo_bc_destroy, + win64_uwinfo_bc_print, + win64_uwinfo_bc_finalize, + NULL, + win64_uwinfo_bc_calc_len, + win64_uwinfo_bc_expand, + win64_uwinfo_bc_tobytes, + 0 +}; + +static const yasm_bytecode_callback win64_uwcode_bc_callback = { + win64_uwcode_bc_destroy, + win64_uwcode_bc_print, + win64_uwcode_bc_finalize, + NULL, + win64_uwcode_bc_calc_len, + win64_uwcode_bc_expand, + win64_uwcode_bc_tobytes, + 0 +}; + + +coff_unwind_info * +yasm_win64__uwinfo_create(void) +{ + coff_unwind_info *info = yasm_xmalloc(sizeof(coff_unwind_info)); + info->proc = NULL; + info->prolog = NULL; + info->ehandler = NULL; + info->framereg = 0; + /* Frameoff is really a 4-bit value, scaled by 16 */ + yasm_value_initialize(&info->frameoff, NULL, 8); + SLIST_INIT(&info->codes); + yasm_value_initialize(&info->prolog_size, NULL, 8); + yasm_value_initialize(&info->codes_count, NULL, 8); + return info; +} + +void +yasm_win64__uwinfo_destroy(coff_unwind_info *info) +{ + coff_unwind_code *code; + + yasm_value_delete(&info->frameoff); + yasm_value_delete(&info->prolog_size); + yasm_value_delete(&info->codes_count); + + while (!SLIST_EMPTY(&info->codes)) { + code = SLIST_FIRST(&info->codes); + SLIST_REMOVE_HEAD(&info->codes, link); + yasm_value_delete(&code->off); + yasm_xfree(code); + } + yasm_xfree(info); +} + +void +yasm_win64__unwind_generate(yasm_section *xdata, coff_unwind_info *info, + unsigned long line) +{ + yasm_bytecode *infobc, *codebc = NULL; + coff_unwind_code *code; + + /* 4-byte align the start of unwind info */ + yasm_section_bcs_append(xdata, yasm_bc_create_align( + yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)), + line), + NULL, NULL, NULL, line)); + + /* Prolog size = end of prolog - start of procedure */ + yasm_value_initialize(&info->prolog_size, + yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(info->prolog), + yasm_expr_sym(info->proc), line), + 8); + + /* Unwind info */ + infobc = yasm_bc_create_common(&win64_uwinfo_bc_callback, info, line); + yasm_section_bcs_append(xdata, infobc); + + /* Code array */ + SLIST_FOREACH(code, &info->codes, link) { + codebc = yasm_bc_create_common(&win64_uwcode_bc_callback, code, + yasm_symrec_get_def_line(code->loc)); + yasm_section_bcs_append(xdata, codebc); + } + + /* Avoid double-free (by code destroy and uwinfo destroy). */ + SLIST_INIT(&info->codes); + + /* Number of codes = (Last code - end of info) >> 1 */ + if (!codebc) { + yasm_value_initialize(&info->codes_count, + yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)), + line), + 8); + } else { + yasm_value_initialize(&info->codes_count, + yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr( + yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(codebc), + yasm_expr_precbc(infobc), line)), + yasm_expr_int(yasm_intnum_create_uint(1)), line), + 8); + } + + /* 4-byte align */ + yasm_section_bcs_append(xdata, yasm_bc_create_align( + yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)), + line), + NULL, NULL, NULL, line)); + + /* Exception handler, if present. Use data bytecode. */ + if (info->ehandler) { + yasm_datavalhead dvs; + + yasm_dvs_initialize(&dvs); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create_ident(yasm_expr_sym(info->ehandler), line))); + yasm_section_bcs_append(xdata, + yasm_bc_create_data(&dvs, 4, 0, NULL, line)); + } +} + +static void +win64_uwinfo_bc_destroy(void *contents) +{ + yasm_win64__uwinfo_destroy((coff_unwind_info *)contents); +} + +static void +win64_uwinfo_bc_print(const void *contents, FILE *f, int indent_level) +{ + /* TODO */ +} + +static void +win64_uwinfo_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + coff_unwind_info *info = (coff_unwind_info *)bc->contents; + + if (yasm_value_finalize(&info->prolog_size, prev_bc)) + yasm_internal_error(N_("prolog size expression too complex")); + + if (yasm_value_finalize(&info->codes_count, prev_bc)) + yasm_internal_error(N_("codes count expression too complex")); + + if (yasm_value_finalize(&info->frameoff, prev_bc)) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset expression too complex")); +} + +static int +win64_uwinfo_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + coff_unwind_info *info = (coff_unwind_info *)bc->contents; + /*@only@*/ /*@null@*/ yasm_intnum *intn; + long intv; + + /* Want to make sure prolog size and codes count doesn't exceed + * byte-size, and scaled frame offset doesn't exceed 4 bits. + */ + add_span(add_span_data, bc, 1, &info->prolog_size, 0, 255); + add_span(add_span_data, bc, 2, &info->codes_count, 0, 255); + + intn = yasm_value_get_intnum(&info->frameoff, bc, 0); + if (intn) { + intv = yasm_intnum_get_int(intn); + if (intv < 0 || intv > 240) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld bytes, must be between 0 and 240"), + intv); + else if ((intv & 0xF) != 0) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld is not a multiple of 16"), intv); + yasm_intnum_destroy(intn); + } else + add_span(add_span_data, bc, 3, &info->frameoff, 0, 240); + + bc->len += 4; + return 0; +} + +static int +win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) +{ + coff_unwind_info *info = (coff_unwind_info *)bc->contents; + switch (span) { + case 1: + yasm_error_set_xref(yasm_symrec_get_def_line(info->prolog), + N_("prologue ended here")); + yasm_error_set(YASM_ERROR_VALUE, + N_("prologue %ld bytes, must be <256"), new_val); + return -1; + case 2: + yasm_error_set(YASM_ERROR_VALUE, + N_("%ld unwind codes, maximum of 255"), new_val); + return -1; + case 3: + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld bytes, must be between 0 and 240"), + new_val); + return -1; + default: + yasm_internal_error(N_("unrecognized span id")); + } + return 0; +} + +static int +win64_uwinfo_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + yasm_output_reloc_func output_reloc) +{ + coff_unwind_info *info = (coff_unwind_info *)bc->contents; + unsigned char *buf = *bufp; + /*@only@*/ /*@null@*/ yasm_intnum *frameoff; + long intv; + + /* Version and flags */ + if (info->ehandler) + YASM_WRITE_8(buf, 1 | (UNW_FLAG_EHANDLER << 3)); + else + YASM_WRITE_8(buf, 1); + + /* Size of prolog */ + output_value(&info->prolog_size, buf, 1, (unsigned long)(buf-bufstart), + bc, 1, d); + buf += 1; + + /* Count of codes */ + output_value(&info->codes_count, buf, 1, (unsigned long)(buf-bufstart), + bc, 1, d); + buf += 1; + + /* Frame register and offset */ + frameoff = yasm_value_get_intnum(&info->frameoff, bc, 1); + if (!frameoff) { + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset expression too complex")); + return 1; + } + intv = yasm_intnum_get_int(frameoff); + if (intv < 0 || intv > 240) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld bytes, must be between 0 and 240"), intv); + else if ((intv & 0xF) != 0) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld is not a multiple of 16"), intv); + + YASM_WRITE_8(buf, ((unsigned long)intv & 0xF0) | (info->framereg & 0x0F)); + yasm_intnum_destroy(frameoff); + + *bufp = buf; + return 0; +} + +static void +win64_uwcode_bc_destroy(void *contents) +{ + coff_unwind_code *code = (coff_unwind_code *)contents; + yasm_value_delete(&code->off); + yasm_xfree(contents); +} + +static void +win64_uwcode_bc_print(const void *contents, FILE *f, int indent_level) +{ + /* TODO */ +} + +static void +win64_uwcode_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + coff_unwind_code *code = (coff_unwind_code *)bc->contents; + if (yasm_value_finalize(&code->off, prev_bc)) + yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex")); +} + +static int +win64_uwcode_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + coff_unwind_code *code = (coff_unwind_code *)bc->contents; + int span = 0; + /*@only@*/ /*@null@*/ yasm_intnum *intn; + long intv; + long low, high, mask; + + bc->len += 2; /* Prolog offset, code, and info */ + + switch (code->opcode) { + case UWOP_PUSH_NONVOL: + case UWOP_SET_FPREG: + case UWOP_PUSH_MACHFRAME: + /* always 1 node */ + return 0; + case UWOP_ALLOC_SMALL: + case UWOP_ALLOC_LARGE: + /* Start with smallest, then work our way up as necessary */ + code->opcode = UWOP_ALLOC_SMALL; + code->info = 0; + span = 1; low = 8; high = 128; mask = 0x7; + break; + case UWOP_SAVE_NONVOL: + case UWOP_SAVE_NONVOL_FAR: + /* Start with smallest, then work our way up as necessary */ + code->opcode = UWOP_SAVE_NONVOL; + bc->len += 2; /* Scaled offset */ + span = 2; + low = 0; + high = 8*64*1024-8; /* 16-bit field, *8 scaling */ + mask = 0x7; + break; + case UWOP_SAVE_XMM128: + case UWOP_SAVE_XMM128_FAR: + /* Start with smallest, then work our way up as necessary */ + code->opcode = UWOP_SAVE_XMM128; + bc->len += 2; /* Scaled offset */ + span = 3; + low = 0; + high = 16*64*1024-16; /* 16-bit field, *16 scaling */ + mask = 0xF; + break; + default: + yasm_internal_error(N_("unrecognied unwind opcode")); + /*@unreached@*/ + return 0; + } + + intn = yasm_value_get_intnum(&code->off, bc, 0); + if (intn) { + intv = yasm_intnum_get_int(intn); + if (intv > high) { + /* Expand it ourselves here if we can and we're already larger */ + if (win64_uwcode_bc_expand(bc, span, intv, intv, &low, &high) > 0) + add_span(add_span_data, bc, span, &code->off, low, high); + } + if (intv < low) + yasm_error_set(YASM_ERROR_VALUE, + N_("negative offset not allowed")); + if ((intv & mask) != 0) + yasm_error_set(YASM_ERROR_VALUE, + N_("offset of %ld is not a multiple of %ld"), intv, mask+1); + yasm_intnum_destroy(intn); + } else + add_span(add_span_data, bc, span, &code->off, low, high); + return 0; +} + +static int +win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) +{ + coff_unwind_code *code = (coff_unwind_code *)bc->contents; + + if (new_val < 0) { + yasm_error_set(YASM_ERROR_VALUE, N_("negative offset not allowed")); + return -1; + } + + if (span == 1) { + /* 3 stages: SMALL, LARGE and info=0, LARGE and info=1 */ + if (code->opcode == UWOP_ALLOC_LARGE && code->info == 1) + yasm_internal_error(N_("expansion on already largest alloc")); + + if (code->opcode == UWOP_ALLOC_SMALL && new_val > 128) { + /* Overflowed small size */ + code->opcode = UWOP_ALLOC_LARGE; + bc->len += 2; + } + if (new_val <= 8*64*1024-8) { + /* Still can grow one more size */ + *pos_thres = 8*64*1024-8; + return 1; + } + /* We're into the largest size */ + code->info = 1; + bc->len += 2; + } else if (code->opcode == UWOP_SAVE_NONVOL && span == 2) { + code->opcode = UWOP_SAVE_NONVOL_FAR; + bc->len += 2; + } else if (code->opcode == UWOP_SAVE_XMM128 && span == 3) { + code->opcode = UWOP_SAVE_XMM128_FAR; + bc->len += 2; + } + return 0; +} + +static int +win64_uwcode_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + yasm_output_reloc_func output_reloc) +{ + coff_unwind_code *code = (coff_unwind_code *)bc->contents; + unsigned char *buf = *bufp; + yasm_value val; + unsigned int size; + int shift; + long intv, low, high, mask; + yasm_intnum *intn; + + /* Offset in prolog */ + yasm_value_initialize(&val, + yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(code->loc), + yasm_expr_sym(code->proc), bc->line), + 8); + output_value(&val, buf, 1, (unsigned long)(buf-bufstart), bc, 1, d); + buf += 1; + yasm_value_delete(&val); + + /* Offset value */ + switch (code->opcode) { + case UWOP_PUSH_NONVOL: + case UWOP_SET_FPREG: + case UWOP_PUSH_MACHFRAME: + /* just 1 node, no offset; write opcode and info and we're done */ + YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF)); + *bufp = buf; + return 0; + case UWOP_ALLOC_SMALL: + /* 1 node, but offset stored in info */ + size = 0; low = 8; high = 128; shift = 3; mask = 0x7; + break; + case UWOP_ALLOC_LARGE: + if (code->info == 0) { + size = 2; low = 136; high = 8*64*1024-8; shift = 3; + } else { + size = 4; low = high = 0; shift = 0; + } + mask = 0x7; + break; + case UWOP_SAVE_NONVOL: + size = 2; low = 0; high = 8*64*1024-8; shift = 3; mask = 0x7; + break; + case UWOP_SAVE_XMM128: + size = 2; low = 0; high = 16*64*1024-16; shift = 4; mask = 0xF; + break; + case UWOP_SAVE_NONVOL_FAR: + size = 4; low = high = 0; shift = 0; mask = 0x7; + break; + case UWOP_SAVE_XMM128_FAR: + size = 4; low = high = 0; shift = 0; mask = 0xF; + break; + default: + yasm_internal_error(N_("unrecognied unwind opcode")); + /*@unreached@*/ + return 1; + } + + /* Check for overflow */ + intn = yasm_value_get_intnum(&code->off, bc, 1); + if (!intn) { + yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex")); + return 1; + } + intv = yasm_intnum_get_int(intn); + if (size != 4 && (intv < low || intv > high)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("offset of %ld bytes, must be between %ld and %ld"), + intv, low, high); + return 1; + } + if ((intv & mask) != 0) { + yasm_error_set(YASM_ERROR_VALUE, + N_("offset of %ld is not a multiple of %ld"), + intv, mask+1); + return 1; + } + + /* Stored value in info instead of extra code space */ + if (size == 0) + code->info = (yasm_intnum_get_uint(intn) >> shift)-1; + + /* Opcode and info */ + YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF)); + + if (size != 0) { + yasm_intnum_get_sized(intn, buf, size, size*8, -shift, 0, 1); + buf += size; + } + + yasm_intnum_destroy(intn); + + *bufp = buf; + return 0; +} |