diff --git a/doc/lang.src b/doc/lang.src index de389a8b7..d76fa3c1f 100644 --- a/doc/lang.src +++ b/doc/lang.src @@ -992,6 +992,7 @@ NASM has the capacity to define other special symbols beginning with a double period: for example, \c{..start} is used to specify the entry point in the \c{obj} output format (see \k{dotdotstart}), \c{..imagebase} is used to find out the offset from a base address -of the current image in the \c{win64} output format (see \k{win64pic}). +of the current image in the \c{win64} output format (see \k{win64pic}), +\c{..symtab} is used to emit the COFF symbol table index (see \k{win32wrt}). So just keep in mind that symbols beginning with a double period are special. diff --git a/doc/outfmt.src b/doc/outfmt.src index b67382f33..63cfafbf0 100644 --- a/doc/outfmt.src +++ b/doc/outfmt.src @@ -759,6 +759,16 @@ data for "safe exception handler table" causes no backward incompatibilities and "safeseh" modules generated by NASM 2.03 and later can still be linked by earlier versions or non-Microsoft linkers. +\S{win32wrt} \c{win32}: Special Symbol and WRT + +The Microsoft linker may require symbol table indexes instead of absolute or +image relative addresses in some more modern structures, like those used for +exception handler control flow guard metadata (ehcont). This can be +accomplished by getting the symbol address with respect to the special +\c{..symtab} symbol. For instance: + +\c __guard_ehcont_main: dd main.cont wrt ..symtab + \S{codeview} Debugging formats for Windows \I{Windows debugging formats} diff --git a/output/outcoff.c b/output/outcoff.c index d38085d43..afeca8783 100644 --- a/output/outcoff.c +++ b/output/outcoff.c @@ -74,6 +74,9 @@ bool win32, win64; static int32_t imagebase_sect; #define WRT_IMAGEBASE "..imagebase" +static int32_t symtab_sect; +#define WRT_SYMTAB "..symtab" + /* * Some common section flags by default */ @@ -192,6 +195,8 @@ static void coff_gen_init(void) coff_strs = saa_init(1); strslen = 0; def_seg = seg_alloc(); + symtab_sect = seg_alloc()+1; + backend_label(WRT_SYMTAB, symtab_sect, 0); } static void coff_cleanup(void) @@ -210,6 +215,11 @@ static void coff_cleanup(void) coff_sects[i]->head = coff_sects[i]->head->next; nasm_free(r); } + while (coff_sects[i]->symidx_reloc_head) { + struct coff_SymIdxReloc * const ir = coff_sects[i]->symidx_reloc_head; + coff_sects[i]->symidx_reloc_head = ir->next; + nasm_free(ir); + } nasm_free(coff_sects[i]->name); nasm_free(coff_sects[i]->comdat_name); nasm_free(coff_sects[i]); @@ -544,7 +554,7 @@ static void coff_deflabel(char *name, int32_t segment, int64_t offset, " special symbol types"); if (name[0] == '.' && name[1] == '.' && name[2] != '@') { - if (strcmp(name,WRT_IMAGEBASE)) + if (strcmp(name,WRT_IMAGEBASE) && strcmp(name, WRT_SYMTAB)) nasm_nonfatal("unrecognized special symbol `%s'", name); return; } @@ -649,6 +659,50 @@ static int32_t coff_add_reloc(struct coff_Section *sect, int32_t segment, return 0; } +/* Helper for coff_out handling wrt ..symtab addressing. */ +static uint32_t coff_out_get_symbol_idx(int32_t tsegment, int64_t offset) +{ + /* No better way of doing this? */ + int section; + if (tsegment == NO_SEG) { + section = -1; + } else { + for (section = 0; section < coff_nsects; section++) + if (tsegment == coff_sects[section]->index) + break; + section++; + } + + /* Do symbol table lookup (unless it is external). */ + if (section <= coff_nsects) { + uint32_t symidx = UINT32_MAX; + int n; + saa_rewind(coff_syms); + for (n = 0; n < coff_nsyms; n++) { + struct coff_Symbol *sym = saa_rstruct(coff_syms); + nasm_try_static_assert(sizeof(sym->value) == sizeof(int32_t)); + if (sym->section == section && sym->value == (int32_t)offset) { + if (sym->type & 0x30) /* N_TMASK */ + return n; /* Prefer symbol with non-NULL type. */ + if (symidx == UINT32_MAX) + symidx = n; + } + } + if (symidx == UINT32_MAX) + nasm_nonfatal("wrt ..symtab: Unable to find symbol for %s:%#" PRIx64, + section > 0 ? coff_sects[section - 1]->name : "ABS", offset); + return symidx; + } + + /* External symbols. */ + if (tsegment != NO_SEG) + return raa_read(bsym, tsegment); + + nasm_nonfatal("wrt ..symtab: Unable to find symbol for %#"PRIx32":%#" PRIx64, + tsegment, offset); + return UINT32_MAX; +} + static void coff_out(const struct out_data *out) { OUT_LEGACY(out,segto,data,type,size,segment,wrt); @@ -656,7 +710,7 @@ static void coff_out(const struct out_data *out) uint8_t mydata[8], *p; int i; - if (wrt != NO_SEG && !win64) { + if (wrt != NO_SEG && wrt != symtab_sect && !win64) { wrt = NO_SEG; /* continue to do _something_ */ nasm_nonfatal("WRT not supported by COFF output formats"); } @@ -716,7 +770,25 @@ static void coff_out(const struct out_data *out) coff_sect_write(s, data, size); } else if (type == OUT_ADDRESS) { int asize = abs((int)size); - if (!win64) { + if (wrt == symtab_sect) { + /* Emit internal fixup since the table starts with sections and + stuff that can be added to after this statement. */ + struct coff_SymIdxReloc *ir; + nasm_new(ir); + ir->offset = s->len; + ir->size = (uint8_t)asize; + ir->symbol = coff_out_get_symbol_idx(segment, out->toffset); + ir->next = s->symidx_reloc_head; + s->symidx_reloc_head = ir; + nasm_assert(asize <= sizeof(mydata)); + coff_sect_write(s, mydata, asize); + if (asize > 4) + nasm_warn(WARN_OTHER, "zero extending 'wrt "WRT_SYMTAB + "' (32-bit) to %u-bit", asize * 8); + else if (asize < 4) + nasm_warn(WARN_OTHER, "truncating 'wrt "WRT_SYMTAB + "' (32-bit) to %u-bit", asize * 8); + } else if (!win64) { if (asize != 4 && (segment != NO_SEG || wrt != NO_SEG)) { nasm_nonfatal("COFF format does not support non-32-bit" " relocations"); @@ -1102,6 +1174,15 @@ static void coff_write(void) */ for (i = 0; i < coff_nsects; i++) if (coff_sects[i]->data) { + /* Apply symbol table index fixups before writing */ + struct coff_SymIdxReloc *ir; + for (ir = coff_sects[i]->symidx_reloc_head; ir; ir = ir->next) { + uint8_t mydata[4]; + setu32(mydata, ir->symbol + initsym); + saa_fwrite(coff_sects[i]->data, ir->offset, + mydata, ir->size >= 4 ? 4 : ir->size); + } + saa_fpwrite(coff_sects[i]->data, ofile); coff_write_relocs(coff_sects[i]); diff --git a/output/pecoff.h b/output/pecoff.h index 9fe4145d5..63a805a08 100644 --- a/output/pecoff.h +++ b/output/pecoff.h @@ -457,6 +457,7 @@ struct coff_Section { int32_t namepos; /* Offset of name into the strings table */ int32_t pos, relpos; int64_t pass_last_seen; + struct coff_SymIdxReloc *symidx_reloc_head; /* comdat-related members */ char *comdat_name; @@ -478,6 +479,13 @@ struct coff_Reloc { int16_t type; }; +struct coff_SymIdxReloc { + struct coff_SymIdxReloc *next; + uint32_t symbol; /* symbol number */ + uint32_t offset; /* byte offset into the secetion. */ + uint32_t size; /* the size of the area to fix up */ +}; + struct coff_Symbol { char name[9]; int32_t strpos; /* string table position of name */ diff --git a/test/wrtsymtab.asm b/test/wrtsymtab.asm new file mode 100644 index 000000000..ff49b39b7 --- /dev/null +++ b/test/wrtsymtab.asm @@ -0,0 +1,79 @@ +;Testname=win32; Arguments=-fwin32 -owrtsymtab.o -Ox; Files=stdout stderr wrtsymtab.o +;Testname=win64; Arguments=-fwin64 -owrtsymtab.o -Ox; Files=stdout stderr wrtsymtab.o + +; Just some symbols & code to work with. +abs_sym1 equ 0x12345678 + +section .text +extern extrn_sym_unused +required extrn_sym1 +mysymbol1: + ret + int3 + int3 +mysymbol2: + ret + int3 +mysymbol3_private: +.start_of_prolog: +mysymbol3: +global mysymbol3:function + ret + +oddsym1: + ret + +required extrn_sym2 +required extrn_sym3 +extern extrn_sym4 + +section .data +abs_sym2 equ 0x91929394 + +section .rodata rdata + ; Various uses with DD + db ' mysymbol1:' + dd mysymbol1 wrt ..symtab + db ' extrn_sym1:' + dd extrn_sym1 wrt ..symtab + db ' abs_sym1:' + dd abs_sym1 wrt ..symtab + + db ' mysymbol2:' + dd mysymbol2 wrt ..symtab + db ' extrn_sym2:' + dd extrn_sym2 wrt ..symtab + db ' abs_sym2:' + dd abs_sym2 wrt ..symtab + + db ' extrn_sym3:' + dd extrn_sym3 wrt ..symtab + db ' mysymbol3:' + dd mysymbol3 wrt ..symtab + db ' extrn_sym4:' + dd extrn_sym4 wrt ..symtab + + ; Uses with DB, DW, DD and DQ. Will generate warnings. + db ' db oddsym1:' + db oddsym1 wrt ..symtab + db ' dw oddsym1:' + dw oddsym1 wrt ..symtab + db ' dd oddsym1:' + dd oddsym1 wrt ..symtab ; ok, no warning + db 'dq same:' + dq oddsym1 wrt ..symtab + +; a little closer to real life use. +section gehcont$y align=4 rdata +__guard_ehcont_main: dd main.cont wrt ..symtab + +section .text +global main:function +main: + call mysymbol3 + xor eax, eax + ret +.cont: + mov eax, -1 + ret +