diff options
Diffstat (limited to 'vere/ext/nasm/output')
31 files changed, 20381 insertions, 0 deletions
diff --git a/vere/ext/nasm/output/codeview.c b/vere/ext/nasm/output/codeview.c new file mode 100644 index 0000000..d1011fc --- /dev/null +++ b/vere/ext/nasm/output/codeview.c @@ -0,0 +1,820 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2020 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * codeview.c Codeview Debug Format support for COFF + */ + +#include "version.h" +#include "compiler.h" + + +#include "nasm.h" +#include "nasmlib.h" +#include "error.h" +#include "preproc.h" +#include "saa.h" +#include "hashtbl.h" +#include "outlib.h" +#include "pecoff.h" +#include "md5.h" + +static void cv8_init(void); +static void cv8_linenum(const char *filename, int32_t linenumber, + int32_t segto); +static void cv8_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special); +static void cv8_typevalue(int32_t type); +static void cv8_output(int type, void *param); +static void cv8_cleanup(void); + +const struct dfmt df_cv8 = { + "Codeview 8+", /* .fullname */ + "cv8", /* .shortname */ + cv8_init, /* .init */ + cv8_linenum, /* .linenum */ + cv8_deflabel, /* .debug_deflabel */ + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, /* .debug_directive */ + cv8_typevalue, /* .debug_typevalue */ + cv8_output, /* .debug_output */ + cv8_cleanup, /* .cleanup */ + NULL /* pragma list */ +}; + +/******************************************************************************* + * dfmt callbacks + ******************************************************************************/ +struct source_file; + +struct source_file { + const char *filename; + char *fullname; + uint32_t fullnamelen; + + struct source_file *next; + + uint32_t filetbl_off; + uint32_t sourcetbl_off; + + struct SAA *lines; + uint32_t num_lines; + + unsigned char md5sum[MD5_HASHBYTES]; +}; + +struct linepair { + uint32_t file_offset; + uint32_t linenumber; +}; + +enum symbol_type { + SYMTYPE_CODE, + SYMTYPE_PROC, + SYMTYPE_LDATA, + SYMTYPE_GDATA, + + SYMTYPE_MAX +}; + +struct cv8_symbol { + enum symbol_type type; + char *name; + + uint32_t secrel; + uint16_t section; + uint32_t size; + uint32_t typeindex; + + enum symtype { + TYPE_UNREGISTERED = 0x0000, /* T_NOTYPE */ + TYPE_BYTE = 0x0020, + TYPE_WORD = 0x0021, + TYPE_DWORD= 0x0022, + TYPE_QUAD = 0x0023, + + TYPE_REAL32 = 0x0040, + TYPE_REAL64 = 0x0041, + TYPE_REAL80 = 0x0042, + TYPE_REAL128= 0x0043, + TYPE_REAL256= 0x0044, + TYPE_REAL512= 0x0045 + } symtype; +}; + +struct cv8_state { + int symbol_sect; + int type_sect; + + uint32_t text_offset; + + struct source_file *source_files, **source_files_tail; + const char *last_filename; + struct source_file *last_source_file; + struct hash_table file_hash; + unsigned num_files; + uint32_t total_filename_len; + + + unsigned total_lines; + + struct SAA *symbols; + struct cv8_symbol *last_sym; + unsigned num_syms[SYMTYPE_MAX]; + unsigned symbol_lengths; + unsigned total_syms; + + struct { + char *name; + size_t namebytes; + } outfile; +}; +struct cv8_state cv8_state; + +static void cv8_init(void) +{ + const uint32_t sect_flags = IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_DISCARDABLE | + IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_ALIGN_1BYTES; + + cv8_state.symbol_sect = coff_make_section(".debug$S", sect_flags); + cv8_state.type_sect = coff_make_section(".debug$T", sect_flags); + + cv8_state.text_offset = 0; + + cv8_state.source_files = NULL; + cv8_state.source_files_tail = &cv8_state.source_files; + + cv8_state.num_files = 0; + cv8_state.total_filename_len = 0; + + cv8_state.total_lines = 0; + + cv8_state.symbols = saa_init(sizeof(struct cv8_symbol)); + cv8_state.last_sym = NULL; +} + +static struct source_file *register_file(const char *filename); +static struct coff_Section *find_section(int32_t segto); + +static void cv8_linenum(const char *filename, int32_t linenumber, + int32_t segto) +{ + struct coff_Section *s; + struct linepair *li; + struct source_file *file; + + file = register_file(filename); + + s = find_section(segto); + if (s == NULL) + return; + + if ((s->flags & IMAGE_SCN_MEM_EXECUTE) == 0) + return; + + li = saa_wstruct(file->lines); + li->file_offset = cv8_state.text_offset; + li->linenumber = linenumber; + + file->num_lines++; + cv8_state.total_lines++; +} + +static void cv8_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special) +{ + struct cv8_symbol *sym; + struct coff_Section *s; + + (void)special; + + s = find_section(segment); + if (s == NULL) + return; + + sym = saa_wstruct(cv8_state.symbols); + + if (s->flags & IMAGE_SCN_MEM_EXECUTE) + sym->type = is_global ? SYMTYPE_PROC : SYMTYPE_CODE; + else + sym->type = is_global ? SYMTYPE_GDATA : SYMTYPE_LDATA; + cv8_state.num_syms[sym->type]++; + cv8_state.total_syms++; + + sym->section = segment; + sym->secrel = offset; + sym->symtype = TYPE_UNREGISTERED; + sym->size = 0; + sym->typeindex = 0; + + sym->name = nasm_strdup(name); + cv8_state.symbol_lengths += strlen(sym->name) + 1; + + if (cv8_state.last_sym && cv8_state.last_sym->section == segment) + cv8_state.last_sym->size = offset - cv8_state.last_sym->secrel; + cv8_state.last_sym = sym; +} + +static void cv8_typevalue(int32_t type) +{ + if (!cv8_state.last_sym) + return; + if (cv8_state.last_sym->symtype != TYPE_UNREGISTERED) + return; + + switch (TYM_TYPE(type)) { + case TY_BYTE: + cv8_state.last_sym->symtype = TYPE_BYTE; + break; + case TY_WORD: + cv8_state.last_sym->symtype = TYPE_WORD; + break; + case TY_DWORD: + cv8_state.last_sym->symtype = TYPE_DWORD; + break; + case TY_QWORD: + cv8_state.last_sym->symtype = TYPE_QUAD; + break; + case TY_FLOAT: + cv8_state.last_sym->symtype = TYPE_REAL32; + break; + case TY_TBYTE: + cv8_state.last_sym->symtype = TYPE_REAL80; + break; + case TY_OWORD: + cv8_state.last_sym->symtype = TYPE_REAL128; + break; + case TY_YWORD: + cv8_state.last_sym->symtype = TYPE_REAL256; + break; + case TY_ZWORD: + cv8_state.last_sym->symtype = TYPE_REAL512; + break; + case TY_UNKNOWN: + break; + case TY_LABEL: + break; + } +} + +static void cv8_output(int type, void *param) +{ + struct coff_DebugInfo *dinfo = param; + + (void)type; + + if (dinfo->section && dinfo->section->name && + !strncmp(dinfo->section->name, ".text", 5)) + cv8_state.text_offset += dinfo->size; +} + +static void build_symbol_table(struct coff_Section *const sect); +static void build_type_table(struct coff_Section *const sect); + +static void cv8_cleanup(void) +{ + struct cv8_symbol *sym; + struct source_file *file, *ftmp; + + struct coff_Section *symbol_sect = coff_sects[cv8_state.symbol_sect]; + struct coff_Section *type_sect = coff_sects[cv8_state.type_sect]; + + cv8_state.outfile.name = nasm_realpath(outname); + cv8_state.outfile.namebytes = strlen(cv8_state.outfile.name) + 1; + + build_symbol_table(symbol_sect); + build_type_table(type_sect); + + list_for_each_safe(file, ftmp, cv8_state.source_files) { + nasm_free(file->fullname); + saa_free(file->lines); + nasm_free(file); + } + hash_free(&cv8_state.file_hash); + + saa_rewind(cv8_state.symbols); + while ((sym = saa_rstruct(cv8_state.symbols))) + nasm_free(sym->name); + saa_free(cv8_state.symbols); + + nasm_free(cv8_state.outfile.name); +} + +/******************************************************************************* + * implementation + ******************************************************************************/ +static void calc_md5(const char *const filename, + unsigned char sum[MD5_HASHBYTES]) +{ + int success = 0; + unsigned char *file_buf; + FILE *f; + MD5_CTX ctx; + + f = pp_input_fopen(filename, NF_BINARY); + if (!f) + goto done; + + file_buf = nasm_zalloc(BUFSIZ); + + MD5Init(&ctx); + while (!feof(f)) { + size_t i = fread(file_buf, 1, BUFSIZ, f); + if (ferror(f)) + goto done_0; + else if (i == 0) + break; + MD5Update(&ctx, file_buf, i); + } + MD5Final(sum, &ctx); + + success = 1; +done_0: + nasm_free(file_buf); + fclose(f); +done: + if (!success) { + nasm_nonfatal("unable to hash file %s. " + "Debug information may be unavailable.", + filename); + } + return; +} + +static struct source_file *register_file(const char *filename) +{ + struct source_file *file; + void **filep; + char *fullpath; + struct hash_insert hi; + + /* + * The common case is that we are invoked with the same filename + * as we were last time. Make this a pointer comparison: this is + * safe because the NASM core code allocates each filename once + * and never frees it. + */ + if (likely(cv8_state.last_filename == filename)) + return cv8_state.last_source_file; + + cv8_state.last_filename = filename; + + filep = hash_find(&cv8_state.file_hash, filename, &hi); + if (likely(filep)) { + file = *filep; + } else { + /* New filename encounter */ + + fullpath = nasm_realpath(filename); + + nasm_new(file); + file->filename = filename; + file->fullname = fullpath; + file->fullnamelen = strlen(fullpath); + file->lines = saa_init(sizeof(struct linepair)); + *cv8_state.source_files_tail = file; + cv8_state.source_files_tail = &file->next; + calc_md5(fullpath, file->md5sum); + + hash_add(&hi, filename, file); + + cv8_state.num_files++; + cv8_state.total_filename_len += file->fullnamelen + 1; + } + + cv8_state.last_source_file = file; + return file; +} + +static struct coff_Section *find_section(int32_t segto) +{ + int i; + + for (i = 0; i < coff_nsects; i++) { + struct coff_Section *sec; + + sec = coff_sects[i]; + if (segto == sec->index) + return sec; + } + return NULL; +} + +static void register_reloc(struct coff_Section *const sect, + char *sym, uint32_t addr, uint16_t type) +{ + struct coff_Reloc *r; + struct coff_Section *sec; + uint32_t i; + + r = *sect->tail = nasm_malloc(sizeof(struct coff_Reloc)); + sect->tail = &r->next; + r->next = NULL; + sect->nrelocs++; + + r->address = addr; + r->symbase = SECT_SYMBOLS; + r->type = type; + + r->symbol = 0; + for (i = 0; i < (uint32_t)coff_nsects; i++) { + sec = coff_sects[i]; + if (!strcmp(sym, sec->name)) { + return; + } + r->symbol += 2; + } + + saa_rewind(coff_syms); + for (i = 0; i < coff_nsyms; i++) { + struct coff_Symbol *s = saa_rstruct(coff_syms); + r->symbol++; + if (s->strpos == -1 && !strcmp(sym, s->name)) { + return; + } else if (s->strpos != -1) { + int res; + char *symname; + + symname = nasm_malloc(s->namlen + 1); + saa_fread(coff_strs, s->strpos-4, symname, s->namlen); + symname[s->namlen] = '\0'; + res = strcmp(sym, symname); + nasm_free(symname); + if (!res) + return; + } + } + nasm_panic("codeview: relocation for unregistered symbol: %s", sym); +} + +static inline void section_write32(struct coff_Section *sect, uint32_t val) +{ + saa_write32(sect->data, val); + sect->len += 4; +} + +static inline void section_write16(struct coff_Section *sect, uint16_t val) +{ + saa_write16(sect->data, val); + sect->len += 2; +} + +static inline void section_write8(struct coff_Section *sect, uint8_t val) +{ + saa_write8(sect->data, val); + sect->len++; +} + +static inline void section_wbytes(struct coff_Section *sect, const void *buf, + size_t len) +{ + saa_wbytes(sect->data, buf, len); + sect->len += len; +} + +static void write_filename_table(struct coff_Section *const sect) +{ + uint32_t field_length; + uint32_t tbl_off = 1; /* offset starts at 1 to skip NULL entry */ + struct source_file *file; + + nasm_assert(cv8_state.source_files != NULL); + nasm_assert(cv8_state.num_files > 0); + nasm_assert(cv8_state.total_filename_len > 0); + + field_length = 1 + cv8_state.total_filename_len; + + section_write32(sect, 0x000000F3); + section_write32(sect, field_length); + + section_write8(sect, 0); + + list_for_each(file, cv8_state.source_files) { + section_wbytes(sect, file->fullname, file->fullnamelen + 1); + file->filetbl_off = tbl_off; + tbl_off += file->fullnamelen + 1; + } +} + +static void write_sourcefile_table(struct coff_Section *const sect) +{ + const uint32_t entry_size = 4 + 2 + MD5_HASHBYTES + 2; + + uint32_t field_length = 0; + uint32_t tbl_off = 0; + struct source_file *file; + + field_length = entry_size * cv8_state.num_files; + + section_write32(sect, 0x000000F4); + section_write32(sect, field_length); + + list_for_each(file, cv8_state.source_files) { + nasm_assert(file->filetbl_off > 0); + section_write32(sect, file->filetbl_off); + section_write16(sect, 0x0110); + section_wbytes(sect, file->md5sum, MD5_HASHBYTES); + section_write16(sect, 0); + + file->sourcetbl_off = tbl_off; + tbl_off += entry_size; + } +} + +static void write_linenumber_table(struct coff_Section *const sect) +{ + const uint32_t file_field_len = 12; + const uint32_t line_field_len = 8; + + int i; + uint32_t field_length = 0; + size_t field_base; + struct source_file *file; + struct coff_Section *s; + + for (i = 0; i < coff_nsects; i++) { + if (!strncmp(coff_sects[i]->name, ".text", 5)) + break; + } + + if (i == coff_nsects) + return; + s = coff_sects[i]; + + field_length = 12; + field_length += (cv8_state.num_files * file_field_len); + field_length += (cv8_state.total_lines * line_field_len); + + section_write32(sect, 0x000000F2); + section_write32(sect, field_length); + + field_base = sect->len; + section_write32(sect, 0); /* SECREL, updated by relocation */ + section_write16(sect, 0); /* SECTION, updated by relocation*/ + section_write16(sect, 0); /* pad */ + section_write32(sect, s->len); + + register_reloc(sect, ".text", field_base, + win64 ? IMAGE_REL_AMD64_SECREL : IMAGE_REL_I386_SECREL); + + register_reloc(sect, ".text", field_base + 4, + win64 ? IMAGE_REL_AMD64_SECTION : IMAGE_REL_I386_SECTION); + + list_for_each(file, cv8_state.source_files) { + struct linepair *li; + + /* source mapping */ + section_write32(sect, file->sourcetbl_off); + section_write32(sect, file->num_lines); + section_write32(sect, file_field_len + (file->num_lines * line_field_len)); + + /* the pairs */ + saa_rewind(file->lines); + while ((li = saa_rstruct(file->lines))) { + section_write32(sect, li->file_offset); + section_write32(sect, li->linenumber |= 0x80000000); + } + } +} + +static uint32_t write_symbolinfo_obj(struct coff_Section *sect) +{ + uint32_t obj_len; + + obj_len = 2 + 4 + cv8_state.outfile.namebytes; + + section_write16(sect, obj_len); + section_write16(sect, 0x1101); + section_write32(sect, 0); /* ASM language */ + section_wbytes(sect, cv8_state.outfile.name, cv8_state.outfile.namebytes); + + return obj_len; +} + +static uint32_t write_symbolinfo_properties(struct coff_Section *sect, + const char *const creator_str) +{ + /* https://github.com/Microsoft/microsoft-pdb/blob/1d60e041/include/cvinfo.h#L3313 */ + uint32_t creator_len; + + creator_len = 2 + 4 + 2 + 3*2 + 3*2 + strlen(creator_str)+1 + 2; + + /* + * We used to use a language ID of 3 for "MASM", since it's closest of the + * options available; however, BinScope from WACK (the Windows Application + * Certification Kit) tests for specific minimum MASM versions and trying to + * match an increasing sequence of random MASM version/build numbers seems + * like a fool's errand. + * + * Instead, use a different language ID (NASM is, after all, not MASM + * syntax) and just write the actual NASM version number. BinScope appears + * to be happy with that. + */ + + section_write16(sect, creator_len); + section_write16(sect, 0x1116); + section_write32(sect, 'N'); /* language: 'N' (0x4e) for "NASM"; flags are 0 */ + if (win64) + section_write16(sect, 0x00D0); /* machine */ + else if (win32) + section_write16(sect, 0x0006); /* machine */ + else + nasm_panic("neither win32 nor win64 are set!"); + section_write16(sect, 0); /* verFEMajor */ + section_write16(sect, 0); /* verFEMinor */ + section_write16(sect, 0); /* verFEBuild */ + + /* BinScope/WACK insist on version >= 8.0.50727 */ + section_write16(sect, NASM_MAJOR_VER); /* verMajor */ + section_write16(sect, NASM_MINOR_VER); /* verMinor */ + section_write16(sect, NASM_SUBMINOR_VER*100 + NASM_PATCHLEVEL_VER); /* verBuild */ + + section_wbytes(sect, creator_str, strlen(creator_str)+1); /* verSt */ + /* + * normally there would be key/value pairs here, but they aren't + * necessary. They are terminated by 2B + */ + section_write16(sect, 0); + + return creator_len; +} + +static uint32_t write_symbolinfo_symbols(struct coff_Section *sect) +{ + uint32_t len = 0, field_len; + uint32_t field_base; + struct cv8_symbol *sym; + + saa_rewind(cv8_state.symbols); + while ((sym = saa_rstruct(cv8_state.symbols))) { + switch (sym->type) { + case SYMTYPE_LDATA: + case SYMTYPE_GDATA: + field_len = 12 + strlen(sym->name) + 1; + len += field_len - 2; + section_write16(sect, field_len); + if (sym->type == SYMTYPE_LDATA) + section_write16(sect, 0x110C); + else + section_write16(sect, 0x110D); + section_write32(sect, sym->symtype); + + field_base = sect->len; + section_write32(sect, 0); /* SECREL */ + section_write16(sect, 0); /* SECTION */ + break; + case SYMTYPE_PROC: + case SYMTYPE_CODE: + field_len = 9 + strlen(sym->name) + 1; + len += field_len - 2; + section_write16(sect, field_len); + section_write16(sect, 0x1105); + + field_base = sect->len; + section_write32(sect, 0); /* SECREL */ + section_write16(sect, 0); /* SECTION */ + section_write8(sect, 0); /* FLAG */ + break; + default: + nasm_panic("unknown symbol type"); + } + + section_wbytes(sect, sym->name, strlen(sym->name) + 1); + + register_reloc(sect, sym->name, field_base, + win64 ? IMAGE_REL_AMD64_SECREL : + IMAGE_REL_I386_SECREL); + register_reloc(sect, sym->name, field_base + 4, + win64 ? IMAGE_REL_AMD64_SECTION : + IMAGE_REL_I386_SECTION); + } + + return len; +} + +static void write_symbolinfo_table(struct coff_Section *const sect) +{ + static const char creator_str[] = "The Netwide Assembler " NASM_VER; + uint32_t obj_length, creator_length, sym_length; + uint32_t field_length = 0, out_len; + + nasm_assert(cv8_state.outfile.namebytes); + + /* signature, language, outfile NULL */ + obj_length = 2 + 4 + cv8_state.outfile.namebytes; + creator_length = 2 + 4 + 2 + 3*2 + 3*2 + strlen(creator_str)+1 + 2; + + sym_length = ( cv8_state.num_syms[SYMTYPE_CODE] * 7) + + ( cv8_state.num_syms[SYMTYPE_PROC] * 7) + + ( cv8_state.num_syms[SYMTYPE_LDATA] * 10) + + ( cv8_state.num_syms[SYMTYPE_GDATA] * 10) + + cv8_state.symbol_lengths; + + field_length = 2 + obj_length + + 2 + creator_length + + (4 * cv8_state.total_syms) + sym_length; + + section_write32(sect, 0x000000F1); + section_write32(sect, field_length); + + /* for sub fields, length proceeds type */ + + out_len = write_symbolinfo_obj(sect); + nasm_assert(out_len == obj_length); + + out_len = write_symbolinfo_properties(sect, creator_str); + nasm_assert(out_len == creator_length); + + out_len = write_symbolinfo_symbols(sect); + nasm_assert(out_len == sym_length); +} + +static inline void align4_table(struct coff_Section *const sect) +{ + unsigned diff; + uint32_t zero = 0; + struct SAA *data = sect->data; + + if (data->wptr % 4 == 0) + return; + + diff = 4 - (data->wptr % 4); + if (diff) + section_wbytes(sect, &zero, diff); +} + +static void build_symbol_table(struct coff_Section *const sect) +{ + section_write32(sect, 0x00000004); + + write_filename_table(sect); + align4_table(sect); + write_sourcefile_table(sect); + align4_table(sect); + write_linenumber_table(sect); + align4_table(sect); + write_symbolinfo_table(sect); + align4_table(sect); +} + +static void build_type_table(struct coff_Section *const sect) +{ + uint32_t field_len; + uint32_t typeindex = 0x1000; + uint32_t idx_arglist; + + section_write32(sect, 0x00000004); + + /* empty argument list type */ + field_len = 2 + 4; + section_write16(sect, field_len); + section_write16(sect, 0x1201); /* ARGLIST */ + section_write32(sect, 0); /* num params */ + idx_arglist = typeindex++; + + /* procedure type: void proc(void) */ + field_len = 2 + 4 + 1 + 1 + 2 + 4; + section_write16(sect, field_len); + section_write16(sect, 0x1008); /* PROC type */ + + section_write32(sect, 0x00000003); /* return type VOID */ + section_write8(sect, 0); /* calling convention (default) */ + section_write8(sect, 0); /* function attributes */ + section_write16(sect, 0); /* # params */ + section_write32(sect, idx_arglist); /* argument list type */ + /* idx_voidfunc = typeindex++; */ +} diff --git a/vere/ext/nasm/output/dwarf.h b/vere/ext/nasm/output/dwarf.h new file mode 100644 index 0000000..87bc0c8 --- /dev/null +++ b/vere/ext/nasm/output/dwarf.h @@ -0,0 +1,621 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2018 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +#ifndef OUTPUT_DWARF_H +#define OUTPUT_DWARF_H + +/* + * based on DWARF 3 standard + */ + +enum dwarf_tag { + DW_TAG_padding = 0x00, + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_global_subroutine = 0x06, + DW_TAG_global_variable = 0x07, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_local_variable = 0x0c, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine = 0x14, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + /* DWARF 3 */ + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + /* DWARF 4 */ + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + /* DWARF 5 */ + DW_TAG_atomic_type = 0x47, + + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xffff +}; + +enum dwarf_child { + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01 +}; + +enum dwarf_form { + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + /* DWARF 4 */ + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_ref_sig8 = 0x20 +}; + +enum dwarf_attribute { + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, + DW_AT_bit_size = 0x0d, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2a, + DW_AT_start_scope = 0x2c, + DW_AT_bit_stride = 0x2e, + DW_AT_upper_bound = 0x2f, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention= 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_item = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + /* DWARF 3 */ + DW_AT_allocated = 0x4e, + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_byte_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + DW_AT_description = 0x5a, + DW_AT_binary_scale = 0x5b, + DW_AT_decimal_scale = 0x5c, + DW_AT_small = 0x5d, + DW_AT_decimal_sign = 0x5e, + DW_AT_digit_count = 0x5f, + DW_AT_picture_string = 0x60, + DW_AT_mutable = 0x61, + DW_AT_threads_scaled = 0x62, + DW_AT_explicit = 0x63, + DW_AT_object_pointer = 0x64, + DW_AT_endianity = 0x65, + DW_AT_elemental = 0x66, + DW_AT_pure = 0x67, + DW_AT_recursive = 0x68, + /* DWARF 4 */ + DW_AT_signature = 0x69, + DW_AT_main_subprogram = 0x6a, + DW_AT_data_bit_offset = 0x6b, + DW_AT_const_expr = 0x6c, + DW_AT_enum_class = 0x6d, + DW_AT_linkage_name = 0x6e, + /* DWARF 5 */ + DW_AT_noreturn = 0x87, + + DW_AT_lo_user = 0x2000, + DW_AT_hi_user = 0x3fff +}; + +enum dwarf_op { + DW_OP_addr = 0x03, + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2f, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit2 = 0x32, + DW_OP_lit3 = 0x33, + DW_OP_lit4 = 0x34, + DW_OP_lit5 = 0x35, + DW_OP_lit6 = 0x36, + DW_OP_lit7 = 0x37, + DW_OP_lit8 = 0x38, + DW_OP_lit9 = 0x39, + DW_OP_lit10 = 0x3a, + DW_OP_lit11 = 0x3b, + DW_OP_lit12 = 0x3c, + DW_OP_lit13 = 0x3d, + DW_OP_lit14 = 0x3e, + DW_OP_lit15 = 0x3f, + DW_OP_lit16 = 0x40, + DW_OP_lit17 = 0x41, + DW_OP_lit18 = 0x42, + DW_OP_lit19 = 0x43, + DW_OP_lit20 = 0x44, + DW_OP_lit21 = 0x45, + DW_OP_lit22 = 0x46, + DW_OP_lit23 = 0x47, + DW_OP_lit24 = 0x48, + DW_OP_lit25 = 0x49, + DW_OP_lit26 = 0x4a, + DW_OP_lit27 = 0x4b, + DW_OP_lit28 = 0x4c, + DW_OP_lit29 = 0x4d, + DW_OP_lit30 = 0x4e, + DW_OP_lit31 = 0x4f, + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg2 = 0x52, + DW_OP_reg3 = 0x53, + DW_OP_reg4 = 0x54, + DW_OP_reg5 = 0x55, + DW_OP_reg6 = 0x56, + DW_OP_reg7 = 0x57, + DW_OP_reg8 = 0x58, + DW_OP_reg9 = 0x59, + DW_OP_reg10 = 0x5a, + DW_OP_reg11 = 0x5b, + DW_OP_reg12 = 0x5c, + DW_OP_reg13 = 0x5d, + DW_OP_reg14 = 0x5e, + DW_OP_reg15 = 0x5f, + DW_OP_reg16 = 0x60, + DW_OP_reg17 = 0x61, + DW_OP_reg18 = 0x62, + DW_OP_reg19 = 0x63, + DW_OP_reg20 = 0x64, + DW_OP_reg21 = 0x65, + DW_OP_reg22 = 0x66, + DW_OP_reg23 = 0x67, + DW_OP_reg24 = 0x68, + DW_OP_reg25 = 0x69, + DW_OP_reg26 = 0x6a, + DW_OP_reg27 = 0x6b, + DW_OP_reg28 = 0x6c, + DW_OP_reg29 = 0x6d, + DW_OP_reg30 = 0x6e, + DW_OP_reg31 = 0x6f, + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg2 = 0x72, + DW_OP_breg3 = 0x73, + DW_OP_breg4 = 0x74, + DW_OP_breg5 = 0x75, + DW_OP_breg6 = 0x76, + DW_OP_breg7 = 0x77, + DW_OP_breg8 = 0x78, + DW_OP_breg9 = 0x79, + DW_OP_breg10 = 0x7a, + DW_OP_breg11 = 0x7b, + DW_OP_breg12 = 0x7c, + DW_OP_breg13 = 0x7d, + DW_OP_breg14 = 0x7e, + DW_OP_breg15 = 0x7f, + DW_OP_breg16 = 0x80, + DW_OP_breg17 = 0x81, + DW_OP_breg18 = 0x82, + DW_OP_breg19 = 0x83, + DW_OP_breg20 = 0x84, + DW_OP_breg21 = 0x85, + DW_OP_breg22 = 0x86, + DW_OP_breg23 = 0x87, + DW_OP_breg24 = 0x88, + DW_OP_breg25 = 0x89, + DW_OP_breg26 = 0x8a, + DW_OP_breg27 = 0x8b, + DW_OP_breg28 = 0x8c, + DW_OP_breg29 = 0x8d, + DW_OP_breg30 = 0x8e, + DW_OP_breg31 = 0x8f, + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96, + /* DWARF 3 */ + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a , + DW_OP_form_tls_address = 0x9b, + DW_OP_call_frame_cfa = 0x9c, + DW_OP_bit_piece = 0x9d, + /* DWARF 4 */ + DW_OP_implicit_value = 0x9e, + DW_OP_stack_value = 0x9f, + + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff +}; + +enum dwarf_base_type { + DW_ATE_address = 0x01, + DW_ATE_boolean = 0x02, + DW_ATE_complex_float = 0x03, + DW_ATE_float = 0x04, + DW_ATE_signed = 0x05, + DW_ATE_signed_char = 0x06, + DW_ATE_unsigned = 0x07, + DW_ATE_unsigned_char = 0x08, + /* DWARF 3 */ + DW_ATE_imaginary_float = 0x09, + DW_ATE_packed_decimal = 0x0a, + DW_ATE_numeric_string = 0x0b, + DW_ATE_edited = 0x0c, + DW_ATE_signed_fixed = 0x0d, + DW_ATE_unsigned_fixed = 0x0e, + DW_ATE_decimal_float = 0x0f, + /* DWARF 4 */ + DW_ATE_UTF = 0x10, + + DW_ATE_lo_user = 0x80, + DW_ATE_hi_user = 0xff +}; + +enum dwarf_decimal_sign { + DW_DS_unsigned = 0x01, + DW_DS_leading_overpunch = 0x02, + DW_DS_trailing_overpunch = 0x03, + DW_DS_leading_separate = 0x04, + DW_DS_trailing_separate = 0x05 +}; + +enum dwarf_endianity { + DW_END_default = 0x00, + DW_END_big = 0x01, + DW_END_little = 0x02, + + DW_END_lo_user = 0x40, + DW_END_hi_user = 0xff +}; + +enum dwarf_accessibility { + DW_ACCESS_public = 0x01, + DW_ACCESS_protected = 0x02, + DW_ACCESS_private = 0x03 +}; + +enum dwarf_visibility { + DW_VIS_local = 0x01, + DW_VIS_exported = 0x02, + DW_VIS_qualified = 0x03 +}; + +enum dwarf_virtuality { + DW_VIRTUALITY_none = 0x00, + DW_VIRTUALITY_virtual = 0x01, + DW_VIRTUALITY_pure_virtual = 0x02 +}; + +enum dwarf_language { + DW_LANG_C89 = 0x0001, + DW_LANG_C = 0x0002, + DW_LANG_Ada83 = 0x0003, + DW_LANG_C_plus_plus = 0x0004, + DW_LANG_Cobol74 = 0x0005, + DW_LANG_Cobol85 = 0x0006, + DW_LANG_Fortran77 = 0x0007, + DW_LANG_Fortran90 = 0x0008, + DW_LANG_Pascal83 = 0x0009, + DW_LANG_Modula2 = 0x000a, + DW_LANG_Java = 0x000b, + DW_LANG_C99 = 0x000c, + DW_LANG_Ada95 = 0x000d, + DW_LANG_Fortran95 = 0x000e, + DW_LANG_PLI = 0x000f, + DW_LANG_ObjC = 0x0010, + DW_LANG_ObjC_plus_plus = 0x0011, + DW_LANG_UPC = 0x0012, + DW_LANG_D = 0x0013, + DW_LANG_Python = 0x0014, + DW_LANG_OpenCL = 0x0015, + DW_LANG_Go = 0x0016, + DW_LANG_Modula3 = 0x0017, + DW_LANG_Haskell = 0x0018, + DW_LANG_C_plus_plus_03 = 0x0019, + DW_LANG_C_plus_plus_11 = 0x001a, + DW_LANG_OCaml = 0x001b, + DW_LANG_Rust = 0x001c, + DW_LANG_C11 = 0x001d, + DW_LANG_Swift = 0x001e, + DW_LANG_Julia = 0x001f, + DW_LANG_Dylan = 0x0020, + DW_LANG_C_plus_plus_14 = 0x0021, + DW_LANG_Fortran03 = 0x0022, + DW_LANG_Fortran08 = 0x0023, + DW_LANG_RenderScript = 0x0024, + + DW_LANG_Mips_Assembler = 0x8001, + + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, + + DW_LANG_Rust_old = 0x9000 +}; + +enum dwarf_identifier_case { + DW_ID_case_sensitive = 0x00, + DW_ID_up_case = 0x01, + DW_ID_down_case = 0x02, + DW_ID_case_insensitive = 0x03 +}; + +enum dwarf_calling_conversion { + DW_CC_normal = 0x01, + DW_CC_program = 0x02, + DW_CC_nocall = 0x03, + DW_CC_pass_by_reference = 0x4, + DW_CC_pass_by_value = 0x5, + + DW_CC_lo_user = 0x40, + DW_CC_hi_user = 0xff, + + DW_CC_GNU_renesas_sh = 0x40, + DW_CC_GNU_borland_fastcall_i386 = 0x41 +}; + +enum dwarf_inline { + DW_INL_not_inlined = 0x00, + DW_INL_inlined = 0x01, + DW_INL_declared_not_inlined = 0x02, + DW_INL_declared_inlined = 0x03 +}; + +enum dwarf_ordering { + DW_ORD_row_major = 0x00, + DW_ORD_col_major = 0x01 +}; + +enum dwarf_discriminant { + DW_DSC_label = 0x00, + DW_DSC_range = 0x01 +}; + +enum dwarf_line_number { + DW_LNS_extended_op = 0x00, + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c +}; + +enum dwarf_line_number_extended { + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_set_discriminator= 0x04, + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff +}; + +enum dwarf_macinfo_type { + DW_MACINFO_define = 0x01, + DW_MACINFO_undef = 0x02, + DW_MACINFO_start_file = 0x03, + DW_MACINFO_end_file = 0x04, + DW_MACINFO_vendor_ext = 0xff +}; + +enum dwarf_call_frame { + DW_CFA_advance_loc = 0x01, + DW_CFA_offset = 0x02, + DW_CFA_restore = 0x03, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + /* DWARF 3 */ + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f +}; + +#endif /* OUTPUT_DWARF_H */ diff --git a/vere/ext/nasm/output/elf.h b/vere/ext/nasm/output/elf.h new file mode 100644 index 0000000..a15688e --- /dev/null +++ b/vere/ext/nasm/output/elf.h @@ -0,0 +1,557 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2018 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +#ifndef OUTPUT_ELF_H +#define OUTPUT_ELF_H + +/* + * Since NASM support both Elf32/64 file formats + * we need to cover all types, structures, typedefs and etc + */ + +#include "compiler.h" + +/* Segment types */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOOS 0x60000000 +#define PT_HIOS 0x6fffffff +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff +#define PT_GNU_EH_FRAME 0x6474e550 /* Extension, eh? */ + +/* ELF file types */ +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + +/* ELF machine types */ +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_486 6 /* Not used in Linux at least */ +#define EM_860 7 +#define EM_MIPS 8 /* R3k, bigendian(?) */ +#define EM_MIPS_RS4_BE 10 /* R4k BE */ +#define EM_PARISC 15 +#define EM_SPARC32PLUS 18 +#define EM_PPC 20 +#define EM_PPC64 21 +#define EM_S390 22 +#define EM_SH 42 +#define EM_SPARCV9 43 /* v9 = SPARC64 */ +#define EM_H8_300H 47 +#define EM_H8S 48 +#define EM_IA_64 50 +#define EM_X86_64 62 +#define EM_CRIS 76 +#define EM_V850 87 +#define EM_ALPHA 0x9026 /* Interim Alpha that stuck around */ +#define EM_CYGNUS_V850 0x9080 /* Old v850 ID used by Cygnus */ +#define EM_S390_OLD 0xA390 /* Obsolete interim value for S/390 */ + +/* Dynamic type values */ +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff + +/* Auxiliary table entries */ +#define AT_NULL 0 /* end of vector */ +#define AT_IGNORE 1 /* entry should be ignored */ +#define AT_EXECFD 2 /* file descriptor of program */ +#define AT_PHDR 3 /* program headers for program */ +#define AT_PHENT 4 /* size of program header entry */ +#define AT_PHNUM 5 /* number of program headers */ +#define AT_PAGESZ 6 /* system page size */ +#define AT_BASE 7 /* base address of interpreter */ +#define AT_FLAGS 8 /* flags */ +#define AT_ENTRY 9 /* entry point of program */ +#define AT_NOTELF 10 /* program is not ELF */ +#define AT_UID 11 /* real uid */ +#define AT_EUID 12 /* effective uid */ +#define AT_GID 13 /* real gid */ +#define AT_EGID 14 /* effective gid */ +#define AT_PLATFORM 15 /* string identifying CPU for optimizations */ +#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ +#define AT_CLKTCK 17 /* frequency at which times() increments */ +/* 18..22 = ? */ +#define AT_SECURE 23 /* secure mode boolean */ + +/* Program header permission flags */ +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 + +/* Section header types */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_INIT_ARRAY 14 +#define SHT_FINI_ARRAY 15 +#define SHT_PREINIT_ARRAY 16 +#define SHT_GROUP 17 +#define SHT_SYMTAB_SHNDX 18 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +/* Section header flags */ +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data */ + +/* Special section numbers */ +#define SHN_UNDEF 0x0000 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_XINDEX 0xffff +#define SHN_HIRESERVE 0xffff + +/* Same, but signed/sign-extended */ +#define XSHN_UNDEF ((int16_t)SHN_UNDEF) +#define XSHN_LORESERVE ((int16_t)SHN_LORESERVE) +#define XSHN_LOPROC ((int16_t)SHN_LOPROC) +#define XSHN_HIPROC ((int16_t)SHN_HIPROC) +#define XSHN_ABS ((int16_t)SHN_ABS) +#define XSHN_COMMON ((int16_t)SHN_COMMON) +#define XSHN_XINDEX ((int16_t)SHN_XINDEX) +#define XSHN_HIRESERVE ((int16_t)SHN_HIRESERVE) + +/* Section align flag */ +#define SHA_ANY 1 /* No alignment constraint */ + +/* Length of magic at the start of a file */ +#define EI_NIDENT 16 + +/* Magic number constants... */ +#define EI_MAG0 0 /* e_ident[] indexes */ +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_ABIVERSION 8 +#define EI_NINDENT 16 + +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define ELFCLASSNONE 0 /* EI_CLASS */ +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define EV_NONE 0 /* e_version, EI_VERSION */ +#define EV_CURRENT 1 +#define EV_NUM 2 + +#define ELFOSABI_NONE 0 +#define ELFOSABI_LINUX 3 + +/* Legal values for ST_BIND subfield of st_info (symbol binding) */ +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Symbol types */ +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object */ +#define STT_NUM 7 /* Number of defined types */ + +/* Symbol visibilities */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field */ +#define ELF32_ST_BIND(i) ((i) >> 4) +#define ELF32_ST_MKBIND(i) ((i) << 4) /* just a helper */ +#define ELF32_ST_TYPE(i) ((i) & 0xf) +#define ELF32_ST_INFO(b, i) (ELF32_ST_MKBIND(b) + ELF32_ST_TYPE(i)) + +#define ELF64_ST_BIND(i) ELF32_ST_BIND(i) +#define ELF64_ST_MKBIND(i) ELF32_ST_MKBIND(i) +#define ELF64_ST_TYPE(i) ELF32_ST_TYPE(i) +#define ELF64_ST_INFO(b, i) ELF32_ST_INFO(b, i) + +/* + * ELF standard typedefs (yet more proof that <stdint.h> was way overdue) + */ + +typedef uint16_t Elf32_Half; +typedef int16_t Elf32_SHalf; +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; + +typedef uint32_t Elf32_Off; +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Section; + +typedef uint16_t Elf64_Half; +typedef int16_t Elf64_SHalf; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +typedef uint64_t Elf64_Off; +typedef uint64_t Elf64_Addr; +typedef uint16_t Elf64_Section; + +/* + * Dynamic header + */ + +typedef struct elf32_dyn { + Elf32_Sword d_tag; + union { + Elf32_Sword d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +typedef struct elf64_dyn { + Elf64_Sxword d_tag; + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +/* + * Relocations + */ + +#define ELF32_R_SYM(x) ((x) >> 8) +#define ELF32_R_TYPE(x) ((x) & 0xff) +#define ELF32_R_INFO(s,t) (((Elf32_Word)(s) << 8) + ELF32_R_TYPE(t)) + +typedef struct elf32_rel { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct elf32_rela { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +enum reloc32_type { + R_386_32 = 1, /* ordinary absolute relocation */ + R_386_PC32 = 2, /* PC-relative relocation */ + R_386_GOT32 = 3, /* an offset into GOT */ + R_386_PLT32 = 4, /* a PC-relative offset into PLT */ + R_386_COPY = 5, /* ??? */ + R_386_GLOB_DAT = 6, /* ??? */ + R_386_JUMP_SLOT = 7, /* ??? */ + R_386_RELATIVE = 8, /* ??? */ + R_386_GOTOFF = 9, /* an offset from GOT base */ + R_386_GOTPC = 10, /* a PC-relative offset _to_ GOT */ + R_386_TLS_TPOFF = 14, /* Offset in static TLS block */ + R_386_TLS_IE = 15, /* Address of GOT entry for static TLS block offset */ + /* These are GNU extensions, but useful */ + R_386_16 = 20, /* A 16-bit absolute relocation */ + R_386_PC16 = 21, /* A 16-bit PC-relative relocation */ + R_386_8 = 22, /* An 8-bit absolute relocation */ + R_386_PC8 = 23, /* An 8-bit PC-relative relocation */ + R_386_SEG16 = 45, /* A 16-bit real-mode segment */ + R_386_SUB16 = 46, /* Subtract 16-bit value */ + R_386_SUB32 = 47 /* Subtract 32-bit value */ +}; + +#define ELF64_R_SYM(x) ((x) >> 32) +#define ELF64_R_TYPE(x) ((x) & 0xffffffff) +#define ELF64_R_INFO(s,t) (((Elf64_Xword)(s) << 32) + ELF64_R_TYPE(t)) + +typedef struct elf64_rel { + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + +typedef struct elf64_rela { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + +enum reloc64_type { + R_X86_64_NONE = 0, /* No reloc */ + R_X86_64_64 = 1, /* Direct 64 bit */ + R_X86_64_PC32 = 2, /* PC relative 32 bit signed */ + R_X86_64_GOT32 = 3, /* 32 bit GOT entry */ + R_X86_64_PLT32 = 4, /* 32 bit PLT address */ + R_X86_64_COPY = 5, /* Copy symbol at runtime */ + R_X86_64_GLOB_DAT = 6, /* Create GOT entry */ + R_X86_64_JUMP_SLOT = 7, /* Create PLT entry */ + R_X86_64_RELATIVE = 8, /* Adjust by program base */ + R_X86_64_GOTPCREL = 9, /* 32 bit signed PC relative offset to GOT */ + R_X86_64_32 = 10, /* Direct 32 bit zero extended */ + R_X86_64_32S = 11, /* Direct 32 bit sign extended */ + R_X86_64_16 = 12, /* Direct 16 bit zero extended */ + R_X86_64_PC16 = 13, /* 16 bit sign extended pc relative */ + R_X86_64_8 = 14, /* Direct 8 bit sign extended */ + R_X86_64_PC8 = 15, /* 8 bit sign extended pc relative */ + R_X86_64_DTPMOD64 = 16, /* ID of module containing symbol */ + R_X86_64_DTPOFF64 = 17, /* Offset in module's TLS block */ + R_X86_64_TPOFF64 = 18, /* Offset in initial TLS block */ + R_X86_64_TLSGD = 19, /* 32 bit signed PC relative offset to two GOT entries for GD symbol */ + R_X86_64_TLSLD = 20, /* 32 bit signed PC relative offset to two GOT entries for LD symbol */ + R_X86_64_DTPOFF32 = 21, /* Offset in TLS block */ + R_X86_64_GOTTPOFF = 22, /* 32 bit signed PC relative offset to GOT entry for IE symbol */ + R_X86_64_TPOFF32 = 23, /* Offset in initial TLS block */ + R_X86_64_PC64 = 24, /* word64 S + A - P */ + R_X86_64_GOTOFF64 = 25, /* word64 S + A - GOT */ + R_X86_64_GOTPC32 = 26, /* word32 GOT + A - P */ + R_X86_64_GOT64 = 27, /* word64 G + A */ + R_X86_64_GOTPCREL64 = 28, /* word64 G + GOT - P + A */ + R_X86_64_GOTPC64 = 29, /* word64 GOT - P + A */ + R_X86_64_GOTPLT64 = 30, /* word64 G + A */ + R_X86_64_PLTOFF64 = 31, /* word64 L - GOT + A */ + R_X86_64_SIZE32 = 32, /* word32 Z + A */ + R_X86_64_SIZE64 = 33, /* word64 Z + A */ + R_X86_64_GOTPC32_TLSDESC= 34, /* word32 */ + R_X86_64_TLSDESC_CALL = 35, /* none */ + R_X86_64_TLSDESC = 36 /* word64?2 */ +}; + +/* + * Symbol + */ + +typedef struct elf32_sym { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct elf64_sym { + Elf64_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +/* + * Main file header + */ + +typedef struct elf32_hdr { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct elf64_hdr { + unsigned char e_ident[EI_NIDENT]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* + * Program header + */ + +typedef struct elf32_phdr { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct elf64_phdr { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +/* + * Section header + */ + +typedef struct elf32_shdr { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct elf64_shdr { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + +/* + * Note header + */ +typedef struct elf32_note { + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ +} Elf32_Nhdr; + +typedef struct elf64_note { + Elf64_Word n_namesz; /* Name size */ + Elf64_Word n_descsz; /* Content size */ + Elf64_Word n_type; /* Content type */ +} Elf64_Nhdr; + +#endif /* OUTPUT_ELF_H */ diff --git a/vere/ext/nasm/output/legacy.c b/vere/ext/nasm/output/legacy.c new file mode 100644 index 0000000..15d22e9 --- /dev/null +++ b/vere/ext/nasm/output/legacy.c @@ -0,0 +1,122 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2016-2022 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * output/legacy.c + * + * Mangle a struct out_data to match the rather bizarre legacy + * backend interface. + * + * The "data" parameter for the output function points to a "int64_t", + * containing the address of the target in question, unless the type is + * OUT_RAWDATA, in which case it points to an "uint8_t" + * array. + * + * Exceptions are OUT_RELxADR, which denote an x-byte relocation + * which will be a relative jump. For this we need to know the + * distance in bytes from the start of the relocated record until + * the end of the containing instruction. _This_ is what is stored + * in the size part of the parameter, in this case. + * + * Also OUT_RESERVE denotes reservation of N bytes of BSS space, + * and the contents of the "data" parameter is irrelevant. + */ + +#include "nasm.h" +#include "outlib.h" + +void nasm_do_legacy_output(const struct out_data *data) +{ + const void *dptr = data->data; + enum out_type type = data->type; + int32_t tsegment = data->tsegment; + int32_t twrt = data->twrt; + uint64_t size = data->size; + + switch (data->type) { + case OUT_RELADDR: + switch (data->size) { + case 1: + type = OUT_REL1ADR; + break; + case 2: + type = OUT_REL2ADR; + break; + case 4: + type = OUT_REL4ADR; + break; + case 8: + type = OUT_REL8ADR; + break; + default: + panic(); + break; + } + + dptr = &data->toffset; + size = data->relbase - data->offset; + break; + + case OUT_SEGMENT: + type = OUT_ADDRESS; + tsegment |= 1; + /* fall through */ + + case OUT_ADDRESS: + dptr = &data->toffset; + size = (data->flags & OUT_SIGNED) ? -data->size : data->size; + break; + + case OUT_RAWDATA: + case OUT_RESERVE: + tsegment = twrt = NO_SEG; + break; + + case OUT_ZERODATA: + tsegment = twrt = NO_SEG; + type = OUT_RAWDATA; + dptr = zero_buffer; + while (size > ZERO_BUF_SIZE) { + ofmt->legacy_output(data->segment, dptr, type, + ZERO_BUF_SIZE, tsegment, twrt); + size -= ZERO_BUF_SIZE; + } + break; + + default: + panic(); + break; + } + + ofmt->legacy_output(data->segment, dptr, type, size, tsegment, twrt); +} diff --git a/vere/ext/nasm/output/macho.h b/vere/ext/nasm/output/macho.h new file mode 100644 index 0000000..538c531 --- /dev/null +++ b/vere/ext/nasm/output/macho.h @@ -0,0 +1,282 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2018 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +#ifndef OUTPUT_MACHO_H +#define OUTPUT_MACHO_H + +#include "compiler.h" + +/* Magics */ +#define MH_MAGIC 0xfeedface +#define MH_MAGIC_64 0xfeedfacf + +/* File types */ +#define MH_OBJECT 0x1 + +/* CPUs */ +#define CPU_ARCH_MASK 0xff000000 +#define CPU_ARCH_ABI64 0x01000000 +#define CPU_TYPE_X86 7 +#define CPU_TYPE_I386 CPU_TYPE_X86 +#define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64) + +#define CPU_SUBTYPE_MASK 0xff000000 +#define CPU_SUBTYPE_I386_ALL 3 + +/* Header flags */ +#define MH_SUBSECTIONS_VIA_SYMBOLS 0x00002000 + +/* Load commands */ +#define LC_SEGMENT 0x1 +#define LC_SEGMENT_64 0x19 +#define LC_SYMTAB 0x2 + +/* Symbol type bits */ +#define N_STAB 0xe0 +#define N_PEXT 0x10 +#define N_TYPE 0x0e +#define N_EXT 0x01 + +/* To mask with N_TYPE */ +#define N_UNDF 0x00 +#define N_ABS 0x02 +#define N_INDR 0x0a +#define N_PBUD 0x0c +#define N_SECT 0x0e + +/* Section ordinals */ +#define NO_SECT 0x00 +#define MAX_SECT 0xff + +/* Section bits */ +#define SECTION_TYPE 0x000000ff +#define SECTION_ATTRIBUTES 0xffffff00 +#define SECTION_ATTRIBUTES_USR 0xff000000 +#define SECTION_ATTRIBUTES_SYS 0x00ffff00 + +#define S_REGULAR 0x00 +#define S_ZEROFILL 0x01 +#define S_CSTRING_LITERALS 0x02 +#define S_4BYTE_LITERALS 0x03 +#define S_8BYTE_LITERALS 0x04 +#define S_LITERAL_POINTERS 0x05 +#define S_NON_LAZY_SYMBOL_POINTERS 0x06 +#define S_LAZY_SYMBOL_POINTERS 0x07 +#define S_SYMBOL_STUBS 0x08 +#define S_MOD_INIT_FUNC_POINTERS 0x09 +#define S_MOD_TERM_FUNC_POINTERS 0x0a +#define S_COALESCED 0x0b +#define S_GB_ZEROFILL 0x0c +#define S_INTERPOSING 0x0d +#define S_16BYTE_LITERALS 0x0e +#define S_DTRACE_DOF 0x0f +#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 +#define S_THREAD_LOCAL_REGULAR 0x11 +#define S_THREAD_LOCAL_ZEROFILL 0x12 +#define S_THREAD_LOCAL_VARIABLES 0x13 +#define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 +#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 + +#define S_ATTR_PURE_INSTRUCTIONS 0x80000000 +#define S_ATTR_NO_TOC 0x40000000 +#define S_ATTR_STRIP_STATIC_SYMS 0x20000000 +#define S_ATTR_NO_DEAD_STRIP 0x10000000 +#define S_ATTR_LIVE_SUPPORT 0x08000000 +#define S_ATTR_SELF_MODIFYING_CODE 0x04000000 +#define S_ATTR_DEBUG 0x02000000 + +#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 +#define S_ATTR_EXT_RELOC 0x00000200 +#define S_ATTR_LOC_RELOC 0x00000100 +#define INDIRECT_SYMBOL_LOCAL 0x80000000 +#define INDIRECT_SYMBOL_ABS 0x40000000 + +/* Relocation info type */ +#define GENERIC_RELOC_VANILLA 0 +#define GENERIC_RELOC_PAIR 1 +#define GENERIC_RELOC_SECTDIFF 2 +#define GENERIC_RELOC_PB_LA_PTR 3 +#define GENERIC_RELOC_LOCAL_SECTDIFF 4 +#define GENERIC_RELOC_TLV 5 + +#define X86_64_RELOC_UNSIGNED 0 +#define X86_64_RELOC_SIGNED 1 +#define X86_64_RELOC_BRANCH 2 +#define X86_64_RELOC_GOT_LOAD 3 +#define X86_64_RELOC_GOT 4 +#define X86_64_RELOC_SUBTRACTOR 5 +#define X86_64_RELOC_SIGNED_1 6 +#define X86_64_RELOC_SIGNED_2 7 +#define X86_64_RELOC_SIGNED_4 8 +#define X86_64_RELOC_TLV 9 + +/* Relocation info */ +#define R_ABS 0 +#define R_SCATTERED 0x80000000 + +/* VM permission constants */ +#define VM_PROT_NONE 0x00 +#define VM_PROT_READ 0x01 +#define VM_PROT_WRITE 0x02 +#define VM_PROT_EXECUTE 0x04 + +typedef struct { + uint32_t magic; + uint32_t cputype; + uint32_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; +} macho_header_t; + +typedef struct { + uint32_t magic; + uint32_t cputype; + uint32_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + uint32_t reserved; +} macho_header_64_t; + +typedef struct { + uint32_t cmd; + uint32_t cmdsize; +} macho_load_command_t; + +typedef struct { + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint32_t vmaddr; + uint32_t vmsize; + uint32_t fileoff; + uint32_t filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; +} macho_segment_command_t; + +typedef struct { + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; +} macho_segment_command_64_t; + +typedef struct { + char sectname[16]; + char segname[16]; + uint32_t addr; + uint32_t size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; +} macho_section_t; + +typedef struct { + char sectname[16]; + char segname[16]; + uint64_t addr; + uint64_t size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; +} macho_section_64_t; + +typedef struct { + uint32_t cmd; + uint32_t cmdsize; + uint32_t symoff; + uint32_t nsyms; + uint32_t stroff; + uint32_t strsize; +} macho_symtab_command_t; + +typedef struct { + int32_t r_address; + union { + struct { + uint32_t r_symbolnum: 24, + r_pcrel: 1, + r_length: 2, + r_extern: 1, + r_type: 4; + } s; + uint32_t r_raw; + } u; +} macho_relocation_info_t; + +typedef struct nlist_base { + uint32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; +} macho_nlist_base_t; + +typedef struct nlist { + uint32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + int16_t n_desc; + uint32_t n_value; +} macho_nlist_t; + +typedef struct { + uint32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + uint64_t n_value; +} macho_nlist_64_t; + +#endif /* OUTPUT_MACHO_H */ diff --git a/vere/ext/nasm/output/nulldbg.c b/vere/ext/nasm/output/nulldbg.c new file mode 100644 index 0000000..93ec3a7 --- /dev/null +++ b/vere/ext/nasm/output/nulldbg.c @@ -0,0 +1,96 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2014 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +#include "nasm.h" +#include "nasmlib.h" +#include "outlib.h" + +void null_debug_init(void) +{ +} + +void null_debug_linenum(const char *filename, int32_t linenumber, int32_t segto) +{ + (void)filename; + (void)linenumber; + (void)segto; +} + +void null_debug_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special) +{ + (void)name; + (void)segment; + (void)offset; + (void)is_global; + (void)special; +} + +void null_debug_directive(const char *directive, const char *params) +{ + (void)directive; + (void)params; +} + +void null_debug_typevalue(int32_t type) +{ + (void)type; +} + +void null_debug_output(int type, void *param) +{ + (void)type; + (void)param; +} + +void null_debug_cleanup(void) +{ +} + +const struct dfmt null_debug_form = { + "Null", + "null", + null_debug_init, + null_debug_linenum, + null_debug_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + null_debug_typevalue, + null_debug_output, + null_debug_cleanup, + NULL /* pragma list */ +}; + +const struct dfmt * const null_debug_arr[2] = { &null_debug_form, NULL }; diff --git a/vere/ext/nasm/output/nullout.c b/vere/ext/nasm/output/nullout.c new file mode 100644 index 0000000..121fe70 --- /dev/null +++ b/vere/ext/nasm/output/nullout.c @@ -0,0 +1,60 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2018 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +#include "nasm.h" +#include "nasmlib.h" +#include "outlib.h" + +enum directive_result +null_directive(enum directive directive, char *value) +{ + (void)directive; + (void)value; + return DIRR_UNKNOWN; +} + +void null_sectalign(int32_t seg, unsigned int value) +{ + (void)seg; + (void)value; +} + +void null_reset(void) +{ + /* Nothing to do */ +} + +int32_t null_segbase(int32_t segment) +{ + return segment; +} diff --git a/vere/ext/nasm/output/outaout.c b/vere/ext/nasm/output/outaout.c new file mode 100644 index 0000000..c59c789 --- /dev/null +++ b/vere/ext/nasm/output/outaout.c @@ -0,0 +1,923 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2013 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outaout.c output routines for the Netwide Assembler to produce + * Linux a.out object files + */ + +#include "compiler.h" + +#include "nctype.h" + +#include "nasm.h" +#include "nasmlib.h" +#include "error.h" +#include "saa.h" +#include "raa.h" +#include "stdscan.h" +#include "eval.h" +#include "outform.h" +#include "outlib.h" + +#if defined OF_AOUT || defined OF_AOUTB + +#define RELTYPE_ABSOLUTE 0x00 +#define RELTYPE_RELATIVE 0x01 +#define RELTYPE_GOTPC 0x01 /* no explicit GOTPC in a.out */ +#define RELTYPE_GOTOFF 0x10 +#define RELTYPE_GOT 0x10 /* distinct from GOTOFF bcos sym not sect */ +#define RELTYPE_PLT 0x21 +#define RELTYPE_SYMFLAG 0x08 + +struct Reloc { + struct Reloc *next; + int32_t address; /* relative to _start_ of section */ + int32_t symbol; /* symbol number or -ve section id */ + int bytes; /* 2 or 4 */ + int reltype; /* see above */ +}; + +struct Symbol { + int32_t strpos; /* string table position of name */ + int type; /* symbol type - see flags below */ + int32_t value; /* address, or COMMON variable size */ + int32_t size; /* size for data or function exports */ + int32_t segment; /* back-reference used by gsym_reloc */ + struct Symbol *next; /* list of globals in each section */ + struct Symbol *nextfwd; /* list of unresolved-size symbols */ + char *name; /* for unresolved-size symbols */ + int32_t symnum; /* index into symbol table */ +}; + +/* + * Section IDs - used in Reloc.symbol when negative, and in + * Symbol.type when positive. + */ +#define SECT_ABS 2 /* absolute value */ +#define SECT_TEXT 4 /* text section */ +#define SECT_DATA 6 /* data section */ +#define SECT_BSS 8 /* bss section */ +#define SECT_MASK 0xE /* mask out any of the above */ + +/* + * More flags used in Symbol.type. + */ +#define SYM_GLOBAL 1 /* it's a global symbol */ +#define SYM_DATA 0x100 /* used for shared libs */ +#define SYM_FUNCTION 0x200 /* used for shared libs */ +#define SYM_WITH_SIZE 0x4000 /* not output; internal only */ + +/* + * Bit more explanation of symbol types: SECT_xxx denotes a local + * symbol. SECT_xxx|SYM_GLOBAL denotes a global symbol, defined in + * this module. Just SYM_GLOBAL, with zero value, denotes an + * external symbol referenced in this module. And just SYM_GLOBAL, + * but with a non-zero value, declares a C `common' variable, of + * size `value'. + */ + +struct Section { + struct SAA *data; + uint32_t len, size, nrelocs; + int32_t index; + struct Reloc *head, **tail; + struct Symbol *gsyms, *asym; +}; + +static struct Section stext, sdata, sbss; + +static struct SAA *syms; +static uint32_t nsyms; + +static struct RAA *bsym; + +static struct SAA *strs; +static uint32_t strslen; + +static struct Symbol *fwds; + +static int bsd; +static int is_pic; + +static void aout_write(void); +static void aout_write_relocs(struct Reloc *); +static void aout_write_syms(void); +static void aout_sect_write(struct Section *, const uint8_t *, + uint32_t); +static void aout_pad_sections(void); +static void aout_fixup_relocs(struct Section *); + +/* + * Special section numbers which are used to define special + * symbols, which can be used with WRT to provide PIC relocation + * types. + */ +static int32_t aout_gotpc_sect, aout_gotoff_sect; +static int32_t aout_got_sect, aout_plt_sect; +static int32_t aout_sym_sect; + +static void aoutg_init(void) +{ + stext.data = saa_init(1L); + stext.head = NULL; + stext.tail = &stext.head; + sdata.data = saa_init(1L); + sdata.head = NULL; + sdata.tail = &sdata.head; + stext.len = stext.size = sdata.len = sdata.size = sbss.len = 0; + stext.nrelocs = sdata.nrelocs = 0; + stext.gsyms = sdata.gsyms = sbss.gsyms = NULL; + stext.index = seg_alloc(); + sdata.index = seg_alloc(); + sbss.index = seg_alloc(); + stext.asym = sdata.asym = sbss.asym = NULL; + syms = saa_init((int32_t)sizeof(struct Symbol)); + nsyms = 0; + bsym = raa_init(); + strs = saa_init(1L); + strslen = 0; + fwds = NULL; +} + +#ifdef OF_AOUT + +static void aout_init(void) +{ + bsd = false; + aoutg_init(); + + aout_gotpc_sect = aout_gotoff_sect = aout_got_sect = + aout_plt_sect = aout_sym_sect = NO_SEG; +} + +#endif + +#ifdef OF_AOUTB + +extern const struct ofmt of_aoutb; + +static void aoutb_init(void) +{ + bsd = true; + aoutg_init(); + + is_pic = 0x00; /* may become 0x40 */ + + aout_gotpc_sect = seg_alloc(); + backend_label("..gotpc", aout_gotpc_sect + 1, 0L); + aout_gotoff_sect = seg_alloc(); + backend_label("..gotoff", aout_gotoff_sect + 1, 0L); + aout_got_sect = seg_alloc(); + backend_label("..got", aout_got_sect + 1, 0L); + aout_plt_sect = seg_alloc(); + backend_label("..plt", aout_plt_sect + 1, 0L); + aout_sym_sect = seg_alloc(); + backend_label("..sym", aout_sym_sect + 1, 0L); +} + +#endif + +static void aout_cleanup(void) +{ + struct Reloc *r; + + aout_pad_sections(); + aout_fixup_relocs(&stext); + aout_fixup_relocs(&sdata); + aout_write(); + saa_free(stext.data); + while (stext.head) { + r = stext.head; + stext.head = stext.head->next; + nasm_free(r); + } + saa_free(sdata.data); + while (sdata.head) { + r = sdata.head; + sdata.head = sdata.head->next; + nasm_free(r); + } + saa_free(syms); + raa_free(bsym); + saa_free(strs); +} + +static int32_t aout_section_names(char *name, int *bits) +{ + /* + * Default to 32 bits. + */ + if (!name) { + *bits = 32; + return stext.index; + } + + if (!strcmp(name, ".text")) + return stext.index; + else if (!strcmp(name, ".data")) + return sdata.index; + else if (!strcmp(name, ".bss")) + return sbss.index; + else + return NO_SEG; +} + +static void aout_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special) +{ + int pos = strslen + 4; + struct Symbol *sym; + int special_used = false; + + if (name[0] == '.' && name[1] == '.' && name[2] != '@') { + /* + * This is a NASM special symbol. We never allow it into + * the a.out symbol table, even if it's a valid one. If it + * _isn't_ a valid one, we should barf immediately. + */ + if (strcmp(name, "..gotpc") && strcmp(name, "..gotoff") && + strcmp(name, "..got") && strcmp(name, "..plt") && + strcmp(name, "..sym")) + nasm_nonfatal("unrecognised special symbol `%s'", name); + return; + } + + if (is_global == 3) { + struct Symbol **s; + /* + * Fix up a forward-reference symbol size from the first + * pass. + */ + for (s = &fwds; *s; s = &(*s)->nextfwd) + if (!strcmp((*s)->name, name)) { + struct tokenval tokval; + expr *e; + char *p = special; + + p = nasm_skip_spaces(nasm_skip_word(p)); + stdscan_reset(); + stdscan_set(p); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, NULL, 1, NULL); + if (e) { + if (!is_simple(e)) + nasm_nonfatal("cannot use relocatable" + " expression as symbol size"); + else + (*s)->size = reloc_value(e); + } + + /* + * Remove it from the list of unresolved sizes. + */ + nasm_free((*s)->name); + *s = (*s)->nextfwd; + return; + } + return; /* it wasn't an important one */ + } + + saa_wbytes(strs, name, (int32_t)(1 + strlen(name))); + strslen += 1 + strlen(name); + + sym = saa_wstruct(syms); + + sym->strpos = pos; + sym->type = is_global ? SYM_GLOBAL : 0; + sym->segment = segment; + if (segment == NO_SEG) + sym->type |= SECT_ABS; + else if (segment == stext.index) { + sym->type |= SECT_TEXT; + if (is_global) { + sym->next = stext.gsyms; + stext.gsyms = sym; + } else if (!stext.asym) + stext.asym = sym; + } else if (segment == sdata.index) { + sym->type |= SECT_DATA; + if (is_global) { + sym->next = sdata.gsyms; + sdata.gsyms = sym; + } else if (!sdata.asym) + sdata.asym = sym; + } else if (segment == sbss.index) { + sym->type |= SECT_BSS; + if (is_global) { + sym->next = sbss.gsyms; + sbss.gsyms = sym; + } else if (!sbss.asym) + sbss.asym = sym; + } else + sym->type = SYM_GLOBAL; + if (is_global == 2) + sym->value = offset; + else + sym->value = (sym->type == SYM_GLOBAL ? 0 : offset); + + if (is_global && sym->type != SYM_GLOBAL) { + /* + * Global symbol exported _from_ this module. We must check + * the special text for type information. + */ + + if (special) { + int n = strcspn(special, " "); + + if (!nasm_strnicmp(special, "function", n)) + sym->type |= SYM_FUNCTION; + else if (!nasm_strnicmp(special, "data", n) || + !nasm_strnicmp(special, "object", n)) + sym->type |= SYM_DATA; + else + nasm_nonfatal("unrecognised symbol type `%.*s'", + n, special); + if (special[n]) { + struct tokenval tokval; + expr *e; + int fwd = false; + char *saveme = stdscan_get(); + + if (!bsd) { + nasm_nonfatal("Linux a.out does not support" + " symbol size information"); + } else { + while (special[n] && nasm_isspace(special[n])) + n++; + /* + * We have a size expression; attempt to + * evaluate it. + */ + sym->type |= SYM_WITH_SIZE; + stdscan_reset(); + stdscan_set(special + n); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, &fwd, 0, NULL); + if (fwd) { + sym->nextfwd = fwds; + fwds = sym; + sym->name = nasm_strdup(name); + } else if (e) { + if (!is_simple(e)) + nasm_nonfatal("cannot use relocatable" + " expression as symbol size"); + else + sym->size = reloc_value(e); + } + } + stdscan_set(saveme); + } + special_used = true; + } + } + + /* + * define the references from external-symbol segment numbers + * to these symbol records. + */ + if (segment != NO_SEG && segment != stext.index && + segment != sdata.index && segment != sbss.index) + bsym = raa_write(bsym, segment, nsyms); + sym->symnum = nsyms; + + nsyms++; + if (sym->type & SYM_WITH_SIZE) + nsyms++; /* and another for the size */ + + if (special && !special_used) + nasm_nonfatal("no special symbol features supported here"); +} + +static void aout_add_reloc(struct Section *sect, int32_t segment, + int reltype, int bytes) +{ + struct Reloc *r; + + r = *sect->tail = nasm_malloc(sizeof(struct Reloc)); + sect->tail = &r->next; + r->next = NULL; + + r->address = sect->len; + r->symbol = (segment == NO_SEG ? -SECT_ABS : + segment == stext.index ? -SECT_TEXT : + segment == sdata.index ? -SECT_DATA : + segment == sbss.index ? -SECT_BSS : + raa_read(bsym, segment)); + r->reltype = reltype; + if (r->symbol >= 0) + r->reltype |= RELTYPE_SYMFLAG; + r->bytes = bytes; + + sect->nrelocs++; +} + +/* + * This routine deals with ..got and ..sym relocations: the more + * complicated kinds. In shared-library writing, some relocations + * with respect to global symbols must refer to the precise symbol + * rather than referring to an offset from the base of the section + * _containing_ the symbol. Such relocations call to this routine, + * which searches the symbol list for the symbol in question. + * + * RELTYPE_GOT references require the _exact_ symbol address to be + * used; RELTYPE_ABSOLUTE references can be at an offset from the + * symbol. The boolean argument `exact' tells us this. + * + * Return value is the adjusted value of `addr', having become an + * offset from the symbol rather than the section. Should always be + * zero when returning from an exact call. + * + * Limitation: if you define two symbols at the same place, + * confusion will occur. + * + * Inefficiency: we search, currently, using a linked list which + * isn't even necessarily sorted. + */ +static int32_t aout_add_gsym_reloc(struct Section *sect, + int32_t segment, int32_t offset, + int type, int bytes, int exact) +{ + struct Symbol *sym, *sm, *shead; + struct Reloc *r; + + /* + * First look up the segment to find whether it's text, data, + * bss or an external symbol. + */ + shead = NULL; + if (segment == stext.index) + shead = stext.gsyms; + else if (segment == sdata.index) + shead = sdata.gsyms; + else if (segment == sbss.index) + shead = sbss.gsyms; + if (!shead) { + if (exact && offset != 0) + nasm_nonfatal("unable to find a suitable global symbol" + " for this reference"); + else + aout_add_reloc(sect, segment, type, bytes); + return offset; + } + + if (exact) { + /* + * Find a symbol pointing _exactly_ at this one. + */ + list_for_each(sym, shead) + if (sym->value == offset) + break; + } else { + /* + * Find the nearest symbol below this one. + */ + sym = NULL; + list_for_each(sm, shead) + if (sm->value <= offset && (!sym || sm->value > sym->value)) + sym = sm; + } + if (!sym && exact) { + nasm_nonfatal("unable to find a suitable global symbol" + " for this reference"); + return 0; + } + + r = *sect->tail = nasm_malloc(sizeof(struct Reloc)); + sect->tail = &r->next; + r->next = NULL; + + r->address = sect->len; + r->symbol = sym->symnum; + r->reltype = type | RELTYPE_SYMFLAG; + r->bytes = bytes; + + sect->nrelocs++; + + return offset - sym->value; +} + +/* + * This routine deals with ..gotoff relocations. These _must_ refer + * to a symbol, due to a perversity of *BSD's PIC implementation, + * and it must be a non-global one as well; so we store `asym', the + * first nonglobal symbol defined in each section, and always work + * from that. Relocation type is always RELTYPE_GOTOFF. + * + * Return value is the adjusted value of `addr', having become an + * offset from the `asym' symbol rather than the section. + */ +static int32_t aout_add_gotoff_reloc(struct Section *sect, int32_t segment, + int32_t offset, int bytes) +{ + struct Reloc *r; + struct Symbol *asym; + + /* + * First look up the segment to find whether it's text, data, + * bss or an external symbol. + */ + asym = NULL; + if (segment == stext.index) + asym = stext.asym; + else if (segment == sdata.index) + asym = sdata.asym; + else if (segment == sbss.index) + asym = sbss.asym; + if (!asym) + nasm_nonfatal("`..gotoff' relocations require a non-global" + " symbol in the section"); + + r = *sect->tail = nasm_malloc(sizeof(struct Reloc)); + sect->tail = &r->next; + r->next = NULL; + + r->address = sect->len; + r->symbol = asym->symnum; + r->reltype = RELTYPE_GOTOFF; + r->bytes = bytes; + + sect->nrelocs++; + + return offset - asym->value; +} + +static void aout_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + struct Section *s; + int32_t addr; + uint8_t mydata[4], *p; + + if (segto == stext.index) + s = &stext; + else if (segto == sdata.index) + s = &sdata; + else if (segto == sbss.index) + s = NULL; + else { + nasm_warn(WARN_OTHER, "attempt to assemble code in" + " segment %d: defaulting to `.text'", segto); + s = &stext; + } + + if (!s && type != OUT_RESERVE) { + nasm_warn(WARN_OTHER, "attempt to initialize memory in the" + " BSS section: ignored"); + sbss.len += realsize(type, size); + return; + } + + memset(mydata, 0, sizeof(mydata)); + + if (type == OUT_RESERVE) { + if (s) { + nasm_warn(WARN_ZEROING, "uninitialized space declared in" + " %s section: zeroing", + (segto == stext.index ? "code" : "data")); + aout_sect_write(s, NULL, size); + } else + sbss.len += size; + } else if (type == OUT_RAWDATA) { + aout_sect_write(s, data, size); + } else if (type == OUT_ADDRESS) { + int asize = abs((int)size); + addr = *(int64_t *)data; + if (segment != NO_SEG) { + if (segment % 2) { + nasm_nonfatal("a.out format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + aout_add_reloc(s, segment, RELTYPE_ABSOLUTE, asize); + } else if (!bsd) { + nasm_nonfatal("Linux a.out format does not support" + " any use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } else if (wrt == aout_gotpc_sect + 1) { + is_pic = 0x40; + aout_add_reloc(s, segment, RELTYPE_GOTPC, asize); + } else if (wrt == aout_gotoff_sect + 1) { + is_pic = 0x40; + addr = aout_add_gotoff_reloc(s, segment, addr, asize); + } else if (wrt == aout_got_sect + 1) { + is_pic = 0x40; + addr = aout_add_gsym_reloc(s, segment, addr, RELTYPE_GOT, + asize, true); + } else if (wrt == aout_sym_sect + 1) { + addr = aout_add_gsym_reloc(s, segment, addr, + RELTYPE_ABSOLUTE, asize, + false); + } else if (wrt == aout_plt_sect + 1) { + is_pic = 0x40; + nasm_nonfatal("a.out format cannot produce non-PC-" + "relative PLT references"); + } else { + nasm_nonfatal("a.out format does not support this" + " use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } + } + } + p = mydata; + if (asize == 2) + WRITESHORT(p, addr); + else + WRITELONG(p, addr); + aout_sect_write(s, mydata, asize); + } else if (type == OUT_REL2ADR) { + if (segment != NO_SEG && segment % 2) { + nasm_nonfatal("a.out format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + aout_add_reloc(s, segment, RELTYPE_RELATIVE, 2); + } else if (!bsd) { + nasm_nonfatal("Linux a.out format does not support" + " any use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } else if (wrt == aout_plt_sect + 1) { + is_pic = 0x40; + aout_add_reloc(s, segment, RELTYPE_PLT, 2); + } else if (wrt == aout_gotpc_sect + 1 || + wrt == aout_gotoff_sect + 1 || + wrt == aout_got_sect + 1) { + nasm_nonfatal("a.out format cannot produce PC-" + "relative GOT references"); + } else { + nasm_nonfatal("a.out format does not support this" + " use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } + } + p = mydata; + WRITESHORT(p, *(int64_t *)data - (size + s->len)); + aout_sect_write(s, mydata, 2L); + } else if (type == OUT_REL4ADR) { + if (segment != NO_SEG && segment % 2) { + nasm_nonfatal("a.out format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + aout_add_reloc(s, segment, RELTYPE_RELATIVE, 4); + } else if (!bsd) { + nasm_nonfatal("Linux a.out format does not support" + " any use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } else if (wrt == aout_plt_sect + 1) { + is_pic = 0x40; + aout_add_reloc(s, segment, RELTYPE_PLT, 4); + } else if (wrt == aout_gotpc_sect + 1 || + wrt == aout_gotoff_sect + 1 || + wrt == aout_got_sect + 1) { + nasm_nonfatal("a.out format cannot produce PC-" + "relative GOT references"); + } else { + nasm_nonfatal("a.out format does not support this" + " use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } + } + p = mydata; + WRITELONG(p, *(int64_t *)data - (size + s->len)); + aout_sect_write(s, mydata, 4L); + } +} + +static void aout_pad_sections(void) +{ + static uint8_t pad[] = { 0x90, 0x90, 0x90, 0x90 }; + /* + * Pad each of the text and data sections with NOPs until their + * length is a multiple of four. (NOP == 0x90.) Also increase + * the length of the BSS section similarly. + */ + aout_sect_write(&stext, pad, (-(int32_t)stext.len) & 3); + aout_sect_write(&sdata, pad, (-(int32_t)sdata.len) & 3); + sbss.len = ALIGN(sbss.len, 4); +} + +/* + * a.out files have the curious property that all references to + * things in the data or bss sections are done by addresses which + * are actually relative to the start of the _text_ section, in the + * _file_. (No relation to what happens after linking. No idea why + * this should be so. It's very strange.) So we have to go through + * the relocation table, _after_ the final size of each section is + * known, and fix up the relocations pointed to. + */ +static void aout_fixup_relocs(struct Section *sect) +{ + struct Reloc *r; + + saa_rewind(sect->data); + list_for_each(r, sect->head) { + uint8_t *p, *q, blk[4]; + int32_t l; + + saa_fread(sect->data, r->address, blk, (int32_t)r->bytes); + p = q = blk; + l = *p++; + if (r->bytes > 1) { + l += ((int32_t)*p++) << 8; + if (r->bytes == 4) { + l += ((int32_t)*p++) << 16; + l += ((int32_t)*p++) << 24; + } + } + if (r->symbol == -SECT_DATA) + l += stext.len; + else if (r->symbol == -SECT_BSS) + l += stext.len + sdata.len; + if (r->bytes == 4) + WRITELONG(q, l); + else if (r->bytes == 2) + WRITESHORT(q, l); + else + *q++ = l & 0xFF; + saa_fwrite(sect->data, r->address, blk, (int32_t)r->bytes); + } +} + +static void aout_write(void) +{ + /* + * Emit the a.out header. + */ + /* OMAGIC, M_386 or MID_I386, no flags */ + fwriteint32_t(bsd ? 0x07018600 | is_pic : 0x640107L, ofile); + fwriteint32_t(stext.len, ofile); + fwriteint32_t(sdata.len, ofile); + fwriteint32_t(sbss.len, ofile); + fwriteint32_t(nsyms * 12, ofile); /* length of symbol table */ + fwriteint32_t(0L, ofile); /* object files have no entry point */ + fwriteint32_t(stext.nrelocs * 8, ofile); /* size of text relocs */ + fwriteint32_t(sdata.nrelocs * 8, ofile); /* size of data relocs */ + + /* + * Write out the code section and the data section. + */ + saa_fpwrite(stext.data, ofile); + saa_fpwrite(sdata.data, ofile); + + /* + * Write out the relocations. + */ + aout_write_relocs(stext.head); + aout_write_relocs(sdata.head); + + /* + * Write the symbol table. + */ + aout_write_syms(); + + /* + * And the string table. + */ + fwriteint32_t(strslen + 4, ofile); /* length includes length count */ + saa_fpwrite(strs, ofile); +} + +static void aout_write_relocs(struct Reloc *r) +{ + list_for_each(r, r) { + uint32_t word2; + + fwriteint32_t(r->address, ofile); + + if (r->symbol >= 0) + word2 = r->symbol; + else + word2 = -r->symbol; + word2 |= r->reltype << 24; + word2 |= (r->bytes == 1 ? 0 : + r->bytes == 2 ? 0x2000000L : 0x4000000L); + fwriteint32_t(word2, ofile); + } +} + +static void aout_write_syms(void) +{ + uint32_t i; + + saa_rewind(syms); + for (i = 0; i < nsyms; i++) { + struct Symbol *sym = saa_rstruct(syms); + fwriteint32_t(sym->strpos, ofile); + fwriteint32_t((int32_t)sym->type & ~SYM_WITH_SIZE, ofile); + /* + * Fix up the symbol value now we know the final section + * sizes. + */ + if ((sym->type & SECT_MASK) == SECT_DATA) + sym->value += stext.len; + if ((sym->type & SECT_MASK) == SECT_BSS) + sym->value += stext.len + sdata.len; + fwriteint32_t(sym->value, ofile); + /* + * Output a size record if necessary. + */ + if (sym->type & SYM_WITH_SIZE) { + fwriteint32_t(sym->strpos, ofile); + fwriteint32_t(0x0DL, ofile); /* special value: means size */ + fwriteint32_t(sym->size, ofile); + i++; /* use up another of `nsyms' */ + } + } +} + +static void aout_sect_write(struct Section *sect, + const uint8_t *data, uint32_t len) +{ + saa_wbytes(sect->data, data, len); + sect->len += len; +} + +extern macros_t aout_stdmac[]; + +#endif /* OF_AOUT || OF_AOUTB */ + +#ifdef OF_AOUT + +const struct ofmt of_aout = { + "Linux a.out", + "aout", + ".o", + 0, + 32, + null_debug_arr, + &null_debug_form, + aout_stdmac, + aout_init, + null_reset, + nasm_do_legacy_output, + aout_out, + aout_deflabel, + aout_section_names, + NULL, + null_sectalign, + null_segbase, + null_directive, + aout_cleanup, + NULL /* pragma list */ +}; + +#endif + +#ifdef OF_AOUTB + +const struct ofmt of_aoutb = { + "NetBSD/FreeBSD a.out", + "aoutb", + ".o", + 0, + 32, + null_debug_arr, + &null_debug_form, + aout_stdmac, + aoutb_init, + null_reset, + nasm_do_legacy_output, + aout_out, + aout_deflabel, + aout_section_names, + NULL, + null_sectalign, + null_segbase, + null_directive, + aout_cleanup, + NULL /* pragma list */ +}; + +#endif diff --git a/vere/ext/nasm/output/outaout.mac b/vere/ext/nasm/output/outaout.mac new file mode 100644 index 0000000..5b7b366 --- /dev/null +++ b/vere/ext/nasm/output/outaout.mac @@ -0,0 +1,37 @@ +;; -------------------------------------------------------------------------- +;; +;; Copyright 1996-2009 The NASM Authors - All Rights Reserved +;; See the file AUTHORS included with the NASM distribution for +;; the specific copyright holders. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following +;; conditions are met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following +;; disclaimer in the documentation and/or other materials provided +;; with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +;; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +;; EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;; +;; -------------------------------------------------------------------------- + +OUT: aout aoutb +%define __?SECT?__ [section .text] +%macro __?NASM_CDecl?__ 1 +%endmacro diff --git a/vere/ext/nasm/output/outas86.c b/vere/ext/nasm/output/outas86.c new file mode 100644 index 0000000..54b22f8 --- /dev/null +++ b/vere/ext/nasm/output/outas86.c @@ -0,0 +1,611 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2017 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outas86.c output routines for the Netwide Assembler to produce + * Linux as86 (bin86-0.3) object files + */ + +#include "compiler.h" + +#include "nctype.h" + +#include "nasm.h" +#include "nasmlib.h" +#include "error.h" +#include "saa.h" +#include "raa.h" +#include "outform.h" +#include "outlib.h" + +#ifdef OF_AS86 + +struct Piece { + struct Piece *next; + int type; /* 0 = absolute, 1 = seg, 2 = sym */ + int32_t offset; /* relative offset */ + int number; /* symbol/segment number (4=bss) */ + int32_t bytes; /* size of reloc or of absolute data */ + bool relative; /* relative address? */ +}; + +struct Symbol { + int32_t strpos; /* string table position of name */ + int flags; /* symbol flags */ + int segment; /* 4=bss at this point */ + int32_t value; /* address, or COMMON variable size */ +}; + +/* + * Section IDs - used in Piece.number and Symbol.segment. + */ +#define SECT_TEXT 0 /* text section */ +#define SECT_DATA 3 /* data section */ +#define SECT_BSS 4 /* bss section */ + +/* + * Flags used in Symbol.flags. + */ +#define SYM_ENTRY (1<<8) +#define SYM_EXPORT (1<<7) +#define SYM_IMPORT (1<<6) +#define SYM_ABSOLUTE (1<<4) + +struct Section { + struct SAA *data; + uint32_t datalen, size, len; + int32_t index; + struct Piece *head, *last, **tail; +}; + +static struct Section stext, sdata; +static uint32_t bsslen; +static int32_t bssindex; + +static struct SAA *syms; +static uint32_t nsyms; + +static struct RAA *bsym; + +static struct SAA *strs; +static size_t strslen; + +static int as86_reloc_size; + +static void as86_write(void); +static void as86_write_section(struct Section *, int); +static size_t as86_add_string(const char *name); +static void as86_sect_write(struct Section *, const uint8_t *, + uint32_t); + +static void as86_init(void) +{ + stext.data = saa_init(1L); + stext.datalen = 0L; + stext.head = stext.last = NULL; + stext.tail = &stext.head; + sdata.data = saa_init(1L); + sdata.datalen = 0L; + sdata.head = sdata.last = NULL; + sdata.tail = &sdata.head; + bsslen = + stext.len = stext.datalen = stext.size = + sdata.len = sdata.datalen = sdata.size = 0; + stext.index = seg_alloc(); + sdata.index = seg_alloc(); + bssindex = seg_alloc(); + syms = saa_init((int32_t)sizeof(struct Symbol)); + nsyms = 0; + bsym = raa_init(); + strs = saa_init(1L); + strslen = 0; + + /* as86 module name = input file minus extension */ + as86_add_string(filename_set_extension(inname, "")); +} + +static void as86_cleanup(void) +{ + struct Piece *p; + + as86_write(); + saa_free(stext.data); + while (stext.head) { + p = stext.head; + stext.head = stext.head->next; + nasm_free(p); + } + saa_free(sdata.data); + while (sdata.head) { + p = sdata.head; + sdata.head = sdata.head->next; + nasm_free(p); + } + saa_free(syms); + raa_free(bsym); + saa_free(strs); +} + +static int32_t as86_section_names(char *name, int *bits) +{ + /* + * Default is 16 bits. + */ + if (!name) { + *bits = 16; + return stext.index; + } + + if (!strcmp(name, ".text")) + return stext.index; + else if (!strcmp(name, ".data")) + return sdata.index; + else if (!strcmp(name, ".bss")) + return bssindex; + else + return NO_SEG; +} + +static size_t as86_add_string(const char *name) +{ + size_t pos = strslen; + size_t length = strlen(name); + + saa_wbytes(strs, name, length + 1); + strslen += 1 + length; + + return pos; +} + +static void as86_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special) +{ + bool is_start = false; + struct Symbol *sym; + + if (special) + nasm_nonfatal("as86 format does not support any" + " special symbol types"); + + + if (name[0] == '.' && name[1] == '.' && name[2] != '@') { + if (strcmp(name, "..start")) { + nasm_nonfatal("unrecognised special symbol `%s'", name); + return; + } else { + is_start = true; + } + } + + sym = saa_wstruct(syms); + + sym->strpos = as86_add_string(name); + sym->flags = 0; + + if (is_start) + sym->flags = SYM_ENTRY; + + if (segment == NO_SEG) + sym->flags |= SYM_ABSOLUTE, sym->segment = 0; + else if (segment == stext.index) + sym->segment = SECT_TEXT; + else if (segment == sdata.index) + sym->segment = SECT_DATA; + else if (segment == bssindex) + sym->segment = SECT_BSS; + else { + sym->flags |= SYM_IMPORT; + sym->segment = 15; + } + + if (is_global == 2) + sym->segment = 3; /* already have IMPORT */ + + if (is_global && !(sym->flags & SYM_IMPORT)) + sym->flags |= SYM_EXPORT; + + sym->value = offset; + + /* + * define the references from external-symbol segment numbers + * to these symbol records. + */ + if (segment != NO_SEG && segment != stext.index && + segment != sdata.index && segment != bssindex) + bsym = raa_write(bsym, segment, nsyms); + + nsyms++; +} + +static void as86_add_piece(struct Section *sect, int type, int32_t offset, + int32_t segment, int32_t bytes, int relative) +{ + struct Piece *p; + + sect->len += bytes; + + if (type == 0 && sect->last && sect->last->type == 0) { + sect->last->bytes += bytes; + return; + } + + p = sect->last = *sect->tail = nasm_malloc(sizeof(struct Piece)); + sect->tail = &p->next; + p->next = NULL; + + p->type = type; + p->offset = offset; + p->bytes = bytes; + p->relative = relative; + + if (type == 1 && segment == stext.index) + p->number = SECT_TEXT; + else if (type == 1 && segment == sdata.index) + p->number = SECT_DATA; + else if (type == 1 && segment == bssindex) + p->number = SECT_BSS; + else if (type == 1) + p->number = raa_read(bsym, segment), p->type = 2; +} + +static void as86_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + struct Section *s; + int32_t offset; + uint8_t mydata[4], *p; + + if (wrt != NO_SEG) { + wrt = NO_SEG; /* continue to do _something_ */ + nasm_nonfatal("WRT not supported by as86 output format"); + } + + if (segto == stext.index) + s = &stext; + else if (segto == sdata.index) + s = &sdata; + else if (segto == bssindex) + s = NULL; + else { + nasm_warn(WARN_OTHER, "attempt to assemble code in" + " segment %d: defaulting to `.text'", segto); + s = &stext; + } + + if (!s && type != OUT_RESERVE) { + nasm_warn(WARN_OTHER, "attempt to initialize memory in the" + " BSS section: ignored"); + bsslen += realsize(type, size); + return; + } + + memset(mydata, 0, sizeof(mydata)); + + if (type == OUT_RESERVE) { + if (s) { + nasm_warn(WARN_ZEROING, "uninitialized space declared in" + " %s section: zeroing", + (segto == stext.index ? "code" : "data")); + as86_sect_write(s, NULL, size); + as86_add_piece(s, 0, 0L, 0L, size, 0); + } else + bsslen += size; + } else if (type == OUT_RAWDATA) { + as86_sect_write(s, data, size); + as86_add_piece(s, 0, 0L, 0L, size, 0); + } else if (type == OUT_ADDRESS) { + int asize = abs((int)size); + if (segment != NO_SEG) { + if (segment % 2) { + nasm_nonfatal("as86 format does not support" + " segment base references"); + } else { + offset = *(int64_t *)data; + as86_add_piece(s, 1, offset, segment, asize, 0); + } + } else { + p = mydata; + WRITELONG(p, *(int64_t *)data); + as86_sect_write(s, data, asize); + as86_add_piece(s, 0, 0L, 0L, asize, 0); + } + } else if (type == OUT_REL2ADR) { + if (segment != NO_SEG) { + if (segment % 2) { + nasm_nonfatal("as86 format does not support" + " segment base references"); + } else { + offset = *(int64_t *)data; + as86_add_piece(s, 1, offset - size + 2, segment, 2L, + 1); + } + } + } else if (type == OUT_REL4ADR) { + if (segment != NO_SEG) { + if (segment % 2) { + nasm_nonfatal("as86 format does not support" + " segment base references"); + } else { + offset = *(int64_t *)data; + as86_add_piece(s, 1, offset - size + 4, segment, 4L, + 1); + } + } + } +} + +static void as86_write(void) +{ + uint32_t i; + int32_t symlen, seglen, segsize; + + /* + * First, go through the symbol records working out how big + * each will be. Also fix up BSS references at this time, and + * set the flags words up completely. + */ + symlen = 0; + saa_rewind(syms); + for (i = 0; i < nsyms; i++) { + struct Symbol *sym = saa_rstruct(syms); + if (sym->segment == SECT_BSS) + sym->segment = SECT_DATA, sym->value += sdata.len; + sym->flags |= sym->segment; + if (sym->value == 0) + sym->flags |= 0 << 14, symlen += 4; + else if (sym->value >= 0 && sym->value <= 255) + sym->flags |= 1 << 14, symlen += 5; + else if (sym->value >= 0 && sym->value <= 65535L) + sym->flags |= 2 << 14, symlen += 6; + else + sym->flags |= 3 << 14, symlen += 8; + } + + /* + * Now do the same for the segments, and get the segment size + * descriptor word at the same time. + */ + seglen = segsize = 0; + if ((uint32_t)stext.len > 65535L) + segsize |= 0x03000000L, seglen += 4; + else + segsize |= 0x02000000L, seglen += 2; + if ((uint32_t)sdata.len > 65535L) + segsize |= 0xC0000000L, seglen += 4; + else + segsize |= 0x80000000L, seglen += 2; + + /* + * Emit the as86 header. + */ + fwriteint32_t(0x000186A3L, ofile); + fputc(0x2A, ofile); + fwriteint32_t(27 + symlen + seglen + strslen, ofile); /* header length */ + fwriteint32_t(stext.len + sdata.len + bsslen, ofile); + fwriteint16_t(strslen, ofile); + fwriteint16_t(0, ofile); /* class = revision = 0 */ + fwriteint32_t(0x55555555L, ofile); /* segment max sizes: always this */ + fwriteint32_t(segsize, ofile); /* segment size descriptors */ + if (segsize & 0x01000000L) + fwriteint32_t(stext.len, ofile); + else + fwriteint16_t(stext.len, ofile); + if (segsize & 0x40000000L) + fwriteint32_t(sdata.len + bsslen, ofile); + else + fwriteint16_t(sdata.len + bsslen, ofile); + fwriteint16_t(nsyms, ofile); + + /* + * Write the symbol table. + */ + saa_rewind(syms); + for (i = 0; i < nsyms; i++) { + struct Symbol *sym = saa_rstruct(syms); + fwriteint16_t(sym->strpos, ofile); + fwriteint16_t(sym->flags, ofile); + switch (sym->flags & (3 << 14)) { + case 0 << 14: + break; + case 1 << 14: + fputc(sym->value, ofile); + break; + case 2 << 14: + fwriteint16_t(sym->value, ofile); + break; + case 3 << 14: + fwriteint32_t(sym->value, ofile); + break; + } + } + + /* + * Write out the string table. + */ + saa_fpwrite(strs, ofile); + + /* + * Write the program text. + */ + as86_reloc_size = -1; + as86_write_section(&stext, SECT_TEXT); + as86_write_section(&sdata, SECT_DATA); + /* + * Append the BSS section to the .data section + */ + if (bsslen > 65535L) { + fputc(0x13, ofile); + fwriteint32_t(bsslen, ofile); + } else if (bsslen > 255) { + fputc(0x12, ofile); + fwriteint16_t(bsslen, ofile); + } else if (bsslen) { + fputc(0x11, ofile); + fputc(bsslen, ofile); + } + + fputc(0, ofile); /* termination */ +} + +static void as86_set_rsize(int size) +{ + if (as86_reloc_size != size) { + switch (as86_reloc_size = size) { + case 1: + fputc(0x01, ofile); + break; + case 2: + fputc(0x02, ofile); + break; + case 4: + fputc(0x03, ofile); + break; + default: + nasm_panic("bizarre relocation size %d", size); + break; + } + } +} + +static void as86_write_section(struct Section *sect, int index) +{ + struct Piece *p; + uint32_t s; + int32_t length; + + fputc(0x20 + index, ofile); /* select the right section */ + + saa_rewind(sect->data); + + for (p = sect->head; p; p = p->next) + switch (p->type) { + case 0: + /* + * Absolute data. Emit it in chunks of at most 64 + * bytes. + */ + length = p->bytes; + do { + char buf[64]; + int32_t tmplen = (length > 64 ? 64 : length); + fputc(0x40 | (tmplen & 0x3F), ofile); + saa_rnbytes(sect->data, buf, tmplen); + nasm_write(buf, tmplen, ofile); + length -= tmplen; + } while (length > 0); + break; + case 1: + /* + * A segment-type relocation. First fix up the BSS. + */ + if (p->number == SECT_BSS) + p->number = SECT_DATA, p->offset += sdata.len; + as86_set_rsize(p->bytes); + fputc(0x80 | (p->relative ? 0x20 : 0) | p->number, ofile); + if (as86_reloc_size == 2) + fwriteint16_t(p->offset, ofile); + else + fwriteint32_t(p->offset, ofile); + break; + case 2: + /* + * A symbol-type relocation. + */ + as86_set_rsize(p->bytes); + s = p->offset; + if (s > 65535L) + s = 3; + else if (s > 255) + s = 2; + else if (s > 0) + s = 1; + else + s = 0; + fputc(0xC0 | + (p->relative ? 0x20 : 0) | + (p->number > 255 ? 0x04 : 0) | s, ofile); + if (p->number > 255) + fwriteint16_t(p->number, ofile); + else + fputc(p->number, ofile); + switch (s) { + case 0: + break; + case 1: + fputc(p->offset, ofile); + break; + case 2: + fwriteint16_t(p->offset, ofile); + break; + case 3: + fwriteint32_t(p->offset, ofile); + break; + } + break; + } +} + +static void as86_sect_write(struct Section *sect, + const uint8_t *data, uint32_t len) +{ + saa_wbytes(sect->data, data, len); + sect->datalen += len; +} + +extern macros_t as86_stdmac[]; + +const struct ofmt of_as86 = { + "as86 (bin86/dev86 toolchain)", + "as86", + ".o", + 0, + 32, + null_debug_arr, + &null_debug_form, + as86_stdmac, + as86_init, + null_reset, + nasm_do_legacy_output, + as86_out, + as86_deflabel, + as86_section_names, + NULL, + null_sectalign, + null_segbase, + null_directive, + as86_cleanup, + NULL /* pragma list */ +}; + +#endif /* OF_AS86 */ diff --git a/vere/ext/nasm/output/outas86.mac b/vere/ext/nasm/output/outas86.mac new file mode 100644 index 0000000..f7c9a48 --- /dev/null +++ b/vere/ext/nasm/output/outas86.mac @@ -0,0 +1,37 @@ +;; -------------------------------------------------------------------------- +;; +;; Copyright 1996-2009 The NASM Authors - All Rights Reserved +;; See the file AUTHORS included with the NASM distribution for +;; the specific copyright holders. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following +;; conditions are met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following +;; disclaimer in the documentation and/or other materials provided +;; with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +;; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +;; EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;; +;; -------------------------------------------------------------------------- + +OUT: as86 +%define __?SECT?__ [section .text] +%macro __?NASM_CDecl?__ 1 +%endmacro diff --git a/vere/ext/nasm/output/outbin.c b/vere/ext/nasm/output/outbin.c new file mode 100644 index 0000000..1522e5c --- /dev/null +++ b/vere/ext/nasm/output/outbin.c @@ -0,0 +1,1669 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2017 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outbin.c output routines for the Netwide Assembler to produce + * flat-form binary files + */ + +/* This is the extended version of NASM's original binary output + * format. It is backward compatible with the original BIN format, + * and contains support for multiple sections and advanced section + * ordering. + * + * Feature summary: + * + * - Users can create an arbitrary number of sections; they are not + * limited to just ".text", ".data", and ".bss". + * + * - Sections can be either progbits or nobits type. + * + * - You can specify that they be aligned at a certain boundary + * following the previous section ("align="), or positioned at an + * arbitrary byte-granular location ("start="). + * + * - You can specify a "virtual" start address for a section, which + * will be used for the calculation for all address references + * with respect to that section ("vstart="). + * + * - The ORG directive, as well as the section/segment directive + * arguments ("align=", "start=", "vstart="), can take a critical + * expression as their value. For example: "align=(1 << 12)". + * + * - You can generate map files using the 'map' directive. + * + */ + +/* Uncomment the following define if you want sections to adapt + * their progbits/nobits state depending on what type of + * instructions are issued, rather than defaulting to progbits. + * Note that this behavior violates the specification. + +#define ABIN_SMART_ADAPT + +*/ + +#include "compiler.h" + +#include "nctype.h" + +#include "nasm.h" +#include "nasmlib.h" +#include "error.h" +#include "saa.h" +#include "stdscan.h" +#include "labels.h" +#include "eval.h" +#include "outform.h" +#include "outlib.h" + +#ifdef OF_BIN + +static FILE *rf = NULL; +static void (*do_output)(void); + +/* Section flags keep track of which attributes the user has defined. */ +#define START_DEFINED 0x001 +#define ALIGN_DEFINED 0x002 +#define FOLLOWS_DEFINED 0x004 +#define VSTART_DEFINED 0x008 +#define VALIGN_DEFINED 0x010 +#define VFOLLOWS_DEFINED 0x020 +#define TYPE_DEFINED 0x040 +#define TYPE_PROGBITS 0x080 +#define TYPE_NOBITS 0x100 + +/* This struct is used to keep track of symbols for map-file generation. */ +static struct bin_label { + char *name; + struct bin_label *next; +} *no_seg_labels, **nsl_tail; + +static struct Section { + char *name; + struct SAA *contents; + int64_t length; /* section length in bytes */ + +/* Section attributes */ + int flags; /* see flag definitions above */ + uint64_t align; /* section alignment */ + uint64_t valign; /* notional section alignment */ + uint64_t start; /* section start address */ + uint64_t vstart; /* section virtual start address */ + char *follows; /* the section that this one will follow */ + char *vfollows; /* the section that this one will notionally follow */ + int32_t start_index; /* NASM section id for non-relocated version */ + int32_t vstart_index; /* the NASM section id */ + + struct bin_label *labels; /* linked-list of label handles for map output. */ + struct bin_label **labels_end; /* Holds address of end of labels list. */ + struct Section *prev; /* Points to previous section (implicit follows). */ + struct Section *next; /* This links sections with a defined start address. */ + +/* The extended bin format allows for sections to have a "virtual" + * start address. This is accomplished by creating two sections: + * one beginning at the Load Memory Address and the other beginning + * at the Virtual Memory Address. The LMA section is only used to + * define the section.<section_name>.start label, but there isn't + * any other good way for us to handle that label. + */ + +} *sections, *last_section; + +static struct Reloc { + struct Reloc *next; + int32_t posn; + int32_t bytes; + int32_t secref; + int32_t secrel; + struct Section *target; +} *relocs, **reloctail; + +static uint64_t origin; +static int origin_defined; + +/* Stuff we need for map-file generation. */ +#define MAP_ORIGIN 1 +#define MAP_SUMMARY 2 +#define MAP_SECTIONS 4 +#define MAP_SYMBOLS 8 +static int map_control = 0; + +extern macros_t bin_stdmac[]; + +static void add_reloc(struct Section *s, int32_t bytes, int32_t secref, + int32_t secrel) +{ + struct Reloc *r; + + r = *reloctail = nasm_malloc(sizeof(struct Reloc)); + reloctail = &r->next; + r->next = NULL; + r->posn = s->length; + r->bytes = bytes; + r->secref = secref; + r->secrel = secrel; + r->target = s; +} + +static struct Section *find_section_by_name(const char *name) +{ + struct Section *s; + + list_for_each(s, sections) + if (!strcmp(s->name, name)) + break; + return s; +} + +static struct Section *find_section_by_index(int32_t index) +{ + struct Section *s; + + list_for_each(s, sections) + if ((index == s->vstart_index) || (index == s->start_index)) + break; + return s; +} + +static struct Section *create_section(char *name) +{ + struct Section *s = nasm_zalloc(sizeof(*s)); + + s->prev = last_section; + s->name = nasm_strdup(name); + s->labels_end = &(s->labels); + s->contents = saa_init(1L); + + /* Register our sections with NASM. */ + s->vstart_index = seg_alloc(); + s->start_index = seg_alloc(); + + /* FIXME: Append to a tail, we need some helper */ + last_section->next = s; + last_section = s; + + return last_section; +} + +static void bin_cleanup(void) +{ + struct Section *g, **gp; + struct Section *gs = NULL, **gsp; + struct Section *s, **sp; + struct Section *nobits = NULL, **nt; + struct Section *last_progbits; + struct bin_label *l; + struct Reloc *r; + uint64_t pend; + int h; + + if (debug_level(1)) { + nasm_debug("bin_cleanup: Sections were initially referenced in this order:\n"); + for (h = 0, s = sections; s; h++, s = s->next) + nasm_debug("%i. %s\n", h, s->name); + } + + /* Assembly has completed, so now we need to generate the output file. + * Step 1: Separate progbits and nobits sections into separate lists. + * Step 2: Sort the progbits sections into their output order. + * Step 3: Compute start addresses for all progbits sections. + * Step 4: Compute vstart addresses for all sections. + * Step 5: Apply relocations. + * Step 6: Write the sections' data to the output file. + * Step 7: Generate the map file. + * Step 8: Release all allocated memory. + */ + + /* To do: Smart section-type adaptation could leave some empty sections + * without a defined type (progbits/nobits). Won't fix now since this + * feature will be disabled. */ + + /* Step 1: Split progbits and nobits sections into separate lists. */ + + nt = &nobits; + /* Move nobits sections into a separate list. Also pre-process nobits + * sections' attributes. */ + for (sp = §ions->next, s = sections->next; s; s = *sp) { /* Skip progbits sections. */ + if (s->flags & TYPE_PROGBITS) { + sp = &s->next; + continue; + } + /* Do some special pre-processing on nobits sections' attributes. */ + if (s->flags & (START_DEFINED | ALIGN_DEFINED | FOLLOWS_DEFINED)) { /* Check for a mixture of real and virtual section attributes. */ + if (s->flags & (VSTART_DEFINED | VALIGN_DEFINED | + VFOLLOWS_DEFINED)) + nasm_fatal("cannot mix real and virtual attributes" + " in nobits section (%s)", s->name); + /* Real and virtual attributes mean the same thing for nobits sections. */ + if (s->flags & START_DEFINED) { + s->vstart = s->start; + s->flags |= VSTART_DEFINED; + } + if (s->flags & ALIGN_DEFINED) { + s->valign = s->align; + s->flags |= VALIGN_DEFINED; + } + if (s->flags & FOLLOWS_DEFINED) { + s->vfollows = s->follows; + s->flags |= VFOLLOWS_DEFINED; + s->flags &= ~FOLLOWS_DEFINED; + } + } + /* Every section must have a start address. */ + if (s->flags & VSTART_DEFINED) { + s->start = s->vstart; + s->flags |= START_DEFINED; + } + /* Move the section into the nobits list. */ + *sp = s->next; + s->next = NULL; + *nt = s; + nt = &s->next; + } + + /* Step 2: Sort the progbits sections into their output order. */ + + /* In Step 2 we move around sections in groups. A group + * begins with a section (group leader) that has a user- + * defined start address or follows section. The remainder + * of the group is made up of the sections that implicitly + * follow the group leader (i.e., they were defined after + * the group leader and were not given an explicit start + * address or follows section by the user). */ + + /* For anyone attempting to read this code: + * g (group) points to a group of sections, the first one of which has + * a user-defined start address or follows section. + * gp (g previous) holds the location of the pointer to g. + * gs (g scan) is a temp variable that we use to scan to the end of the group. + * gsp (gs previous) holds the location of the pointer to gs. + * nt (nobits tail) points to the nobits section-list tail. + */ + + /* Link all 'follows' groups to their proper position. To do + * this we need to know three things: the start of the group + * to relocate (g), the section it is following (s), and the + * end of the group we're relocating (gs). */ + for (gp = §ions, g = sections; g; g = gs) { /* Find the next follows group that is out of place (g). */ + if (!(g->flags & FOLLOWS_DEFINED)) { + while (g->next) { + if ((g->next->flags & FOLLOWS_DEFINED) && + strcmp(g->name, g->next->follows)) + break; + g = g->next; + } + if (!g->next) + break; + gp = &g->next; + g = g->next; + } + /* Find the section that this group follows (s). */ + for (sp = §ions, s = sections; + s && strcmp(s->name, g->follows); + sp = &s->next, s = s->next) ; + if (!s) + nasm_fatal("section %s follows an invalid or" + " unknown section (%s)", g->name, g->follows); + if (s == g) + nasm_fatal("section %s is self following", s->name); + if (s->next && (s->next->flags & FOLLOWS_DEFINED) && + !strcmp(s->name, s->next->follows)) + nasm_fatal("sections %s and %s can't both follow" + " section %s", g->name, s->next->name, s->name); + /* Find the end of the current follows group (gs). */ + for (gsp = &g->next, gs = g->next; + gs && (gs != s) && !(gs->flags & START_DEFINED); + gsp = &gs->next, gs = gs->next) { + if (gs->next && (gs->next->flags & FOLLOWS_DEFINED) && + strcmp(gs->name, gs->next->follows)) { + gsp = &gs->next; + gs = gs->next; + break; + } + } + /* Re-link the group after its follows section. */ + *gsp = s->next; + s->next = g; + *gp = gs; + } + + /* Link all 'start' groups to their proper position. Once + * again we need to know g, s, and gs (see above). The main + * difference is we already know g since we sort by moving + * groups from the 'unsorted' list into a 'sorted' list (g + * will always be the first section in the unsorted list). */ + for (g = sections, sections = NULL; g; g = gs) { /* Find the section that we will insert this group before (s). */ + for (sp = §ions, s = sections; s; sp = &s->next, s = s->next) + if ((s->flags & START_DEFINED) && (g->start < s->start)) + break; + /* Find the end of the group (gs). */ + for (gs = g->next, gsp = &g->next; + gs && !(gs->flags & START_DEFINED); + gsp = &gs->next, gs = gs->next) ; + /* Re-link the group before the target section. */ + *sp = g; + *gsp = s; + } + + /* Step 3: Compute start addresses for all progbits sections. */ + + /* Make sure we have an origin and a start address for the first section. */ + if (origin_defined) { + if (sections->flags & START_DEFINED) { + /* Make sure this section doesn't begin before the origin. */ + if (sections->start < origin) + nasm_fatal("section %s begins" + " before program origin", sections->name); + } else if (sections->flags & ALIGN_DEFINED) { + sections->start = ALIGN(origin, sections->align); + } else { + sections->start = origin; + } + } else { + if (!(sections->flags & START_DEFINED)) + sections->start = 0; + origin = sections->start; + } + sections->flags |= START_DEFINED; + + /* Make sure each section has an explicit start address. If it + * doesn't, then compute one based its alignment and the end of + * the previous section. */ + for (pend = sections->start, g = s = sections; g; g = g->next) { /* Find the next section that could cause an overlap situation + * (has a defined start address, and is not zero length). */ + if (g == s) + for (s = g->next; + s && ((s->length == 0) || !(s->flags & START_DEFINED)); + s = s->next) ; + /* Compute the start address of this section, if necessary. */ + if (!(g->flags & START_DEFINED)) { /* Default to an alignment of 4 if unspecified. */ + if (!(g->flags & ALIGN_DEFINED)) { + g->align = 4; + g->flags |= ALIGN_DEFINED; + } + /* Set the section start address. */ + g->start = ALIGN(pend, g->align); + g->flags |= START_DEFINED; + } + /* Ugly special case for progbits sections' virtual attributes: + * If there is a defined valign, but no vstart and no vfollows, then + * we valign after the previous progbits section. This case doesn't + * really make much sense for progbits sections with a defined start + * address, but it is possible and we must do *something*. + * Not-so-ugly special case: + * If a progbits section has no virtual attributes, we set the + * vstart equal to the start address. */ + if (!(g->flags & (VSTART_DEFINED | VFOLLOWS_DEFINED))) { + if (g->flags & VALIGN_DEFINED) + g->vstart = ALIGN(pend, g->valign); + else + g->vstart = g->start; + g->flags |= VSTART_DEFINED; + } + /* Ignore zero-length sections. */ + if (g->start < pend) + continue; + /* Compute the span of this section. */ + pend = g->start + g->length; + /* Check for section overlap. */ + if (s) { + if (s->start < origin) + nasm_fatal("section %s beings before program origin", + s->name); + if (g->start > s->start) + nasm_fatal("sections %s ~ %s and %s overlap!", + gs->name, g->name, s->name); + if (pend > s->start) + nasm_fatal("sections %s and %s overlap!", + g->name, s->name); + } + /* Remember this section as the latest >0 length section. */ + gs = g; + } + + /* Step 4: Compute vstart addresses for all sections. */ + + /* Attach the nobits sections to the end of the progbits sections. */ + for (s = sections; s->next; s = s->next) ; + s->next = nobits; + last_progbits = s; + /* + * Scan for sections that don't have a vstart address. If we find + * one we'll attempt to compute its vstart. If we can't compute + * the vstart, we leave it alone and come back to it in a + * subsequent scan. We continue scanning and re-scanning until + * we've gone one full cycle without computing any vstarts. + */ + do { /* Do one full scan of the sections list. */ + for (h = 0, g = sections; g; g = g->next) { + if (g->flags & VSTART_DEFINED) + continue; + /* Find the section that this one virtually follows. */ + if (g->flags & VFOLLOWS_DEFINED) { + for (s = sections; s && strcmp(g->vfollows, s->name); + s = s->next) ; + if (!s) + nasm_fatal("section %s vfollows unknown section (%s)", + g->name, g->vfollows); + } else if (g->prev != NULL) + for (s = sections; s && (s != g->prev); s = s->next) ; + /* The .bss section is the only one with prev = NULL. + In this case we implicitly follow the last progbits + section. */ + else + s = last_progbits; + + /* If the section we're following has a vstart, we can proceed. */ + if (s->flags & VSTART_DEFINED) { /* Default to virtual alignment of four. */ + if (!(g->flags & VALIGN_DEFINED)) { + g->valign = 4; + g->flags |= VALIGN_DEFINED; + } + /* Compute the vstart address. */ + g->vstart = ALIGN(s->vstart + s->length, g->valign); + g->flags |= VSTART_DEFINED; + h++; + /* Start and vstart mean the same thing for nobits sections. */ + if (g->flags & TYPE_NOBITS) + g->start = g->vstart; + } + } + } while (h); + + /* Now check for any circular vfollows references, which will manifest + * themselves as sections without a defined vstart. */ + for (h = 0, s = sections; s; s = s->next) { + if (!(s->flags & VSTART_DEFINED)) { /* Non-fatal errors after assembly has completed are generally a + * no-no, but we'll throw a fatal one eventually so it's ok. */ + nasm_nonfatal("cannot compute vstart for section %s", s->name); + h++; + } + } + if (h) + nasm_fatal("circular vfollows path detected"); + + if (debug_level(1)) { + nasm_debug("bin_cleanup: Confirm final section order for output file:\n"); + for (h = 0, s = sections; s && (s->flags & TYPE_PROGBITS); + h++, s = s->next) + nasm_debug("%i. %s\n", h, s->name); + } + + /* Step 5: Apply relocations. */ + + /* Prepare the sections for relocating. */ + list_for_each(s, sections) + saa_rewind(s->contents); + /* Apply relocations. */ + list_for_each(r, relocs) { + uint8_t *p, mydata[8]; + int64_t l; + int b; + + nasm_assert(r->bytes <= 8); + + memset(mydata, 0, sizeof(mydata)); + + saa_fread(r->target->contents, r->posn, mydata, r->bytes); + p = mydata; + l = 0; + for (b = r->bytes - 1; b >= 0; b--) + l = (l << 8) + mydata[b]; + + s = find_section_by_index(r->secref); + if (s) { + if (r->secref == s->start_index) + l += s->start; + else + l += s->vstart; + } + s = find_section_by_index(r->secrel); + if (s) { + if (r->secrel == s->start_index) + l -= s->start; + else + l -= s->vstart; + } + + WRITEADDR(p, l, r->bytes); + saa_fwrite(r->target->contents, r->posn, mydata, r->bytes); + } + + /* Step 6: Write the section data to the output file. */ + do_output(); + + /* Step 7: Generate the map file. */ + + if (map_control) { + static const char not_defined[] = "not defined"; + + /* Display input and output file names. */ + fprintf(rf, "\n- NASM Map file "); + for (h = 63; h; h--) + fputc('-', rf); + fprintf(rf, "\n\nSource file: %s\nOutput file: %s\n\n", + inname, outname); + + if (map_control & MAP_ORIGIN) { /* Display program origin. */ + fprintf(rf, "-- Program origin "); + for (h = 61; h; h--) + fputc('-', rf); + fprintf(rf, "\n\n%08"PRIX64"\n\n", origin); + } + /* Display sections summary. */ + if (map_control & MAP_SUMMARY) { + fprintf(rf, "-- Sections (summary) "); + for (h = 57; h; h--) + fputc('-', rf); + fprintf(rf, "\n\nVstart Start Stop " + "Length Class Name\n"); + list_for_each(s, sections) { + fprintf(rf, "%16"PRIX64" %16"PRIX64" %16"PRIX64" %08"PRIX64" ", + s->vstart, s->start, s->start + s->length, + s->length); + if (s->flags & TYPE_PROGBITS) + fprintf(rf, "progbits "); + else + fprintf(rf, "nobits "); + fprintf(rf, "%s\n", s->name); + } + fprintf(rf, "\n"); + } + /* Display detailed section information. */ + if (map_control & MAP_SECTIONS) { + fprintf(rf, "-- Sections (detailed) "); + for (h = 56; h; h--) + fputc('-', rf); + fprintf(rf, "\n\n"); + list_for_each(s, sections) { + fprintf(rf, "---- Section %s ", s->name); + if (strlen(s->name) < 65) + for (h = 65 - strlen(s->name); h; h--) + fputc('-', rf); + fprintf(rf, "\n\nclass: "); + if (s->flags & TYPE_PROGBITS) + fprintf(rf, "progbits"); + else + fprintf(rf, "nobits"); + fprintf(rf, "\nlength: %16"PRIX64"\nstart: %16"PRIX64"" + "\nalign: ", s->length, s->start); + if (s->flags & ALIGN_DEFINED) + fprintf(rf, "%16"PRIX64"", s->align); + else + fputs(not_defined, rf); + fprintf(rf, "\nfollows: "); + if (s->flags & FOLLOWS_DEFINED) + fprintf(rf, "%s", s->follows); + else + fputs(not_defined, rf); + fprintf(rf, "\nvstart: %16"PRIX64"\nvalign: ", s->vstart); + if (s->flags & VALIGN_DEFINED) + fprintf(rf, "%16"PRIX64"", s->valign); + else + fputs(not_defined, rf); + fprintf(rf, "\nvfollows: "); + if (s->flags & VFOLLOWS_DEFINED) + fprintf(rf, "%s", s->vfollows); + else + fputs(not_defined, rf); + fprintf(rf, "\n\n"); + } + } + /* Display symbols information. */ + if (map_control & MAP_SYMBOLS) { + int32_t segment; + int64_t offset; + enum label_type found_label; + + fprintf(rf, "-- Symbols "); + for (h = 68; h; h--) + fputc('-', rf); + fprintf(rf, "\n\n"); + if (no_seg_labels) { + fprintf(rf, "---- No Section "); + for (h = 63; h; h--) + fputc('-', rf); + fprintf(rf, "\n\nValue Name\n"); + list_for_each(l, no_seg_labels) { + found_label = lookup_label(l->name, &segment, &offset); + nasm_assert(found_label != LBL_none); + fprintf(rf, "%08"PRIX64" %s\n", offset, l->name); + } + fprintf(rf, "\n\n"); + } + list_for_each(s, sections) { + if (s->labels) { + fprintf(rf, "---- Section %s ", s->name); + for (h = 65 - strlen(s->name); h; h--) + fputc('-', rf); + fprintf(rf, "\n\nReal Virtual Name\n"); + list_for_each(l, s->labels) { + found_label = lookup_label(l->name, &segment, &offset); + nasm_assert(found_label != LBL_none); + fprintf(rf, "%16"PRIX64" %16"PRIX64" %s\n", + s->start + offset, s->vstart + offset, + l->name); + } + fprintf(rf, "\n"); + } + } + } + } + + /* Close the report file. */ + if (map_control && (rf != stdout) && (rf != stderr)) + fclose(rf); + + /* Step 8: Release all allocated memory. */ + + /* Free sections, label pointer structs, etc.. */ + while (sections) { + s = sections; + sections = s->next; + saa_free(s->contents); + nasm_free(s->name); + if (s->flags & FOLLOWS_DEFINED) + nasm_free(s->follows); + if (s->flags & VFOLLOWS_DEFINED) + nasm_free(s->vfollows); + while (s->labels) { + l = s->labels; + s->labels = l->next; + nasm_free(l); + } + nasm_free(s); + } + + /* Free no-section labels. */ + while (no_seg_labels) { + l = no_seg_labels; + no_seg_labels = l->next; + nasm_free(l); + } + + /* Free relocation structures. */ + while (relocs) { + r = relocs->next; + nasm_free(relocs); + relocs = r; + } +} + +static void bin_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + uint8_t *p, mydata[8]; + struct Section *s; + + if (wrt != NO_SEG) { + wrt = NO_SEG; /* continue to do _something_ */ + nasm_nonfatal("WRT not supported by binary output format"); + } + + /* Find the segment we are targeting. */ + s = find_section_by_index(segto); + if (!s) + nasm_panic("code directed to nonexistent segment?"); + + /* "Smart" section-type adaptation code. */ + if (!(s->flags & TYPE_DEFINED)) { + if (type == OUT_RESERVE) + s->flags |= TYPE_DEFINED | TYPE_NOBITS; + else + s->flags |= TYPE_DEFINED | TYPE_PROGBITS; + } + + if ((s->flags & TYPE_NOBITS) && (type != OUT_RESERVE)) + nasm_warn(WARN_OTHER, "attempt to initialize memory in a" + " nobits section: ignored"); + + switch (type) { + case OUT_ADDRESS: + { + int asize = abs((int)size); + + if (segment != NO_SEG && !find_section_by_index(segment)) { + if (segment % 2) + nasm_nonfatal("binary output format does not support" + " segment base references"); + else + nasm_nonfatal("binary output format does not support" + " external references"); + segment = NO_SEG; + } + if (s->flags & TYPE_PROGBITS) { + if (segment != NO_SEG) + add_reloc(s, asize, segment, -1L); + p = mydata; + WRITEADDR(p, *(int64_t *)data, asize); + saa_wbytes(s->contents, mydata, asize); + } + + /* + * Reassign size with sign dropped, we will need it + * for section length calculation. + */ + size = asize; + break; + } + + case OUT_RAWDATA: + if (s->flags & TYPE_PROGBITS) + saa_wbytes(s->contents, data, size); + break; + + case OUT_RESERVE: + if (s->flags & TYPE_PROGBITS) { + nasm_warn(WARN_ZEROING, "uninitialized space declared in" + " %s section: zeroing", s->name); + saa_wbytes(s->contents, NULL, size); + } + break; + + case OUT_REL1ADR: + case OUT_REL2ADR: + case OUT_REL4ADR: + case OUT_REL8ADR: + { + int64_t addr = *(int64_t *)data - size; + size = realsize(type, size); + if (segment != NO_SEG && !find_section_by_index(segment)) { + if (segment % 2) + nasm_nonfatal("binary output format does not support" + " segment base references"); + else + nasm_nonfatal("binary output format does not support" + " external references"); + segment = NO_SEG; + } + if (s->flags & TYPE_PROGBITS) { + add_reloc(s, size, segment, segto); + p = mydata; + WRITEADDR(p, addr - s->length, size); + saa_wbytes(s->contents, mydata, size); + } + break; + } + + default: + nasm_nonfatal("unsupported relocation type %d\n", type); + break; + } + + s->length += size; +} + +static void bin_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special) +{ + (void)segment; /* Don't warn that this parameter is unused */ + (void)offset; /* Don't warn that this parameter is unused */ + + if (special) + nasm_nonfatal("binary format does not support any" + " special symbol types"); + else if (name[0] == '.' && name[1] == '.' && name[2] != '@') + nasm_nonfatal("unrecognised special symbol `%s'", name); + else if (is_global == 2) + nasm_nonfatal("binary output format does not support common" + " variables"); + else { + struct Section *s; + struct bin_label ***ltp; + + /* Remember label definition so we can look it up later when + * creating the map file. */ + s = find_section_by_index(segment); + if (s) + ltp = &(s->labels_end); + else + ltp = &nsl_tail; + (**ltp) = nasm_malloc(sizeof(struct bin_label)); + (**ltp)->name = name; + (**ltp)->next = NULL; + *ltp = &((**ltp)->next); + } + +} + +/* These constants and the following function are used + * by bin_secname() to parse attribute assignments. */ + +enum { ATTRIB_START, ATTRIB_ALIGN, ATTRIB_FOLLOWS, + ATTRIB_VSTART, ATTRIB_VALIGN, ATTRIB_VFOLLOWS, + ATTRIB_NOBITS, ATTRIB_PROGBITS +}; + +static int bin_read_attribute(char **line, int *attribute, + uint64_t *value) +{ + expr *e; + int attrib_name_size; + struct tokenval tokval; + char *exp; + + /* Skip whitespace. */ + while (**line && nasm_isspace(**line)) + (*line)++; + if (!**line) + return 0; + + /* Figure out what attribute we're reading. */ + if (!nasm_strnicmp(*line, "align=", 6)) { + *attribute = ATTRIB_ALIGN; + attrib_name_size = 6; + } else { + if (!nasm_strnicmp(*line, "start=", 6)) { + *attribute = ATTRIB_START; + attrib_name_size = 6; + } else if (!nasm_strnicmp(*line, "follows=", 8)) { + *attribute = ATTRIB_FOLLOWS; + *line += 8; + return 1; + } else if (!nasm_strnicmp(*line, "vstart=", 7)) { + *attribute = ATTRIB_VSTART; + attrib_name_size = 7; + } else if (!nasm_strnicmp(*line, "valign=", 7)) { + *attribute = ATTRIB_VALIGN; + attrib_name_size = 7; + } else if (!nasm_strnicmp(*line, "vfollows=", 9)) { + *attribute = ATTRIB_VFOLLOWS; + *line += 9; + return 1; + } else if (!nasm_strnicmp(*line, "nobits", 6) && + (nasm_isspace((*line)[6]) || ((*line)[6] == '\0'))) { + *attribute = ATTRIB_NOBITS; + *line += 6; + return 1; + } else if (!nasm_strnicmp(*line, "progbits", 8) && + (nasm_isspace((*line)[8]) || ((*line)[8] == '\0'))) { + *attribute = ATTRIB_PROGBITS; + *line += 8; + return 1; + } else + return 0; + } + + /* Find the end of the expression. */ + if ((*line)[attrib_name_size] != '(') { + /* Single term (no parenthesis). */ + exp = *line += attrib_name_size; + while (**line && !nasm_isspace(**line)) + (*line)++; + if (**line) { + **line = '\0'; + (*line)++; + } + } else { + char c; + int pcount = 1; + + /* Full expression (delimited by parenthesis) */ + exp = *line += attrib_name_size + 1; + while (1) { + (*line) += strcspn(*line, "()'\""); + if (**line == '(') { + ++(*line); + ++pcount; + } + if (**line == ')') { + ++(*line); + --pcount; + if (!pcount) + break; + } + if ((**line == '"') || (**line == '\'')) { + c = **line; + while (**line) { + ++(*line); + if (**line == c) + break; + } + if (!**line) { + nasm_nonfatal("invalid syntax in `section' directive"); + return -1; + } + ++(*line); + } + if (!**line) { + nasm_nonfatal("expecting `)'"); + return -1; + } + } + *(*line - 1) = '\0'; /* Terminate the expression. */ + } + + /* Check for no value given. */ + if (!*exp) { + nasm_warn(WARN_OTHER, "No value given to attribute in" + " `section' directive"); + return -1; + } + + /* Read and evaluate the expression. */ + stdscan_reset(); + stdscan_set(exp); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, NULL, 1, NULL); + if (e) { + if (!is_really_simple(e)) { + nasm_nonfatal("section attribute value must be" + " a critical expression"); + return -1; + } + } else { + nasm_nonfatal("Invalid attribute value" + " specified in `section' directive."); + return -1; + } + *value = (uint64_t)reloc_value(e); + return 1; +} + +static void bin_sectalign(int32_t seg, unsigned int value) +{ + struct Section *s = find_section_by_index(seg); + + if (!s || !is_power2(value)) + return; + + if (value > s->align) + s->align = value; + + if (!(s->flags & ALIGN_DEFINED)) + s->flags |= ALIGN_DEFINED; +} + +static void bin_assign_attributes(struct Section *sec, char *astring) +{ + int attribute, check; + uint64_t value; + char *p; + + while (1) { /* Get the next attribute. */ + check = bin_read_attribute(&astring, &attribute, &value); + /* Skip bad attribute. */ + if (check == -1) + continue; + /* Unknown section attribute, so skip it and warn the user. */ + if (!check) { + if (!*astring) + break; /* End of line. */ + else { + p = astring; + while (*astring && !nasm_isspace(*astring)) + astring++; + if (*astring) { + *astring = '\0'; + astring++; + } + nasm_warn(WARN_OTHER, "ignoring unknown section attribute: \"%s\"", p); + } + continue; + } + + switch (attribute) { /* Handle nobits attribute. */ + case ATTRIB_NOBITS: + if ((sec->flags & TYPE_DEFINED) + && (sec->flags & TYPE_PROGBITS)) + nasm_nonfatal("attempt to change section type" + " from progbits to nobits"); + else + sec->flags |= TYPE_DEFINED | TYPE_NOBITS; + continue; + + /* Handle progbits attribute. */ + case ATTRIB_PROGBITS: + if ((sec->flags & TYPE_DEFINED) && (sec->flags & TYPE_NOBITS)) + nasm_nonfatal("attempt to change section type" + " from nobits to progbits"); + else + sec->flags |= TYPE_DEFINED | TYPE_PROGBITS; + continue; + + /* Handle align attribute. */ + case ATTRIB_ALIGN: + if (!value || ((value - 1) & value)) { + nasm_nonfatal("argument to `align' is not a power of two"); + } else { + /* + * Alignment is already satisfied if + * the previous align value is greater + */ + if ((sec->flags & ALIGN_DEFINED) && (value < sec->align)) + value = sec->align; + + /* Don't allow a conflicting align value. */ + if ((sec->flags & START_DEFINED) && (sec->start & (value - 1))) { + nasm_nonfatal("`align' value conflicts with section start address"); + } else { + sec->align = value; + sec->flags |= ALIGN_DEFINED; + } + } + continue; + + /* Handle valign attribute. */ + case ATTRIB_VALIGN: + if (!value || ((value - 1) & value)) + nasm_nonfatal("argument to `valign' is not a power of two"); + else { /* Alignment is already satisfied if the previous + * align value is greater. */ + if ((sec->flags & VALIGN_DEFINED) && (value < sec->valign)) + value = sec->valign; + + /* Don't allow a conflicting valign value. */ + if ((sec->flags & VSTART_DEFINED) + && (sec->vstart & (value - 1))) + nasm_nonfatal("`valign' value conflicts with `vstart' address"); + else { + sec->valign = value; + sec->flags |= VALIGN_DEFINED; + } + } + continue; + + /* Handle start attribute. */ + case ATTRIB_START: + if (sec->flags & FOLLOWS_DEFINED) + nasm_nonfatal("cannot combine `start' and `follows'" + " section attributes"); + else if ((sec->flags & START_DEFINED) && (value != sec->start)) + nasm_nonfatal("section start address redefined"); + else { + sec->start = value; + sec->flags |= START_DEFINED; + if (sec->flags & ALIGN_DEFINED) { + if (sec->start & (sec->align - 1)) + nasm_nonfatal("`start' address conflicts" + " with section alignment"); + sec->flags ^= ALIGN_DEFINED; + } + } + continue; + + /* Handle vstart attribute. */ + case ATTRIB_VSTART: + if (sec->flags & VFOLLOWS_DEFINED) + nasm_nonfatal("cannot combine `vstart' and `vfollows'" + " section attributes"); + else if ((sec->flags & VSTART_DEFINED) + && (value != sec->vstart)) + nasm_nonfatal("section virtual start address" + " (vstart) redefined"); + else { + sec->vstart = value; + sec->flags |= VSTART_DEFINED; + if (sec->flags & VALIGN_DEFINED) { + if (sec->vstart & (sec->valign - 1)) + nasm_nonfatal("`vstart' address conflicts" + " with `valign' value"); + sec->flags ^= VALIGN_DEFINED; + } + } + continue; + + /* Handle follows attribute. */ + case ATTRIB_FOLLOWS: + p = astring; + astring += strcspn(astring, " \t"); + if (astring == p) + nasm_nonfatal("expecting section name for `follows'" + " attribute"); + else { + *(astring++) = '\0'; + if (sec->flags & START_DEFINED) + nasm_nonfatal("cannot combine `start' and `follows'" + " section attributes"); + sec->follows = nasm_strdup(p); + sec->flags |= FOLLOWS_DEFINED; + } + continue; + + /* Handle vfollows attribute. */ + case ATTRIB_VFOLLOWS: + if (sec->flags & VSTART_DEFINED) + nasm_nonfatal("cannot combine `vstart' and `vfollows'" + " section attributes"); + else { + p = astring; + astring += strcspn(astring, " \t"); + if (astring == p) + nasm_nonfatal("expecting section name for `vfollows'" + " attribute"); + else { + *(astring++) = '\0'; + sec->vfollows = nasm_strdup(p); + sec->flags |= VFOLLOWS_DEFINED; + } + } + continue; + } + } +} + +static void bin_define_section_labels(void) +{ + static int labels_defined = 0; + struct Section *sec; + char *label_name; + size_t base_len; + + if (labels_defined) + return; + list_for_each(sec, sections) { + base_len = strlen(sec->name) + 8; + label_name = nasm_malloc(base_len + 8); + strcpy(label_name, "section."); + strcpy(label_name + 8, sec->name); + + /* section.<name>.start */ + strcpy(label_name + base_len, ".start"); + define_label(label_name, sec->start_index, 0L, false); + + /* section.<name>.vstart */ + strcpy(label_name + base_len, ".vstart"); + define_label(label_name, sec->vstart_index, 0L, false); + + nasm_free(label_name); + } + labels_defined = 1; +} + +static int32_t bin_secname(char *name, int *bits) +{ + char *p; + struct Section *sec; + + /* bin_secname is called with *name = NULL at the start of each + * pass. Use this opportunity to establish the default section + * (default is BITS-16 ".text" segment). + */ + if (!name) { + /* Reset ORG and section attributes at the start of each pass. */ + origin_defined = 0; + list_for_each(sec, sections) + sec->flags &= ~(START_DEFINED | VSTART_DEFINED | + ALIGN_DEFINED | VALIGN_DEFINED); + + /* Define section start and vstart labels. */ + if (!pass_first()) + bin_define_section_labels(); + + /* Establish the default (.text) section. */ + *bits = 16; + sec = find_section_by_name(".text"); + sec->flags |= TYPE_DEFINED | TYPE_PROGBITS; + return sec->vstart_index; + } + + /* Attempt to find the requested section. If it does not + * exist, create it. */ + p = name; + while (*p && !nasm_isspace(*p)) + p++; + if (*p) + *p++ = '\0'; + sec = find_section_by_name(name); + if (!sec) { + sec = create_section(name); + if (!strcmp(name, ".data")) + sec->flags |= TYPE_DEFINED | TYPE_PROGBITS; + else if (!strcmp(name, ".bss")) { + sec->flags |= TYPE_DEFINED | TYPE_NOBITS; + sec->prev = NULL; + } + } + + /* Handle attribute assignments. */ + if (!pass_first()) + bin_assign_attributes(sec, p); + +#ifndef ABIN_SMART_ADAPT + /* The following line disables smart adaptation of + * PROGBITS/NOBITS section types (it forces sections to + * default to PROGBITS). */ + if (!pass_first() && !(sec->flags & TYPE_DEFINED)) + sec->flags |= TYPE_DEFINED | TYPE_PROGBITS; +#endif + + return sec->vstart_index; +} + +static enum directive_result +bin_directive(enum directive directive, char *args) +{ + switch (directive) { + case D_ORG: + { + struct tokenval tokval; + uint64_t value; + expr *e; + + stdscan_reset(); + stdscan_set(args); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, NULL, 1, NULL); + if (e) { + if (!is_really_simple(e)) + nasm_nonfatal("org value must be a critical" + " expression"); + else { + value = reloc_value(e); + /* Check for ORG redefinition. */ + if (origin_defined && (value != origin)) + nasm_nonfatal("program origin redefined"); + else { + origin = value; + origin_defined = 1; + } + } + } else + nasm_nonfatal("No or invalid offset specified" + " in ORG directive."); + return DIRR_OK; + } + case D_MAP: + { + /* The 'map' directive allows the user to generate section + * and symbol information to stdout, stderr, or to a file. */ + char *p; + + if (!pass_first()) + return DIRR_OK; + args += strspn(args, " \t"); + while (*args) { + p = args; + args += strcspn(args, " \t"); + if (*args != '\0') + *(args++) = '\0'; + if (!nasm_stricmp(p, "all")) + map_control |= + MAP_ORIGIN | MAP_SUMMARY | MAP_SECTIONS | MAP_SYMBOLS; + else if (!nasm_stricmp(p, "brief")) + map_control |= MAP_ORIGIN | MAP_SUMMARY; + else if (!nasm_stricmp(p, "sections")) + map_control |= MAP_ORIGIN | MAP_SUMMARY | MAP_SECTIONS; + else if (!nasm_stricmp(p, "segments")) + map_control |= MAP_ORIGIN | MAP_SUMMARY | MAP_SECTIONS; + else if (!nasm_stricmp(p, "symbols")) + map_control |= MAP_SYMBOLS; + else if (!rf) { + if (!nasm_stricmp(p, "stdout")) + rf = stdout; + else if (!nasm_stricmp(p, "stderr")) + rf = stderr; + else { /* Must be a filename. */ + rf = nasm_open_write(p, NF_TEXT); + if (!rf) { + nasm_warn(WARN_OTHER, "unable to open map file `%s'", p); + map_control = 0; + return DIRR_OK; + } + } + } else + nasm_warn(WARN_OTHER, "map file already specified"); + } + if (map_control == 0) + map_control |= MAP_ORIGIN | MAP_SUMMARY; + if (!rf) + rf = stdout; + return DIRR_OK; + } + default: + return DIRR_UNKNOWN; + } +} + +const struct ofmt of_bin, of_ith, of_srec; +static void binfmt_init(void); +static void do_output_bin(void); +static void do_output_ith(void); +static void do_output_srec(void); + +static void bin_init(void) +{ + do_output = do_output_bin; + binfmt_init(); +} + +static void ith_init(void) +{ + do_output = do_output_ith; + binfmt_init(); +} + +static void srec_init(void) +{ + do_output = do_output_srec; + binfmt_init(); +} + +static void binfmt_init(void) +{ + relocs = NULL; + reloctail = &relocs; + origin_defined = 0; + no_seg_labels = NULL; + nsl_tail = &no_seg_labels; + + /* Create default section (.text). */ + sections = last_section = nasm_zalloc(sizeof(struct Section)); + last_section->name = nasm_strdup(".text"); + last_section->contents = saa_init(1L); + last_section->flags = TYPE_DEFINED | TYPE_PROGBITS; + last_section->labels_end = &(last_section->labels); + last_section->start_index = seg_alloc(); + last_section->vstart_index = seg_alloc(); +} + +/* Generate binary file output */ +static void do_output_bin(void) +{ + struct Section *s; + uint64_t addr = origin; + + /* Write the progbits sections to the output file. */ + list_for_each(s, sections) { + /* Skip non-progbits sections */ + if (!(s->flags & TYPE_PROGBITS)) + continue; + /* Skip zero-length sections */ + if (s->length == 0) + continue; + + /* Pad the space between sections. */ + nasm_assert(addr <= s->start); + fwritezero(s->start - addr, ofile); + + /* Write the section to the output file. */ + saa_fpwrite(s->contents, ofile); + + /* Keep track of the current file position */ + addr = s->start + s->length; + } +} + +/* Generate Intel hex file output */ +static void write_ith_record(unsigned int len, uint16_t addr, + uint8_t type, void *data) +{ + char buf[1+2+4+2+255*2+2+2]; + char *p = buf; + uint8_t csum, *dptr = data; + unsigned int i; + + nasm_assert(len <= 255); + + csum = len + addr + (addr >> 8) + type; + for (i = 0; i < len; i++) + csum += dptr[i]; + csum = -csum; + + p += sprintf(p, ":%02X%04X%02X", len, addr, type); + for (i = 0; i < len; i++) + p += sprintf(p, "%02X", dptr[i]); + p += sprintf(p, "%02X\n", csum); + + nasm_write(buf, p-buf, ofile); +} + +static void do_output_ith(void) +{ + uint8_t buf[32]; + struct Section *s; + uint64_t addr, hiaddr, hilba; + uint64_t length; + unsigned int chunk; + + /* Write the progbits sections to the output file. */ + hilba = 0; + list_for_each(s, sections) { + /* Skip non-progbits sections */ + if (!(s->flags & TYPE_PROGBITS)) + continue; + /* Skip zero-length sections */ + if (s->length == 0) + continue; + + addr = s->start; + length = s->length; + saa_rewind(s->contents); + + while (length) { + hiaddr = addr >> 16; + if (hiaddr != hilba) { + buf[0] = hiaddr >> 8; + buf[1] = hiaddr; + write_ith_record(2, 0, 4, buf); + hilba = hiaddr; + } + + chunk = 32 - (addr & 31); + if (length < chunk) + chunk = length; + + saa_rnbytes(s->contents, buf, chunk); + write_ith_record(chunk, (uint16_t)addr, 0, buf); + + addr += chunk; + length -= chunk; + } + } + + /* Write closing record */ + write_ith_record(0, 0, 1, NULL); +} + +/* Generate Motorola S-records */ +static void write_srecord(unsigned int len, unsigned int alen, + uint32_t addr, uint8_t type, void *data) +{ + char buf[2+2+8+255*2+2+2]; + char *p = buf; + uint8_t csum, *dptr = data; + unsigned int i; + + nasm_assert(len <= 255); + + switch (alen) { + case 2: + addr &= 0xffff; + break; + case 3: + addr &= 0xffffff; + break; + case 4: + break; + default: + panic(); + break; + } + + csum = (len+alen+1) + addr + (addr >> 8) + (addr >> 16) + (addr >> 24); + for (i = 0; i < len; i++) + csum += dptr[i]; + csum = 0xff-csum; + + p += sprintf(p, "S%c%02X%0*X", type, len+alen+1, alen*2, addr); + for (i = 0; i < len; i++) + p += sprintf(p, "%02X", dptr[i]); + p += sprintf(p, "%02X\n", csum); + + nasm_write(buf, p-buf, ofile); +} + +static void do_output_srec(void) +{ + uint8_t buf[32]; + struct Section *s; + uint64_t addr, maxaddr; + uint64_t length; + int alen; + unsigned int chunk; + char dtype, etype; + + maxaddr = 0; + list_for_each(s, sections) { + /* Skip non-progbits sections */ + if (!(s->flags & TYPE_PROGBITS)) + continue; + /* Skip zero-length sections */ + if (s->length == 0) + continue; + + addr = s->start + s->length - 1; + if (addr > maxaddr) + maxaddr = addr; + } + + if (maxaddr <= 0xffff) { + alen = 2; + dtype = '1'; /* S1 = 16-bit data */ + etype = '9'; /* S9 = 16-bit end */ + } else if (maxaddr <= 0xffffff) { + alen = 3; + dtype = '2'; /* S2 = 24-bit data */ + etype = '8'; /* S8 = 24-bit end */ + } else { + alen = 4; + dtype = '3'; /* S3 = 32-bit data */ + etype = '7'; /* S7 = 32-bit end */ + } + + /* Write head record */ + write_srecord(0, 2, 0, '0', NULL); + + /* Write the progbits sections to the output file. */ + list_for_each(s, sections) { + /* Skip non-progbits sections */ + if (!(s->flags & TYPE_PROGBITS)) + continue; + /* Skip zero-length sections */ + if (s->length == 0) + continue; + + addr = s->start; + length = s->length; + saa_rewind(s->contents); + + while (length) { + chunk = 32 - (addr & 31); + if (length < chunk) + chunk = length; + + saa_rnbytes(s->contents, buf, chunk); + write_srecord(chunk, alen, (uint32_t)addr, dtype, buf); + + addr += chunk; + length -= chunk; + } + } + + /* Write closing record */ + write_srecord(0, alen, 0, etype, NULL); +} + + +const struct ofmt of_bin = { + "Flat raw binary (MS-DOS, embedded, ...)", + "bin", + "", + 0, + 64, + null_debug_arr, + &null_debug_form, + bin_stdmac, + bin_init, + null_reset, + nasm_do_legacy_output, + bin_out, + bin_deflabel, + bin_secname, + NULL, + bin_sectalign, + null_segbase, + bin_directive, + bin_cleanup, + NULL /* pragma list */ +}; + +const struct ofmt of_ith = { + "Intel Hex encoded flat binary", + "ith", + ".ith", /* really should have been ".hex"... */ + OFMT_TEXT, + 64, + null_debug_arr, + &null_debug_form, + bin_stdmac, + ith_init, + null_reset, + nasm_do_legacy_output, + bin_out, + bin_deflabel, + bin_secname, + NULL, + bin_sectalign, + null_segbase, + bin_directive, + bin_cleanup, + NULL /* pragma list */ +}; + +const struct ofmt of_srec = { + "Motorola S-records encoded flat binary", + "srec", + ".srec", + OFMT_TEXT, + 64, + null_debug_arr, + &null_debug_form, + bin_stdmac, + srec_init, + null_reset, + nasm_do_legacy_output, + bin_out, + bin_deflabel, + bin_secname, + NULL, + bin_sectalign, + null_segbase, + bin_directive, + bin_cleanup, + NULL /* pragma list */ +}; + +#endif /* #ifdef OF_BIN */ diff --git a/vere/ext/nasm/output/outbin.mac b/vere/ext/nasm/output/outbin.mac new file mode 100644 index 0000000..99c15b6 --- /dev/null +++ b/vere/ext/nasm/output/outbin.mac @@ -0,0 +1,40 @@ +;; -------------------------------------------------------------------------- +;; +;; Copyright 1996-2009 The NASM Authors - All Rights Reserved +;; See the file AUTHORS included with the NASM distribution for +;; the specific copyright holders. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following +;; conditions are met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following +;; disclaimer in the documentation and/or other materials provided +;; with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +;; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +;; EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;; +;; -------------------------------------------------------------------------- + +OUT: bin +%define __?SECT?__ [section .text] +%imacro org 1+.nolist +[org %1] +%endmacro +%macro __?NASM_CDecl?__ 1 +%endmacro diff --git a/vere/ext/nasm/output/outcoff.c b/vere/ext/nasm/output/outcoff.c new file mode 100644 index 0000000..c2b4eb6 --- /dev/null +++ b/vere/ext/nasm/output/outcoff.c @@ -0,0 +1,1436 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2020 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outcoff.c output routines for the Netwide Assembler to produce + * COFF object files (for DJGPP and Win32) + */ + +#include "compiler.h" + +#include "nctype.h" +#include <time.h> +#include "ver.h" + +#include "nasm.h" +#include "nasmlib.h" +#include "ilog2.h" +#include "error.h" +#include "saa.h" +#include "raa.h" +#include "eval.h" +#include "outform.h" +#include "outlib.h" +#include "pecoff.h" + +#if defined(OF_COFF) || defined(OF_WIN32) || defined(OF_WIN64) + +/* + * Notes on COFF: + * + * (0) When I say `standard COFF' below, I mean `COFF as output and + * used by DJGPP'. I assume DJGPP gets it right. + * + * (1) Win32 appears to interpret the term `relative relocation' + * differently from standard COFF. Standard COFF understands a + * relative relocation to mean that during relocation you add the + * address of the symbol you're referencing, and subtract the base + * address of the section you're in. Win32 COFF, by contrast, seems + * to add the address of the symbol and then subtract the address + * of THE BYTE AFTER THE RELOCATED DWORD. Hence the two formats are + * subtly incompatible. + * + * (2) Win32 doesn't bother putting any flags in the header flags + * field (at offset 0x12 into the file). + * + * (3) Win32/64 uses some extra flags into the section header table: + * it defines flags 0x80000000 (writable), 0x40000000 (readable) + * and 0x20000000 (executable), and uses them in the expected + * combinations. It also defines 0x00100000 through 0x00f00000 for + * section alignments of 1 through 8192 bytes. + * + * (4) Both standard COFF and Win32 COFF seem to use the DWORD + * field directly after the section name in the section header + * table for something strange: they store what the address of the + * section start point _would_ be, if you laid all the sections end + * to end starting at zero. Dunno why. Microsoft's documentation + * lists this field as "Virtual Size of Section", which doesn't + * seem to fit at all. In fact, Win32 even includes non-linked + * sections such as .drectve in this calculation. + * + * Newer versions of MASM seem to have changed this to be zero, and + * that apparently matches the COFF spec, so go with that. + * + * (5) Standard COFF does something very strange to common + * variables: the relocation point for a common variable is as far + * _before_ the variable as its size stretches out _after_ it. So + * we must fix up common variable references. Win32 seems to be + * sensible on this one. + */ + +/* Flag which version of COFF we are currently outputting. */ +bool win32, win64; + +static int32_t imagebase_sect; +#define WRT_IMAGEBASE "..imagebase" + +/* + * Some common section flags by default + */ +#define TEXT_FLAGS_WIN \ + (IMAGE_SCN_CNT_CODE | \ + IMAGE_SCN_ALIGN_16BYTES | \ + IMAGE_SCN_MEM_EXECUTE | \ + IMAGE_SCN_MEM_READ) +#define TEXT_FLAGS_DOS \ + (IMAGE_SCN_CNT_CODE) + +#define DATA_FLAGS_WIN \ + (IMAGE_SCN_CNT_INITIALIZED_DATA | \ + IMAGE_SCN_ALIGN_4BYTES | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_WRITE) +#define DATA_FLAGS_DOS \ + (IMAGE_SCN_CNT_INITIALIZED_DATA) + +#define BSS_FLAGS_WIN \ + (IMAGE_SCN_CNT_UNINITIALIZED_DATA | \ + IMAGE_SCN_ALIGN_4BYTES | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_WRITE) +#define BSS_FLAGS_DOS \ + (IMAGE_SCN_CNT_UNINITIALIZED_DATA) + +#define RDATA_FLAGS_WIN \ + (IMAGE_SCN_CNT_INITIALIZED_DATA | \ + IMAGE_SCN_ALIGN_8BYTES | \ + IMAGE_SCN_MEM_READ) + +#define RDATA_FLAGS_DOS \ + (IMAGE_SCN_CNT_INITIALIZED_DATA) + +#define PDATA_FLAGS \ + (IMAGE_SCN_CNT_INITIALIZED_DATA | \ + IMAGE_SCN_ALIGN_4BYTES | \ + IMAGE_SCN_MEM_READ) + +#define XDATA_FLAGS \ + (IMAGE_SCN_CNT_INITIALIZED_DATA | \ + IMAGE_SCN_ALIGN_8BYTES | \ + IMAGE_SCN_MEM_READ) + +#define INFO_FLAGS \ + (IMAGE_SCN_ALIGN_1BYTES | \ + IMAGE_SCN_LNK_INFO | \ + IMAGE_SCN_LNK_REMOVE) + +#define TEXT_FLAGS ((win32 | win64) ? TEXT_FLAGS_WIN : TEXT_FLAGS_DOS) +#define DATA_FLAGS ((win32 | win64) ? DATA_FLAGS_WIN : DATA_FLAGS_DOS) +#define BSS_FLAGS ((win32 | win64) ? BSS_FLAGS_WIN : BSS_FLAGS_DOS) +#define RDATA_FLAGS ((win32 | win64) ? RDATA_FLAGS_WIN : RDATA_FLAGS_DOS) + +#define COFF_MAX_ALIGNMENT 8192 + +#define SECT_DELTA 32 +struct coff_Section **coff_sects; +static int sectlen; +int coff_nsects; + +struct SAA *coff_syms; +uint32_t coff_nsyms; + +static int32_t def_seg; + +static int initsym; + +static struct RAA *bsym, *symval; + +struct SAA *coff_strs; +static uint32_t strslen; + +static void coff_gen_init(void); +static void coff_sect_write(struct coff_Section *, const uint8_t *, uint32_t); +static void coff_write(void); +static void coff_section_header(char *, int32_t, int32_t, int32_t, int32_t, int32_t, int, int32_t); +static void coff_write_relocs(struct coff_Section *); +static void coff_write_symbols(void); +static void coff_defcomdatname(char *name, int32_t segment); + +#define COMDAT_PLACEHOLDER_NAME ".tmpcmdt" + +static void coff_win32_init(void) +{ + win32 = true; + win64 = false; + coff_gen_init(); +} + +static void coff_win64_init(void) +{ + win32 = false; + win64 = true; + coff_gen_init(); + imagebase_sect = seg_alloc()+1; + backend_label(WRT_IMAGEBASE, imagebase_sect, 0); +} + +static void coff_std_init(void) +{ + win32 = win64 = false; + coff_gen_init(); +} + +static void coff_gen_init(void) +{ + + coff_sects = NULL; + coff_nsects = sectlen = 0; + coff_syms = saa_init(sizeof(struct coff_Symbol)); + coff_nsyms = 0; + bsym = raa_init(); + symval = raa_init(); + coff_strs = saa_init(1); + strslen = 0; + def_seg = seg_alloc(); +} + +static void coff_cleanup(void) +{ + struct coff_Reloc *r; + int i; + + dfmt->cleanup(); + + coff_write(); + for (i = 0; i < coff_nsects; i++) { + if (coff_sects[i]->data) + saa_free(coff_sects[i]->data); + while (coff_sects[i]->head) { + r = coff_sects[i]->head; + coff_sects[i]->head = coff_sects[i]->head->next; + nasm_free(r); + } + nasm_free(coff_sects[i]->name); + nasm_free(coff_sects[i]->comdat_name); + nasm_free(coff_sects[i]); + } + nasm_free(coff_sects); + saa_free(coff_syms); + raa_free(bsym); + raa_free(symval); + saa_free(coff_strs); +} + +int coff_make_section(char *name, uint32_t flags) +{ + struct coff_Section *s; + size_t namelen; + + s = nasm_zalloc(sizeof(*s)); + + if (flags != BSS_FLAGS) + s->data = saa_init(1); + s->tail = &s->head; + if (!strcmp(name, ".text")) + s->index = def_seg; + else + s->index = seg_alloc(); + s->namepos = -1; + namelen = strlen(name); + if (namelen > 8) { + if (win32 || win64) { + s->namepos = strslen + 4; + saa_wbytes(coff_strs, name, namelen + 1); + strslen += namelen + 1; + } else { + namelen = 8; + } + } + s->name = nasm_malloc(namelen + 1); + strncpy(s->name, name, namelen); + s->name[namelen] = '\0'; + s->flags = flags; + + if (coff_nsects >= sectlen) { + sectlen += SECT_DELTA; + coff_sects = nasm_realloc(coff_sects, sectlen * sizeof(*coff_sects)); + } + coff_sects[coff_nsects++] = s; + + return coff_nsects - 1; +} + +/* + * Update the name and flags of an existing section + */ +static void coff_update_section(int section, char *name, uint32_t flags) +{ + struct coff_Section *s = coff_sects[section]; + size_t namelen = strlen(name); + + if (namelen > 8) { + if (win32 || win64) { + s->namepos = strslen + 4; + saa_wbytes(coff_strs, name, namelen + 1); + strslen += namelen + 1; + } else { + namelen = 8; + } + } + + nasm_free(s->name); + s->name = nasm_malloc(namelen + 1); + strncpy(s->name, name, namelen); + s->name[namelen] = '\0'; + s->flags = flags; +} + +/* + * Convert an alignment value to the corresponding flags. + * An alignment value of 0 means no flags should be set. + */ +static inline uint32_t coff_sectalign_flags(unsigned int align) +{ + return (alignlog2_32(align) + 1) << 20; +} + +/* + * Get the default section flags (based on section name) + */ +static uint32_t coff_section_flags(char *name, uint32_t flags) +{ + if (!flags) { + flags = TEXT_FLAGS; + + if (!strcmp(name, ".data")) { + flags = DATA_FLAGS; + } else if (!strcmp(name, ".rdata")) { + flags = RDATA_FLAGS; + } else if (!strcmp(name, ".bss")) { + flags = BSS_FLAGS; + } else if (win64) { + if (!strcmp(name, ".pdata")) + flags = PDATA_FLAGS; + else if (!strcmp(name, ".xdata")) + flags = XDATA_FLAGS; + } + } + + return flags; +} + +static int32_t coff_section_names(char *name, int *bits) +{ + char *p, *comdat_name; + uint32_t flags, align_flags; + int i, j; + int8_t comdat_selection; + int32_t comdat_associated; + + /* + * Set default bits. + */ + if (!name) { + if(win64) + *bits = 64; + else + *bits = 32; + + return def_seg; + } + + p = name; + while (*p && !nasm_isspace(*p)) + p++; + if (*p) + *p++ = '\0'; + if (strlen(name) > 8) { + if (!win32 && !win64) { + nasm_warn(WARN_OTHER, "COFF section names limited to 8 characters: truncating"); + name[8] = '\0'; + } + } + flags = align_flags = comdat_selection = comdat_associated = 0; + comdat_name = NULL; + + while (*p && nasm_isspace(*p)) + p++; + while (*p) { + char *q = p; + while (*p && !nasm_isspace(*p)) + p++; + if (*p) + *p++ = '\0'; + while (*p && nasm_isspace(*p)) + p++; + + if (!nasm_stricmp(q, "code") || !nasm_stricmp(q, "text")) { + flags = TEXT_FLAGS; + } else if (!nasm_stricmp(q, "data")) { + flags = DATA_FLAGS; + } else if (!nasm_stricmp(q, "rdata")) { + if (win32 | win64) + flags = RDATA_FLAGS; + else { + flags = DATA_FLAGS; /* gotta do something */ + nasm_nonfatal("standard COFF does not support" + " read-only data sections"); + } + } else if (!nasm_stricmp(q, "bss")) { + flags = BSS_FLAGS; + } else if (!nasm_stricmp(q, "info")) { + if (win32 | win64) + flags = INFO_FLAGS; + else { + flags = DATA_FLAGS; /* gotta do something */ + nasm_nonfatal("standard COFF does not support" + " informational sections"); + } + } else if (!nasm_strnicmp(q, "align=", 6)) { + if (q[6 + strspn(q + 6, "0123456789")]) + nasm_nonfatal("argument to `align' is not numeric"); + else { + unsigned int align = atoi(q + 6); + /* Allow align=0 meaning use default */ + if (!align) { + align_flags = 0; + } else if (!is_power2(align)) { + nasm_nonfatal("argument to `align' is not a" + " power of two"); + } else if (align > COFF_MAX_ALIGNMENT) { + nasm_nonfatal("maximum alignment in COFF is %d bytes", + COFF_MAX_ALIGNMENT); + } else { + align_flags = coff_sectalign_flags(align); + } + } + } else if (!nasm_strnicmp(q, "comdat=", 7)) { + /* + * Expected format: comdat=num:name] + * where + * num is a number: one of the IMAGE_COMDAT_SELECT_* constants + * name is a string: the "COMDAT name" + */ + comdat_selection = strtoul(q + 7, &q, 10); + if (!comdat_selection) + nasm_nonfatal("invalid argument to `comdat'"); + else if (*q != ':' || q[1] == '\0') + nasm_nonfatal("missing name in `comdat'"); + else { + comdat_name = q + 1; + } + } + } + + for (i = 0; i < coff_nsects; i++) + if (!strcmp(name, coff_sects[i]->name)) { + if (!comdat_name && !coff_sects[i]->comdat_name) + break; + else if (comdat_name && coff_sects[i]->comdat_name && + !strcmp(comdat_name, coff_sects[i]->comdat_name)) { + /* + * For COMDAT, it makes sense to have multiple sections with + * the same name (different comdat name though) + */ + if ((coff_sects[i]->comdat_selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE && + comdat_selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) || + (coff_sects[i]->comdat_selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE && + comdat_selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE)) { + /* + * Let's also allow an associative/other pair with the same name + */ + break; + } + } + } + else if (comdat_name && coff_sects[i]->comdat_name && + !coff_sects[i]->comdat_selection && + !strcmp(comdat_name, coff_sects[i]->comdat_name) && + comdat_selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + /* + * This seems to be a "placeholder section" we've created before + * to be the associate of a previous comdat section. + * We'll just update the name and flags with the real ones now. + */ + flags = coff_section_flags(name, flags); + coff_update_section(i, name, flags | IMAGE_SCN_LNK_COMDAT); + coff_sects[i]->comdat_selection = comdat_selection; + break; + } + + if (i == coff_nsects) { + flags = coff_section_flags(name, flags); + + if (comdat_name) { + flags |= IMAGE_SCN_LNK_COMDAT; + + if (comdat_selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + /* + * Find an existing section with given comdat name + */ + for (j = 0; j < coff_nsects; j++) + if (coff_sects[j]->comdat_name && + !strcmp(coff_sects[j]->comdat_name, comdat_name)) + break; + + if (j == coff_nsects) { + /* + * The associated section doesn't exist (yet) + * Even though the specs don't enforce a particular order, + * VS (2019) linker doesn't accept .obj files where the + * target section is a later one (than the one with sel==5) + * + * So let's insert another section now (a placeholder), + * hoping it will be turned into the target section later. + */ + j = coff_make_section(COMDAT_PLACEHOLDER_NAME, TEXT_FLAGS); + coff_sects[j]->comdat_name = nasm_strdup(comdat_name); + } + + comdat_associated = j + 1; + } + } + + i = coff_make_section(name, flags); + coff_sects[i]->align_flags = align_flags; + + if (comdat_name) { + coff_sects[i]->comdat_selection = comdat_selection; + coff_sects[i]->comdat_associated = comdat_associated; + coff_sects[i]->comdat_name = nasm_strdup(comdat_name); + } + } else { + if (flags) { + if (comdat_name) + flags |= IMAGE_SCN_LNK_COMDAT; + + /* Warn if non-alignment flags differ */ + if (((flags ^ coff_sects[i]->flags) & ~IMAGE_SCN_ALIGN_MASK) && + coff_sects[i]->pass_last_seen == pass_count()) { + nasm_warn(WARN_OTHER, "section attributes changed on" + " redeclaration of section `%s'", name); + } + } + + /* + * Alignment can be increased, but never decreased. However, + * specifying a narrower alignment is permitted and ignored. + */ + if (align_flags > coff_sects[i]->align_flags) { + coff_sects[i]->align_flags = align_flags; + } + + if (comdat_name) { + if ((coff_sects[i]->comdat_selection != comdat_selection) && + coff_sects[i]->pass_last_seen == pass_count()) { + nasm_warn(WARN_OTHER, "comdat selection changed on" + " redeclaration of name `%s'", comdat_name); + } + } + } + + coff_sects[i]->pass_last_seen = pass_count(); + return coff_sects[i]->index; +} + +static void coff_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special) +{ + int pos, section; + struct coff_Symbol *sym; + + if (special) + nasm_nonfatal("COFF format does not support any" + " special symbol types"); + + if (name[0] == '.' && name[1] == '.' && name[2] != '@') { + if (strcmp(name,WRT_IMAGEBASE)) + nasm_nonfatal("unrecognized special symbol `%s'", name); + return; + } + + if (segment == NO_SEG) + section = -1; /* absolute symbol */ + else { + int i; + section = 0; + for (i = 0; i < coff_nsects; i++) + if (segment == coff_sects[i]->index) { + section = i + 1; + + if (coff_sects[i]->comdat_name && !coff_sects[i]->comdat_symbol) { + /* + * The "comdat symbol" must be the first one in symbol table + * So we'll insert/define it - before defining the other one + */ + coff_sects[i]->comdat_symbol = 1; + + if (coff_sects[i]->comdat_selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE && + 0 != strcmp(coff_sects[i]->comdat_name, name)) { + coff_defcomdatname(coff_sects[i]->comdat_name, segment); + } + } + break; + } + } + + pos = strslen + 4; + if (strlen(name) > 8) { + size_t nlen = strlen(name)+1; + saa_wbytes(coff_strs, name, nlen); + strslen += nlen; + } else + pos = -1; + + sym = saa_wstruct(coff_syms); + + sym->strpos = pos; + sym->namlen = strlen(name); + if (pos == -1) + strcpy(sym->name, name); + sym->is_global = !!is_global; + sym->type = 0; /* Default to T_NULL (no type) */ + sym->section = section; + if (!sym->section) + sym->is_global = true; + if (is_global == 2) + sym->value = offset; + else + sym->value = (sym->section == 0 ? 0 : offset); + + /* + * define the references from external-symbol segment numbers + * to these symbol records. + */ + if (sym->section == 0) + bsym = raa_write(bsym, segment, coff_nsyms); + + if (segment != NO_SEG) + symval = raa_write(symval, segment, sym->section ? 0 : sym->value); + + coff_nsyms++; +} + +static int32_t coff_add_reloc(struct coff_Section *sect, int32_t segment, + int16_t type) +{ + struct coff_Reloc *r; + + r = *sect->tail = nasm_malloc(sizeof(struct coff_Reloc)); + sect->tail = &r->next; + r->next = NULL; + + r->address = sect->len; + if (segment == NO_SEG) { + r->symbol = 0, r->symbase = ABS_SYMBOL; + } else { + int i; + r->symbase = REAL_SYMBOLS; + for (i = 0; i < coff_nsects; i++) { + if (segment == coff_sects[i]->index) { + r->symbol = i * 2; + r->symbase = SECT_SYMBOLS; + break; + } + } + if (r->symbase == REAL_SYMBOLS) + r->symbol = raa_read(bsym, segment); + } + r->type = type; + + sect->nrelocs++; + + /* + * Return the fixup for standard COFF common variables. + */ + if (r->symbase == REAL_SYMBOLS && !(win32 | win64)) + return raa_read(symval, segment); + + return 0; +} + +static void coff_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + struct coff_Section *s; + uint8_t mydata[8], *p; + int i; + + if (wrt != NO_SEG && !win64) { + wrt = NO_SEG; /* continue to do _something_ */ + nasm_nonfatal("WRT not supported by COFF output formats"); + } + + s = NULL; + for (i = 0; i < coff_nsects; i++) { + if (segto == coff_sects[i]->index) { + s = coff_sects[i]; + break; + } + } + if (!s) { + int tempint; /* ignored */ + if (segto != coff_section_names(".text", &tempint)) + nasm_panic("strange segment conditions in COFF driver"); + else + s = coff_sects[coff_nsects - 1]; + } + + /* magically default to 'wrt ..imagebase' in .pdata and .xdata */ + if (win64 && wrt == NO_SEG) { + if (!strcmp(s->name,".pdata") || !strcmp(s->name,".xdata")) + wrt = imagebase_sect; + } + + if (!s->data && type != OUT_RESERVE) { + nasm_warn(WARN_OTHER, "attempt to initialize memory in" + " BSS section `%s': ignored", s->name); + s->len += realsize(type, size); + return; + } + + memset(mydata, 0, sizeof(mydata)); + + if (dfmt && dfmt->debug_output) { + struct coff_DebugInfo dinfo; + dinfo.segto = segto; + dinfo.seg = segment; + dinfo.section = s; + + if (type == OUT_ADDRESS) + dinfo.size = abs((int)size); + else + dinfo.size = realsize(type, size); + + dfmt->debug_output(type, &dinfo); + } + + if (type == OUT_RESERVE) { + if (s->data) { + nasm_warn(WARN_ZEROING, "uninitialised space declared in" + " non-BSS section `%s': zeroing", s->name); + coff_sect_write(s, NULL, size); + } else + s->len += size; + } else if (type == OUT_RAWDATA) { + coff_sect_write(s, data, size); + } else if (type == OUT_ADDRESS) { + int asize = abs((int)size); + if (!win64) { + if (asize != 4 && (segment != NO_SEG || wrt != NO_SEG)) { + nasm_nonfatal("COFF format does not support non-32-bit" + " relocations"); + } else { + int32_t fix = 0; + if (segment != NO_SEG || wrt != NO_SEG) { + if (wrt != NO_SEG) { + nasm_nonfatal("COFF format does not support WRT types"); + } else if (segment % 2) { + nasm_nonfatal("COFF format does not support" + " segment base references"); + } else + fix = coff_add_reloc(s, segment, IMAGE_REL_I386_DIR32); + } + p = mydata; + WRITELONG(p, *(int64_t *)data + fix); + coff_sect_write(s, mydata, asize); + } + } else { + int32_t fix = 0; + p = mydata; + if (asize == 8) { + if (wrt == imagebase_sect) { + nasm_nonfatal("operand size mismatch: 'wrt " + WRT_IMAGEBASE "' is a 32-bit operand"); + } + fix = coff_add_reloc(s, segment, IMAGE_REL_AMD64_ADDR64); + WRITEDLONG(p, *(int64_t *)data + fix); + coff_sect_write(s, mydata, asize); + } else { + fix = coff_add_reloc(s, segment, + wrt == imagebase_sect ? IMAGE_REL_AMD64_ADDR32NB: + IMAGE_REL_AMD64_ADDR32); + WRITELONG(p, *(int64_t *)data + fix); + coff_sect_write(s, mydata, asize); + } + } + } else if (type == OUT_REL2ADR) { + nasm_nonfatal("COFF format does not support 16-bit relocations"); + } else if (type == OUT_REL4ADR) { + if (segment == segto && !(win64)) /* Acceptable for RIP-relative */ + nasm_panic("intra-segment OUT_REL4ADR"); + else if (segment == NO_SEG && win32) + nasm_nonfatal("Win32 COFF does not correctly support" + " relative references to absolute addresses"); + else { + int32_t fix = 0; + if (segment != NO_SEG && segment % 2) { + nasm_nonfatal("COFF format does not support" + " segment base references"); + } else + fix = coff_add_reloc(s, segment, + win64 ? IMAGE_REL_AMD64_REL32 : IMAGE_REL_I386_REL32); + p = mydata; + if (win32 | win64) { + WRITELONG(p, *(int64_t *)data + 4 - size + fix); + } else { + WRITELONG(p, *(int64_t *)data - (size + s->len) + fix); + } + coff_sect_write(s, mydata, 4L); + } + + } +} + +static void coff_sect_write(struct coff_Section *sect, + const uint8_t *data, uint32_t len) +{ + saa_wbytes(sect->data, data, len); + sect->len += len; +} + +typedef struct tagString { + struct tagString *next; + int len; + char *String; +} STRING; + +#define EXPORT_SECTION_NAME ".drectve" +#define EXPORT_SECTION_FLAGS INFO_FLAGS +/* + * #define EXPORT_SECTION_NAME ".text" + * #define EXPORT_SECTION_FLAGS TEXT_FLAGS + */ + +static STRING *Exports = NULL; +static struct coff_Section *directive_sec; +static void AddExport(char *name) +{ + STRING *rvp = Exports, *newS; + + newS = (STRING *) nasm_malloc(sizeof(STRING)); + newS->len = strlen(name); + newS->next = NULL; + newS->String = (char *)nasm_malloc(newS->len + 1); + strcpy(newS->String, name); + if (rvp == NULL) { + int i; + + for (i = 0; i < coff_nsects; i++) { + if (!strcmp(EXPORT_SECTION_NAME, coff_sects[i]->name)) + break; + } + + if (i == coff_nsects) + i = coff_make_section(EXPORT_SECTION_NAME, EXPORT_SECTION_FLAGS); + + directive_sec = coff_sects[i]; + Exports = newS; + } else { + while (rvp->next) { + if (!strcmp(rvp->String, name)) + return; + rvp = rvp->next; + } + rvp->next = newS; + } +} + +static void BuildExportTable(STRING **rvp) +{ + STRING *p, *t; + + if (!rvp || !*rvp) + return; + + list_for_each_safe(p, t, *rvp) { + coff_sect_write(directive_sec, (uint8_t *)"-export:", 8); + coff_sect_write(directive_sec, (uint8_t *)p->String, p->len); + coff_sect_write(directive_sec, (uint8_t *)" ", 1); + nasm_free(p->String); + nasm_free(p); + } + + *rvp = NULL; +} + +static void coff_defcomdatname(char *name, int32_t segment) +{ + coff_deflabel(name, segment, 0, 1, NULL); +} + +static enum directive_result +coff_directives(enum directive directive, char *value) +{ + switch (directive) { + case D_EXPORT: + { + char *q, *name; + + /* + * XXX: pass_first() is really wrong here, but AddExport() + * needs to be modified to handle duplicate calls for the + * same value in order to change that. The right thing to do + * is probably to mark a label as an export in the label + * structure, in case the label doesn't actually exist. + */ + if (!pass_first()) + return DIRR_OK; /* ignore in pass two */ + name = q = value; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + + if (!*name) { + nasm_nonfatal("`export' directive requires export name"); + return DIRR_ERROR; + } + if (*q) { + nasm_nonfatal("unrecognized export qualifier `%s'", q); + return DIRR_ERROR; + } + AddExport(name); + return DIRR_OK; + } + case D_SAFESEH: + { + static int sxseg=-1; + int i; + + if (!win32) /* Only applicable for -f win32 */ + return 0; + + if (sxseg == -1) { + for (i = 0; i < coff_nsects; i++) + if (!strcmp(".sxdata",coff_sects[i]->name)) + break; + if (i == coff_nsects) + sxseg = coff_make_section(".sxdata", IMAGE_SCN_LNK_INFO); + else + sxseg = i; + } + /* + * pass_final() is the only time when the full set of symbols are + * guaranteed to be present as it is the final output pass. + */ + if (pass_final()) { + uint32_t n; + saa_rewind(coff_syms); + for (n = 0; n < coff_nsyms; n++) { + struct coff_Symbol *sym = saa_rstruct(coff_syms); + bool equals; + + /* + * sym->strpos is biased by 4, because symbol + * table is prefixed with table length + */ + if (sym->strpos >=4) { + char *name = nasm_malloc(sym->namlen+1); + saa_fread(coff_strs, sym->strpos-4, name, sym->namlen); + name[sym->namlen] = '\0'; + equals = !strcmp(value,name); + nasm_free(name); + } else { + equals = !strcmp(value,sym->name); + } + + if (equals) { + /* + * this value arithmetic effectively reflects + * initsym in coff_write(): 2 for file, 1 for + * .absolute and two per each section + */ + unsigned char value[4],*p=value; + WRITELONG(p,n + 2 + 1 + coff_nsects*2); + coff_sect_write(coff_sects[sxseg],value,4); + sym->type = 0x20; + break; + } + } + if (n == coff_nsyms) { + nasm_nonfatal("`safeseh' directive requires valid symbol"); + return DIRR_ERROR; + } + } + return DIRR_OK; + } + default: + return DIRR_UNKNOWN; + } +} + +/* handle relocations storm, valid for win32/64 only */ +static inline void coff_adjust_relocs(struct coff_Section *s) +{ + if (s->nrelocs < IMAGE_SCN_MAX_RELOC) + return; +#ifdef OF_COFF + else + { + if (ofmt == &of_coff) + nasm_fatal("Too many relocations (%d) for section `%s'", + s->nrelocs, s->name); + } +#endif + + s->flags |= IMAGE_SCN_LNK_NRELOC_OVFL; + s->nrelocs++; +} + +/* + * Make sure we satisfy all section alignment requirements and put the + * resulting alignment flags into the flags value in the header. If + * no user-specified alignment is given, use the default for the + * section type; then either way round up to alignment specified by + * sectalign directives. + */ +static inline void coff_adjust_alignment(struct coff_Section *s) +{ + uint32_t align_flags = s->align_flags; + + if (!align_flags) { + /* No user-specified alignment, use default for partition type */ + align_flags = s->flags & IMAGE_SCN_ALIGN_MASK; + } + + if (align_flags < s->sectalign_flags) + align_flags = s->sectalign_flags; + + s->flags = (s->flags & ~IMAGE_SCN_ALIGN_MASK) | align_flags; +} + +static void coff_write(void) +{ + int32_t pos, sympos, vsize; + int i; + + /* fill in the .drectve section with -export's */ + BuildExportTable(&Exports); + + if (win32) { + /* add default value for @feat.00, this allows to 'link /safeseh' */ + uint32_t n; + + saa_rewind(coff_syms); + for (n = 0; n < coff_nsyms; n++) { + struct coff_Symbol *sym = saa_rstruct(coff_syms); + if (sym->strpos == -1 && !strcmp("@feat.00",sym->name)) + break; + } + if (n == coff_nsyms) + coff_deflabel("@feat.00", NO_SEG, 1, 0, NULL); + } + + /* + * Check all comdat sections + */ + for (i = 0; i < coff_nsects; i++) + if (coff_sects[i]->comdat_name) { + if (!coff_sects[i]->comdat_symbol && + coff_sects[i]->comdat_selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + /* + * This section doesn't have its comdat symbol defined; do it + */ + coff_defcomdatname(coff_sects[i]->comdat_name, coff_sects[i]->index); + } + if (!coff_sects[i]->comdat_selection) { + /* + * This is a placeholder section that wasn't properly defined + */ + nasm_nonfatal("`comdat' associate with symbol `%s` wasn't defined", + coff_sects[i]->comdat_name); + } + } + + /* + * Work out how big the file will get. + * Calculate the start of the `real' symbols at the same time. + * Check for massive relocations. + */ + pos = 0x14 + 0x28 * coff_nsects; + initsym = 3; /* two for the file, one absolute */ + for (i = 0; i < coff_nsects; i++) { + coff_adjust_alignment(coff_sects[i]); + if (coff_sects[i]->data) { + coff_adjust_relocs(coff_sects[i]); + coff_sects[i]->pos = pos; + pos += coff_sects[i]->len; + coff_sects[i]->relpos = pos; + pos += 10 * coff_sects[i]->nrelocs; + } else + coff_sects[i]->pos = coff_sects[i]->relpos = 0L; + initsym += 2; /* two for each section */ + } + sympos = pos; + + /* + * Output the COFF header. + */ + if (win64) + i = IMAGE_FILE_MACHINE_AMD64; + else + i = IMAGE_FILE_MACHINE_I386; + fwriteint16_t(i, ofile); /* machine type */ + fwriteint16_t(coff_nsects, ofile); /* number of sections */ + fwriteint32_t(posix_timestamp(), ofile); /* timestamp */ + fwriteint32_t(sympos, ofile); + fwriteint32_t(coff_nsyms + initsym, ofile); + fwriteint16_t(0, ofile); /* no optional header */ + /* Flags: 32-bit, no line numbers. Win32 doesn't even bother with them. */ + fwriteint16_t((win32 | win64) ? 0 : 0x104, ofile); + + /* + * Output the section headers. + */ + vsize = 0L; + for (i = 0; i < coff_nsects; i++) { + coff_section_header(coff_sects[i]->name, coff_sects[i]->namepos, vsize, coff_sects[i]->len, + coff_sects[i]->pos, coff_sects[i]->relpos, + coff_sects[i]->nrelocs, coff_sects[i]->flags); + vsize += coff_sects[i]->len; + } + + /* + * Output the sections and their relocations. + */ + for (i = 0; i < coff_nsects; i++) + if (coff_sects[i]->data) { + saa_fpwrite(coff_sects[i]->data, ofile); + coff_write_relocs(coff_sects[i]); + + if (coff_sects[i]->flags & IMAGE_SCN_LNK_COMDAT) { + /* + * Checksum the section data + */ + uint32_t checksum = 0; + const char *data; + size_t len; + + saa_rewind(coff_sects[i]->data); + while (len = coff_sects[i]->data->datalen, + (data = saa_rbytes(coff_sects[i]->data, &len)) != NULL) + checksum = crc32b(checksum, data, len); + + coff_sects[i]->checksum = checksum; + } + } + + /* + * Output the symbol and string tables. + */ + coff_write_symbols(); + fwriteint32_t(strslen + 4, ofile); /* length includes length count */ + saa_fpwrite(coff_strs, ofile); +} + +static void coff_section_header(char *name, int32_t namepos, int32_t vsize, + int32_t datalen, int32_t datapos, + int32_t relpos, int nrelocs, int32_t flags) +{ + char padname[8]; + + (void)vsize; + + if (namepos == -1) { + strncpy(padname, name, 8); + nasm_write(padname, 8, ofile); + } else { + /* + * If name is longer than 8 bytes, write '/' followed + * by offset into the strings table represented as + * decimal number. + */ + namepos = namepos % 100000000; + padname[0] = '/'; + padname[1] = '0' + (namepos / 1000000); + namepos = namepos % 1000000; + padname[2] = '0' + (namepos / 100000); + namepos = namepos % 100000; + padname[3] = '0' + (namepos / 10000); + namepos = namepos % 10000; + padname[4] = '0' + (namepos / 1000); + namepos = namepos % 1000; + padname[5] = '0' + (namepos / 100); + namepos = namepos % 100; + padname[6] = '0' + (namepos / 10); + namepos = namepos % 10; + padname[7] = '0' + (namepos); + nasm_write(padname, 8, ofile); + } + + fwriteint32_t(0, ofile); /* Virtual size field - set to 0 or vsize */ + fwriteint32_t(0L, ofile); /* RVA/offset - we ignore */ + fwriteint32_t(datalen, ofile); + fwriteint32_t(datapos, ofile); + fwriteint32_t(relpos, ofile); + fwriteint32_t(0L, ofile); /* no line numbers - we don't do 'em */ + + /* + * a special case -- if there are too many relocs + * we have to put IMAGE_SCN_MAX_RELOC here and write + * the real relocs number into VirtualAddress of first + * relocation + */ + if (flags & IMAGE_SCN_LNK_NRELOC_OVFL) + fwriteint16_t(IMAGE_SCN_MAX_RELOC, ofile); + else + fwriteint16_t(nrelocs, ofile); + + fwriteint16_t(0, ofile); /* again, no line numbers */ + fwriteint32_t(flags, ofile); +} + +static void coff_write_relocs(struct coff_Section *s) +{ + struct coff_Reloc *r; + + /* a real number of relocations if needed */ + if (s->flags & IMAGE_SCN_LNK_NRELOC_OVFL) { + fwriteint32_t(s->nrelocs, ofile); + fwriteint32_t(0, ofile); + fwriteint16_t(0, ofile); + } + + for (r = s->head; r; r = r->next) { + fwriteint32_t(r->address, ofile); + fwriteint32_t(r->symbol + (r->symbase == REAL_SYMBOLS ? initsym : + r->symbase == ABS_SYMBOL ? initsym - 1 : + r->symbase == SECT_SYMBOLS ? 2 : 0), + ofile); + fwriteint16_t(r->type, ofile); + } +} + +static void coff_symbol(char *name, int32_t strpos, int32_t value, + int section, int type, int storageclass, int aux) +{ + char padname[8]; + + if (name) { + strncpy(padname, name, 8); + nasm_write(padname, 8, ofile); + } else { + fwriteint32_t(0, ofile); + fwriteint32_t(strpos, ofile); + } + + fwriteint32_t(value, ofile); + fwriteint16_t(section, ofile); + fwriteint16_t(type, ofile); + + fputc(storageclass, ofile); + fputc(aux, ofile); +} + +static void coff_write_symbols(void) +{ + char filename[18]; + uint32_t i; + + /* + * The `.file' record, and the file name auxiliary record. + */ + coff_symbol(".file", 0L, 0L, -2, 0, 0x67, 1); + if (reproducible) + memset(filename, 0, 18); + else + strncpy(filename, inname, 18); + nasm_write(filename, 18, ofile); + + /* + * The section records, with their auxiliaries. + */ + memset(filename, 0, 18); /* useful zeroed buffer */ + + for (i = 0; i < (uint32_t) coff_nsects; i++) { + coff_symbol(coff_sects[i]->name, 0L, 0L, i + 1, 0, 3, 1); + fwriteint32_t(coff_sects[i]->len, ofile); + fwriteint16_t(coff_sects[i]->nrelocs,ofile); + if (coff_sects[i]->flags & IMAGE_SCN_LNK_COMDAT) { + fwriteint16_t(0, ofile); + fwriteint32_t(coff_sects[i]->checksum, ofile); + fwriteint16_t(coff_sects[i]->comdat_associated, ofile); + fputc(coff_sects[i]->comdat_selection, ofile); + nasm_write(filename, 3, ofile); + } + else + nasm_write(filename, 12, ofile); + } + + /* + * The absolute symbol, for relative-to-absolute relocations. + */ + coff_symbol(".absolut", 0L, 0L, -1, 0, 3, 0); + + /* + * The real symbols. + */ + saa_rewind(coff_syms); + for (i = 0; i < coff_nsyms; i++) { + struct coff_Symbol *sym = saa_rstruct(coff_syms); + coff_symbol(sym->strpos == -1 ? sym->name : NULL, + sym->strpos, sym->value, sym->section, + sym->type, sym->is_global ? 2 : 3, 0); + } +} + +static void coff_sectalign(int32_t seg, unsigned int value) +{ + struct coff_Section *s = NULL; + uint32_t flags; + int i; + + for (i = 0; i < coff_nsects; i++) { + if (coff_sects[i]->index == seg) { + s = coff_sects[i]; + break; + } + } + + if (!s || !is_power2(value)) + return; + + if (value > COFF_MAX_ALIGNMENT) + value = COFF_MAX_ALIGNMENT; /* Do our best... */ + + flags = coff_sectalign_flags(value); + if (flags > s->sectalign_flags) + s->sectalign_flags = flags; +} + +extern macros_t coff_stdmac[]; + +#endif /* defined(OF_COFF) || defined(OF_WIN32) */ + +#ifdef OF_COFF + +static const struct pragma_facility coff_pragma_list[] = { + { "coff", NULL }, + { NULL, NULL } +}; + +const struct ofmt of_coff = { + "COFF (i386) (DJGPP, some Unix variants)", + "coff", + ".o", + 0, + 32, + null_debug_arr, + &null_debug_form, + coff_stdmac, + coff_std_init, + null_reset, + nasm_do_legacy_output, + coff_out, + coff_deflabel, + coff_section_names, + NULL, + coff_sectalign, + null_segbase, + coff_directives, + coff_cleanup, + coff_pragma_list +}; + +#endif + + +#ifdef OF_WIN32 + +static const struct pragma_facility coff_win_pragma_list[] = { + { "win", NULL }, + { "coff", NULL }, + { NULL, NULL } +}; + +extern const struct dfmt df_cv8; +static const struct dfmt * const win32_debug_arr[2] = { &df_cv8, NULL }; + +const struct ofmt of_win32 = { + "Microsoft extended COFF for Win32 (i386)", + "win32", + ".obj", + 0, + 32, + win32_debug_arr, + &df_cv8, + coff_stdmac, + coff_win32_init, + null_reset, + nasm_do_legacy_output, + coff_out, + coff_deflabel, + coff_section_names, + NULL, + coff_sectalign, + null_segbase, + coff_directives, + coff_cleanup, + coff_win_pragma_list +}; + +#endif + +#ifdef OF_WIN64 + +static const struct dfmt * const win64_debug_arr[2] = { &df_cv8, NULL }; + +const struct ofmt of_win64 = { + "Microsoft extended COFF for Win64 (x86-64)", + "win64", + ".obj", + 0, + 64, + win64_debug_arr, + &df_cv8, + coff_stdmac, + coff_win64_init, + null_reset, + nasm_do_legacy_output, + coff_out, + coff_deflabel, + coff_section_names, + NULL, + coff_sectalign, + null_segbase, + coff_directives, + coff_cleanup, + coff_win_pragma_list +}; + +#endif diff --git a/vere/ext/nasm/output/outcoff.mac b/vere/ext/nasm/output/outcoff.mac new file mode 100644 index 0000000..8d338ef --- /dev/null +++ b/vere/ext/nasm/output/outcoff.mac @@ -0,0 +1,43 @@ +;; -------------------------------------------------------------------------- +;; +;; Copyright 1996-2009 The NASM Authors - All Rights Reserved +;; See the file AUTHORS included with the NASM distribution for +;; the specific copyright holders. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following +;; conditions are met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following +;; disclaimer in the documentation and/or other materials provided +;; with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +;; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +;; EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;; +;; -------------------------------------------------------------------------- + +OUT: coff win32 win64 +%define __?SECT?__ [section .text] +%macro __?NASM_CDecl?__ 1 +%endmacro +%imacro export 1+.nolist +[export %1] +%endmacro +%imacro safeseh 1.nolist +[safeseh %1] +%endmacro diff --git a/vere/ext/nasm/output/outdbg.c b/vere/ext/nasm/output/outdbg.c new file mode 100644 index 0000000..e7a9a4e --- /dev/null +++ b/vere/ext/nasm/output/outdbg.c @@ -0,0 +1,576 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2020 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outdbg.c output routines for the Netwide Assembler to produce + * a debugging trace + */ + +#include "compiler.h" + +#include "nctype.h" +#include <errno.h> + +#include "nasm.h" +#include "nasmlib.h" +#include "outform.h" +#include "outlib.h" +#include "insns.h" +#include "dbginfo.h" + +#ifdef OF_DBG + +struct Section { + struct Section *next; + int32_t number; + char *name; +} *dbgsect; + +static unsigned long dbg_max_data_dump = 128; +static bool section_labels = true; +static bool subsections_via_symbols = false; +static int32_t init_seg; + +const struct ofmt of_dbg; +static void dbg_init(void) +{ + dbgsect = NULL; + fprintf(ofile, "NASM Output format debug dump\n"); + fprintf(ofile, "input file = %s\n", inname); + fprintf(ofile, "output file = %s\n", outname); + init_seg = seg_alloc(); +} + +static void dbg_reset(void) +{ + fprintf(ofile, "*** pass reset: pass = %"PRId64" (%s)\n", + pass_count(), pass_type_name()); +} + +static void dbg_cleanup(void) +{ + dfmt->cleanup(); + while (dbgsect) { + struct Section *tmp = dbgsect; + dbgsect = dbgsect->next; + nasm_free(tmp->name); + nasm_free(tmp); + } +} + +static int32_t dbg_add_section(char *name, int *bits, const char *whatwecallit) +{ + int seg; + + /* + * We must have an initial default: let's make it 16. + */ + if (!name) + *bits = 16; + + if (!name) { + fprintf(ofile, "section_name on init: returning %d\n", init_seg); + seg = init_seg; + } else { + int n = strcspn(name, " \t"); + char *sname = nasm_strndup(name, n); + char *tail = nasm_skip_spaces(name+n); + struct Section *s; + + seg = NO_SEG; + for (s = dbgsect; s; s = s->next) + if (!strcmp(s->name, sname)) + seg = s->number; + + if (seg == NO_SEG) { + s = nasm_malloc(sizeof(*s)); + s->name = sname; + s->number = seg = seg_alloc(); + s->next = dbgsect; + dbgsect = s; + fprintf(ofile, "%s %s (%s) pass %"PRId64" (%s) : returning %d\n", + whatwecallit, name, tail, pass_count(), pass_type_name(), seg); + + if (section_labels) + backend_label(s->name, s->number + 1, 0); + } + } + return seg; +} + +static int32_t dbg_section_names(char *name, int *bits) +{ + return dbg_add_section(name, bits, "section_names"); +} + +static int32_t dbg_herelabel(const char *name, enum label_type type, + int32_t oldseg, int32_t *subsection, + bool *copyoffset) +{ + int32_t newseg = oldseg; + + if (subsections_via_symbols && type != LBL_LOCAL) { + newseg = *subsection; + if (newseg == NO_SEG) { + newseg = *subsection = seg_alloc(); + *copyoffset = true; /* Minic MachO for now */ + } + } + fprintf(ofile, "herelabel %s type %d (seg %08x) -> %08x\n", + name, type, oldseg, newseg); + + return newseg; +} + +static void dbg_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special) +{ + fprintf(ofile, "deflabel %s := %08"PRIx32":%016"PRIx64" %s (%d)%s%s\n", + name, segment, offset, + is_global == 2 ? "common" : is_global ? "global" : "local", + is_global, special ? ": " : "", special); +} + +static const char *out_type(enum out_type type) +{ + static const char *out_types[] = { + "rawdata", + "reserve", + "zerodata", + "address", + "reladdr", + "segment" + }; + static char invalid_buf[64]; + + if (type >= sizeof(out_types)/sizeof(out_types[0])) { + sprintf(invalid_buf, "[invalid type %d]", type); + return invalid_buf; + } + + return out_types[type]; +} + +static const char *out_flags(enum out_flags flags) +{ + static const char *out_flags[] = { + "signed", + "unsigned" + }; + static char flags_buf[1024]; + unsigned long flv = flags; + size_t n; + size_t left = sizeof flags_buf - 1; + char *p = flags_buf; + unsigned int i; + + for (i = 0; flv; flv >>= 1, i++) { + if (flv & 1) { + if (i < ARRAY_SIZE(out_flags)) + n = snprintf(p, left, "%s,", out_flags[i]); + else + n = snprintf(p, left, "%u,", i); + if (n >= left) + break; + left -= n; + p += n; + } + } + if (p > flags_buf) + p--; /* Delete final comma */ + *p = '\0'; + + return flags_buf; +} + +static void dbg_out(const struct out_data *data) +{ + fprintf(ofile, + "out to %"PRIx32":%"PRIx64" %s(%s) bits %d insoffs %d/%d " + "size %"PRIu64, + data->segment, data->offset, + out_type(data->type), out_flags(data->flags), + data->bits, data->insoffs, data->inslen, data->size); + if (data->itemp) { + fprintf(ofile, " ins %s(%d)", + nasm_insn_names[data->itemp->opcode], data->itemp->operands); + } else { + fprintf(ofile, " no ins (plain data)"); + } + + if (data->type == OUT_ADDRESS || data->type == OUT_RELADDR || + data->type == OUT_SEGMENT) { + fprintf(ofile, " target %"PRIx32":%"PRIx64, + data->tsegment, data->toffset); + if (data->twrt != NO_SEG) + fprintf(ofile, " wrt %"PRIx32, data->twrt); + } + if (data->type == OUT_RELADDR) + fprintf(ofile, " relbase %"PRIx64, data->relbase); + + putc('\n', ofile); + + if (data->type == OUT_RAWDATA) { + if ((size_t)data->size != data->size) { + fprintf(ofile, " data: <error: impossible size>\n"); + } else if (!data->data) { + fprintf(ofile, " data: <error: null pointer>\n"); + } else if (dbg_max_data_dump != -1UL && + data->size > dbg_max_data_dump) { + fprintf(ofile, " data: <%"PRIu64" bytes>\n", data->size); + } else { + size_t i, j; + const uint8_t *bytes = data->data; + for (i = 0; i < data->size; i += 16) { + fprintf(ofile, " data:"); + for (j = 0; j < 16; j++) { + if (i+j >= data->size) + fprintf(ofile, " "); + else + fprintf(ofile, "%c%02x", + (j == 8) ? '-' : ' ', bytes[i+j]); + } + fprintf(ofile," "); + for (j = 0; j < 16; j++) { + if (i+j >= data->size) { + putc(' ', ofile); + } else { + if (bytes[i+j] >= 32 && bytes[i+j] <= 126) + putc(bytes[i+j], ofile); + else + putc('.', ofile); + } + } + putc('\n', ofile); + } + } + } + + /* This is probably the only place were we'll call this this way... */ + nasm_do_legacy_output(data); +} + +static void dbg_legacy_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + int32_t ldata; + + if (type == OUT_ADDRESS) + fprintf(ofile, " legacy: out to %"PRIx32", len = %d: ", + segto, (int)abs((int)size)); + else + fprintf(ofile, " legacy: out to %"PRIx32", len = %"PRId64" (0x%"PRIx64"): ", + segto, (int64_t)size, size); + + switch (type) { + case OUT_RESERVE: + fprintf(ofile, "reserved.\n"); + break; + case OUT_RAWDATA: + fprintf(ofile, "rawdata\n"); /* Already have a data dump */ + break; + case OUT_ADDRESS: + ldata = *(int64_t *)data; + fprintf(ofile, "addr %08"PRIx32" (seg %08"PRIx32", wrt %08"PRIx32")\n", + ldata, segment, wrt); + break; + case OUT_REL1ADR: + fprintf(ofile, "rel1adr %02"PRIx8" (seg %08"PRIx32")\n", + (uint8_t)*(int64_t *)data, segment); + break; + case OUT_REL2ADR: + fprintf(ofile, "rel2adr %04"PRIx16" (seg %08"PRIx32")\n", + (uint16_t)*(int64_t *)data, segment); + break; + case OUT_REL4ADR: + fprintf(ofile, "rel4adr %08"PRIx32" (seg %08"PRIx32")\n", + (uint32_t)*(int64_t *)data, + segment); + break; + case OUT_REL8ADR: + fprintf(ofile, "rel8adr %016"PRIx64" (seg %08"PRIx32")\n", + (uint64_t)*(int64_t *)data, segment); + break; + default: + fprintf(ofile, "unknown\n"); + break; + } +} + +static void dbg_sectalign(int32_t seg, unsigned int value) +{ + fprintf(ofile, "set alignment (%d) for segment (%u)\n", + seg, value); +} + +static enum directive_result +dbg_directive(enum directive directive, char *value) +{ + switch (directive) { + /* + * The .obj GROUP directive is nontrivial to emulate in a macro. + * It effectively creates a "pseudo-section" containing the first + * space-separated argument; the rest we ignore. + */ + case D_GROUP: + { + int dummy; + dbg_add_section(value, &dummy, "directive:group"); + break; + } + + default: + break; + } + + fprintf(ofile, "directive [%s] value [%s] pass %"PRId64" (%s)\n", + directive_dname(directive), value, pass_count(), pass_type_name()); + return DIRR_OK; +} + +static enum directive_result +dbg_pragma(const struct pragma *pragma); + +static const struct pragma_facility dbg_pragma_list[] = { + { NULL, dbg_pragma } +}; + +static enum directive_result +dbg_pragma(const struct pragma *pragma) +{ + fprintf(ofile, "pragma %s(%s) %s[%s] %s\n", + pragma->facility_name, + pragma->facility->name ? pragma->facility->name : "<default>", + pragma->opname, directive_dname(pragma->opcode), + pragma->tail); + + if (pragma->facility == &dbg_pragma_list[0]) { + switch (pragma->opcode) { + case D_MAXDUMP: + if (!nasm_stricmp(pragma->tail, "unlimited")) { + dbg_max_data_dump = -1UL; + } else { + char *ep; + unsigned long arg; + + errno = 0; + arg = strtoul(pragma->tail, &ep, 0); + if (errno || *nasm_skip_spaces(ep)) { + nasm_warn(WARN_PRAGMA_BAD | ERR_PASS2, + "invalid %%pragma dbg maxdump argument"); + return DIRR_ERROR; + } else { + dbg_max_data_dump = arg; + } + } + break; + case D_NOSECLABELS: + section_labels = false; + break; + case D_SUBSECTIONS_VIA_SYMBOLS: + subsections_via_symbols = true; + break; + default: + break; + } + } + return DIRR_OK; +} + +static const char * const types[] = { + "unknown", "label", "byte", "word", "dword", "float", "qword", "tbyte" +}; +static void dbgdbg_init(void) +{ + fprintf(ofile, "dbg init: debug information enabled\n"); +} +static void dbgdbg_cleanup(void) +{ + fprintf(ofile, "dbg cleanup: called\n"); +} + +static void dbgdbg_linnum(const char *lnfname, int32_t lineno, int32_t segto) +{ + fprintf(ofile, "dbg linenum: %s(%"PRId32") segment %"PRIx32"\n", + lnfname, lineno, segto); +} +static void dbgdbg_deflabel(char *name, int32_t segment, + int64_t offset, int is_global, char *special) +{ + fprintf(ofile, "dbg deflabel: %s = %08"PRIx32":%016"PRIx64" %s (%d)%s%s\n", + name, + segment, offset, + is_global == 2 ? "common" : is_global ? "global" : "local", + is_global, special ? ": " : "", special); +} + +static void dbgdbg_debug_smacros(bool define, const char *def) +{ + fprintf(ofile, "dbg define: %s [%s]\n", + define ? "define " : "undef ", def); +} +static void dbgdbg_debug_include(bool start, struct src_location outer, + struct src_location inner) +{ + fprintf(ofile, "dbg include: %s include: %s:%"PRId32" %s %s:%"PRId32"\n", + start ? "start" : "end", outer.filename, outer.lineno, + start ? "->" : "<-", inner.filename, inner.lineno); +} + +static void dbgdbg_output(int output_type, void *param) +{ + (void)output_type; + (void)param; + fprintf(ofile, "dbg output: called\n"); +} +static void dbgdbg_typevalue(int32_t type) +{ + fprintf(ofile, "dbg typevalue: %s(%"PRIX32")\n", + types[TYM_TYPE(type) >> 3], TYM_ELEMENTS(type)); +} + +static void +write_macro_inv_list(const struct debug_macro_inv *inv, int level) +{ + int indent = (level+1) << 1; + + while (inv) { + const struct rbtree *rb; + + fprintf(ofile, "%*smacro: %s, invoked at %s:%"PRId32 + ", %"PRIu32" ranges\n", + indent, "", inv->def->name, inv->where.filename, + inv->where.lineno, inv->naddr); + + for (rb = rb_first(inv->addr.tree); rb; rb = rb_next(rb)) { + const struct debug_macro_addr *addr = + (const struct debug_macro_addr *)rb; + if (!addr->len) { + fprintf(ofile, "%*s%08"PRIx32": empty\n", + indent+2, "", debug_macro_seg(addr)); + } else { + fprintf(ofile, + "%*s%08"PRIx32":[%016"PRIx64" ... %016"PRIx64"] " + "len %"PRIu64"\n", + indent+2, "", + debug_macro_seg(addr), addr->start, + addr->start + addr->len - 1, addr->len); + } + } + + write_macro_inv_list(inv->down.l, level+1); + inv = inv->next; + } +} + +static void dbgdbg_debug_mmacros(const struct debug_macro_info *dmi) +{ + const struct debug_macro_def *def; + + fprintf(ofile, "dbg macros: %llu macros defined\n", + (unsigned long long)dmi->def.n); + + fprintf(ofile, " macro definitions:\n"); + list_for_each(def, dmi->def.l) { + fprintf(ofile, " macro: %s, count %llu, defined at %s:%"PRId32"\n", + def->name, (unsigned long long)def->ninv, + def->where.filename, def->where.lineno); + } + + fprintf(ofile, " macro invocations:\n"); + write_macro_inv_list(dmi->inv.l, 1); + + fprintf(ofile, " end macro debug information\n"); +} + +static void dbgdbg_debug_directive(const char *id, const char *value) +{ + fprintf(ofile, "dbg directive: id [%s] value [%s] pass %"PRId64" (%s)\n", + id, value, pass_count(), pass_type_name()); +} + + +static const struct pragma_facility dbgdbg_pragma_list[] = { + { "dbgdbg", dbg_pragma }, + { NULL, dbg_pragma } /* Won't trigger, "debug" is a reserved ns */ +}; + +static const struct dfmt debug_debug_form = { + "Trace of all info passed to debug stage", + "debug", + dbgdbg_init, + dbgdbg_linnum, + dbgdbg_deflabel, + dbgdbg_debug_smacros, + dbgdbg_debug_include, + dbgdbg_debug_mmacros, + dbgdbg_debug_directive, + dbgdbg_typevalue, + dbgdbg_output, + dbgdbg_cleanup, + dbgdbg_pragma_list +}; + +static const struct dfmt * const debug_debug_arr[3] = { + &debug_debug_form, + &null_debug_form, + NULL +}; + +extern macros_t dbg_stdmac[]; + +const struct ofmt of_dbg = { + "Trace of all info passed to output stage", + "dbg", + ".dbg", + OFMT_TEXT|OFMT_KEEP_ADDR, + 64, + debug_debug_arr, + &debug_debug_form, + dbg_stdmac, + dbg_init, + dbg_reset, + dbg_out, + dbg_legacy_out, + dbg_deflabel, + dbg_section_names, + dbg_herelabel, + dbg_sectalign, + null_segbase, + dbg_directive, + dbg_cleanup, + dbg_pragma_list +}; + +#endif /* OF_DBG */ diff --git a/vere/ext/nasm/output/outdbg.mac b/vere/ext/nasm/output/outdbg.mac new file mode 100644 index 0000000..44cd11c --- /dev/null +++ b/vere/ext/nasm/output/outdbg.mac @@ -0,0 +1,56 @@ +;; -------------------------------------------------------------------------- +;; +;; Copyright 1996-2017 The NASM Authors - All Rights Reserved +;; See the file AUTHORS included with the NASM distribution for +;; the specific copyright holders. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following +;; conditions are met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following +;; disclaimer in the documentation and/or other materials provided +;; with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +;; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +;; EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;; +;; -------------------------------------------------------------------------- + +; +; Define a few macros which lets the dbg format process files intended +; for the .obj and .bin formats. +; +OUT: dbg +%define __?SECT?__ [section .text] +%imacro group 1+.nolist +[group %1] +%endmacro +%imacro uppercase 0+.nolist + %pragma dbg uppercase %1 +%endmacro +%imacro export 1+.nolist + %pragma dbg export %1 +%endmacro +%imacro import 1+.nolist + %pragma dbg import %1 +%endmacro +%imacro org 1+.nolist + %pragma dbg org %1 +%endmacro +%macro __?NASM_CDecl?__ 1 +%endmacro diff --git a/vere/ext/nasm/output/outelf.c b/vere/ext/nasm/output/outelf.c new file mode 100644 index 0000000..ad8d210 --- /dev/null +++ b/vere/ext/nasm/output/outelf.c @@ -0,0 +1,3672 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2022 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * Common code for outelf32 and outelf64 + */ + +#include "compiler.h" + + +#include "nasm.h" +#include "nasmlib.h" +#include "error.h" +#include "saa.h" +#include "raa.h" +#include "stdscan.h" +#include "eval.h" +#include "outform.h" +#include "outlib.h" +#include "rbtree.h" +#include "hashtbl.h" +#include "ver.h" + +#include "dwarf.h" +#include "stabs.h" +#include "outelf.h" +#include "elf.h" + +#if defined(OF_ELF32) || defined(OF_ELF64) || defined(OF_ELFX32) + +#define SECT_DELTA 32 +static struct elf_section **sects; +static int nsects, sectlen; + +#define SHSTR_DELTA 256 +static char *shstrtab; +static int shstrtablen, shstrtabsize; + +static struct SAA *syms; +static uint32_t nlocals, nglobs, ndebugs; /* Symbol counts */ + +static int32_t def_seg; + +static struct RAA *bsym; + +static struct SAA *symtab, *symtab_shndx; + +static struct SAA *strs; +static uint32_t strslen; + +static struct RAA *section_by_index; +static struct hash_table section_by_name; + +static struct elf_symbol *fwds; + +static char elf_module[FILENAME_MAX]; +static char elf_dir[FILENAME_MAX]; + +extern const struct ofmt of_elf32; +extern const struct ofmt of_elf64; +extern const struct ofmt of_elfx32; + +static struct ELF_SECTDATA { + void *data; + int64_t len; + bool is_saa; +} *elf_sects; + +static int elf_nsect, nsections; +static int64_t elf_foffs; + +static void elf_write(void); +static void elf_sect_write(struct elf_section *, const void *, size_t); +static void elf_sect_writeaddr(struct elf_section *, int64_t, size_t); +static void elf_section_header(int name, int type, uint64_t flags, + void *data, bool is_saa, uint64_t datalen, + int link, int info, + uint64_t align, uint64_t entsize); +static void elf_write_sections(void); +static size_t elf_build_symtab(void); +static int add_sectname(const char *, const char *); + +/* First debugging section index */ +static int sec_debug; + +struct symlininfo { + int offset; + int section; /* index into sects[] */ + int segto; /* internal section number */ + char *name; /* shallow-copied pointer of section name */ +}; + +struct linelist { + struct linelist *next; + struct linelist *last; + struct symlininfo info; + char *filename; + int line; +}; + +struct sectlist { + struct SAA *psaa; + int section; + int line; + int offset; + int file; + struct sectlist *next; + struct sectlist *last; +}; + +/* common debug variables */ +static int currentline = 1; +static int debug_immcall = 0; + +/* stabs debug variables */ +static struct linelist *stabslines = 0; +static int numlinestabs = 0; +static char *stabs_filename = 0; +static uint8_t *stabbuf = 0, *stabstrbuf = 0, *stabrelbuf = 0; +static int stablen, stabstrlen, stabrellen; + +/* dwarf debug variables */ +static struct linelist *dwarf_flist = 0, *dwarf_clist = 0, *dwarf_elist = 0; +static struct sectlist *dwarf_fsect = 0, *dwarf_csect = 0, *dwarf_esect = 0; +static int dwarf_numfiles = 0, dwarf_nsections; +static uint8_t *arangesbuf = 0, *arangesrelbuf = 0, *pubnamesbuf = 0, *infobuf = 0, *inforelbuf = 0, + *abbrevbuf = 0, *linebuf = 0, *linerelbuf = 0, *framebuf = 0, *locbuf = 0; +static int8_t line_base = -5, line_range = 14, opcode_base = 13; +static int arangeslen, arangesrellen, pubnameslen, infolen, inforellen, + abbrevlen, linelen, linerellen, framelen, loclen; +static int64_t dwarf_infosym, dwarf_abbrevsym, dwarf_linesym; + +static struct elf_symbol *lastsym; + +/* common debugging routines */ +static void debug_typevalue(int32_t); + +/* stabs debugging routines */ +static void stabs_linenum(const char *filename, int32_t linenumber, int32_t); +static void stabs_output(int, void *); +static void stabs_generate(void); +static void stabs_cleanup(void); + +/* dwarf debugging routines */ + +/* This should match the order in elf_write() */ +enum dwarf_sect { + DWARF_ARANGES, + DWARF_RELA_ARANGES, + DWARF_PUBNAMES, + DWARF_INFO, + DWARF_RELA_INFO, + DWARF_ABBREV, + DWARF_LINE, + DWARF_RELA_LINE, + DWARF_FRAME, + DWARF_LOC, + DWARF_NSECT +}; + +struct dwarf_format { + uint16_t dwarf_version; + uint16_t sect_version[DWARF_NSECT]; + /* ... add more here to generalize further */ +}; +const struct dwarf_format *dwfmt; + +static void dwarf32_init(void); +static void dwarfx32_init(void); +static void dwarf64_init(void); +static void dwarf_linenum(const char *filename, int32_t linenumber, int32_t); +static void dwarf_output(int, void *); +static void dwarf_generate(void); +static void dwarf_cleanup(void); +static void dwarf_findfile(const char *); +static void dwarf_findsect(const int); + +struct elf_format_info { + size_t word; /* Word size (4 or 8) */ + size_t ehdr_size; /* Size of the ELF header */ + size_t shdr_size; /* Size of a section header */ + size_t sym_size; /* Size of a symbol */ + size_t relsize; /* Size of a reltype relocation */ + char relpfx[8]; /* Relocation section prefix */ + uint32_t reltype; /* Relocation section type */ + uint16_t e_machine; /* Header e_machine field */ + uint8_t ei_class; /* ELFCLASS32 or ELFCLASS64 */ + bool elf64; /* 64-bit ELF */ + + /* Write a symbol */ + void (*elf_sym)(const struct elf_symbol *); + + /* Build a relocation table */ + struct SAA *(*elf_build_reltab)(const struct elf_reloc *); +}; +static const struct elf_format_info *efmt; + +static void elf32_sym(const struct elf_symbol *sym); +static void elf64_sym(const struct elf_symbol *sym); + +static struct SAA *elf32_build_reltab(const struct elf_reloc *r); +static struct SAA *elfx32_build_reltab(const struct elf_reloc *r); +static struct SAA *elf64_build_reltab(const struct elf_reloc *r); + +static bool dfmt_is_stabs(void); +static bool dfmt_is_dwarf(void); + +/* + * Special NASM section numbers which are used to define ELF special + * symbols. + */ +static int32_t elf_gotpc_sect, elf_gotoff_sect; +static int32_t elf_got_sect, elf_plt_sect; +static int32_t elf_sym_sect, elf_gottpoff_sect, elf_tlsie_sect; + +uint8_t elf_osabi = 0; /* Default OSABI = 0 (System V or Linux) */ +uint8_t elf_abiver = 0; /* Current ABI version */ + +/* Known sections with nonstandard defaults. -n means n*pointer size. */ +struct elf_known_section { + const char *name; /* Name of section */ + int type; /* Section type (SHT_) */ + uint32_t flags; /* Section flags (SHF_) */ + int align; /* Section alignment */ + int entsize; /* Entry size, if applicable */ +}; + +static const struct elf_known_section elf_known_sections[] = { + { ".text", SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR, 16, 0 }, + { ".rodata", SHT_PROGBITS, SHF_ALLOC, 4, 0 }, + { ".lrodata", SHT_PROGBITS, SHF_ALLOC, 4, 0 }, + { ".data", SHT_PROGBITS, SHF_ALLOC|SHF_WRITE, 4, 0 }, + { ".ldata", SHT_PROGBITS, SHF_ALLOC|SHF_WRITE, 4, 0 }, + { ".bss", SHT_NOBITS, SHF_ALLOC|SHF_WRITE, 4, 0 }, + { ".lbss", SHT_NOBITS, SHF_ALLOC|SHF_WRITE, 4, 0 }, + { ".tdata", SHT_PROGBITS, SHF_ALLOC|SHF_WRITE|SHF_TLS, 4, 0 }, + { ".tbss", SHT_NOBITS, SHF_ALLOC|SHF_WRITE|SHF_TLS, 4, 0 }, + { ".comment", SHT_PROGBITS, 0, 1, 0 }, + { ".preinit_array", SHT_PREINIT_ARRAY, SHF_ALLOC, -1, -1 }, + { ".init_array", SHT_INIT_ARRAY, SHF_ALLOC, -1, -1 }, + { ".fini_array", SHT_FINI_ARRAY, SHF_ALLOC, -1, -1 }, + { ".note", SHT_NOTE, 0, 4, 0 }, + { NULL /*default*/, SHT_PROGBITS, SHF_ALLOC, 1, 0 } +}; + +struct size_unit { + char name[8]; + int bytes; + int align; +}; +static const struct size_unit size_units[] = +{ + { "byte", 1, 1 }, + { "word", 2, 2 }, + { "dword", 4, 4 }, + { "qword", 8, 8 }, + { "tword", 10, 2 }, + { "tbyte", 10, 2 }, + { "oword", 16, 16 }, + { "xword", 16, 16 }, + { "yword", 32, 32 }, + { "zword", 64, 64 }, + { "pointer", -1, -1 }, + { "", 0, 0 } +}; + +static inline size_t to_bytes(int val) +{ + return (val >= 0) ? (size_t)val : -val * efmt->word; +} + +/* parse section attributes */ +static void elf_section_attrib(char *name, char *attr, uint32_t *flags_and, uint32_t *flags_or, + uint64_t *alignp, uint64_t *entsize, int *type) +{ + char *opt, *val, *next; + uint64_t align = 0; + uint64_t xalign = 0; + + opt = nasm_skip_spaces(attr); + if (!opt || !*opt) + return; + + while ((opt = nasm_opt_val(opt, &val, &next))) { + if (!nasm_stricmp(opt, "align")) { + if (!val) { + nasm_nonfatal("section align without value specified"); + } else { + bool err; + uint64_t a = readnum(val, &err); + if (a && !is_power2(a)) { + nasm_error(ERR_NONFATAL, + "section alignment %"PRId64" is not a power of two", + a); + } else if (a > align) { + align = a; + } + } + } else if (!nasm_stricmp(opt, "alloc")) { + *flags_and |= SHF_ALLOC; + *flags_or |= SHF_ALLOC; + } else if (!nasm_stricmp(opt, "noalloc")) { + *flags_and |= SHF_ALLOC; + *flags_or &= ~SHF_ALLOC; + } else if (!nasm_stricmp(opt, "exec")) { + *flags_and |= SHF_EXECINSTR; + *flags_or |= SHF_EXECINSTR; + } else if (!nasm_stricmp(opt, "noexec")) { + *flags_and |= SHF_EXECINSTR; + *flags_or &= ~SHF_EXECINSTR; + } else if (!nasm_stricmp(opt, "write")) { + *flags_and |= SHF_WRITE; + *flags_or |= SHF_WRITE; + } else if (!nasm_stricmp(opt, "nowrite") || + !nasm_stricmp(opt, "readonly")) { + *flags_and |= SHF_WRITE; + *flags_or &= ~SHF_WRITE; + } else if (!nasm_stricmp(opt, "tls")) { + *flags_and |= SHF_TLS; + *flags_or |= SHF_TLS; + } else if (!nasm_stricmp(opt, "notls")) { + *flags_and |= SHF_TLS; + *flags_or &= ~SHF_TLS; + } else if (!nasm_stricmp(opt, "merge")) { + *flags_and |= SHF_MERGE; + *flags_or |= SHF_MERGE; + } else if (!nasm_stricmp(opt, "nomerge")) { + *flags_and |= SHF_MERGE; + *flags_or &= ~SHF_MERGE; + } else if (!nasm_stricmp(opt, "strings")) { + *flags_and |= SHF_STRINGS; + *flags_or |= SHF_STRINGS; + } else if (!nasm_stricmp(opt, "nostrings")) { + *flags_and |= SHF_STRINGS; + *flags_or &= ~SHF_STRINGS; + } else if (!nasm_stricmp(opt, "progbits")) { + *type = SHT_PROGBITS; + } else if (!nasm_stricmp(opt, "nobits")) { + *type = SHT_NOBITS; + } else if (!nasm_stricmp(opt, "note")) { + *type = SHT_NOTE; + } else if (!nasm_stricmp(opt, "preinit_array")) { + *type = SHT_PREINIT_ARRAY; + } else if (!nasm_stricmp(opt, "init_array")) { + *type = SHT_INIT_ARRAY; + } else if (!nasm_stricmp(opt, "fini_array")) { + *type = SHT_FINI_ARRAY; + } else { + uint64_t mult; + size_t l; + const char *a = strchr(opt, '*'); + bool err; + const struct size_unit *su; + + if (a) { + l = a - opt - 1; + mult = readnum(a+1, &err); + } else { + l = strlen(opt); + mult = 1; + } + + for (su = size_units; su->bytes; su++) { + if (!nasm_strnicmp(opt, su->name, l)) + break; + } + + if (su->bytes) { + *entsize = to_bytes(su->bytes) * mult; + xalign = to_bytes(su->align); + } else { + /* Unknown attribute */ + nasm_warn(WARN_OTHER, + "unknown section attribute '%s' ignored on" + " declaration of section `%s'", opt, name); + } + } + opt = next; + } + + switch (*type) { + case SHT_PREINIT_ARRAY: + case SHT_INIT_ARRAY: + case SHT_FINI_ARRAY: + if (!xalign) + xalign = efmt->word; + if (!*entsize) + *entsize = efmt->word; + break; + default: + break; + } + + if (!align) + align = xalign; + if (!align) + align = SHA_ANY; + + *alignp = align; +} + +static enum directive_result +elf_directive(enum directive directive, char *value) +{ + int64_t n; + bool err; + char *p; + + switch (directive) { + case D_OSABI: + if (!pass_first()) /* XXX: Why? */ + return DIRR_OK; + + n = readnum(value, &err); + if (err) { + nasm_nonfatal("`osabi' directive requires a parameter"); + return DIRR_ERROR; + } + + if (n < 0 || n > 255) { + nasm_nonfatal("valid osabi numbers are 0 to 255"); + return DIRR_ERROR; + } + + elf_osabi = n; + elf_abiver = 0; + + p = strchr(value,','); + if (!p) + return DIRR_OK; + + n = readnum(p + 1, &err); + if (err || n < 0 || n > 255) { + nasm_nonfatal("invalid ABI version number (valid: 0 to 255)"); + return DIRR_ERROR; + } + + elf_abiver = n; + return DIRR_OK; + + default: + return DIRR_UNKNOWN; + } +} + +static void elf_init(void); + +static void elf32_init(void) +{ + static const struct elf_format_info ef_elf32 = { + 4, + sizeof(Elf32_Ehdr), + sizeof(Elf32_Shdr), + sizeof(Elf32_Sym), + sizeof(Elf32_Rel), + ".rel", + SHT_REL, + EM_386, + ELFCLASS32, + false, + + elf32_sym, + elf32_build_reltab + }; + efmt = &ef_elf32; + elf_init(); +} + +static void elfx32_init(void) +{ + static const struct elf_format_info ef_elfx32 = { + 4, + sizeof(Elf32_Ehdr), + sizeof(Elf32_Shdr), + sizeof(Elf32_Sym), + sizeof(Elf32_Rela), + ".rela", + SHT_RELA, + EM_X86_64, + ELFCLASS32, + false, + + elf32_sym, + elfx32_build_reltab + }; + efmt = &ef_elfx32; + elf_init(); +} + +static void elf64_init(void) +{ + static const struct elf_format_info ef_elf64 = { + 8, + sizeof(Elf64_Ehdr), + sizeof(Elf64_Shdr), + sizeof(Elf64_Sym), + sizeof(Elf64_Rela), + ".rela", + SHT_RELA, + EM_X86_64, + ELFCLASS64, + true, + + elf64_sym, + elf64_build_reltab + }; + efmt = &ef_elf64; + elf_init(); +} + +static void elf_init(void) +{ + static const char * const reserved_sections[] = { + ".shstrtab", ".strtab", ".symtab", ".symtab_shndx", NULL + }; + const char * const *p; + const char * cur_path = nasm_realpath(inname); + + strlcpy(elf_module, inname, sizeof(elf_module)); + strlcpy(elf_dir, nasm_dirname(cur_path), sizeof(elf_dir)); + sects = NULL; + nsects = sectlen = 0; + syms = saa_init((int32_t)sizeof(struct elf_symbol)); + nlocals = nglobs = ndebugs = 0; + bsym = raa_init(); + strs = saa_init(1L); + saa_wbytes(strs, "\0", 1L); + saa_wbytes(strs, elf_module, strlen(elf_module)+1); + strslen = 2 + strlen(elf_module); + shstrtab = NULL; + shstrtablen = shstrtabsize = 0;; + add_sectname("", ""); /* SHN_UNDEF */ + + fwds = NULL; + + section_by_index = raa_init(); + + /* + * Add reserved section names to the section hash, with NULL + * as the data pointer + */ + for (p = reserved_sections; *p; p++) { + struct hash_insert hi; + hash_find(§ion_by_name, *p, &hi); + hash_add(&hi, *p, NULL); + } + + /* + * FIXME: tlsie is Elf32 only and + * gottpoff is Elfx32|64 only. + */ + elf_gotpc_sect = seg_alloc(); + backend_label("..gotpc", elf_gotpc_sect + 1, 0L); + elf_gotoff_sect = seg_alloc(); + backend_label("..gotoff", elf_gotoff_sect + 1, 0L); + elf_got_sect = seg_alloc(); + backend_label("..got", elf_got_sect + 1, 0L); + elf_plt_sect = seg_alloc(); + backend_label("..plt", elf_plt_sect + 1, 0L); + elf_sym_sect = seg_alloc(); + backend_label("..sym", elf_sym_sect + 1, 0L); + elf_gottpoff_sect = seg_alloc(); + backend_label("..gottpoff", elf_gottpoff_sect + 1, 0L); + elf_tlsie_sect = seg_alloc(); + backend_label("..tlsie", elf_tlsie_sect + 1, 0L); + + def_seg = seg_alloc(); +} + +static void elf_cleanup(void) +{ + struct elf_reloc *r; + int i; + + elf_write(); + for (i = 0; i < nsects; i++) { + if (sects[i]->type != SHT_NOBITS) + saa_free(sects[i]->data); + if (sects[i]->rel) + saa_free(sects[i]->rel); + while (sects[i]->head) { + r = sects[i]->head; + sects[i]->head = sects[i]->head->next; + nasm_free(r); + } + } + hash_free(§ion_by_name); + raa_free(section_by_index); + nasm_free(sects); + saa_free(syms); + raa_free(bsym); + saa_free(strs); + dfmt->cleanup(); +} + +/* + * Add entry to the elf .shstrtab section and increment nsections. + * Returns the section index for this new section. + * + * IMPORTANT: this needs to match the order the section headers are + * emitted. + */ +static int add_sectname(const char *firsthalf, const char *secondhalf) +{ + int l1 = strlen(firsthalf); + int l2 = strlen(secondhalf); + + while (shstrtablen + l1 + l2 + 1 > shstrtabsize) + shstrtab = nasm_realloc(shstrtab, (shstrtabsize += SHSTR_DELTA)); + + memcpy(shstrtab + shstrtablen, firsthalf, l1); + shstrtablen += l1; + memcpy(shstrtab + shstrtablen, secondhalf, l2+1); + shstrtablen += l2 + 1; + + return nsections++; +} + +static struct elf_section * +elf_make_section(char *name, int type, int flags, uint64_t align) +{ + struct elf_section *s; + + s = nasm_zalloc(sizeof(*s)); + + if (type != SHT_NOBITS) + s->data = saa_init(1L); + s->tail = &s->head; + if (!strcmp(name, ".text")) + s->index = def_seg; + else + s->index = seg_alloc(); + + s->name = nasm_strdup(name); + s->type = type; + s->flags = flags; + s->align = align; + s->shndx = add_sectname("", name); + + if (nsects >= sectlen) + sects = nasm_realloc(sects, (sectlen += SECT_DELTA) * sizeof(*sects)); + sects[nsects++] = s; + + return s; +} + +static int32_t elf_section_names(char *name, int *bits) +{ + char *p; + uint32_t flags, flags_and, flags_or; + uint64_t align, entsize; + void **hp; + struct elf_section *s; + struct hash_insert hi; + int type; + + if (!name) { + *bits = ofmt->maxbits; + return def_seg; + } + + p = nasm_skip_word(name); + if (*p) + *p++ = '\0'; + flags_and = flags_or = type = align = entsize = 0; + + elf_section_attrib(name, p, &flags_and, &flags_or, &align, &entsize, &type); + + hp = hash_find(§ion_by_name, name, &hi); + if (hp) { + s = *hp; + if (!s) { + nasm_nonfatal("attempt to redefine reserved section name `%s'", name); + return NO_SEG; + } + } else { + const struct elf_known_section *ks = elf_known_sections; + + while (ks->name) { + if (!strcmp(name, ks->name)) + break; + ks++; + } + + type = type ? type : ks->type; + if (!align) + align = to_bytes(ks->align); + if (!entsize) + entsize = to_bytes(ks->entsize); + flags = (ks->flags & ~flags_and) | flags_or; + + s = elf_make_section(name, type, flags, align); + hash_add(&hi, s->name, s); + section_by_index = raa_write_ptr(section_by_index, s->index >> 1, s); + } + + if ((type && s->type != type) + || ((s->flags & flags_and) != flags_or) + || (entsize && s->entsize && entsize != s->entsize)) { + nasm_warn(WARN_OTHER, "incompatible section attributes ignored on" + " redeclaration of section `%s'", name); + } + + if (align > s->align) + s->align = align; + + if (entsize && !s->entsize) + s->entsize = entsize; + + if ((flags_or & SHF_MERGE) && s->entsize == 0) { + if (!(s->flags & SHF_STRINGS)) + nasm_nonfatal("section attribute merge specified without an entry size or `strings'"); + s->entsize = 1; + } + + return s->index; +} + +static inline bool sym_type_local(int type) +{ + return ELF32_ST_BIND(type) == STB_LOCAL; +} + +static void elf_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special) +{ + int pos = strslen; + struct elf_symbol *sym; + const char *spcword = nasm_skip_spaces(special); + int bind, type; /* st_info components */ + const struct elf_section *sec = NULL; + + if (debug_level(2)) { + nasm_debug(" elf_deflabel: %s, seg=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n", + name, segment, offset, is_global, special); + } + + if (name[0] == '.' && name[1] == '.' && name[2] != '@') { + /* + * This is a NASM special symbol. We never allow it into + * the ELF symbol table, even if it's a valid one. If it + * _isn't_ a valid one, we should barf immediately. + * + * FIXME: tlsie is Elf32 only, and gottpoff is Elfx32|64 only. + */ + if (strcmp(name, "..gotpc") && strcmp(name, "..gotoff") && + strcmp(name, "..got") && strcmp(name, "..plt") && + strcmp(name, "..sym") && strcmp(name, "..gottpoff") && + strcmp(name, "..tlsie")) + nasm_nonfatal("unrecognised special symbol `%s'", name); + return; + } + + if (is_global == 3) { + struct elf_symbol **s; + /* + * Fix up a forward-reference symbol size from the first + * pass. + */ + for (s = &fwds; *s; s = &(*s)->nextfwd) + if (!strcmp((*s)->name, name)) { + struct tokenval tokval; + expr *e; + char *p = nasm_skip_spaces(nasm_skip_word(special)); + + stdscan_reset(); + stdscan_set(p); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, NULL, 1, NULL); + if (e) { + if (!is_simple(e)) + nasm_nonfatal("cannot use relocatable" + " expression as symbol size"); + else + (*s)->size = reloc_value(e); + } + + /* + * Remove it from the list of unresolved sizes. + */ + nasm_free((*s)->name); + *s = (*s)->nextfwd; + return; + } + return; /* it wasn't an important one */ + } + + saa_wbytes(strs, name, (int32_t)(1 + strlen(name))); + strslen += 1 + strlen(name); + + lastsym = sym = saa_wstruct(syms); + + memset(&sym->symv, 0, sizeof(struct rbtree)); + + sym->strpos = pos; + bind = is_global ? STB_GLOBAL : STB_LOCAL; + type = STT_NOTYPE; + sym->other = STV_DEFAULT; + sym->size = 0; + if (segment == NO_SEG) { + sym->section = XSHN_ABS; + } else { + sym->section = XSHN_UNDEF; + if (segment == def_seg) { + /* we have to be sure at least text section is there */ + int tempint; + if (segment != elf_section_names(".text", &tempint)) + nasm_panic("strange segment conditions in ELF driver"); + } + sec = raa_read_ptr(section_by_index, segment >> 1); + if (sec) + sym->section = sec->shndx; + } + + if (is_global == 2) { + sym->size = offset; + sym->symv.key = 0; + sym->section = XSHN_COMMON; + /* + * We have a common variable. Check the special text to see + * if it's a valid number and power of two; if so, store it + * as the alignment for the common variable. + * + * XXX: this should allow an expression. + */ + if (spcword) { + bool err; + sym->symv.key = readnum(spcword, &err); + if (err) + nasm_nonfatal("alignment constraint `%s' is not a" + " valid number", special); + else if (!is_power2(sym->symv.key)) + nasm_nonfatal("alignment constraint `%s' is not a" + " power of two", special); + spcword = nasm_skip_spaces(nasm_skip_word(spcword)); + } + } else { + sym->symv.key = (sym->section == XSHN_UNDEF ? 0 : offset); + } + + if (spcword && *spcword) { + const char *wend; + bool ok = true; + + while (ok) { + size_t wlen; + wend = nasm_skip_word(spcword); + wlen = wend - spcword; + + switch (wlen) { + case 4: + if (!nasm_strnicmp(spcword, "data", wlen)) + type = STT_OBJECT; + else if (!nasm_strnicmp(spcword, "weak", wlen)) + bind = STB_WEAK; + else + ok = false; + break; + + case 6: + if (!nasm_strnicmp(spcword, "notype", wlen)) + type = STT_NOTYPE; + else if (!nasm_strnicmp(spcword, "object", wlen)) + type = STT_OBJECT; + else if (!nasm_strnicmp(spcword, "hidden", wlen)) + sym->other = STV_HIDDEN; + else if (!nasm_strnicmp(spcword, "strong", wlen)) + bind = STB_GLOBAL; + else + ok = false; + break; + + case 7: + if (!nasm_strnicmp(spcword, "default", wlen)) + sym->other = STV_DEFAULT; + else + ok = false; + break; + + case 8: + if (!nasm_strnicmp(spcword, "function", wlen)) + type = STT_FUNC; + else if (!nasm_stricmp(spcword, "internal")) + sym->other = STV_INTERNAL; + else + ok = false; + break; + + case 9: + if (!nasm_strnicmp(spcword, "protected", wlen)) + sym->other = STV_PROTECTED; + else + ok = false; + break; + + default: + ok = false; + break; + } + + if (ok) + spcword = nasm_skip_spaces(wend); + } + if (!is_global && bind != STB_LOCAL) { + nasm_nonfatal("weak and strong only applies to global symbols"); + bind = STB_LOCAL; + } + + if (spcword && *spcword) { + struct tokenval tokval; + expr *e; + int fwd = 0; + char *saveme = stdscan_get(); + + /* + * We have a size expression; attempt to + * evaluate it. + */ + stdscan_reset(); + stdscan_set((char *)spcword); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, &fwd, 0, NULL); + if (fwd) { + sym->nextfwd = fwds; + fwds = sym; + sym->name = nasm_strdup(name); + } else if (e) { + if (!is_simple(e)) + nasm_nonfatal("cannot use relocatable" + " expression as symbol size"); + else + sym->size = reloc_value(e); + } + stdscan_set(saveme); + } + } + + /* + * If it is in a TLS segment, mark symbol accordingly. + */ + if (sec && (sec->flags & SHF_TLS)) + type = STT_TLS; + + /* Note: ELF32_ST_INFO() and ELF64_ST_INFO() are identical */ + sym->type = ELF32_ST_INFO(bind, type); + + if (sym_type_local(sym->type)) { + nlocals++; + } else { + /* + * If sym->section == SHN_ABS, then the first line of the + * else section would cause a core dump, because its a reference + * beyond the end of the section array. + * This behaviour is exhibited by this code: + * GLOBAL crash_nasm + * crash_nasm equ 0 + * To avoid such a crash, such requests are silently discarded. + * This may not be the best solution. + */ + if (sym->section == XSHN_UNDEF || sym->section == XSHN_COMMON) { + bsym = raa_write(bsym, segment, nglobs); + } else if (sym->section != XSHN_ABS) { + /* + * This is a global symbol; so we must add it to the rbtree + * of global symbols in its section. + * + * In addition, we check the special text for symbol + * type and size information. + */ + sects[sym->section-1]->gsyms = + rb_insert(sects[sym->section-1]->gsyms, &sym->symv); + + } + sym->globnum = nglobs; + nglobs++; + } +} + +static void elf_add_reloc(struct elf_section *sect, int32_t segment, + int64_t offset, int type) +{ + struct elf_reloc *r; + + r = *sect->tail = nasm_zalloc(sizeof(struct elf_reloc)); + sect->tail = &r->next; + + r->address = sect->len; + r->offset = offset; + + if (segment != NO_SEG) { + const struct elf_section *s; + s = raa_read_ptr(section_by_index, segment >> 1); + if (s) + r->symbol = s->shndx + 1; + else + r->symbol = GLOBAL_TEMP_BASE + raa_read(bsym, segment); + } + r->type = type; + + sect->nrelocs++; +} + +/* + * This routine deals with ..got and ..sym relocations: the more + * complicated kinds. In shared-library writing, some relocations + * with respect to global symbols must refer to the precise symbol + * rather than referring to an offset from the base of the section + * _containing_ the symbol. Such relocations call to this routine, + * which searches the symbol list for the symbol in question. + * + * R_386_GOT32 | R_X86_64_GOT32 references require the _exact_ symbol address to be + * used; R_386_32 | R_X86_64_32 references can be at an offset from the symbol. + * The boolean argument `exact' tells us this. + * + * Return value is the adjusted value of `addr', having become an + * offset from the symbol rather than the section. Should always be + * zero when returning from an exact call. + * + * Limitation: if you define two symbols at the same place, + * confusion will occur. + * + * Inefficiency: we search, currently, using a linked list which + * isn't even necessarily sorted. + */ +static int64_t elf_add_gsym_reloc(struct elf_section *sect, + int32_t segment, uint64_t offset, + int64_t pcrel, int type, bool exact) +{ + struct elf_reloc *r; + struct elf_section *s; + struct elf_symbol *sym; + struct rbtree *srb; + + /* + * First look up the segment/offset pair and find a global + * symbol corresponding to it. If it's not one of our segments, + * then it must be an external symbol, in which case we're fine + * doing a normal elf_add_reloc after first sanity-checking + * that the offset from the symbol is zero. + */ + s = raa_read_ptr(section_by_index, segment >> 1); + if (!s) { + if (exact && offset) + nasm_nonfatal("invalid access to an external symbol"); + else + elf_add_reloc(sect, segment, offset - pcrel, type); + return 0; + } + + srb = rb_search(s->gsyms, offset); + if (!srb || (exact && srb->key != offset)) { + nasm_nonfatal("unable to find a suitable global symbol" + " for this reference"); + return 0; + } + sym = container_of(srb, struct elf_symbol, symv); + + r = *sect->tail = nasm_malloc(sizeof(struct elf_reloc)); + sect->tail = &r->next; + + r->next = NULL; + r->address = sect->len; + r->offset = offset - pcrel - sym->symv.key; + r->symbol = GLOBAL_TEMP_BASE + sym->globnum; + r->type = type; + + sect->nrelocs++; + return r->offset; +} + +static void elf32_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + struct elf_section *s; + int64_t addr; + int reltype, bytes; + static struct symlininfo sinfo; + + /* + * handle absolute-assembly (structure definitions) + */ + if (segto == NO_SEG) { + if (type != OUT_RESERVE) + nasm_nonfatal("attempt to assemble code in [ABSOLUTE] space"); + return; + } + + s = raa_read_ptr(section_by_index, segto >> 1); + if (!s) { + int tempint; /* ignored */ + if (segto != elf_section_names(".text", &tempint)) + nasm_panic("strange segment conditions in ELF driver"); + else + s = sects[nsects - 1]; + } + + /* again some stabs debugging stuff */ + sinfo.offset = s->len; + /* Adjust to an index of the section table. */ + sinfo.section = s->shndx - 1; + sinfo.segto = segto; + sinfo.name = s->name; + dfmt->debug_output(TY_DEBUGSYMLIN, &sinfo); + /* end of debugging stuff */ + + if (s->type == SHT_NOBITS && type != OUT_RESERVE) { + nasm_warn(WARN_OTHER, "attempt to initialize memory in" + " BSS section `%s': ignored", s->name); + s->len += realsize(type, size); + return; + } + + switch (type) { + case OUT_RESERVE: + if (s->type != SHT_NOBITS) { + nasm_warn(WARN_ZEROING, "uninitialized space declared in" + " non-BSS section `%s': zeroing", s->name); + elf_sect_write(s, NULL, size); + } else + s->len += size; + break; + + case OUT_RAWDATA: + elf_sect_write(s, data, size); + break; + + case OUT_ADDRESS: + { + bool err = false; + int asize = abs((int)size); + + addr = *(int64_t *)data; + if (segment != NO_SEG) { + if (segment & 1) { + nasm_nonfatal("ELF format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + /* + * The if() is a hack to deal with compilers which + * don't handle switch() statements with 64-bit + * expressions. + */ + switch (asize) { + case 1: + elf_add_reloc(s, segment, 0, R_386_8); + break; + case 2: + elf_add_reloc(s, segment, 0, R_386_16); + break; + case 4: + elf_add_reloc(s, segment, 0, R_386_32); + break; + default: /* Error issued further down */ + err = true; + break; + } + } else if (wrt == elf_gotpc_sect + 1) { + /* + * The user will supply GOT relative to $$. ELF + * will let us have GOT relative to $. So we + * need to fix up the data item by $-$$. + */ + err = asize != 4; + addr += s->len; + elf_add_reloc(s, segment, 0, R_386_GOTPC); + } else if (wrt == elf_gotoff_sect + 1) { + err = asize != 4; + elf_add_reloc(s, segment, 0, R_386_GOTOFF); + } else if (wrt == elf_tlsie_sect + 1) { + err = asize != 4; + addr = elf_add_gsym_reloc(s, segment, addr, 0, + R_386_TLS_IE, true); + } else if (wrt == elf_got_sect + 1) { + err = asize != 4; + addr = elf_add_gsym_reloc(s, segment, addr, 0, + R_386_GOT32, true); + } else if (wrt == elf_sym_sect + 1) { + switch (asize) { + case 1: + addr = elf_add_gsym_reloc(s, segment, addr, 0, + R_386_8, false); + break; + case 2: + addr = elf_add_gsym_reloc(s, segment, addr, 0, + R_386_16, false); + break; + case 4: + addr = elf_add_gsym_reloc(s, segment, addr, 0, + R_386_32, false); + break; + default: + err = true; + break; + } + } else if (wrt == elf_plt_sect + 1) { + nasm_nonfatal("ELF format cannot produce non-PC-" + "relative PLT references"); + } else { + nasm_nonfatal("ELF format does not support this" + " use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } + } + } + + if (err) { + nasm_nonfatal("Unsupported %d-bit ELF relocation", asize << 3); + } + elf_sect_writeaddr(s, addr, asize); + break; + } + + case OUT_REL1ADR: + reltype = R_386_PC8; + bytes = 1; + goto rel12adr; + case OUT_REL2ADR: + reltype = R_386_PC16; + bytes = 2; + goto rel12adr; + +rel12adr: + addr = *(int64_t *)data - size; + nasm_assert(segment != segto); + if (segment != NO_SEG && (segment & 1)) { + nasm_nonfatal("ELF format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + elf_add_reloc(s, segment, 0, reltype); + } else { + nasm_nonfatal("Unsupported %d-bit ELF relocation", bytes << 3); + } + } + elf_sect_writeaddr(s, addr, bytes); + break; + + case OUT_REL4ADR: + addr = *(int64_t *)data - size; + if (segment == segto) + nasm_panic("intra-segment OUT_REL4ADR"); + if (segment != NO_SEG && (segment & 1)) { + nasm_nonfatal("ELF format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + elf_add_reloc(s, segment, 0, R_386_PC32); + } else if (wrt == elf_plt_sect + 1) { + elf_add_reloc(s, segment, 0, R_386_PLT32); + } else if (wrt == elf_gotpc_sect + 1 || + wrt == elf_gotoff_sect + 1 || + wrt == elf_got_sect + 1) { + nasm_nonfatal("ELF format cannot produce PC-" + "relative GOT references"); + } else { + nasm_nonfatal("ELF format does not support this" + " use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } + } + elf_sect_writeaddr(s, addr, 4); + break; + + case OUT_REL8ADR: + nasm_nonfatal("32-bit ELF format does not support 64-bit relocations"); + addr = 0; + elf_sect_writeaddr(s, addr, 8); + break; + + default: + panic(); + } +} +static void elf64_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + struct elf_section *s; + int64_t addr; + int reltype, bytes; + static struct symlininfo sinfo; + + /* + * handle absolute-assembly (structure definitions) + */ + if (segto == NO_SEG) { + if (type != OUT_RESERVE) + nasm_nonfatal("attempt to assemble code in [ABSOLUTE] space"); + return; + } + + s = raa_read_ptr(section_by_index, segto >> 1); + if (!s) { + int tempint; /* ignored */ + if (segto != elf_section_names(".text", &tempint)) + nasm_panic("strange segment conditions in ELF driver"); + else + s = sects[nsects - 1]; + } + + /* again some stabs debugging stuff */ + sinfo.offset = s->len; + /* Adjust to an index of the section table. */ + sinfo.section = s->shndx - 1; + sinfo.segto = segto; + sinfo.name = s->name; + dfmt->debug_output(TY_DEBUGSYMLIN, &sinfo); + /* end of debugging stuff */ + + if (s->type == SHT_NOBITS && type != OUT_RESERVE) { + nasm_warn(WARN_OTHER, "attempt to initialize memory in" + " BSS section `%s': ignored", s->name); + s->len += realsize(type, size); + return; + } + + switch (type) { + case OUT_RESERVE: + if (s->type != SHT_NOBITS) { + nasm_warn(WARN_ZEROING, "uninitialized space declared in" + " non-BSS section `%s': zeroing", s->name); + elf_sect_write(s, NULL, size); + } else + s->len += size; + break; + + case OUT_RAWDATA: + if (segment != NO_SEG) + nasm_panic("OUT_RAWDATA with other than NO_SEG"); + elf_sect_write(s, data, size); + break; + + case OUT_ADDRESS: + { + int isize = (int)size; + int asize = abs((int)size); + + addr = *(int64_t *)data; + if (segment == NO_SEG) { + /* Do nothing */ + } else if (segment & 1) { + nasm_nonfatal("ELF format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + switch (isize) { + case 1: + case -1: + elf_add_reloc(s, segment, addr, R_X86_64_8); + break; + case 2: + case -2: + elf_add_reloc(s, segment, addr, R_X86_64_16); + break; + case 4: + elf_add_reloc(s, segment, addr, R_X86_64_32); + break; + case -4: + elf_add_reloc(s, segment, addr, R_X86_64_32S); + break; + case 8: + case -8: + elf_add_reloc(s, segment, addr, R_X86_64_64); + break; + default: + nasm_panic("internal error elf64-hpa-871"); + break; + } + addr = 0; + } else if (wrt == elf_gotpc_sect + 1) { + /* + * The user will supply GOT relative to $$. ELF + * will let us have GOT relative to $. So we + * need to fix up the data item by $-$$. + */ + addr += s->len; + elf_add_reloc(s, segment, addr, R_X86_64_GOTPC32); + addr = 0; + } else if (wrt == elf_gotoff_sect + 1) { + if (asize != 8) { + nasm_nonfatal("ELF64 requires ..gotoff " + "references to be qword"); + } else { + elf_add_reloc(s, segment, addr, R_X86_64_GOTOFF64); + addr = 0; + } + } else if (wrt == elf_got_sect + 1) { + switch (asize) { + case 4: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_GOT32, true); + addr = 0; + break; + case 8: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_GOT64, true); + addr = 0; + break; + default: + nasm_nonfatal("invalid ..got reference"); + break; + } + } else if (wrt == elf_sym_sect + 1) { + switch (isize) { + case 1: + case -1: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_8, false); + addr = 0; + break; + case 2: + case -2: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_16, false); + addr = 0; + break; + case 4: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_32, false); + addr = 0; + break; + case -4: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_32S, false); + addr = 0; + break; + case 8: + case -8: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_64, false); + addr = 0; + break; + default: + nasm_panic("internal error elf64-hpa-903"); + break; + } + } else if (wrt == elf_plt_sect + 1) { + nasm_nonfatal("ELF format cannot produce non-PC-" + "relative PLT references"); + } else { + nasm_nonfatal("ELF format does not support this" + " use of WRT"); + } + } + elf_sect_writeaddr(s, addr, asize); + break; + } + + case OUT_REL1ADR: + reltype = R_X86_64_PC8; + bytes = 1; + goto rel12adr; + + case OUT_REL2ADR: + reltype = R_X86_64_PC16; + bytes = 2; + goto rel12adr; + +rel12adr: + addr = *(int64_t *)data - size; + if (segment == segto) + nasm_panic("intra-segment OUT_REL1ADR"); + if (segment == NO_SEG) { + /* Do nothing */ + } else if (segment & 1) { + nasm_nonfatal("ELF format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + elf_add_reloc(s, segment, addr, reltype); + addr = 0; + } else { + nasm_nonfatal("Unsupported %d-bit ELF relocation", bytes << 3); + } + } + elf_sect_writeaddr(s, addr, bytes); + break; + + case OUT_REL4ADR: + addr = *(int64_t *)data - size; + if (segment == segto) + nasm_panic("intra-segment OUT_REL4ADR"); + if (segment == NO_SEG) { + /* Do nothing */ + } else if (segment & 1) { + nasm_nonfatal("ELF64 format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + elf_add_reloc(s, segment, addr, R_X86_64_PC32); + addr = 0; + } else if (wrt == elf_plt_sect + 1) { + elf_add_gsym_reloc(s, segment, addr+size, size, + R_X86_64_PLT32, true); + addr = 0; + } else if (wrt == elf_gotpc_sect + 1 || + wrt == elf_got_sect + 1) { + elf_add_gsym_reloc(s, segment, addr+size, size, + R_X86_64_GOTPCREL, true); + addr = 0; + } else if (wrt == elf_gotoff_sect + 1 || + wrt == elf_got_sect + 1) { + nasm_nonfatal("ELF64 requires ..gotoff references to be " + "qword absolute"); + } else if (wrt == elf_gottpoff_sect + 1) { + elf_add_gsym_reloc(s, segment, addr+size, size, + R_X86_64_GOTTPOFF, true); + addr = 0; + } else { + nasm_nonfatal("ELF64 format does not support this" + " use of WRT"); + } + } + elf_sect_writeaddr(s, addr, 4); + break; + + case OUT_REL8ADR: + addr = *(int64_t *)data - size; + if (segment == segto) + nasm_panic("intra-segment OUT_REL8ADR"); + if (segment == NO_SEG) { + /* Do nothing */ + } else if (segment & 1) { + nasm_nonfatal("ELF64 format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + elf_add_reloc(s, segment, addr, R_X86_64_PC64); + addr = 0; + } else if (wrt == elf_gotpc_sect + 1 || + wrt == elf_got_sect + 1) { + elf_add_gsym_reloc(s, segment, addr+size, size, + R_X86_64_GOTPCREL64, true); + addr = 0; + } else if (wrt == elf_gotoff_sect + 1 || + wrt == elf_got_sect + 1) { + nasm_nonfatal("ELF64 requires ..gotoff references to be " + "absolute"); + } else if (wrt == elf_gottpoff_sect + 1) { + nasm_nonfatal("ELF64 requires ..gottpoff references to be " + "dword"); + } else { + nasm_nonfatal("ELF64 format does not support this" + " use of WRT"); + } + } + elf_sect_writeaddr(s, addr, 8); + break; + + default: + panic(); + } +} + +static void elfx32_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + struct elf_section *s; + int64_t addr; + int reltype, bytes; + static struct symlininfo sinfo; + + /* + * handle absolute-assembly (structure definitions) + */ + if (segto == NO_SEG) { + if (type != OUT_RESERVE) + nasm_nonfatal("attempt to assemble code in [ABSOLUTE] space"); + return; + } + + s = raa_read_ptr(section_by_index, segto >> 1); + if (!s) { + int tempint; /* ignored */ + if (segto != elf_section_names(".text", &tempint)) + nasm_panic("strange segment conditions in ELF driver"); + else + s = sects[nsects - 1]; + } + + /* again some stabs debugging stuff */ + sinfo.offset = s->len; + /* Adjust to an index of the section table. */ + sinfo.section = s->shndx - 1; + sinfo.segto = segto; + sinfo.name = s->name; + dfmt->debug_output(TY_DEBUGSYMLIN, &sinfo); + /* end of debugging stuff */ + + if (s->type == SHT_NOBITS && type != OUT_RESERVE) { + nasm_warn(WARN_OTHER, "attempt to initialize memory in" + " BSS section `%s': ignored", s->name); + s->len += realsize(type, size); + return; + } + + switch (type) { + case OUT_RESERVE: + if (s->type != SHT_NOBITS) { + nasm_warn(WARN_ZEROING, "uninitialized space declared in" + " non-BSS section `%s': zeroing", s->name); + elf_sect_write(s, NULL, size); + } else + s->len += size; + break; + + case OUT_RAWDATA: + if (segment != NO_SEG) + nasm_panic("OUT_RAWDATA with other than NO_SEG"); + elf_sect_write(s, data, size); + break; + + case OUT_ADDRESS: + { + int isize = (int)size; + int asize = abs((int)size); + + addr = *(int64_t *)data; + if (segment == NO_SEG) { + /* Do nothing */ + } else if (segment & 1) { + nasm_nonfatal("ELF format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + switch (isize) { + case 1: + case -1: + elf_add_reloc(s, segment, addr, R_X86_64_8); + break; + case 2: + case -2: + elf_add_reloc(s, segment, addr, R_X86_64_16); + break; + case 4: + elf_add_reloc(s, segment, addr, R_X86_64_32); + break; + case -4: + elf_add_reloc(s, segment, addr, R_X86_64_32S); + break; + case 8: + case -8: + elf_add_reloc(s, segment, addr, R_X86_64_64); + break; + default: + nasm_panic("internal error elfx32-hpa-871"); + break; + } + addr = 0; + } else if (wrt == elf_gotpc_sect + 1) { + /* + * The user will supply GOT relative to $$. ELF + * will let us have GOT relative to $. So we + * need to fix up the data item by $-$$. + */ + addr += s->len; + elf_add_reloc(s, segment, addr, R_X86_64_GOTPC32); + addr = 0; + } else if (wrt == elf_gotoff_sect + 1) { + nasm_nonfatal("ELFX32 doesn't support " + "R_X86_64_GOTOFF64"); + } else if (wrt == elf_got_sect + 1) { + switch (asize) { + case 4: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_GOT32, true); + addr = 0; + break; + default: + nasm_nonfatal("invalid ..got reference"); + break; + } + } else if (wrt == elf_sym_sect + 1) { + switch (isize) { + case 1: + case -1: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_8, false); + addr = 0; + break; + case 2: + case -2: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_16, false); + addr = 0; + break; + case 4: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_32, false); + addr = 0; + break; + case -4: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_32S, false); + addr = 0; + break; + case 8: + case -8: + elf_add_gsym_reloc(s, segment, addr, 0, + R_X86_64_64, false); + addr = 0; + break; + default: + nasm_panic("internal error elfx32-hpa-903"); + break; + } + } else if (wrt == elf_plt_sect + 1) { + nasm_nonfatal("ELF format cannot produce non-PC-" + "relative PLT references"); + } else { + nasm_nonfatal("ELF format does not support this" + " use of WRT"); + } + } + elf_sect_writeaddr(s, addr, asize); + break; + } + + case OUT_REL1ADR: + reltype = R_X86_64_PC8; + bytes = 1; + goto rel12adr; + + case OUT_REL2ADR: + reltype = R_X86_64_PC16; + bytes = 2; + goto rel12adr; + +rel12adr: + addr = *(int64_t *)data - size; + if (segment == segto) + nasm_panic("intra-segment OUT_REL1ADR"); + if (segment == NO_SEG) { + /* Do nothing */ + } else if (segment & 1) { + nasm_nonfatal("ELF format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + elf_add_reloc(s, segment, addr, reltype); + addr = 0; + } else { + nasm_nonfatal("unsupported %d-bit ELF relocation", bytes << 3); + } + } + elf_sect_writeaddr(s, addr, bytes); + break; + + case OUT_REL4ADR: + addr = *(int64_t *)data - size; + if (segment == segto) + nasm_panic("intra-segment OUT_REL4ADR"); + if (segment == NO_SEG) { + /* Do nothing */ + } else if (segment & 1) { + nasm_nonfatal("ELFX32 format does not support" + " segment base references"); + } else { + if (wrt == NO_SEG) { + elf_add_reloc(s, segment, addr, R_X86_64_PC32); + addr = 0; + } else if (wrt == elf_plt_sect + 1) { + elf_add_gsym_reloc(s, segment, addr+size, size, + R_X86_64_PLT32, true); + addr = 0; + } else if (wrt == elf_gotpc_sect + 1 || + wrt == elf_got_sect + 1) { + elf_add_gsym_reloc(s, segment, addr+size, size, + R_X86_64_GOTPCREL, true); + addr = 0; + } else if (wrt == elf_gotoff_sect + 1 || + wrt == elf_got_sect + 1) { + nasm_nonfatal("invalid ..gotoff reference"); + } else if (wrt == elf_gottpoff_sect + 1) { + elf_add_gsym_reloc(s, segment, addr+size, size, + R_X86_64_GOTTPOFF, true); + addr = 0; + } else { + nasm_nonfatal("ELFX32 format does not support this use of WRT"); + } + } + elf_sect_writeaddr(s, addr, 4); + break; + + case OUT_REL8ADR: + nasm_nonfatal("32-bit ELF format does not support 64-bit relocations"); + addr = 0; + elf_sect_writeaddr(s, addr, 8); + break; + + default: + panic(); + } +} + +/* + * Section index/count with a specified overflow value (usually SHN_INDEX, + * but 0 for e_shnum. + */ +static inline uint16_t elf_shndx(int section, uint16_t overflow) +{ + return cpu_to_le16(section < (int)SHN_LORESERVE ? section : overflow); +} + +struct ehdr_common { + uint8_t e_ident[EI_NIDENT]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; +}; + +union ehdr { + Elf32_Ehdr ehdr32; + Elf64_Ehdr ehdr64; + struct ehdr_common com; +}; + +static void elf_write(void) +{ + int align; + char *p; + int i; + size_t symtablocal; + int sec_shstrtab, sec_symtab, sec_strtab; + union ehdr ehdr; + + /* + * Add any sections we don't already have: + * rel/rela sections for the user sections, debug sections, and + * the ELF special sections. + */ + + sec_debug = nsections; + if (dfmt_is_stabs()) { + /* in case the debug information is wanted, just add these three sections... */ + add_sectname("", ".stab"); + add_sectname("", ".stabstr"); + add_sectname(efmt->relpfx, ".stab"); + } else if (dfmt_is_dwarf()) { + /* the dwarf debug standard specifies the following ten sections, + not all of which are currently implemented, + although all of them are defined. */ + add_sectname("", ".debug_aranges"); + add_sectname(efmt->relpfx, ".debug_aranges"); + add_sectname("", ".debug_pubnames"); + add_sectname("", ".debug_info"); + add_sectname(efmt->relpfx, ".debug_info"); + add_sectname("", ".debug_abbrev"); + add_sectname("", ".debug_line"); + add_sectname(efmt->relpfx, ".debug_line"); + add_sectname("", ".debug_frame"); + add_sectname("", ".debug_loc"); + } + + sec_shstrtab = add_sectname("", ".shstrtab"); + sec_symtab = add_sectname("", ".symtab"); + sec_strtab = add_sectname("", ".strtab"); + + /* + * Build the symbol table and relocation tables. + */ + symtablocal = elf_build_symtab(); + + /* Do we need an .symtab_shndx section? */ + if (symtab_shndx) + add_sectname("", ".symtab_shndx"); + + for (i = 0; i < nsects; i++) { + if (sects[i]->head) { + add_sectname(efmt->relpfx, sects[i]->name); + sects[i]->rel = efmt->elf_build_reltab(sects[i]->head); + } + } + + /* + * Output the ELF header. + */ + nasm_zero(ehdr); + + /* These fields are in the same place for 32 and 64 bits */ + memcpy(&ehdr.com.e_ident[EI_MAG0], ELFMAG, SELFMAG); + ehdr.com.e_ident[EI_CLASS] = efmt->ei_class; + ehdr.com.e_ident[EI_DATA] = ELFDATA2LSB; + ehdr.com.e_ident[EI_VERSION] = EV_CURRENT; + ehdr.com.e_ident[EI_OSABI] = elf_osabi; + ehdr.com.e_ident[EI_ABIVERSION] = elf_abiver; + ehdr.com.e_type = cpu_to_le16(ET_REL); + ehdr.com.e_machine = cpu_to_le16(efmt->e_machine); + ehdr.com.e_version = cpu_to_le16(EV_CURRENT); + + if (!efmt->elf64) { + ehdr.ehdr32.e_shoff = cpu_to_le32(sizeof ehdr); + ehdr.ehdr32.e_ehsize = cpu_to_le16(sizeof(Elf32_Ehdr)); + ehdr.ehdr32.e_shentsize = cpu_to_le16(sizeof(Elf32_Shdr)); + ehdr.ehdr32.e_shnum = elf_shndx(nsections, 0); + ehdr.ehdr32.e_shstrndx = elf_shndx(sec_shstrtab, SHN_XINDEX); + } else { + ehdr.ehdr64.e_shoff = cpu_to_le64(sizeof ehdr); + ehdr.ehdr64.e_ehsize = cpu_to_le16(sizeof(Elf64_Ehdr)); + ehdr.ehdr64.e_shentsize = cpu_to_le16(sizeof(Elf64_Shdr)); + ehdr.ehdr64.e_shnum = elf_shndx(nsections, 0); + ehdr.ehdr64.e_shstrndx = elf_shndx(sec_shstrtab, SHN_XINDEX); + } + + nasm_write(&ehdr, sizeof(ehdr), ofile); + elf_foffs = sizeof ehdr + efmt->shdr_size * nsections; + + /* + * Now output the section header table. + */ + align = ALIGN(elf_foffs, SEC_FILEALIGN) - elf_foffs; + elf_foffs += align; + elf_nsect = 0; + elf_sects = nasm_malloc(sizeof(*elf_sects) * nsections); + + /* SHN_UNDEF */ + elf_section_header(0, SHT_NULL, 0, NULL, false, + nsections > (int)SHN_LORESERVE ? nsections : 0, + sec_shstrtab >= (int)SHN_LORESERVE ? sec_shstrtab : 0, + 0, 0, 0); + p = shstrtab + 1; + + /* The normal sections */ + for (i = 0; i < nsects; i++) { + elf_section_header(p - shstrtab, sects[i]->type, sects[i]->flags, + sects[i]->data, true, + sects[i]->len, 0, 0, + sects[i]->align, sects[i]->entsize); + p += strlen(p) + 1; + } + + /* The debugging sections */ + if (dfmt_is_stabs()) { + /* for debugging information, create the last three sections + which are the .stab , .stabstr and .rel.stab sections respectively */ + + /* this function call creates the stab sections in memory */ + stabs_generate(); + + if (stabbuf && stabstrbuf && stabrelbuf) { + elf_section_header(p - shstrtab, SHT_PROGBITS, 0, stabbuf, false, + stablen, sec_stabstr, 0, 4, 12); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, SHT_STRTAB, 0, stabstrbuf, false, + stabstrlen, 0, 0, 4, 0); + p += strlen(p) + 1; + + /* link -> symtable info -> section to refer to */ + elf_section_header(p - shstrtab, efmt->reltype, 0, + stabrelbuf, false, stabrellen, + sec_symtab, sec_stab, + efmt->word, efmt->relsize); + p += strlen(p) + 1; + } + } else if (dfmt_is_dwarf()) { + /* for dwarf debugging information, create the ten dwarf sections */ + + /* this function call creates the dwarf sections in memory */ + if (dwarf_fsect) + dwarf_generate(); + + elf_section_header(p - shstrtab, SHT_PROGBITS, 0, arangesbuf, false, + arangeslen, 0, 0, 1, 0); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, efmt->reltype, 0, arangesrelbuf, false, + arangesrellen, sec_symtab, + sec_debug_aranges, + efmt->word, efmt->relsize); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, SHT_PROGBITS, 0, pubnamesbuf, + false, pubnameslen, 0, 0, 1, 0); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, SHT_PROGBITS, 0, infobuf, false, + infolen, 0, 0, 1, 0); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, efmt->reltype, 0, inforelbuf, false, + inforellen, sec_symtab, + sec_debug_info, + efmt->word, efmt->relsize); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, SHT_PROGBITS, 0, abbrevbuf, false, + abbrevlen, 0, 0, 1, 0); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, SHT_PROGBITS, 0, linebuf, false, + linelen, 0, 0, 1, 0); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, efmt->reltype, 0, linerelbuf, false, + linerellen, sec_symtab, + sec_debug_line, + efmt->word, efmt->relsize); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, SHT_PROGBITS, 0, framebuf, false, + framelen, 0, 0, 8, 0); + p += strlen(p) + 1; + + elf_section_header(p - shstrtab, SHT_PROGBITS, 0, locbuf, false, + loclen, 0, 0, 1, 0); + p += strlen(p) + 1; + } + + /* .shstrtab */ + elf_section_header(p - shstrtab, SHT_STRTAB, 0, shstrtab, false, + shstrtablen, 0, 0, 1, 0); + p += strlen(p) + 1; + + /* .symtab */ + elf_section_header(p - shstrtab, SHT_SYMTAB, 0, symtab, true, + symtab->datalen, sec_strtab, symtablocal, + efmt->word, efmt->sym_size); + p += strlen(p) + 1; + + /* .strtab */ + elf_section_header(p - shstrtab, SHT_STRTAB, 0, strs, true, + strslen, 0, 0, 1, 0); + p += strlen(p) + 1 +; + /* .symtab_shndx */ + if (symtab_shndx) { + elf_section_header(p - shstrtab, SHT_SYMTAB_SHNDX, 0, + symtab_shndx, true, symtab_shndx->datalen, + sec_symtab, 0, 1, 0); + p += strlen(p) + 1; + } + + /* The relocation sections */ + for (i = 0; i < nsects; i++) { + if (sects[i]->rel) { + elf_section_header(p - shstrtab, efmt->reltype, 0, + sects[i]->rel, true, sects[i]->rel->datalen, + sec_symtab, sects[i]->shndx, + efmt->word, efmt->relsize); + p += strlen(p) + 1; + } + } + fwritezero(align, ofile); + + /* + * Now output the sections. + */ + elf_write_sections(); + + nasm_free(elf_sects); + saa_free(symtab); + if (symtab_shndx) + saa_free(symtab_shndx); +} + +static size_t nsyms; + +static void elf_sym(const struct elf_symbol *sym) +{ + int shndx = sym->section; + + /* + * Careful here. This relies on sym->section being signed; for + * special section indices this value needs to be cast to + * (int16_t) so that it sign-extends, however, here SHN_LORESERVE + * is used as an unsigned constant. + */ + if (shndx >= (int)SHN_LORESERVE) { + if (unlikely(!symtab_shndx)) { + /* Create symtab_shndx and fill previous entries with zero */ + symtab_shndx = saa_init(1); + saa_wbytes(symtab_shndx, NULL, nsyms << 2); + } + } else { + shndx = 0; /* Section index table always write zero */ + } + + if (symtab_shndx) + saa_write32(symtab_shndx, shndx); + + efmt->elf_sym(sym); + nsyms++; +} + +static void elf32_sym(const struct elf_symbol *sym) +{ + Elf32_Sym sym32; + + sym32.st_name = cpu_to_le32(sym->strpos); + sym32.st_value = cpu_to_le32(sym->symv.key); + sym32.st_size = cpu_to_le32(sym->size); + sym32.st_info = sym->type; + sym32.st_other = sym->other; + sym32.st_shndx = elf_shndx(sym->section, SHN_XINDEX); + saa_wbytes(symtab, &sym32, sizeof sym32); +} + +static void elf64_sym(const struct elf_symbol *sym) +{ + Elf64_Sym sym64; + + sym64.st_name = cpu_to_le32(sym->strpos); + sym64.st_value = cpu_to_le64(sym->symv.key); + sym64.st_size = cpu_to_le64(sym->size); + sym64.st_info = sym->type; + sym64.st_other = sym->other; + sym64.st_shndx = elf_shndx(sym->section, SHN_XINDEX); + saa_wbytes(symtab, &sym64, sizeof sym64); +} + +static size_t elf_build_symtab(void) +{ + struct elf_symbol *sym, xsym; + size_t nlocal; + int i; + + symtab = saa_init(1); + symtab_shndx = NULL; + + /* + * Zero symbol first as required by spec. + */ + nasm_zero(xsym); + elf_sym(&xsym); + + /* + * Next, an entry for the file name. + */ + nasm_zero(xsym); + xsym.strpos = 1; + xsym.type = ELF32_ST_INFO(STB_LOCAL, STT_FILE); + xsym.section = XSHN_ABS; + elf_sym(&xsym); + + /* + * Now some standard symbols defining the segments, for relocation + * purposes. + */ + nasm_zero(xsym); + for (i = 1; i <= nsects; i++) { + xsym.type = ELF64_ST_INFO(STB_LOCAL, STT_SECTION); + xsym.section = i; + elf_sym(&xsym); + } + + /* + * dwarf needs symbols for debug sections + * which are relocation targets. + */ + if (dfmt_is_dwarf()) { + dwarf_infosym = nsyms; + xsym.section = sec_debug_info; + elf_sym(&xsym); + + dwarf_abbrevsym = nsyms; + xsym.section = sec_debug_abbrev; + elf_sym(&xsym); + + dwarf_linesym = nsyms; + xsym.section = sec_debug_line; + elf_sym(&xsym); + } + + /* + * Now the other local symbols. + */ + saa_rewind(syms); + while ((sym = saa_rstruct(syms))) { + if (!sym_type_local(sym->type)) + continue; + + elf_sym(sym); + } + + nlocal = nsyms; + + /* + * Now the global symbols. + */ + saa_rewind(syms); + while ((sym = saa_rstruct(syms))) { + if (sym_type_local(sym->type)) + continue; + + elf_sym(sym); + } + + return nlocal; +} + +static struct SAA *elf32_build_reltab(const struct elf_reloc *r) +{ + struct SAA *s; + int32_t global_offset; + Elf32_Rel rel32; + + if (!r) + return NULL; + + s = saa_init(1L); + + /* + * How to onvert from a global placeholder to a real symbol index; + * the +2 refers to the two special entries, the null entry and + * the filename entry. + */ + global_offset = -GLOBAL_TEMP_BASE + nsects + nlocals + ndebugs + 2; + + while (r) { + int32_t sym = r->symbol; + + if (sym >= GLOBAL_TEMP_BASE) + sym += global_offset; + + rel32.r_offset = cpu_to_le32(r->address); + rel32.r_info = cpu_to_le32(ELF32_R_INFO(sym, r->type)); + saa_wbytes(s, &rel32, sizeof rel32); + + r = r->next; + } + + return s; +} + +static struct SAA *elfx32_build_reltab(const struct elf_reloc *r) +{ + struct SAA *s; + int32_t global_offset; + Elf32_Rela rela32; + + if (!r) + return NULL; + + s = saa_init(1L); + + /* + * How to onvert from a global placeholder to a real symbol index; + * the +2 refers to the two special entries, the null entry and + * the filename entry. + */ + global_offset = -GLOBAL_TEMP_BASE + nsects + nlocals + ndebugs + 2; + + while (r) { + int32_t sym = r->symbol; + + if (sym >= GLOBAL_TEMP_BASE) + sym += global_offset; + + rela32.r_offset = cpu_to_le32(r->address); + rela32.r_info = cpu_to_le32(ELF32_R_INFO(sym, r->type)); + rela32.r_addend = cpu_to_le32(r->offset); + saa_wbytes(s, &rela32, sizeof rela32); + + r = r->next; + } + + return s; +} + +static struct SAA *elf64_build_reltab(const struct elf_reloc *r) +{ + struct SAA *s; + int32_t global_offset; + Elf64_Rela rela64; + + if (!r) + return NULL; + + s = saa_init(1L); + + /* + * How to onvert from a global placeholder to a real symbol index; + * the +2 refers to the two special entries, the null entry and + * the filename entry. + */ + global_offset = -GLOBAL_TEMP_BASE + nsects + nlocals + ndebugs + 2; + + while (r) { + int32_t sym = r->symbol; + + if (sym >= GLOBAL_TEMP_BASE) + sym += global_offset; + + rela64.r_offset = cpu_to_le64(r->address); + rela64.r_info = cpu_to_le64(ELF64_R_INFO(sym, r->type)); + rela64.r_addend = cpu_to_le64(r->offset); + saa_wbytes(s, &rela64, sizeof rela64); + + r = r->next; + } + + return s; +} + +static void elf_section_header(int name, int type, uint64_t flags, + void *data, bool is_saa, uint64_t datalen, + int link, int info, + uint64_t align, uint64_t entsize) +{ + elf_sects[elf_nsect].data = data; + elf_sects[elf_nsect].len = datalen; + elf_sects[elf_nsect].is_saa = is_saa; + elf_nsect++; + + if (!efmt->elf64) { + Elf32_Shdr shdr; + + shdr.sh_name = cpu_to_le32(name); + shdr.sh_type = cpu_to_le32(type); + shdr.sh_flags = cpu_to_le32(flags); + shdr.sh_addr = 0; + shdr.sh_offset = cpu_to_le32(type == SHT_NULL ? 0 : elf_foffs); + shdr.sh_size = cpu_to_le32(datalen); + if (data) + elf_foffs += ALIGN(datalen, SEC_FILEALIGN); + shdr.sh_link = cpu_to_le32(link); + shdr.sh_info = cpu_to_le32(info); + shdr.sh_addralign = cpu_to_le32(align); + shdr.sh_entsize = cpu_to_le32(entsize); + + nasm_write(&shdr, sizeof shdr, ofile); + } else { + Elf64_Shdr shdr; + + shdr.sh_name = cpu_to_le32(name); + shdr.sh_type = cpu_to_le32(type); + shdr.sh_flags = cpu_to_le64(flags); + shdr.sh_addr = 0; + shdr.sh_offset = cpu_to_le64(type == SHT_NULL ? 0 : elf_foffs); + shdr.sh_size = cpu_to_le64(datalen); + if (data) + elf_foffs += ALIGN(datalen, SEC_FILEALIGN); + shdr.sh_link = cpu_to_le32(link); + shdr.sh_info = cpu_to_le32(info); + shdr.sh_addralign = cpu_to_le64(align); + shdr.sh_entsize = cpu_to_le64(entsize); + + nasm_write(&shdr, sizeof shdr, ofile); + } +} + +static void elf_write_sections(void) +{ + int i; + for (i = 0; i < elf_nsect; i++) + if (elf_sects[i].data) { + int32_t len = elf_sects[i].len; + int32_t reallen = ALIGN(len, SEC_FILEALIGN); + int32_t align = reallen - len; + if (elf_sects[i].is_saa) + saa_fpwrite(elf_sects[i].data, ofile); + else + nasm_write(elf_sects[i].data, len, ofile); + fwritezero(align, ofile); + } +} + +static void elf_sect_write(struct elf_section *sect, const void *data, size_t len) +{ + saa_wbytes(sect->data, data, len); + sect->len += len; +} + +static void elf_sect_writeaddr(struct elf_section *sect, int64_t data, size_t len) +{ + saa_writeaddr(sect->data, data, len); + sect->len += len; +} + +static void elf_sectalign(int32_t seg, unsigned int value) +{ + struct elf_section *s; + + s = raa_read_ptr(section_by_index, seg >> 1); + if (!s || !is_power2(value)) + return; + + if (value > s->align) + s->align = value; +} + +extern macros_t elf_stdmac[]; + +/* Claim "elf" as a pragma namespace, for the future */ +static const struct pragma_facility elf_pragma_list[] = +{ + { "elf", NULL }, + { NULL, NULL } /* Implements the canonical output name */ +}; + + +static const struct dfmt elf32_df_dwarf = { + "ELF32 (i386) dwarf (newer)", + "dwarf", + dwarf32_init, + dwarf_linenum, + null_debug_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + debug_typevalue, + dwarf_output, + dwarf_cleanup, + NULL /* pragma list */ +}; + +static const struct dfmt elf32_df_stabs = { + "ELF32 (i386) stabs (older)", + "stabs", + null_debug_init, + stabs_linenum, + null_debug_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + debug_typevalue, + stabs_output, + stabs_cleanup, + NULL /* pragma list */ +}; + +static const struct dfmt * const elf32_debugs_arr[3] = + { &elf32_df_dwarf, &elf32_df_stabs, NULL }; + +const struct ofmt of_elf32 = { + "ELF32 (i386) (Linux, most Unix variants)", + "elf32", + ".o", + 0, + 32, + elf32_debugs_arr, + &elf32_df_dwarf, + elf_stdmac, + elf32_init, + null_reset, + nasm_do_legacy_output, + elf32_out, + elf_deflabel, + elf_section_names, + NULL, + elf_sectalign, + null_segbase, + elf_directive, + elf_cleanup, + elf_pragma_list, +}; + +static const struct dfmt elf64_df_dwarf = { + "ELF64 (x86-64) dwarf (newer)", + "dwarf", + dwarf64_init, + dwarf_linenum, + null_debug_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + debug_typevalue, + dwarf_output, + dwarf_cleanup, + NULL /* pragma list */ +}; + +static const struct dfmt elf64_df_stabs = { + "ELF64 (x86-64) stabs (older)", + "stabs", + null_debug_init, + stabs_linenum, + null_debug_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + debug_typevalue, + stabs_output, + stabs_cleanup, + NULL /* pragma list */ +}; + +static const struct dfmt * const elf64_debugs_arr[3] = + { &elf64_df_dwarf, &elf64_df_stabs, NULL }; + +const struct ofmt of_elf64 = { + "ELF64 (x86-64) (Linux, most Unix variants)", + "elf64", + ".o", + 0, + 64, + elf64_debugs_arr, + &elf64_df_dwarf, + elf_stdmac, + elf64_init, + null_reset, + nasm_do_legacy_output, + elf64_out, + elf_deflabel, + elf_section_names, + NULL, + elf_sectalign, + null_segbase, + elf_directive, + elf_cleanup, + elf_pragma_list, +}; + +static const struct dfmt elfx32_df_dwarf = { + "ELFx32 (x86-64) dwarf (newer)", + "dwarf", + dwarfx32_init, + dwarf_linenum, + null_debug_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + debug_typevalue, + dwarf_output, + dwarf_cleanup, + NULL /* pragma list */ +}; + +static const struct dfmt elfx32_df_stabs = { + "ELFx32 (x86-64) stabs (older)", + "stabs", + null_debug_init, + stabs_linenum, + null_debug_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + debug_typevalue, + stabs_output, + stabs_cleanup, + elf_pragma_list, +}; + +static const struct dfmt * const elfx32_debugs_arr[3] = + { &elfx32_df_dwarf, &elfx32_df_stabs, NULL }; + +const struct ofmt of_elfx32 = { + "ELFx32 (ELF32 for x86-64) (Linux)", + "elfx32", + ".o", + 0, + 64, + elfx32_debugs_arr, + &elfx32_df_dwarf, + elf_stdmac, + elfx32_init, + null_reset, + nasm_do_legacy_output, + elfx32_out, + elf_deflabel, + elf_section_names, + NULL, + elf_sectalign, + null_segbase, + elf_directive, + elf_cleanup, + NULL /* pragma list */ +}; + +static bool is_elf64(void) +{ + return ofmt == &of_elf64; +} + +static bool is_elf32(void) +{ + return ofmt == &of_elf32; +} + +static bool is_elfx32(void) +{ + return ofmt == &of_elfx32; +} + +static bool dfmt_is_stabs(void) +{ + return dfmt == &elf32_df_stabs || + dfmt == &elfx32_df_stabs || + dfmt == &elf64_df_stabs; +} + +static bool dfmt_is_dwarf(void) +{ + return dfmt == &elf32_df_dwarf || + dfmt == &elfx32_df_dwarf || + dfmt == &elf64_df_dwarf; +} + +/* common debugging routines */ +static void debug_typevalue(int32_t type) +{ + int32_t stype, ssize; + switch (TYM_TYPE(type)) { + case TY_LABEL: + ssize = 0; + stype = STT_NOTYPE; + break; + case TY_BYTE: + ssize = 1; + stype = STT_OBJECT; + break; + case TY_WORD: + ssize = 2; + stype = STT_OBJECT; + break; + case TY_DWORD: + ssize = 4; + stype = STT_OBJECT; + break; + case TY_FLOAT: + ssize = 4; + stype = STT_OBJECT; + break; + case TY_QWORD: + ssize = 8; + stype = STT_OBJECT; + break; + case TY_TBYTE: + ssize = 10; + stype = STT_OBJECT; + break; + case TY_OWORD: + ssize = 16; + stype = STT_OBJECT; + break; + case TY_YWORD: + ssize = 32; + stype = STT_OBJECT; + break; + case TY_ZWORD: + ssize = 64; + stype = STT_OBJECT; + break; + case TY_COMMON: + ssize = 0; + stype = STT_COMMON; + break; + case TY_SEG: + ssize = 0; + stype = STT_SECTION; + break; + case TY_EXTERN: + ssize = 0; + stype = STT_NOTYPE; + break; + case TY_EQU: + ssize = 0; + stype = STT_NOTYPE; + break; + default: + ssize = 0; + stype = STT_NOTYPE; + break; + } + /* Set type and size info on most recently seen symbol if we haven't set it already. + But avoid setting size info on object (data) symbols in absolute sections (which + is primarily structs); some environments get confused with non-zero-extent absolute + object symbols and end up showing them in backtraces for NULL fn pointer calls. */ + if (stype == STT_OBJECT && lastsym && !lastsym->type && lastsym->section != XSHN_ABS) { + lastsym->size = ssize; + lastsym->type = stype; + } +} + +/* stabs debugging routines */ + +static void stabs_linenum(const char *filename, int32_t linenumber, int32_t segto) +{ + (void)segto; + if (!stabs_filename) { + stabs_filename = nasm_malloc(strlen(filename) + 1); + strcpy(stabs_filename, filename); + } else { + if (strcmp(stabs_filename, filename)) { + /* yep, a memory leak...this program is one-shot anyway, so who cares... + in fact, this leak comes in quite handy to maintain a list of files + encountered so far in the symbol lines... */ + + /* why not nasm_free(stabs_filename); we're done with the old one */ + + stabs_filename = nasm_malloc(strlen(filename) + 1); + strcpy(stabs_filename, filename); + } + } + debug_immcall = 1; + currentline = linenumber; +} + +static void stabs_output(int type, void *param) +{ + struct symlininfo *s; + struct linelist *el; + if (type == TY_DEBUGSYMLIN) { + if (debug_immcall) { + s = (struct symlininfo *)param; + if (!(sects[s->section]->flags & SHF_EXECINSTR)) + return; /* line info is only collected for executable sections */ + numlinestabs++; + el = nasm_malloc(sizeof(struct linelist)); + el->info.offset = s->offset; + el->info.section = s->section; + el->info.name = s->name; + el->line = currentline; + el->filename = stabs_filename; + el->next = 0; + if (stabslines) { + stabslines->last->next = el; + stabslines->last = el; + } else { + stabslines = el; + stabslines->last = el; + } + } + } + debug_immcall = 0; +} + +/* for creating the .stab , .stabstr and .rel.stab sections in memory */ + +static void stabs_generate(void) +{ + int i, numfiles, strsize, numstabs = 0, currfile, mainfileindex; + uint8_t *sbuf, *ssbuf, *rbuf, *sptr, *rptr; + char **allfiles; + int *fileidx; + + struct linelist *ptr; + + ptr = stabslines; + + allfiles = nasm_zalloc(numlinestabs * sizeof(char *)); + numfiles = 0; + while (ptr) { + if (numfiles == 0) { + allfiles[0] = ptr->filename; + numfiles++; + } else { + for (i = 0; i < numfiles; i++) { + if (!strcmp(allfiles[i], ptr->filename)) + break; + } + if (i >= numfiles) { + allfiles[i] = ptr->filename; + numfiles++; + } + } + ptr = ptr->next; + } + strsize = 1; + fileidx = nasm_malloc(numfiles * sizeof(int)); + for (i = 0; i < numfiles; i++) { + fileidx[i] = strsize; + strsize += strlen(allfiles[i]) + 1; + } + currfile = mainfileindex = 0; + for (i = 0; i < numfiles; i++) { + if (!strcmp(allfiles[i], elf_module)) { + currfile = mainfileindex = i; + break; + } + } + + /* + * worst case size of the stab buffer would be: + * the sourcefiles changes each line, which would mean 1 SOL, 1 SYMLIN per line + * plus one "ending" entry + */ + sbuf = nasm_malloc((numlinestabs * 2 + 4) * + sizeof(struct stabentry)); + ssbuf = nasm_malloc(strsize); + rbuf = nasm_malloc(numlinestabs * (is_elf64() ? 16 : 8) * (2 + 3)); + rptr = rbuf; + + for (i = 0; i < numfiles; i++) + strcpy((char *)ssbuf + fileidx[i], allfiles[i]); + ssbuf[0] = 0; + + stabstrlen = strsize; /* set global variable for length of stab strings */ + + sptr = sbuf; + ptr = stabslines; + numstabs = 0; + + if (ptr) { + /* + * this is the first stab, its strx points to the filename of the + * source-file, the n_desc field should be set to the number + * of remaining stabs + */ + WRITE_STAB(sptr, fileidx[0], 0, 0, 0, stabstrlen); + + /* this is the stab for the main source file */ + WRITE_STAB(sptr, fileidx[mainfileindex], N_SO, 0, 0, 0); + + /* relocation table entry */ + + /* + * Since the symbol table has two entries before + * the section symbols, the index in the info.section + * member must be adjusted by adding 2 + */ + + if (is_elf32()) { + WRITELONG(rptr, (sptr - sbuf) - 4); + WRITELONG(rptr, ((ptr->info.section + 2) << 8) | R_386_32); + } else if (is_elfx32()) { + WRITELONG(rptr, (sptr - sbuf) - 4); + WRITELONG(rptr, ((ptr->info.section + 2) << 8) | R_X86_64_32); + WRITELONG(rptr, 0); + } else { + nasm_assert(is_elf64()); + WRITEDLONG(rptr, (int64_t)(sptr - sbuf) - 4); + WRITELONG(rptr, R_X86_64_32); + WRITELONG(rptr, ptr->info.section + 2); + WRITEDLONG(rptr, 0); + } + numstabs++; + } + + if (is_elf32()) { + while (ptr) { + if (strcmp(allfiles[currfile], ptr->filename)) { + /* oops file has changed... */ + for (i = 0; i < numfiles; i++) + if (!strcmp(allfiles[i], ptr->filename)) + break; + currfile = i; + WRITE_STAB(sptr, fileidx[currfile], N_SOL, 0, 0, + ptr->info.offset); + numstabs++; + + /* relocation table entry */ + WRITELONG(rptr, (sptr - sbuf) - 4); + WRITELONG(rptr, ((ptr->info.section + 2) << 8) | R_386_32); + } + + WRITE_STAB(sptr, 0, N_SLINE, 0, ptr->line, ptr->info.offset); + numstabs++; + + /* relocation table entry */ + WRITELONG(rptr, (sptr - sbuf) - 4); + WRITELONG(rptr, ((ptr->info.section + 2) << 8) | R_386_32); + + ptr = ptr->next; + } + } else if (is_elfx32()) { + while (ptr) { + if (strcmp(allfiles[currfile], ptr->filename)) { + /* oops file has changed... */ + for (i = 0; i < numfiles; i++) + if (!strcmp(allfiles[i], ptr->filename)) + break; + currfile = i; + WRITE_STAB(sptr, fileidx[currfile], N_SOL, 0, 0, + ptr->info.offset); + numstabs++; + + /* relocation table entry */ + WRITELONG(rptr, (sptr - sbuf) - 4); + WRITELONG(rptr, ((ptr->info.section + 2) << 8) | R_X86_64_32); + WRITELONG(rptr, ptr->info.offset); + } + + WRITE_STAB(sptr, 0, N_SLINE, 0, ptr->line, ptr->info.offset); + numstabs++; + + /* relocation table entry */ + WRITELONG(rptr, (sptr - sbuf) - 4); + WRITELONG(rptr, ((ptr->info.section + 2) << 8) | R_X86_64_32); + WRITELONG(rptr, ptr->info.offset); + + ptr = ptr->next; + } + } else { + nasm_assert(is_elf64()); + while (ptr) { + if (strcmp(allfiles[currfile], ptr->filename)) { + /* oops file has changed... */ + for (i = 0; i < numfiles; i++) + if (!strcmp(allfiles[i], ptr->filename)) + break; + currfile = i; + WRITE_STAB(sptr, fileidx[currfile], N_SOL, 0, 0, + ptr->info.offset); + numstabs++; + + /* relocation table entry */ + WRITEDLONG(rptr, (int64_t)(sptr - sbuf) - 4); + WRITELONG(rptr, R_X86_64_32); + WRITELONG(rptr, ptr->info.section + 2); + WRITEDLONG(rptr, ptr->info.offset); + } + + WRITE_STAB(sptr, 0, N_SLINE, 0, ptr->line, ptr->info.offset); + numstabs++; + + /* relocation table entry */ + WRITEDLONG(rptr, (int64_t)(sptr - sbuf) - 4); + WRITELONG(rptr, R_X86_64_32); + WRITELONG(rptr, ptr->info.section + 2); + WRITEDLONG(rptr, ptr->info.offset); + + ptr = ptr->next; + } + } + + /* this is an "ending" token */ + WRITE_STAB(sptr, 0, N_SO, 0, 0, 0); + numstabs++; + + ((struct stabentry *)sbuf)->n_desc = numstabs; + + nasm_free(allfiles); + nasm_free(fileidx); + + stablen = (sptr - sbuf); + stabrellen = (rptr - rbuf); + stabrelbuf = rbuf; + stabbuf = sbuf; + stabstrbuf = ssbuf; +} + +static void stabs_cleanup(void) +{ + struct linelist *ptr, *del; + if (!stabslines) + return; + + ptr = stabslines; + while (ptr) { + del = ptr; + ptr = ptr->next; + nasm_free(del); + } + + nasm_free(stabbuf); + nasm_free(stabrelbuf); + nasm_free(stabstrbuf); +} + +/* dwarf routines */ + +static void dwarf_init_common(const struct dwarf_format *fmt) +{ + dwfmt = fmt; + ndebugs = 3; /* 3 debug symbols */ +} + +static void dwarf32_init(void) +{ + static const struct dwarf_format dwfmt32 = { + 2, /* DWARF 2 */ + /* section version numbers: */ + { 2, /* .debug_aranges */ + 0, /* .rela.debug_aranges */ + 2, /* .debug_pubnames */ + 2, /* .debug_info */ + 0, /* .rela.debug_info */ + 0, /* .debug_abbrev */ + 2, /* .debug_line */ + 0, /* .rela.debug_line */ + 1, /* .debug_frame */ + 0 } /* .debug_loc */ + }; + dwarf_init_common(&dwfmt32); +} + +static void dwarfx32_init(void) +{ + static const struct dwarf_format dwfmtx32 = { + 3, /* DWARF 3 */ + /* section version numbers: */ + { 2, /* .debug_aranges */ + 0, /* .rela.debug_aranges */ + 2, /* .debug_pubnames */ + 3, /* .debug_info */ + 0, /* .rela.debug_info */ + 0, /* .debug_abbrev */ + 3, /* .debug_line */ + 0, /* .rela.debug_line */ + 3, /* .debug_frame */ + 0 } /* .debug_loc */ + }; + dwarf_init_common(&dwfmtx32); +} + +static void dwarf64_init(void) +{ + static const struct dwarf_format dwfmt64 = { + 3, /* DWARF 3 */ + /* section version numbers: */ + { 2, /* .debug_aranges */ + 0, /* .rela.debug_aranges */ + 2, /* .debug_pubnames */ + 3, /* .debug_info */ + 0, /* .rela.debug_info */ + 0, /* .debug_abbrev */ + 3, /* .debug_line */ + 0, /* .rela.debug_line */ + 3, /* .debug_frame */ + 0 } /* .debug_loc */ + }; + dwarf_init_common(&dwfmt64); +} + +static void dwarf_linenum(const char *filename, int32_t linenumber, + int32_t segto) +{ + (void)segto; + dwarf_findfile(filename); + debug_immcall = 1; + currentline = linenumber; +} + +/* called from elf_out with type == TY_DEBUGSYMLIN */ +static void dwarf_output(int type, void *param) +{ + int ln, aa, inx, maxln, soc; + struct symlininfo *s; + struct SAA *plinep; + + (void)type; + + s = (struct symlininfo *)param; + + /* line number info is only gathered for executable sections */ + if (!(sects[s->section]->flags & SHF_EXECINSTR)) + return; + + /* Check if section index has changed */ + if (!(dwarf_csect && (dwarf_csect->section) == (s->section))) + dwarf_findsect(s->section); + + /* do nothing unless line or file has changed */ + if (!debug_immcall) + return; + + ln = currentline - dwarf_csect->line; + aa = s->offset - dwarf_csect->offset; + inx = dwarf_clist->line; + plinep = dwarf_csect->psaa; + /* check for file change */ + if (!(inx == dwarf_csect->file)) { + saa_write8(plinep,DW_LNS_set_file); + saa_write8(plinep,inx); + dwarf_csect->file = inx; + } + /* check for line change */ + if (ln) { + /* test if in range of special op code */ + maxln = line_base + line_range; + soc = (ln - line_base) + (line_range * aa) + opcode_base; + if (ln >= line_base && ln < maxln && soc < 256) { + saa_write8(plinep,soc); + } else { + saa_write8(plinep,DW_LNS_advance_line); + saa_wleb128s(plinep,ln); + if (aa) { + saa_write8(plinep,DW_LNS_advance_pc); + saa_wleb128u(plinep,aa); + } + saa_write8(plinep,DW_LNS_copy); + } + dwarf_csect->line = currentline; + dwarf_csect->offset = s->offset; + } + + /* show change handled */ + debug_immcall = 0; +} + + +static void dwarf_generate(void) +{ + uint8_t *pbuf; + int indx; + struct linelist *ftentry; + struct SAA *paranges, *ppubnames, *pinfo, *pabbrev, *plines, *plinep; + struct SAA *parangesrel, *plinesrel, *pinforel; + struct sectlist *psect; + size_t saalen, linepoff, totlen, highaddr; + + if (is_elf32()) { + /* write epilogues for each line program range */ + /* and build aranges section */ + paranges = saa_init(1L); + parangesrel = saa_init(1L); + saa_write16(paranges, dwfmt->sect_version[DWARF_ARANGES]); + saa_write32(parangesrel, paranges->datalen+4); + saa_write32(parangesrel, (dwarf_infosym << 8) + R_386_32); /* reloc to info */ + saa_write32(paranges,0); /* offset into info */ + saa_write8(paranges,4); /* pointer size */ + saa_write8(paranges,0); /* not segmented */ + saa_write32(paranges,0); /* padding */ + /* iterate though sectlist entries */ + psect = dwarf_fsect; + totlen = 0; + highaddr = 0; + for (indx = 0; indx < dwarf_nsections; indx++) { + plinep = psect->psaa; + /* Line Number Program Epilogue */ + saa_write8(plinep,2); /* std op 2 */ + saa_write8(plinep,(sects[psect->section]->len)-psect->offset); + saa_write8(plinep,DW_LNS_extended_op); + saa_write8(plinep,1); /* operand length */ + saa_write8(plinep,DW_LNE_end_sequence); + totlen += plinep->datalen; + /* range table relocation entry */ + saa_write32(parangesrel, paranges->datalen + 4); + saa_write32(parangesrel, ((uint32_t) (psect->section + 2) << 8) + R_386_32); + /* range table entry */ + saa_write32(paranges,0x0000); /* range start */ + saa_write32(paranges,sects[psect->section]->len); /* range length */ + highaddr += sects[psect->section]->len; + /* done with this entry */ + psect = psect->next; + } + saa_write32(paranges,0); /* null address */ + saa_write32(paranges,0); /* null length */ + saalen = paranges->datalen; + arangeslen = saalen + 4; + arangesbuf = pbuf = nasm_malloc(arangeslen); + WRITELONG(pbuf,saalen); /* initial length */ + saa_rnbytes(paranges, pbuf, saalen); + saa_free(paranges); + } else if (is_elfx32()) { + /* write epilogues for each line program range */ + /* and build aranges section */ + paranges = saa_init(1L); + parangesrel = saa_init(1L); + saa_write16(paranges, dwfmt->sect_version[DWARF_ARANGES]); + saa_write32(parangesrel, paranges->datalen+4); + saa_write32(parangesrel, (dwarf_infosym << 8) + R_X86_64_32); /* reloc to info */ + saa_write32(parangesrel, 0); + saa_write32(paranges,0); /* offset into info */ + saa_write8(paranges,4); /* pointer size */ + saa_write8(paranges,0); /* not segmented */ + saa_write32(paranges,0); /* padding */ + /* iterate though sectlist entries */ + psect = dwarf_fsect; + totlen = 0; + highaddr = 0; + for (indx = 0; indx < dwarf_nsections; indx++) { + plinep = psect->psaa; + /* Line Number Program Epilogue */ + saa_write8(plinep,2); /* std op 2 */ + saa_write8(plinep,(sects[psect->section]->len)-psect->offset); + saa_write8(plinep,DW_LNS_extended_op); + saa_write8(plinep,1); /* operand length */ + saa_write8(plinep,DW_LNE_end_sequence); + totlen += plinep->datalen; + /* range table relocation entry */ + saa_write32(parangesrel, paranges->datalen + 4); + saa_write32(parangesrel, ((uint32_t) (psect->section + 2) << 8) + R_X86_64_32); + saa_write32(parangesrel, (uint32_t) 0); + /* range table entry */ + saa_write32(paranges,0x0000); /* range start */ + saa_write32(paranges,sects[psect->section]->len); /* range length */ + highaddr += sects[psect->section]->len; + /* done with this entry */ + psect = psect->next; + } + saa_write32(paranges,0); /* null address */ + saa_write32(paranges,0); /* null length */ + saalen = paranges->datalen; + arangeslen = saalen + 4; + arangesbuf = pbuf = nasm_malloc(arangeslen); + WRITELONG(pbuf,saalen); /* initial length */ + saa_rnbytes(paranges, pbuf, saalen); + saa_free(paranges); + } else { + nasm_assert(is_elf64()); + /* write epilogues for each line program range */ + /* and build aranges section */ + paranges = saa_init(1L); + parangesrel = saa_init(1L); + saa_write16(paranges, dwfmt->sect_version[DWARF_ARANGES]); + saa_write64(parangesrel, paranges->datalen+4); + saa_write64(parangesrel, (dwarf_infosym << 32) + R_X86_64_32); /* reloc to info */ + saa_write64(parangesrel, 0); + saa_write32(paranges,0); /* offset into info */ + saa_write8(paranges,8); /* pointer size */ + saa_write8(paranges,0); /* not segmented */ + saa_write32(paranges,0); /* padding */ + /* iterate though sectlist entries */ + psect = dwarf_fsect; + totlen = 0; + highaddr = 0; + for (indx = 0; indx < dwarf_nsections; indx++) { + plinep = psect->psaa; + /* Line Number Program Epilogue */ + saa_write8(plinep,2); /* std op 2 */ + saa_write8(plinep,(sects[psect->section]->len)-psect->offset); + saa_write8(plinep,DW_LNS_extended_op); + saa_write8(plinep,1); /* operand length */ + saa_write8(plinep,DW_LNE_end_sequence); + totlen += plinep->datalen; + /* range table relocation entry */ + saa_write64(parangesrel, paranges->datalen + 4); + saa_write64(parangesrel, ((uint64_t) (psect->section + 2) << 32) + R_X86_64_64); + saa_write64(parangesrel, (uint64_t) 0); + /* range table entry */ + saa_write64(paranges,0x0000); /* range start */ + saa_write64(paranges,sects[psect->section]->len); /* range length */ + highaddr += sects[psect->section]->len; + /* done with this entry */ + psect = psect->next; + } + saa_write64(paranges,0); /* null address */ + saa_write64(paranges,0); /* null length */ + saalen = paranges->datalen; + arangeslen = saalen + 4; + arangesbuf = pbuf = nasm_malloc(arangeslen); + WRITELONG(pbuf,saalen); /* initial length */ + saa_rnbytes(paranges, pbuf, saalen); + saa_free(paranges); + } + + /* build rela.aranges section */ + arangesrellen = saalen = parangesrel->datalen; + arangesrelbuf = pbuf = nasm_malloc(arangesrellen); + saa_rnbytes(parangesrel, pbuf, saalen); + saa_free(parangesrel); + + /* build pubnames section */ + if (0) { + ppubnames = saa_init(1L); + saa_write16(ppubnames,dwfmt->sect_version[DWARF_PUBNAMES]); + saa_write32(ppubnames,0); /* offset into info */ + saa_write32(ppubnames,0); /* space used in info */ + saa_write32(ppubnames,0); /* end of list */ + saalen = ppubnames->datalen; + pubnameslen = saalen + 4; + pubnamesbuf = pbuf = nasm_malloc(pubnameslen); + WRITELONG(pbuf,saalen); /* initial length */ + saa_rnbytes(ppubnames, pbuf, saalen); + saa_free(ppubnames); + } else { + /* Don't write a section without actual information */ + pubnameslen = 0; + } + + if (is_elf32()) { + /* build info section */ + pinfo = saa_init(1L); + pinforel = saa_init(1L); + saa_write16(pinfo, dwfmt->sect_version[DWARF_INFO]); + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, (dwarf_abbrevsym << 8) + R_386_32); /* reloc to abbrev */ + saa_write32(pinfo,0); /* offset into abbrev */ + saa_write8(pinfo,4); /* pointer size */ + saa_write8(pinfo,1); /* abbrviation number LEB128u */ + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, ((dwarf_fsect->section + 2) << 8) + R_386_32); + saa_write32(pinfo,0); /* DW_AT_low_pc */ + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, ((dwarf_fsect->section + 2) << 8) + R_386_32); + saa_write32(pinfo,highaddr); /* DW_AT_high_pc */ + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, (dwarf_linesym << 8) + R_386_32); /* reloc to line */ + saa_write32(pinfo,0); /* DW_AT_stmt_list */ + saa_wbytes(pinfo, elf_module, strlen(elf_module)+1); /* DW_AT_name */ + saa_wbytes(pinfo, elf_dir, strlen(elf_dir)+1); /* DW_AT_comp_dir */ + saa_wbytes(pinfo, nasm_signature(), nasm_signature_len()+1); + saa_write16(pinfo,DW_LANG_Mips_Assembler); + saa_write8(pinfo,2); /* abbrviation number LEB128u */ + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, ((dwarf_fsect->section + 2) << 8) + R_386_32); + saa_write32(pinfo,0); /* DW_AT_low_pc */ + saa_write32(pinfo,0); /* DW_AT_frame_base */ + saa_write8(pinfo,0); /* end of entries */ + saalen = pinfo->datalen; + infolen = saalen + 4; + infobuf = pbuf = nasm_malloc(infolen); + WRITELONG(pbuf,saalen); /* initial length */ + saa_rnbytes(pinfo, pbuf, saalen); + saa_free(pinfo); + } else if (is_elfx32()) { + /* build info section */ + pinfo = saa_init(1L); + pinforel = saa_init(1L); + saa_write16(pinfo, dwfmt->sect_version[DWARF_INFO]); + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, (dwarf_abbrevsym << 8) + R_X86_64_32); /* reloc to abbrev */ + saa_write32(pinforel, 0); + saa_write32(pinfo,0); /* offset into abbrev */ + saa_write8(pinfo,4); /* pointer size */ + saa_write8(pinfo,1); /* abbrviation number LEB128u */ + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, ((dwarf_fsect->section + 2) << 8) + R_X86_64_32); + saa_write32(pinforel, 0); + saa_write32(pinfo,0); /* DW_AT_low_pc */ + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, ((dwarf_fsect->section + 2) << 8) + R_X86_64_32); + saa_write32(pinforel, highaddr); + saa_write32(pinfo,0); /* DW_AT_high_pc */ + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, (dwarf_linesym << 8) + R_X86_64_32); /* reloc to line */ + saa_write32(pinforel, 0); + saa_write32(pinfo,0); /* DW_AT_stmt_list */ + saa_wbytes(pinfo, elf_module, strlen(elf_module)+1); /* DW_AT_name */ + saa_wbytes(pinfo, elf_dir, strlen(elf_dir)+1); /* DW_AT_comp_dir */ + saa_wbytes(pinfo, nasm_signature(), nasm_signature_len()+1); + saa_write16(pinfo,DW_LANG_Mips_Assembler); + saa_write8(pinfo,2); /* abbrviation number LEB128u */ + saa_write32(pinforel, pinfo->datalen + 4); + saa_write32(pinforel, ((dwarf_fsect->section + 2) << 8) + R_X86_64_32); + saa_write32(pinforel, 0); + saa_write32(pinfo,0); /* DW_AT_low_pc */ + saa_write32(pinfo,0); /* DW_AT_frame_base */ + saa_write8(pinfo,0); /* end of entries */ + saalen = pinfo->datalen; + infolen = saalen + 4; + infobuf = pbuf = nasm_malloc(infolen); + WRITELONG(pbuf,saalen); /* initial length */ + saa_rnbytes(pinfo, pbuf, saalen); + saa_free(pinfo); + } else { + nasm_assert(is_elf64()); + /* build info section */ + pinfo = saa_init(1L); + pinforel = saa_init(1L); + saa_write16(pinfo, dwfmt->sect_version[DWARF_INFO]); + saa_write64(pinforel, pinfo->datalen + 4); + saa_write64(pinforel, (dwarf_abbrevsym << 32) + R_X86_64_32); /* reloc to abbrev */ + saa_write64(pinforel, 0); + saa_write32(pinfo,0); /* offset into abbrev */ + saa_write8(pinfo,8); /* pointer size */ + saa_write8(pinfo,1); /* abbrviation number LEB128u */ + saa_write64(pinforel, pinfo->datalen + 4); + saa_write64(pinforel, ((uint64_t)(dwarf_fsect->section + 2) << 32) + R_X86_64_64); + saa_write64(pinforel, 0); + saa_write64(pinfo,0); /* DW_AT_low_pc */ + saa_write64(pinforel, pinfo->datalen + 4); + saa_write64(pinforel, ((uint64_t)(dwarf_fsect->section + 2) << 32) + R_X86_64_64); + saa_write64(pinforel, highaddr); + saa_write64(pinfo,0); /* DW_AT_high_pc */ + saa_write64(pinforel, pinfo->datalen + 4); + saa_write64(pinforel, (dwarf_linesym << 32) + R_X86_64_32); /* reloc to line */ + saa_write64(pinforel, 0); + saa_write32(pinfo,0); /* DW_AT_stmt_list */ + saa_wbytes(pinfo, elf_module, strlen(elf_module)+1); /* DW_AT_name */ + saa_wbytes(pinfo, elf_dir, strlen(elf_dir)+1); /* DW_AT_comp_dir */ + saa_wbytes(pinfo, nasm_signature(), nasm_signature_len()+1); + saa_write16(pinfo,DW_LANG_Mips_Assembler); + saa_write8(pinfo,2); /* abbrviation number LEB128u */ + saa_write64(pinforel, pinfo->datalen + 4); + saa_write64(pinforel, ((uint64_t)(dwarf_fsect->section + 2) << 32) + R_X86_64_64); + saa_write64(pinforel, 0); + saa_write64(pinfo,0); /* DW_AT_low_pc */ + saa_write64(pinfo,0); /* DW_AT_frame_base */ + saa_write8(pinfo,0); /* end of entries */ + saalen = pinfo->datalen; + infolen = saalen + 4; + infobuf = pbuf = nasm_malloc(infolen); + WRITELONG(pbuf,saalen); /* initial length */ + saa_rnbytes(pinfo, pbuf, saalen); + saa_free(pinfo); + } + + /* build rela.info section */ + inforellen = saalen = pinforel->datalen; + inforelbuf = pbuf = nasm_malloc(inforellen); + saa_rnbytes(pinforel, pbuf, saalen); + saa_free(pinforel); + + /* build abbrev section */ + pabbrev = saa_init(1L); + saa_write8(pabbrev,1); /* entry number LEB128u */ + saa_write8(pabbrev,DW_TAG_compile_unit); /* tag LEB128u */ + saa_write8(pabbrev,1); /* has children */ + /* the following attributes and forms are all LEB128u values */ + saa_write8(pabbrev,DW_AT_low_pc); + saa_write8(pabbrev,DW_FORM_addr); + saa_write8(pabbrev,DW_AT_high_pc); + saa_write8(pabbrev,DW_FORM_addr); + saa_write8(pabbrev,DW_AT_stmt_list); + saa_write8(pabbrev,DW_FORM_data4); + saa_write8(pabbrev,DW_AT_name); + saa_write8(pabbrev,DW_FORM_string); + saa_write8(pabbrev,DW_AT_comp_dir); + saa_write8(pabbrev,DW_FORM_string); + saa_write8(pabbrev,DW_AT_producer); + saa_write8(pabbrev,DW_FORM_string); + saa_write8(pabbrev,DW_AT_language); + saa_write8(pabbrev,DW_FORM_data2); + saa_write16(pabbrev,0); /* end of entry */ + /* LEB128u usage same as above */ + saa_write8(pabbrev,2); /* entry number */ + saa_write8(pabbrev,DW_TAG_subprogram); + saa_write8(pabbrev,0); /* no children */ + saa_write8(pabbrev,DW_AT_low_pc); + saa_write8(pabbrev,DW_FORM_addr); + saa_write8(pabbrev,DW_AT_frame_base); + saa_write8(pabbrev,DW_FORM_data4); + saa_write16(pabbrev,0); /* end of entry */ + /* Terminal zero entry */ + saa_write8(pabbrev,0); + abbrevlen = saalen = pabbrev->datalen; + abbrevbuf = pbuf = nasm_malloc(saalen); + saa_rnbytes(pabbrev, pbuf, saalen); + saa_free(pabbrev); + + /* build line section */ + /* prolog */ + plines = saa_init(1L); + saa_write8(plines,1); /* Minimum Instruction Length */ + saa_write8(plines,1); /* Initial value of 'is_stmt' */ + saa_write8(plines,line_base); /* Line Base */ + saa_write8(plines,line_range); /* Line Range */ + saa_write8(plines,opcode_base); /* Opcode Base */ + /* standard opcode lengths (# of LEB128u operands) */ + saa_write8(plines,0); /* Std opcode 1 length */ + saa_write8(plines,1); /* Std opcode 2 length */ + saa_write8(plines,1); /* Std opcode 3 length */ + saa_write8(plines,1); /* Std opcode 4 length */ + saa_write8(plines,1); /* Std opcode 5 length */ + saa_write8(plines,0); /* Std opcode 6 length */ + saa_write8(plines,0); /* Std opcode 7 length */ + saa_write8(plines,0); /* Std opcode 8 length */ + saa_write8(plines,1); /* Std opcode 9 length */ + saa_write8(plines,0); /* Std opcode 10 length */ + saa_write8(plines,0); /* Std opcode 11 length */ + saa_write8(plines,1); /* Std opcode 12 length */ + /* Directory Table */ + saa_write8(plines,0); /* End of table */ + /* File Name Table */ + ftentry = dwarf_flist; + for (indx = 0; indx < dwarf_numfiles; indx++) { + saa_wbytes(plines, ftentry->filename, (int32_t)(strlen(ftentry->filename) + 1)); + saa_write8(plines,0); /* directory LEB128u */ + saa_write8(plines,0); /* time LEB128u */ + saa_write8(plines,0); /* size LEB128u */ + ftentry = ftentry->next; + } + saa_write8(plines,0); /* End of table */ + linepoff = plines->datalen; + linelen = linepoff + totlen + 10; + linebuf = pbuf = nasm_malloc(linelen); + WRITELONG(pbuf,linelen-4); /* initial length */ + WRITESHORT(pbuf,dwfmt->sect_version[DWARF_LINE]); + WRITELONG(pbuf,linepoff); /* offset to line number program */ + /* write line header */ + saalen = linepoff; + saa_rnbytes(plines, pbuf, saalen); /* read a given no. of bytes */ + pbuf += linepoff; + saa_free(plines); + /* concatenate line program ranges */ + linepoff += 13; + plinesrel = saa_init(1L); + psect = dwarf_fsect; + if (is_elf32()) { + for (indx = 0; indx < dwarf_nsections; indx++) { + saa_write32(plinesrel, linepoff); + saa_write32(plinesrel, ((uint32_t) (psect->section + 2) << 8) + R_386_32); + plinep = psect->psaa; + saalen = plinep->datalen; + saa_rnbytes(plinep, pbuf, saalen); + pbuf += saalen; + linepoff += saalen; + saa_free(plinep); + /* done with this entry */ + psect = psect->next; + } + } else if (is_elfx32()) { + for (indx = 0; indx < dwarf_nsections; indx++) { + saa_write32(plinesrel, linepoff); + saa_write32(plinesrel, ((psect->section + 2) << 8) + R_X86_64_32); + saa_write32(plinesrel, 0); + plinep = psect->psaa; + saalen = plinep->datalen; + saa_rnbytes(plinep, pbuf, saalen); + pbuf += saalen; + linepoff += saalen; + saa_free(plinep); + /* done with this entry */ + psect = psect->next; + } + } else { + nasm_assert(is_elf64()); + for (indx = 0; indx < dwarf_nsections; indx++) { + saa_write64(plinesrel, linepoff); + saa_write64(plinesrel, ((uint64_t) (psect->section + 2) << 32) + R_X86_64_64); + saa_write64(plinesrel, (uint64_t) 0); + plinep = psect->psaa; + saalen = plinep->datalen; + saa_rnbytes(plinep, pbuf, saalen); + pbuf += saalen; + linepoff += saalen; + saa_free(plinep); + /* done with this entry */ + psect = psect->next; + } + } + + /* build rela.lines section */ + linerellen =saalen = plinesrel->datalen; + linerelbuf = pbuf = nasm_malloc(linerellen); + saa_rnbytes(plinesrel, pbuf, saalen); + saa_free(plinesrel); + + /* build .debug_frame section */ + if (0) { + framelen = 4; + framebuf = pbuf = nasm_malloc(framelen); + WRITELONG(pbuf,framelen-4); /* initial length */ + } else { + /* Leave .debug_frame empty if not used! */ + framelen = 0; + } + + /* build .debug_loc section */ + if (0) { + loclen = 16; + locbuf = pbuf = nasm_malloc(loclen); + if (is_elf32() || is_elfx32()) { + WRITELONG(pbuf,0); /* null beginning offset */ + WRITELONG(pbuf,0); /* null ending offset */ + } else { + nasm_assert(is_elf64()); + WRITEDLONG(pbuf,0); /* null beginning offset */ + WRITEDLONG(pbuf,0); /* null ending offset */ + } + } else { + /* Leave .debug_frame empty if not used! */ + loclen = 0; + } +} + +static void dwarf_cleanup(void) +{ + nasm_free(arangesbuf); + nasm_free(arangesrelbuf); + nasm_free(pubnamesbuf); + nasm_free(infobuf); + nasm_free(inforelbuf); + nasm_free(abbrevbuf); + nasm_free(linebuf); + nasm_free(linerelbuf); + nasm_free(framebuf); + nasm_free(locbuf); +} + +static void dwarf_findfile(const char * fname) +{ + int finx; + struct linelist *match; + + /* return if fname is current file name */ + if (dwarf_clist && !(strcmp(fname, dwarf_clist->filename))) + return; + + /* search for match */ + match = 0; + if (dwarf_flist) { + match = dwarf_flist; + for (finx = 0; finx < dwarf_numfiles; finx++) { + if (!(strcmp(fname, match->filename))) { + dwarf_clist = match; + return; + } + match = match->next; + } + } + + /* add file name to end of list */ + dwarf_clist = nasm_malloc(sizeof(struct linelist)); + dwarf_numfiles++; + dwarf_clist->line = dwarf_numfiles; + dwarf_clist->filename = nasm_malloc(strlen(fname) + 1); + strcpy(dwarf_clist->filename,fname); + dwarf_clist->next = 0; + if (!dwarf_flist) { /* if first entry */ + dwarf_flist = dwarf_elist = dwarf_clist; + dwarf_clist->last = 0; + } else { /* chain to previous entry */ + dwarf_elist->next = dwarf_clist; + dwarf_elist = dwarf_clist; + } +} + +static void dwarf_findsect(const int index) +{ + int sinx; + struct sectlist *match; + struct SAA *plinep; + + /* return if index is current section index */ + if (dwarf_csect && (dwarf_csect->section == index)) + return; + + /* search for match */ + match = 0; + if (dwarf_fsect) { + match = dwarf_fsect; + for (sinx = 0; sinx < dwarf_nsections; sinx++) { + if (match->section == index) { + dwarf_csect = match; + return; + } + match = match->next; + } + } + + /* add entry to end of list */ + dwarf_csect = nasm_malloc(sizeof(struct sectlist)); + dwarf_nsections++; + dwarf_csect->psaa = plinep = saa_init(1L); + dwarf_csect->line = 1; + dwarf_csect->offset = 0; + dwarf_csect->file = 1; + dwarf_csect->section = index; + dwarf_csect->next = 0; + /* set relocatable address at start of line program */ + saa_write8(plinep,DW_LNS_extended_op); + saa_write8(plinep,is_elf64() ? 9 : 5); /* operand length */ + saa_write8(plinep,DW_LNE_set_address); + if (is_elf64()) + saa_write64(plinep,0); /* Start Address */ + else + saa_write32(plinep,0); /* Start Address */ + + if (!dwarf_fsect) { /* if first entry */ + dwarf_fsect = dwarf_esect = dwarf_csect; + dwarf_csect->last = 0; + } else { /* chain to previous entry */ + dwarf_esect->next = dwarf_csect; + dwarf_esect = dwarf_csect; + } +} + +#endif /* defined(OF_ELF32) || defined(OF_ELF64) || defined(OF_ELFX32) */ diff --git a/vere/ext/nasm/output/outelf.h b/vere/ext/nasm/output/outelf.h new file mode 100644 index 0000000..fcb91db --- /dev/null +++ b/vere/ext/nasm/output/outelf.h @@ -0,0 +1,133 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2019 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * Internal definitions common to outelf32 and outelf64 + */ +#ifndef OUTPUT_OUTELF_H +#define OUTPUT_OUTELF_H + +#include "elf.h" +#include "rbtree.h" +#include "saa.h" + +#define GLOBAL_TEMP_BASE 0x40000000 /* bigger than any sane symbol index */ + +/* alignment of sections in file */ +#define SEC_FILEALIGN 16 + +/* this stuff is needed for the dwarf/stabs debugging format */ +#define TY_DEBUGSYMLIN 0x40 /* internal call to debug_out */ + +/* + * Debugging ELF sections (section indices starting with sec_debug) + */ + +/* stabs */ +#define sec_stab (sec_debug + 0) +#define sec_stabstr (sec_debug + 1) +#define sec_rel_stab (sec_debug + 2) + +/* stabs symbol table format */ +struct stabentry { + uint32_t n_strx; + uint8_t n_type; + uint8_t n_other; + uint16_t n_desc; + uint32_t n_value; +}; + +/* dwarf */ +#define sec_debug_aranges (sec_debug + 0) +#define sec_rela_debug_aranges (sec_debug + 1) +#define sec_debug_pubnames (sec_debug + 2) +#define sec_debug_info (sec_debug + 3) +#define sec_rela_debug_info (sec_debug + 4) +#define sec_debug_abbrev (sec_debug + 5) +#define sec_debug_line (sec_debug + 6) +#define sec_rela_debug_line (sec_debug + 7) +#define sec_debug_frame (sec_debug + 8) +#define sec_debug_loc (sec_debug + 9) + +extern uint8_t elf_osabi; +extern uint8_t elf_abiver; + +#define WRITE_STAB(p,n_strx,n_type,n_other,n_desc,n_value) \ + do { \ + WRITELONG(p, n_strx); \ + WRITECHAR(p, n_type); \ + WRITECHAR(p, n_other); \ + WRITESHORT(p, n_desc); \ + WRITELONG(p, n_value); \ + } while (0) + +struct elf_reloc { + struct elf_reloc *next; + int64_t address; /* relative to _start_ of section */ + int64_t symbol; /* symbol index */ + int64_t offset; /* symbol addend */ + int type; /* type of relocation */ +}; + +struct elf_symbol { + struct rbtree symv; /* symbol value and symbol rbtree */ + int32_t strpos; /* string table position of name */ + int32_t section; /* section ID of the symbol */ + int type; /* symbol type */ + int other; /* symbol visibility */ + int32_t size; /* size of symbol */ + int32_t globnum; /* symbol table offset if global */ + struct elf_symbol *nextfwd; /* list of unresolved-size symbols */ + char *name; /* used temporarily if in above list */ +}; + +struct elf_section { + struct SAA *data; + uint64_t len; + uint64_t size; + uint64_t nrelocs; + int32_t index; /* NASM index or NO_SEG if internal */ + int shndx; /* ELF index */ + int type; /* SHT_* */ + uint64_t align; /* alignment: power of two */ + uint64_t flags; /* section flags */ + int64_t pass_last_seen; + uint64_t entsize; /* entry size */ + char *name; + struct SAA *rel; + struct elf_reloc *head; + struct elf_reloc **tail; + struct rbtree *gsyms; /* global symbols in section */ +}; + +#endif /* OUTPUT_OUTELF_H */ diff --git a/vere/ext/nasm/output/outelf.mac b/vere/ext/nasm/output/outelf.mac new file mode 100644 index 0000000..94babe3 --- /dev/null +++ b/vere/ext/nasm/output/outelf.mac @@ -0,0 +1,41 @@ +;; -------------------------------------------------------------------------- +;; +;; Copyright 1996-2017 The NASM Authors - All Rights Reserved +;; See the file AUTHORS included with the NASM distribution for +;; the specific copyright holders. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following +;; conditions are met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following +;; disclaimer in the documentation and/or other materials provided +;; with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +;; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +;; EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;; +;; -------------------------------------------------------------------------- + +OUT: elf elf32 elf64 +%define __?SECT?__ [section .text] +%macro __?NASM_CDecl?__ 1 +%define $_%1 $%1 +%endmacro +%imacro osabi 1+.nolist +[%? %1] +%endmacro diff --git a/vere/ext/nasm/output/outform.c b/vere/ext/nasm/output/outform.c new file mode 100644 index 0000000..a2fdde8 --- /dev/null +++ b/vere/ext/nasm/output/outform.c @@ -0,0 +1,135 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2011 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outform.c manages a list of output formats, and associates + * them with their relevant drivers. Also has a + * routine to find the correct driver given a name + * for it + */ + +#include "compiler.h" + + +#define BUILD_DRIVERS_ARRAY +#include "outform.h" +#include "outlib.h" + +const struct ofmt *ofmt_find(const char *name, + const struct ofmt_alias **ofmt_alias) +{ + const struct ofmt * const *ofp; + const struct ofmt *of; + unsigned int i; + + *ofmt_alias = NULL; + + /* primary targets first */ + for (ofp = drivers; (of = *ofp); ofp++) { + if (!nasm_stricmp(name, of->shortname)) + return of; + } + + /* lets walk thru aliases then */ + for (i = 0; i < ARRAY_SIZE(ofmt_aliases); i++) { + if (ofmt_aliases[i].shortname && + !nasm_stricmp(name, ofmt_aliases[i].shortname)) { + *ofmt_alias = &ofmt_aliases[i]; + return ofmt_aliases[i].ofmt; + } + } + + return NULL; +} + +const struct dfmt *dfmt_find(const struct ofmt *ofmt, const char *name) +{ + const struct dfmt * const *dfp; + const struct dfmt *df; + + for (dfp = ofmt->debug_formats; (df = *dfp); dfp++) { + if (!nasm_stricmp(name, df->shortname)) + return df; + } + return NULL; +} + +void ofmt_list(const struct ofmt *deffmt, FILE * fp) +{ + const struct ofmt * const *ofp, *of; + unsigned int i; + + /* primary targets first */ + for (ofp = drivers; (of = *ofp); ofp++) { + fprintf(fp, " %-20s %s%s\n", + of->shortname, + of->fullname, + of == deffmt ? " [default]" : ""); + } + + /* lets walk through aliases then */ + for (i = 0; i < ARRAY_SIZE(ofmt_aliases); i++) { + if (!ofmt_aliases[i].shortname) + continue; + fprintf(fp, " %-20s Legacy alias for \"%s\"\n", + ofmt_aliases[i].shortname, + ofmt_aliases[i].ofmt->shortname); + } +} + +void dfmt_list(FILE *fp) +{ + const struct ofmt * const *ofp; + const struct ofmt *of; + const struct dfmt * const *dfp; + const struct dfmt *df; + char prefixbuf[32]; + const char *prefix; + + for (ofp = drivers; (of = *ofp); ofp++) { + if (of->debug_formats && of->debug_formats != null_debug_arr) { + snprintf(prefixbuf, sizeof prefixbuf, "%s:", + of->shortname); + prefix = prefixbuf; + + for (dfp = of->debug_formats; (df = *dfp); dfp++) { + if (df != &null_debug_form) + fprintf(fp, " %-10s %-9s %s%s\n", + prefix, + df->shortname, df->fullname, + df == of->default_dfmt ? " [default]" : ""); + prefix = ""; + } + } + } +} diff --git a/vere/ext/nasm/output/outform.h b/vere/ext/nasm/output/outform.h new file mode 100644 index 0000000..3b84674 --- /dev/null +++ b/vere/ext/nasm/output/outform.h @@ -0,0 +1,354 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2022 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outform.h header file for binding output format drivers to the + * remainder of the code in the Netwide Assembler + */ + +/* + * This header file allows configuration of which output formats + * get compiled into the NASM binary. You can configure by defining + * various preprocessor symbols beginning with "OF_", either on the + * compiler command line or at the top of this file. + * + * OF_ONLY -- only include specified object formats + * OF_name -- ensure that output format 'name' is included + * OF_NO_name -- remove output format 'name' + * OF_DOS -- ensure that 'obj', 'bin', 'win32' & 'win64' are included. + * OF_UNIX -- ensure that 'aout', 'aoutb', 'coff', 'elf32' & 'elf64' are in. + * OF_OTHERS -- ensure that 'bin', 'as86', 'rdf' 'macho32' & 'macho64' are in. + * OF_ALL -- ensure that all formats are included. + * note that this doesn't include 'dbg', which is + * only really useful if you're doing development + * work on NASM. Define OF_DBG if you want this. + * + * OF_DEFAULT=of_name -- ensure that 'name' is the default format. + * + * eg: -DOF_UNIX -DOF_ELF32 -DOF_DEFAULT=of_elf32 would be a suitable config + * for an average linux system. + * + * Default config = -DOF_ALL -DOF_DEFAULT=of_bin + * + * You probably only want to set these options while compiling 'nasm.c'. */ + +#ifndef NASM_OUTFORM_H +#define NASM_OUTFORM_H + +#include "nasm.h" + +/* -------------- USER MODIFIABLE PART ---------------- */ + +/* + * Insert #defines here in accordance with the configuration + * instructions above. + * + * E.g. + * + * #define OF_ONLY + * #define OF_OBJ + * #define OF_BIN + * + * for a 16-bit DOS assembler with no extraneous formats. + */ + +/* ------------ END USER MODIFIABLE PART -------------- */ + +/* ====configurable info begins here==== */ +/* formats configurable: + * bin,obj,elf32,elf64,aout,aoutb,coff,win32,as86,rdf2,macho32,macho64 */ + +/* process options... */ + +#ifndef OF_ONLY +#ifndef OF_ALL +#define OF_ALL /* default is to have all formats */ +#endif +#endif + +#ifdef OF_ALL /* set all formats on... */ +#ifndef OF_BIN +#define OF_BIN +#endif +#ifndef OF_OBJ +#define OF_OBJ +#endif +#ifndef OF_ELF32 +#define OF_ELF32 +#endif +#ifndef OF_ELFX32 +#define OF_ELFX32 +#endif +#ifndef OF_ELF64 +#define OF_ELF64 +#endif +#ifndef OF_COFF +#define OF_COFF +#endif +#ifndef OF_AOUT +#define OF_AOUT +#endif +#ifndef OF_AOUTB +#define OF_AOUTB +#endif +#ifndef OF_WIN32 +#define OF_WIN32 +#endif +#ifndef OF_WIN64 +#define OF_WIN64 +#endif +#ifndef OF_AS86 +#define OF_AS86 +#endif +#ifndef OF_IEEE +#define OF_IEEE +#endif +#ifndef OF_MACHO32 +#define OF_MACHO32 +#endif +#ifndef OF_MACHO64 +#define OF_MACHO64 +#endif +#ifndef OF_DBG +#define OF_DBG +#endif +#endif /* OF_ALL */ + +/* turn on groups of formats specified.... */ +#ifdef OF_DOS +#ifndef OF_OBJ +#define OF_OBJ +#endif +#ifndef OF_BIN +#define OF_BIN +#endif +#ifndef OF_COFF +#define OF_COFF /* COFF is used by DJGPP */ +#endif +#ifndef OF_WIN32 +#define OF_WIN32 +#endif +#ifndef OF_WIN64 +#define OF_WIN64 +#endif +#endif + +#ifdef OF_UNIX +#ifndef OF_AOUT +#define OF_AOUT +#endif +#ifndef OF_AOUTB +#define OF_AOUTB +#endif +#ifndef OF_COFF +#define OF_COFF +#endif +#ifndef OF_ELF32 +#define OF_ELF32 +#endif +#ifndef OF_ELF64 +#define OF_ELF64 +#endif +#ifndef OF_ELFX32 +#define OF_ELFX32 +#endif +#endif + +#ifdef OF_OTHERS +#ifndef OF_BIN +#define OF_BIN +#endif +#ifndef OF_AS86 +#define OF_AS86 +#endif +#ifndef OF_IEEE +#define OF_IEEE +#endif +#ifndef OF_MACHO32 +#define OF_MACHO32 +#endif +#ifndef OF_MACHO64 +#define OF_MACHO64 +#endif +#endif + +/* finally... override any format specifically specified to be off */ +#ifdef OF_NO_BIN +#undef OF_BIN +#endif +#ifdef OF_NO_OBJ +#undef OF_OBJ +#endif +#ifdef OF_NO_ELF32 +#undef OF_ELF32 +#endif +#ifdef OF_NO_ELF64 +#undef OF_ELF64 +#endif +#ifdef OF_NO_ELFX32 +#undef OF_ELFX32 +#endif +#ifdef OF_NO_AOUT +#undef OF_AOUT +#endif +#ifdef OF_NO_AOUTB +#undef OF_AOUTB +#endif +#ifdef OF_NO_COFF +#undef OF_COFF +#endif +#ifdef OF_NO_WIN32 +#undef OF_WIN32 +#endif +#ifdef OF_NO_WIN64 +#undef OF_WIN64 +#endif +#ifdef OF_NO_AS86 +#undef OF_AS86 +#endif +#ifdef OF_NO_IEEE +#undef OF_IEEE +#endif +#ifdef OF_NO_MACHO32 +#undef OF_MACHO32 +#endif +#ifdef OF_NO_MACHO64 +#undef OF_MACHO64 +#endif +#ifdef OF_NO_DBG +#undef OF_DBG +#endif + +#ifndef OF_DEFAULT +#define OF_DEFAULT of_bin +#endif + +extern const struct ofmt of_bin; +extern const struct ofmt of_ith; +extern const struct ofmt of_srec; +extern const struct ofmt of_aout; +extern const struct ofmt of_aoutb; +extern const struct ofmt of_coff; +extern const struct ofmt of_elf32; +extern const struct ofmt of_elfx32; +extern const struct ofmt of_elf64; +extern const struct ofmt of_as86; +extern const struct ofmt of_obj; +extern const struct ofmt of_win32; +extern const struct ofmt of_win64; +extern const struct ofmt of_ieee; +extern const struct ofmt of_macho32; +extern const struct ofmt of_macho64; +extern const struct ofmt of_dbg; + +#ifdef BUILD_DRIVERS_ARRAY /* only if included from outform.c */ + +/* + * pull in the externs for the different formats, then make the + * drivers array based on the above defines + */ + +static const struct ofmt * const drivers[] = { +#ifdef OF_BIN + &of_bin, + &of_ith, + &of_srec, +#endif +#ifdef OF_AOUT + &of_aout, +#endif +#ifdef OF_AOUTB + &of_aoutb, +#endif +#ifdef OF_COFF + &of_coff, +#endif +#ifdef OF_ELF32 + &of_elf32, +#endif +#ifdef OF_ELF64 + &of_elf64, +#endif +#ifdef OF_ELFX32 + &of_elfx32, +#endif +#ifdef OF_AS86 + &of_as86, +#endif +#ifdef OF_OBJ + &of_obj, +#endif +#ifdef OF_WIN32 + &of_win32, +#endif +#ifdef OF_WIN64 + &of_win64, +#endif +#ifdef OF_IEEE + &of_ieee, +#endif +#ifdef OF_MACHO32 + &of_macho32, +#endif +#ifdef OF_MACHO64 + &of_macho64, +#endif +#ifdef OF_DBG + &of_dbg, +#endif + + NULL +}; + +static const struct ofmt_alias ofmt_aliases[] = { +#ifdef OF_ELF32 + { "elf", &of_elf32 }, +#endif +#ifdef OF_MACHO32 + { "macho", &of_macho32 }, +#endif +#ifdef OF_WIN32 + { "win", &of_win32 }, +#endif + { NULL, NULL } +}; + +#endif /* BUILD_DRIVERS_ARRAY */ + +const struct ofmt *ofmt_find(const char *name, const struct ofmt_alias **ofmt_alias); +const struct dfmt *dfmt_find(const struct ofmt *, const char *); +void ofmt_list(const struct ofmt *, FILE *); +void dfmt_list(FILE *); +extern const struct dfmt null_debug_form; + +#endif /* NASM_OUTFORM_H */ diff --git a/vere/ext/nasm/output/outieee.c b/vere/ext/nasm/output/outieee.c new file mode 100644 index 0000000..7ba9036 --- /dev/null +++ b/vere/ext/nasm/output/outieee.c @@ -0,0 +1,1521 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2022 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outieee.c output routines for the Netwide Assembler to produce + * IEEE-std object files + */ + +/* notes: I have tried to make this correspond to the IEEE version + * of the standard, specifically the primary ASCII version. It should + * be trivial to create the binary version given this source (which is + * one of MANY things that have to be done to make this correspond to + * the hp-microtek version of the standard). + * + * 16-bit support is assumed to use 24-bit addresses + * The linker can sort out segmentation-specific stuff + * if it keeps track of externals + * in terms of being relative to section bases + * + * A non-standard variable type, the 'Yn' variable, has been introduced. + * Basically it is a reference to extern 'n'- denoting the low limit + * (L-variable) of the section that extern 'n' is defined in. Like the + * x variable, there may be no explicit assignment to it, it is derived + * from the public definition corresponding to the extern name. This + * is required because the one thing the mufom guys forgot to do well was + * take into account segmented architectures. + * + * I use comment classes for various things and these are undefined by + * the standard. + * + * Debug info should be considered totally non-standard (local labels are + * standard but linenum records are not covered by the standard. + * Type defs have the standard format but absolute meanings for ordinal + * types are not covered by the standard.) + * + * David Lindauer, LADsoft + */ +#include "compiler.h" + +#include <time.h> +#include <ctype.h> /* For toupper() */ +#include "nctype.h" + +#include "nasm.h" +#include "nasmlib.h" +#include "error.h" +#include "ver.h" + +#include "outform.h" +#include "outlib.h" + +#ifdef OF_IEEE + +#define ARRAY_BOT 0x1 + +static char ieee_infile[FILENAME_MAX]; +static int ieee_uppercase; + +static bool any_segs; +static int arrindex; + +#define HUNKSIZE 1024 /* Size of the data hunk */ +#define EXT_BLKSIZ 512 +#define LDPERLINE 32 /* bytes per line in output */ + +struct ieeeSection; + +struct LineNumber { + struct LineNumber *next; + struct ieeeSection *segment; + int32_t offset; + int32_t lineno; +}; + +static struct FileName { + struct FileName *next; + char *name; + int32_t index; +} *fnhead, **fntail; + +static struct Array { + struct Array *next; + unsigned size; + int basetype; +} *arrhead, **arrtail; + +static struct ieeePublic { + struct ieeePublic *next; + char *name; + int32_t offset; + int32_t segment; /* only if it's far-absolute */ + int32_t index; + int type; /* for debug purposes */ +} *fpubhead, **fpubtail, *last_defined; + +static struct ieeeExternal { + struct ieeeExternal *next; + char *name; + int32_t commonsize; +} *exthead, **exttail; + +static int externals; + +static struct ExtBack { + struct ExtBack *next; + int index[EXT_BLKSIZ]; +} *ebhead, **ebtail; + +/* NOTE: the first segment MUST be the lineno segment */ +static struct ieeeSection { + struct ieeeSection *next; + char *name; + struct ieeeObjData *data, *datacurr; + struct ieeeFixupp *fptr, *flptr; + int32_t index; /* the NASM segment id */ + int32_t ieee_index; /* the IEEE-file segment index */ + int32_t currentpos; + int32_t align; /* can be SEG_ABS + absolute addr */ + int32_t startpos; + int32_t use32; /* is this segment 32-bit? */ + int64_t pass_last_seen; + struct ieeePublic *pubhead, **pubtail, *lochead, **loctail; + enum { + CMB_PRIVATE = 0, + CMB_PUBLIC = 2, + CMB_COMMON = 6 + } combine; +} *seghead, **segtail, *ieee_seg_needs_update; + +struct ieeeObjData { + struct ieeeObjData *next; + uint8_t data[HUNKSIZE]; +}; + +struct ieeeFixupp { + struct ieeeFixupp *next; + enum { + FT_SEG = 0, + FT_REL = 1, + FT_OFS = 2, + FT_EXT = 3, + FT_WRT = 4, + FT_EXTREL = 5, + FT_EXTWRT = 6, + FT_EXTSEG = 7 + } ftype; + int16_t size; + int32_t id1; + int32_t id2; + int32_t offset; + int32_t addend; +}; + +static int32_t ieee_entry_seg, ieee_entry_ofs; +static int checksum; + +extern const struct ofmt of_ieee; +static const struct dfmt ladsoft_debug_form; + +static void ieee_data_new(struct ieeeSection *); +static void ieee_write_fixup(int32_t, int32_t, struct ieeeSection *, + int, uint64_t, int32_t); +static void ieee_install_fixup(struct ieeeSection *, struct ieeeFixupp *); +static int32_t ieee_segment(char *, int *); +static void ieee_write_file(void); +static void ieee_write_byte(struct ieeeSection *, int); +static void ieee_write_word(struct ieeeSection *, int); +static void ieee_write_dword(struct ieeeSection *, int32_t); +static void ieee_putascii(char *, ...); +static void ieee_putcs(int); +static int32_t ieee_putld(int32_t, int32_t, uint8_t *); +static int32_t ieee_putlr(struct ieeeFixupp *); +static void ieee_unqualified_name(char *, char *); + +/* + * pup init + */ +static void ieee_init(void) +{ + strlcpy(ieee_infile, inname, sizeof(ieee_infile)); + any_segs = false; + fpubhead = NULL; + fpubtail = &fpubhead; + exthead = NULL; + exttail = &exthead; + externals = 1; + ebhead = NULL; + ebtail = &ebhead; + seghead = ieee_seg_needs_update = NULL; + segtail = &seghead; + ieee_entry_seg = NO_SEG; + ieee_uppercase = false; + checksum = 0; +} + +/* + * Rundown + */ +static void ieee_cleanup(void) +{ + ieee_write_file(); + dfmt->cleanup(); + while (seghead) { + struct ieeeSection *segtmp = seghead; + seghead = seghead->next; + while (segtmp->pubhead) { + struct ieeePublic *pubtmp = segtmp->pubhead; + segtmp->pubhead = pubtmp->next; + nasm_free(pubtmp); + } + while (segtmp->fptr) { + struct ieeeFixupp *fixtmp = segtmp->fptr; + segtmp->fptr = fixtmp->next; + nasm_free(fixtmp); + } + while (segtmp->data) { + struct ieeeObjData *dattmp = segtmp->data; + segtmp->data = dattmp->next; + nasm_free(dattmp); + } + nasm_free(segtmp); + } + while (fpubhead) { + struct ieeePublic *pubtmp = fpubhead; + fpubhead = fpubhead->next; + nasm_free(pubtmp); + } + while (exthead) { + struct ieeeExternal *exttmp = exthead; + exthead = exthead->next; + nasm_free(exttmp); + } + while (ebhead) { + struct ExtBack *ebtmp = ebhead; + ebhead = ebhead->next; + nasm_free(ebtmp); + } +} + +/* + * callback for labels + */ +static void ieee_deflabel(char *name, int32_t segment, + int64_t offset, int is_global, char *special) +{ + /* + * We have three cases: + * + * (i) `segment' is a segment-base. If so, set the name field + * for the segment structure it refers to, and then + * return. + * + * (ii) `segment' is one of our segments, or a SEG_ABS segment. + * Save the label position for later output of a PUBDEF record. + * + * + * (iii) `segment' is not one of our segments. Save the label + * position for later output of an EXTDEF. + */ + struct ieeeExternal *ext; + struct ExtBack *eb; + struct ieeeSection *seg; + int i; + + if (special) + nasm_nonfatal("unrecognised symbol type `%s'", special); + /* + * First check for the double-period, signifying something + * unusual. + */ + if (name[0] == '.' && name[1] == '.' && name[2] != '@') { + if (!strcmp(name, "..start")) { + ieee_entry_seg = segment; + ieee_entry_ofs = offset; + } + return; + } + + /* + * Case (i): + */ + if (ieee_seg_needs_update) { + ieee_seg_needs_update->name = name; + return; + } + if (segment < SEG_ABS && segment != NO_SEG && segment % 2) + return; + + /* + * case (ii) + */ + if (segment >= SEG_ABS) { + /* + * SEG_ABS subcase of (ii). + */ + if (is_global) { + struct ieeePublic *pub; + + pub = *fpubtail = nasm_malloc(sizeof(*pub)); + fpubtail = &pub->next; + pub->next = NULL; + pub->name = name; + pub->offset = offset; + pub->segment = segment & ~SEG_ABS; + } + return; + } + + for (seg = seghead; seg && is_global; seg = seg->next) + if (seg->index == segment) { + struct ieeePublic *pub; + + last_defined = pub = *seg->pubtail = nasm_malloc(sizeof(*pub)); + seg->pubtail = &pub->next; + pub->next = NULL; + pub->name = name; + pub->offset = offset; + pub->index = seg->ieee_index; + pub->segment = -1; + return; + } + + /* + * Case (iii). + */ + if (is_global) { + ext = *exttail = nasm_malloc(sizeof(*ext)); + ext->next = NULL; + exttail = &ext->next; + ext->name = name; + if (is_global == 2) + ext->commonsize = offset; + else + ext->commonsize = 0; + i = segment / 2; + eb = ebhead; + if (!eb) { + eb = *ebtail = nasm_zalloc(sizeof(*eb)); + eb->next = NULL; + ebtail = &eb->next; + } + while (i > EXT_BLKSIZ) { + if (eb && eb->next) + eb = eb->next; + else { + eb = *ebtail = nasm_zalloc(sizeof(*eb)); + eb->next = NULL; + ebtail = &eb->next; + } + i -= EXT_BLKSIZ; + } + eb->index[i] = externals++; + } + +} + +/* + * Put data out + */ +static void ieee_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + const uint8_t *ucdata; + int32_t ldata; + struct ieeeSection *seg; + + /* + * If `any_segs' is still false, we must define a default + * segment. + */ + if (!any_segs) { + int tempint; /* ignored */ + if (segto != ieee_segment("__NASMDEFSEG", &tempint)) + nasm_panic("strange segment conditions in IEEE driver"); + } + + /* + * Find the segment we are targeting. + */ + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segto) + break; + if (!seg) + nasm_panic("code directed to nonexistent segment?"); + + if (type == OUT_RAWDATA) { + ucdata = data; + while (size--) + ieee_write_byte(seg, *ucdata++); + } else if (type == OUT_ADDRESS || type == OUT_REL2ADR || + type == OUT_REL4ADR) { + if (type == OUT_ADDRESS) + size = abs((int)size); + else if (segment == NO_SEG) + nasm_nonfatal("relative call to absolute address not" + " supported by IEEE format"); + ldata = *(int64_t *)data; + if (type == OUT_REL2ADR) + ldata += (size - 2); + if (type == OUT_REL4ADR) + ldata += (size - 4); + ieee_write_fixup(segment, wrt, seg, size, type, ldata); + if (size == 2) + ieee_write_word(seg, ldata); + else + ieee_write_dword(seg, ldata); + } else if (type == OUT_RESERVE) { + while (size--) + ieee_write_byte(seg, 0); + } +} + +static void ieee_data_new(struct ieeeSection *segto) +{ + + if (!segto->data) + segto->data = segto->datacurr = + nasm_malloc(sizeof(*(segto->datacurr))); + else + segto->datacurr = segto->datacurr->next = + nasm_malloc(sizeof(*(segto->datacurr))); + segto->datacurr->next = NULL; +} + +/* + * this routine is unalduterated bloatware. I usually don't do this + * but I might as well see what it is like on a harmless program. + * If anyone wants to optimize this is a good canditate! + */ +static void ieee_write_fixup(int32_t segment, int32_t wrt, + struct ieeeSection *segto, int size, + uint64_t realtype, int32_t offset) +{ + struct ieeeSection *target; + struct ieeeFixupp s; + + /* Don't put a fixup for things NASM can calculate */ + if (wrt == NO_SEG && segment == NO_SEG) + return; + + s.ftype = -1; + /* if it is a WRT offset */ + if (wrt != NO_SEG) { + s.ftype = FT_WRT; + s.addend = offset; + if (wrt >= SEG_ABS) + s.id1 = -(wrt - SEG_ABS); + else { + if (wrt % 2 && realtype != OUT_REL2ADR + && realtype != OUT_REL4ADR) { + wrt--; + + for (target = seghead; target; target = target->next) + if (target->index == wrt) + break; + if (target) { + s.id1 = target->ieee_index; + for (target = seghead; target; target = target->next) + if (target->index == segment) + break; + + if (target) + s.id2 = target->ieee_index; + else { + /* + * Now we assume the segment field is being used + * to hold an extern index + */ + int32_t i = segment / 2; + struct ExtBack *eb = ebhead; + while (i > EXT_BLKSIZ) { + if (eb) + eb = eb->next; + else + break; + i -= EXT_BLKSIZ; + } + /* if we have an extern decide the type and make a record + */ + if (eb) { + s.ftype = FT_EXTWRT; + s.addend = 0; + s.id2 = eb->index[i]; + } else + nasm_nonfatal("source of WRT must be an offset"); + } + + } else + nasm_panic("unrecognised WRT value in ieee_write_fixup"); + } else + nasm_nonfatal("target of WRT must be a section"); + } + s.size = size; + ieee_install_fixup(segto, &s); + return; + } + /* Pure segment fixup ? */ + if (segment != NO_SEG) { + s.ftype = FT_SEG; + s.id1 = 0; + if (segment >= SEG_ABS) { + /* absolute far segment fixup */ + s.id1 = -(segment - ~SEG_ABS); + } else if (segment % 2) { + /* fixup to named segment */ + /* look it up */ + for (target = seghead; target; target = target->next) + if (target->index == segment - 1) + break; + if (target) + s.id1 = target->ieee_index; + else { + /* + * Now we assume the segment field is being used + * to hold an extern index + */ + int32_t i = segment / 2; + struct ExtBack *eb = ebhead; + while (i > EXT_BLKSIZ) { + if (eb) + eb = eb->next; + else + break; + i -= EXT_BLKSIZ; + } + /* if we have an extern decide the type and make a record + */ + if (eb) { + if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) { + nasm_panic("Segment of a rel not supported in ieee_write_fixup"); + } else { + /* If we want the segment */ + s.ftype = FT_EXTSEG; + s.addend = 0; + s.id1 = eb->index[i]; + } + + } else + /* If we get here the seg value doesn't make sense */ + nasm_panic("unrecognised segment value in ieee_write_fixup"); + } + + } else { + /* Assume we are offsetting directly from a section + * So look up the target segment + */ + for (target = seghead; target; target = target->next) + if (target->index == segment) + break; + if (target) { + if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) { + /* PC rel to a known offset */ + s.id1 = target->ieee_index; + s.ftype = FT_REL; + s.size = size; + s.addend = offset; + } else { + /* We were offsetting from a seg */ + s.id1 = target->ieee_index; + s.ftype = FT_OFS; + s.size = size; + s.addend = offset; + } + } else { + /* + * Now we assume the segment field is being used + * to hold an extern index + */ + int32_t i = segment / 2; + struct ExtBack *eb = ebhead; + while (i > EXT_BLKSIZ) { + if (eb) + eb = eb->next; + else + break; + i -= EXT_BLKSIZ; + } + /* if we have an extern decide the type and make a record + */ + if (eb) { + if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) { + s.ftype = FT_EXTREL; + s.addend = 0; + s.id1 = eb->index[i]; + } else { + /* else we want the external offset */ + s.ftype = FT_EXT; + s.addend = 0; + s.id1 = eb->index[i]; + } + + } else + /* If we get here the seg value doesn't make sense */ + nasm_panic("unrecognised segment value in ieee_write_fixup"); + } + } + if (size != 2 && s.ftype == FT_SEG) + nasm_nonfatal("IEEE format can only handle 2-byte" + " segment base references"); + s.size = size; + ieee_install_fixup(segto, &s); + return; + } + /* should never get here */ +} +static void ieee_install_fixup(struct ieeeSection *seg, + struct ieeeFixupp *fix) +{ + struct ieeeFixupp *f; + f = nasm_malloc(sizeof(struct ieeeFixupp)); + memcpy(f, fix, sizeof(struct ieeeFixupp)); + f->offset = seg->currentpos; + seg->currentpos += fix->size; + f->next = NULL; + if (seg->fptr) + seg->flptr = seg->flptr->next = f; + else + seg->fptr = seg->flptr = f; + +} + +/* + * segment registry + */ +static int32_t ieee_segment(char *name, int *bits) +{ + /* + * We call the label manager here to define a name for the new + * segment, and when our _own_ label-definition stub gets + * called in return, it should register the new segment name + * using the pointer it gets passed. That way we save memory, + * by sponging off the label manager. + */ + if (!name) { + *bits = 16; + if (!any_segs) + return 0; + return seghead->index; + } else { + struct ieeeSection *seg; + int ieee_idx, attrs; + bool rn_error; + char *p; + + /* + * Look for segment attributes. + */ + attrs = 0; + while (*name == '.') + name++; /* hack, but a documented one */ + p = name; + while (*p && !nasm_isspace(*p)) + p++; + if (*p) { + *p++ = '\0'; + while (*p && nasm_isspace(*p)) + *p++ = '\0'; + } + while (*p) { + while (*p && !nasm_isspace(*p)) + p++; + if (*p) { + *p++ = '\0'; + while (*p && nasm_isspace(*p)) + *p++ = '\0'; + } + + attrs++; + } + + ieee_idx = 1; + for (seg = seghead; seg; seg = seg->next) { + ieee_idx++; + if (!strcmp(seg->name, name)) { + if (attrs > 0 && seg->pass_last_seen == pass_count()) + nasm_warn(WARN_OTHER, "segment attributes specified on" + " redeclaration of segment: ignoring"); + if (seg->use32) + *bits = 32; + else + *bits = 16; + + seg->pass_last_seen = pass_count(); + return seg->index; + } + } + + *segtail = seg = nasm_malloc(sizeof(*seg)); + seg->next = NULL; + segtail = &seg->next; + seg->index = seg_alloc(); + seg->ieee_index = ieee_idx; + any_segs = true; + seg->name = NULL; + seg->currentpos = 0; + seg->align = 1; /* default */ + seg->use32 = *bits == 32; /* default to user spec */ + seg->combine = CMB_PUBLIC; /* default */ + seg->pubhead = NULL; + seg->pubtail = &seg->pubhead; + seg->data = NULL; + seg->fptr = NULL; + seg->lochead = NULL; + seg->loctail = &seg->lochead; + + /* + * Process the segment attributes. + */ + p = name; + while (attrs--) { + p += strlen(p); + while (!*p) + p++; + + /* + * `p' contains a segment attribute. + */ + if (!nasm_stricmp(p, "private")) + seg->combine = CMB_PRIVATE; + else if (!nasm_stricmp(p, "public")) + seg->combine = CMB_PUBLIC; + else if (!nasm_stricmp(p, "common")) + seg->combine = CMB_COMMON; + else if (!nasm_stricmp(p, "use16")) + seg->use32 = false; + else if (!nasm_stricmp(p, "use32")) + seg->use32 = true; + else if (!nasm_strnicmp(p, "align=", 6)) { + seg->align = readnum(p + 6, &rn_error); + if (seg->align == 0) + seg->align = 1; + if (rn_error) { + seg->align = 1; + nasm_nonfatal("segment alignment should be numeric"); + } + switch (seg->align) { + case 1: /* BYTE */ + case 2: /* WORD */ + case 4: /* DWORD */ + case 16: /* PARA */ + case 256: /* PAGE */ + case 8: + case 32: + case 64: + case 128: + break; + default: + nasm_nonfatal("invalid alignment value %d", seg->align); + seg->align = 1; + break; + } + } else if (!nasm_strnicmp(p, "absolute=", 9)) { + seg->align = SEG_ABS + readnum(p + 9, &rn_error); + if (rn_error) + nasm_nonfatal("argument to `absolute' segment" + " attribute should be numeric"); + } + } + + ieee_seg_needs_update = seg; + if (seg->align >= SEG_ABS) + define_label(name, NO_SEG, seg->align - SEG_ABS, false); + else + define_label(name, seg->index + 1, 0L, false); + ieee_seg_needs_update = NULL; + + /* + * In commit 98578071b9d71ecaa2344dd9c185237c1765041e + * we reworked labels significantly which in turn lead + * to the case where seg->name = NULL here and we get + * nil dereference in next segments definitions. + * + * Lets placate this case with explicit name setting + * if labels engine didn't set it yet. + * + * FIXME: Need to revisit this moment if such fix doesn't + * break anything but since IEEE 695 format is veeery + * old I don't expect there are many users left. In worst + * case this should only lead to a memory leak. + */ + if (!seg->name) + seg->name = nasm_strdup(name); + + if (seg->use32) + *bits = 32; + else + *bits = 16; + return seg->index; + } +} + +/* + * directives supported + */ +static enum directive_result +ieee_directive(enum directive directive, char *value) +{ + (void)value; + + switch (directive) { + case D_UPPERCASE: + ieee_uppercase = true; + return DIRR_OK; + + default: + return DIRR_UNKNOWN; + } +} + +static void ieee_sectalign(int32_t seg, unsigned int value) +{ + struct ieeeSection *s; + + list_for_each(s, seghead) { + if (s->index == seg) + break; + } + + /* + * 256 is maximum there, note it may happen + * that align is issued on "absolute" segment + * it's fine since SEG_ABS > 256 and we never + * get escape this test + */ + if (!s || !is_power2(value) || value > 256) + return; + + if ((unsigned int)s->align < value) + s->align = value; +} + +/* + * Return segment data + */ +static int32_t ieee_segbase(int32_t segment) +{ + struct ieeeSection *seg; + + /* + * Find the segment in our list. + */ + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segment - 1) + break; + + if (!seg) + return segment; /* not one of ours - leave it alone */ + + if (seg->align >= SEG_ABS) + return seg->align; /* absolute segment */ + + return segment; /* no special treatment */ +} + +static void ieee_write_file(void) +{ + const struct tm * const thetime = &official_compile_time.local; + struct FileName *fn; + struct ieeeSection *seg; + struct ieeePublic *pub, *loc; + struct ieeeExternal *ext; + struct ieeeObjData *data; + struct ieeeFixupp *fix; + struct Array *arr; + int i; + const bool debuginfo = (dfmt == &ladsoft_debug_form); + + /* + * Write the module header + */ + ieee_putascii("MBFNASM,%02X%s.\n", strlen(ieee_infile), ieee_infile); + + /* + * Write the NASM boast comment. + */ + ieee_putascii("CO0,%02X%s.\n", nasm_comment_len(), nasm_comment()); + + /* + * write processor-specific information + */ + ieee_putascii("AD8,4,L.\n"); + + /* + * date and time + */ + ieee_putascii("DT%04d%02d%02d%02d%02d%02d.\n", + 1900 + thetime->tm_year, thetime->tm_mon + 1, + thetime->tm_mday, thetime->tm_hour, thetime->tm_min, + thetime->tm_sec); + /* + * if debugging, dump file names + */ + for (fn = fnhead; fn && debuginfo; fn = fn->next) { + ieee_putascii("C0105,%02X%s.\n", strlen(fn->name), fn->name); + } + + ieee_putascii("CO101,07ENDHEAD.\n"); + /* + * the standard doesn't specify when to put checksums, + * we'll just do it periodically. + */ + ieee_putcs(false); + + /* + * Write the section headers + */ + seg = seghead; + if (!debuginfo && seg && !strcmp(seg->name, "??LINE")) + seg = seg->next; + while (seg) { + char buf[256]; + char attrib; + switch (seg->combine) { + case CMB_PUBLIC: + default: + attrib = 'C'; + break; + case CMB_PRIVATE: + attrib = 'S'; + break; + case CMB_COMMON: + attrib = 'M'; + break; + } + ieee_unqualified_name(buf, seg->name); + if (seg->align >= SEG_ABS) { + ieee_putascii("ST%X,A,%02X%s.\n", seg->ieee_index, + strlen(buf), buf); + ieee_putascii("ASL%X,%lX.\n", seg->ieee_index, + (seg->align - SEG_ABS) * 16); + } else { + ieee_putascii("ST%X,%c,%02X%s.\n", seg->ieee_index, attrib, + strlen(buf), buf); + ieee_putascii("SA%X,%lX.\n", seg->ieee_index, seg->align); + ieee_putascii("ASS%X,%X.\n", seg->ieee_index, + seg->currentpos); + } + seg = seg->next; + } + /* + * write the start address if there is one + */ + if (ieee_entry_seg && seghead) { + for (seg = seghead; seg; seg = seg->next) + if (seg->index == ieee_entry_seg) + break; + if (!seg) + nasm_panic("Start address records are incorrect"); + else + ieee_putascii("ASG,R%X,%lX,+.\n", seg->ieee_index, + ieee_entry_ofs); + } + + ieee_putcs(false); + /* + * Write the publics + */ + i = 1; + for (seg = seghead; seg; seg = seg->next) { + for (pub = seg->pubhead; pub; pub = pub->next) { + char buf[256]; + ieee_unqualified_name(buf, pub->name); + ieee_putascii("NI%X,%02X%s.\n", i, strlen(buf), buf); + if (pub->segment == -1) + ieee_putascii("ASI%X,R%X,%lX,+.\n", i, pub->index, + pub->offset); + else + ieee_putascii("ASI%X,%lX,%lX,+.\n", i, pub->segment * 16, + pub->offset); + if (debuginfo) { + if (pub->type >= 0x100) + ieee_putascii("ATI%X,T%X.\n", i, pub->type - 0x100); + else + ieee_putascii("ATI%X,%X.\n", i, pub->type); + } + i++; + } + } + pub = fpubhead; + i = 1; + while (pub) { + char buf[256]; + ieee_unqualified_name(buf, pub->name); + ieee_putascii("NI%X,%02X%s.\n", i, strlen(buf), buf); + if (pub->segment == -1) + ieee_putascii("ASI%X,R%X,%lX,+.\n", i, pub->index, + pub->offset); + else + ieee_putascii("ASI%X,%lX,%lX,+.\n", i, pub->segment * 16, + pub->offset); + if (debuginfo) { + if (pub->type >= 0x100) + ieee_putascii("ATI%X,T%X.\n", i, pub->type - 0x100); + else + ieee_putascii("ATI%X,%X.\n", i, pub->type); + } + i++; + pub = pub->next; + } + /* + * Write the externals + */ + ext = exthead; + i = 1; + while (ext) { + char buf[256]; + ieee_unqualified_name(buf, ext->name); + ieee_putascii("NX%X,%02X%s.\n", i++, strlen(buf), buf); + ext = ext->next; + } + ieee_putcs(false); + + /* + * IEEE doesn't have a standard pass break record + * so use the ladsoft variant + */ + ieee_putascii("CO100,06ENDSYM.\n"); + + /* + * now put types + */ + i = ARRAY_BOT; + for (arr = arrhead; arr && debuginfo; arr = arr->next) { + ieee_putascii("TY%X,20,%X,%lX.\n", i++, arr->basetype, + arr->size); + } + /* + * now put locals + */ + i = 1; + for (seg = seghead; seg && debuginfo; seg = seg->next) { + for (loc = seg->lochead; loc; loc = loc->next) { + char buf[256]; + ieee_unqualified_name(buf, loc->name); + ieee_putascii("NN%X,%02X%s.\n", i, strlen(buf), buf); + if (loc->segment == -1) + ieee_putascii("ASN%X,R%X,%lX,+.\n", i, loc->index, + loc->offset); + else + ieee_putascii("ASN%X,%lX,%lX,+.\n", i, loc->segment * 16, + loc->offset); + if (debuginfo) { + if (loc->type >= 0x100) + ieee_putascii("ATN%X,T%X.\n", i, loc->type - 0x100); + else + ieee_putascii("ATN%X,%X.\n", i, loc->type); + } + i++; + } + } + + /* + * put out section data; + */ + seg = seghead; + if (!debuginfo && seg && !strcmp(seg->name, "??LINE")) + seg = seg->next; + while (seg) { + if (seg->currentpos) { + int32_t size, org = 0; + data = seg->data; + ieee_putascii("SB%X.\n", seg->ieee_index); + fix = seg->fptr; + while (fix) { + size = HUNKSIZE - (org % HUNKSIZE); + size = + size + org > + seg->currentpos ? seg->currentpos - org : size; + size = fix->offset - org > size ? size : fix->offset - org; + org = ieee_putld(org, org + size, data->data); + if (org % HUNKSIZE == 0) + data = data->next; + if (org == fix->offset) { + org += ieee_putlr(fix); + fix = fix->next; + } + } + while (org < seg->currentpos && data) { + size = + seg->currentpos - org > + HUNKSIZE ? HUNKSIZE : seg->currentpos - org; + org = ieee_putld(org, org + size, data->data); + data = data->next; + } + ieee_putcs(false); + + } + seg = seg->next; + } + /* + * module end record + */ + ieee_putascii("ME.\n"); +} + +static void ieee_write_byte(struct ieeeSection *seg, int data) +{ + int temp; + if (!(temp = seg->currentpos++ % HUNKSIZE)) + ieee_data_new(seg); + seg->datacurr->data[temp] = data; +} + +static void ieee_write_word(struct ieeeSection *seg, int data) +{ + ieee_write_byte(seg, data & 0xFF); + ieee_write_byte(seg, (data >> 8) & 0xFF); +} + +static void ieee_write_dword(struct ieeeSection *seg, int32_t data) +{ + ieee_write_byte(seg, data & 0xFF); + ieee_write_byte(seg, (data >> 8) & 0xFF); + ieee_write_byte(seg, (data >> 16) & 0xFF); + ieee_write_byte(seg, (data >> 24) & 0xFF); +} +static void printf_func(1, 2) ieee_putascii(char *format, ...) +{ + char buffer[256]; + size_t i, l; + va_list ap; + + va_start(ap, format); + l = vsnprintf(buffer, sizeof(buffer), format, ap); + nasm_assert(l < sizeof(buffer)); + for (i = 0; i < l; i++) + if ((uint8_t)buffer[i] > 31) + checksum += buffer[i]; + va_end(ap); + fputs(buffer, ofile); +} + +/* + * put out a checksum record */ +static void ieee_putcs(int toclear) +{ + if (toclear) { + ieee_putascii("CS.\n"); + } else { + checksum += 'C'; + checksum += 'S'; + ieee_putascii("CS%02X.\n", checksum & 127); + } + checksum = 0; +} + +static int32_t ieee_putld(int32_t start, int32_t end, uint8_t *buf) +{ + int32_t val; + if (start == end) + return (start); + val = start % HUNKSIZE; + /* fill up multiple lines */ + while (end - start >= LDPERLINE) { + int i; + ieee_putascii("LD"); + for (i = 0; i < LDPERLINE; i++) { + ieee_putascii("%02X", buf[val++]); + start++; + } + ieee_putascii(".\n"); + } + /* if no partial lines */ + if (start == end) + return (start); + /* make a partial line */ + ieee_putascii("LD"); + while (start < end) { + ieee_putascii("%02X", buf[val++]); + start++; + } + ieee_putascii(".\n"); + return (start); +} +static int32_t ieee_putlr(struct ieeeFixupp *p) +{ +/* + * To deal with the vagaries of segmentation the LADsoft linker + * defines two types of segments: absolute and virtual. Note that + * 'absolute' in this context is a different thing from the IEEE + * definition of an absolute segment type, which is also supported. If a + * segment is linked in virtual mode the low limit (L-var) is + * subtracted from each R,X, and P variable which appears in an + * expression, so that we can have relative offsets. Meanwhile + * in the ABSOLUTE mode this subtraction is not done and + * so we can use absolute offsets from 0. In the LADsoft linker + * this configuration is not done in the assemblker source but in + * a source the linker reads. Generally this type of thing only + * becomes an issue if real mode code is used. A pure 32-bit linker could + * get away without defining the virtual mode... + */ + char buf[40]; + int32_t size = p->size; + switch (p->ftype) { + case FT_SEG: + if (p->id1 < 0) + sprintf(buf, "%"PRIX32"", -p->id1); + else + sprintf(buf, "L%"PRIX32",10,/", p->id1); + break; + case FT_OFS: + sprintf(buf, "R%"PRIX32",%"PRIX32",+", p->id1, p->addend); + break; + case FT_REL: + sprintf(buf, "R%"PRIX32",%"PRIX32",+,P,-,%X,-", p->id1, p->addend, p->size); + break; + + case FT_WRT: + if (p->id2 < 0) + sprintf(buf, "R%"PRIX32",%"PRIX32",+,L%"PRIX32",+,%"PRIX32",-", p->id2, p->addend, + p->id2, -p->id1 * 16); + else + sprintf(buf, "R%"PRIX32",%"PRIX32",+,L%"PRIX32",+,L%"PRIX32",-", p->id2, p->addend, + p->id2, p->id1); + break; + case FT_EXT: + sprintf(buf, "X%"PRIX32"", p->id1); + break; + case FT_EXTREL: + sprintf(buf, "X%"PRIX32",P,-,%"PRIX32",-", p->id1, size); + break; + case FT_EXTSEG: + /* We needed a non-ieee hack here. + * We introduce the Y variable, which is the low + * limit of the native segment the extern resides in + */ + sprintf(buf, "Y%"PRIX32",10,/", p->id1); + break; + case FT_EXTWRT: + if (p->id2 < 0) + sprintf(buf, "X%"PRIX32",Y%"PRIX32",+,%"PRIX32",-", p->id2, p->id2, + -p->id1 * 16); + else + sprintf(buf, "X%"PRIX32",Y%"PRIX32",+,L%"PRIX32",-", p->id2, p->id2, p->id1); + break; + } + ieee_putascii("LR(%s,%"PRIX32").\n", buf, size); + + return (size); +} + +/* Dump all segment data (text and fixups )*/ + +static void ieee_unqualified_name(char *dest, char *source) +{ + if (ieee_uppercase) { + while (*source) + *dest++ = toupper(*source++); + *dest = 0; + } else + strcpy(dest, source); +} +static void dbgls_init(void) +{ + int tempint; + + fnhead = NULL; + fntail = &fnhead; + arrindex = ARRAY_BOT; + arrhead = NULL; + arrtail = &arrhead; + ieee_segment("??LINE", &tempint); + any_segs = false; +} +static void dbgls_cleanup(void) +{ + struct ieeeSection *segtmp; + while (fnhead) { + struct FileName *fntemp = fnhead; + fnhead = fnhead->next; + nasm_free(fntemp->name); + nasm_free(fntemp); + } + for (segtmp = seghead; segtmp; segtmp = segtmp->next) { + while (segtmp->lochead) { + struct ieeePublic *loctmp = segtmp->lochead; + segtmp->lochead = loctmp->next; + nasm_free(loctmp->name); + nasm_free(loctmp); + } + } + while (arrhead) { + struct Array *arrtmp = arrhead; + arrhead = arrhead->next; + nasm_free(arrtmp); + } +} + +/* + * because this routine is not bracketed in + * the main program, this routine will be called even if there + * is no request for debug info + * so, we have to make sure the ??LINE segment is available + * as the first segment when this debug format is selected + */ +static void dbgls_linnum(const char *lnfname, int32_t lineno, int32_t segto) +{ + struct FileName *fn; + struct ieeeSection *seg; + int i = 0; + if (segto == NO_SEG) + return; + + /* + * If `any_segs' is still false, we must define a default + * segment. + */ + if (!any_segs) { + int tempint; /* ignored */ + if (segto != ieee_segment("__NASMDEFSEG", &tempint)) + nasm_panic("strange segment conditions in IEEE driver"); + } + + /* + * Find the segment we are targeting. + */ + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segto) + break; + if (!seg) + nasm_panic("lineno directed to nonexistent segment?"); + + for (fn = fnhead; fn; fn = fn->next) { + if (!nasm_stricmp(lnfname, fn->name)) + break; + i++; + } + if (!fn) { + fn = nasm_malloc(sizeof(*fn)); + fn->name = nasm_malloc(strlen(lnfname) + 1); + fn->index = i; + strcpy(fn->name, lnfname); + fn->next = NULL; + *fntail = fn; + fntail = &fn->next; + } + ieee_write_byte(seghead, fn->index); + ieee_write_word(seghead, lineno); + ieee_write_fixup(segto, NO_SEG, seghead, 4, OUT_ADDRESS, + seg->currentpos); + +} +static void dbgls_deflabel(char *name, int32_t segment, + int64_t offset, int is_global, char *special) +{ + struct ieeeSection *seg; + + /* Keep compiler from warning about special */ + (void)special; + + /* + * Note: ..[^@] special symbols are filtered in labels.c + */ + + /* + * If it's a special-retry from pass two, discard it. + */ + if (is_global == 3) + return; + + /* + * Case (i): + */ + if (ieee_seg_needs_update) + return; + if (segment < SEG_ABS && segment != NO_SEG && segment % 2) + return; + + if (segment >= SEG_ABS || segment == NO_SEG) { + return; + } + + /* + * If `any_segs' is still false, we might need to define a + * default segment, if they're trying to declare a label in + * `first_seg'. But the label should exist due to a prior + * call to ieee_deflabel so we can skip that. + */ + + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segment) { + struct ieeePublic *loc; + /* + * Case (ii). Maybe MODPUB someday? + */ + if (!is_global) { + last_defined = loc = nasm_malloc(sizeof(*loc)); + *seg->loctail = loc; + seg->loctail = &loc->next; + loc->next = NULL; + loc->name = nasm_strdup(name); + loc->offset = offset; + loc->segment = -1; + loc->index = seg->ieee_index; + } + } +} +static void dbgls_typevalue(int32_t type) +{ + int elem = TYM_ELEMENTS(type); + type = TYM_TYPE(type); + + if (!last_defined) + return; + + switch (type) { + case TY_BYTE: + last_defined->type = 1; /* uint8_t */ + break; + case TY_WORD: + last_defined->type = 3; /* unsigned word */ + break; + case TY_DWORD: + last_defined->type = 5; /* unsigned dword */ + break; + case TY_FLOAT: + last_defined->type = 9; /* float */ + break; + case TY_QWORD: + last_defined->type = 10; /* qword */ + break; + case TY_TBYTE: + last_defined->type = 11; /* TBYTE */ + break; + default: + last_defined->type = 0x10; /* near label */ + break; + } + + if (elem > 1) { + struct Array *arrtmp = nasm_malloc(sizeof(*arrtmp)); + int vtype = last_defined->type; + arrtmp->size = elem; + arrtmp->basetype = vtype; + arrtmp->next = NULL; + last_defined->type = arrindex++ + 0x100; + *arrtail = arrtmp; + arrtail = &(arrtmp->next); + } + last_defined = NULL; +} +static void dbgls_output(int output_type, void *param) +{ + (void)output_type; + (void)param; +} +static const struct dfmt ladsoft_debug_form = { + "LADsoft Debug Records", + "ladsoft", + dbgls_init, + dbgls_linnum, + dbgls_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + dbgls_typevalue, + dbgls_output, + dbgls_cleanup, + NULL /* pragma list */ +}; +static const struct dfmt * const ladsoft_debug_arr[3] = { + &ladsoft_debug_form, + &null_debug_form, + NULL +}; +const struct ofmt of_ieee = { + "IEEE-695 (LADsoft variant) object file format", + "ieee", + ".o", + OFMT_TEXT, + 32, + ladsoft_debug_arr, + &ladsoft_debug_form, + NULL, + ieee_init, + null_reset, + nasm_do_legacy_output, + ieee_out, + ieee_deflabel, + ieee_segment, + NULL, + ieee_sectalign, + ieee_segbase, + ieee_directive, + ieee_cleanup, + NULL /* pragma list */ +}; + +#endif /* OF_IEEE */ diff --git a/vere/ext/nasm/output/outlib.c b/vere/ext/nasm/output/outlib.c new file mode 100644 index 0000000..54c8753 --- /dev/null +++ b/vere/ext/nasm/output/outlib.c @@ -0,0 +1,324 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2020 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outlib.c + * + * Common routines for the output backends. + */ + +#include "outlib.h" +#include "raa.h" + +uint64_t realsize(enum out_type type, uint64_t size) +{ + switch (type) { + case OUT_REL1ADR: + return 1; + case OUT_REL2ADR: + return 2; + case OUT_REL4ADR: + return 4; + case OUT_REL8ADR: + return 8; + default: + return size; + } +} + +/* Common section/symbol handling */ + +struct ol_sect *_ol_sect_list; +uint64_t _ol_nsects; /* True sections, not external symbols */ +static struct ol_sect **ol_sect_tail = &_ol_sect_list; +static struct hash_table ol_secthash; +static struct RAA *ol_sect_index_tbl; + +struct ol_sym *_ol_sym_list; +uint64_t _ol_nsyms; +static struct ol_sym **ol_sym_tail = &_ol_sym_list; +static struct hash_table ol_symhash; + +void ol_init(void) +{ +} + +static void ol_free_symbols(void) +{ + struct ol_sym *s, *stmp; + + hash_free(&ol_symhash); + + list_for_each_safe(s, stmp, _ol_sym_list) { + nasm_free((char *)s->name); + nasm_free(s); + } + + _ol_nsyms = 0; + _ol_sym_list = NULL; + ol_sym_tail = &_ol_sym_list; +} + +static void ol_free_sections(void) +{ + struct ol_sect *s, *stmp; + + hash_free(&ol_secthash); + raa_free(ol_sect_index_tbl); + ol_sect_index_tbl = NULL; + + list_for_each_safe(s, stmp, _ol_sect_list) { + saa_free(s->data); + saa_free(s->reloc); + nasm_free((char *)s->name); + nasm_free(s); + } + + _ol_nsects = 0; + _ol_sect_list = NULL; + ol_sect_tail = &_ol_sect_list; +} + +void ol_cleanup(void) +{ + ol_free_symbols(); + ol_free_sections(); +} + +/* + * Allocate a section index and add a section, subsection, or external + * symbol to the section-by-index table. If the index provided is zero, + * allocate a new index via seg_alloc(). + */ +static uint32_t ol_seg_alloc(void *s, uint32_t ix) +{ + if (!ix) + ix = seg_alloc(); + ol_sect_index_tbl = raa_write_ptr(ol_sect_index_tbl, ix >> 1, s); + return ix; +} + +/* + * Find a section or create a new section structure if it does not exist + * and allocate it an index value via seg_alloc(). + */ +struct ol_sect *_ol_get_sect(const char *name, size_t ssize, size_t rsize) +{ + struct ol_sect *s, **sp; + struct hash_insert hi; + + sp = (struct ol_sect **)hash_find(&ol_secthash, name, &hi); + if (sp) + return *sp; + + s = nasm_zalloc(ssize); + s->syml.tail = &s->syml.head; + s->name = nasm_strdup(name); + s->data = saa_init(1); + s->reloc = saa_init(rsize); + *ol_sect_tail = s; + ol_sect_tail = &s->next; + _ol_nsects++; + s->index = s->subindex = ol_seg_alloc(s, 0); + + hash_add(&hi, s->name, s); + return s; +} + +/* Find a section by name without creating one */ +struct ol_sect *_ol_sect_by_name(const char *name) +{ + struct ol_sect **sp; + + sp = (struct ol_sect **)hash_find(&ol_secthash, name, NULL); + return sp ? *sp : NULL; +} + +/* Find a section or external symbol by index; NULL if not valid */ +struct ol_sect *_ol_sect_by_index(int32_t index) +{ + uint32_t ix = index; + + if (unlikely(ix >= SEG_ABS)) + return NULL; + + return raa_read_ptr(ol_sect_index_tbl, ix >> 1); +} + +/* + * Start a new subsection for the given section. At the moment, once a + * subsection has been created, it is not possible to revert to an + * earlier subsection. ol_sect_by_index() will return the main section + * structure. Returns the new section index. This is used to prevent + * the front end from optimizing across subsection boundaries. + */ +int32_t _ol_new_subsection(struct ol_sect *sect) +{ + if (unlikely(!sect)) + return NO_SEG; + + return sect->subindex = ol_seg_alloc(sect, 0); +} + +/* + * Insert a symbol into a list; need to use upcasting using container_of() + * to walk the list later. + */ +static void ol_add_sym_to(struct ol_symlist *syml, struct ol_symhead *head, + uint64_t offset) +{ + syml->tree.key = offset; + head->tree = rb_insert(head->tree, &syml->tree); + *head->tail = syml; + head->tail = &syml->next; + head->n++; +} + +/* + * Create a location structure from seg:offs + */ +void ol_mkloc(struct ol_loc *loc, int64_t offs, int32_t seg) +{ + nasm_zero(*loc); + loc->offs = offs; + + if (unlikely((uint32_t)seg >= SEG_ABS)) { + if (likely(seg == NO_SEG)) { + loc->seg.t = OS_NOSEG; + } else { + loc->seg.t = OS_ABS; + loc->seg.index = seg - SEG_ABS; + } + } else { + loc->seg.index = seg & ~1; + loc->seg.t = OS_SECT | (seg & 1); + loc->seg.s.sect = _ol_sect_by_index(loc->seg.index); + } +} + +/* + * Create a new symbol. If this symbol is OS_OFFS, add it to the relevant + * section, too. If the symbol already exists, return NULL; this is + * different from ol_get_section() as a single section may be invoked + * many times. On the contrary, the front end will prevent a single symbol + * from being defined more than once. + * + * If flags has OF_GLOBAL set, add it to the global symbol hash for + * the containing section if applicable. + * + * If flags has OF_IMPSEC set, allocate a segment index for it via + * seg_alloc() unless v->index is already set, and add it to the + * section by index list. + */ +struct ol_sym *_ol_new_sym(const char *name, const struct ol_loc *v, + uint32_t flags, size_t size) +{ + struct hash_insert hi; + struct ol_sym *sym; + + if (hash_find(&ol_symhash, name, &hi)) + return NULL; /* Symbol already exists */ + + flags |= OF_SYMBOL; + + sym = nasm_zalloc(size); + sym->name = nasm_strdup(name); + sym->v = *v; + + if (sym->v.seg.t & OS_SECT) { + struct ol_sect *sect = sym->v.seg.s.sect; + + if (!sect || (sect->flags & OF_SYMBOL)) + /* Must be an external or common reference */ + flags |= OF_IMPSEC; + + if (flags & OF_IMPSEC) { + /* Metasection */ + if (!sym->v.seg.s.sym) { + sym->v.seg.s.sym = sym; + sym->v.seg.index = ol_seg_alloc(sym, sym->v.seg.index); + } + } else if (sym->v.seg.t == OS_OFFS) { + struct ol_sect * const sect = sym->v.seg.s.sect; + const uint64_t offs = sym->v.offs; + + ol_add_sym_to(&sym->syml, §->syml, offs); + if (flags & OF_GLOBAL) + ol_add_sym_to(&sym->symg, §->symg, offs); + } + } + sym->flags = flags; + + *ol_sym_tail = sym; + ol_sym_tail = &sym->next; + _ol_nsyms++; + + hash_add(&hi, sym->name, sym); + return sym; +} + +/* Find a symbol in the global namespace */ +struct ol_sym *_ol_sym_by_name(const char *name) +{ + struct ol_sym **symp; + + symp = (struct ol_sym **)hash_find(&ol_symhash, name, NULL); + return symp ? *symp : NULL; +} + +/* + * Find a symbol by address in a specific section. If no symbol is defined + * at that exact address, return the immediately previously defined one. + * If global is set, then only return global symbols. + */ +struct ol_sym *_ol_sym_by_address(struct ol_sect *sect, int64_t addr, + bool global) +{ + struct ol_symhead *head; + size_t t_offs; + struct rbtree *t; + + if (global) { + head = §->symg; + t_offs = offsetof(struct ol_sym, symg.tree); + } else { + head = §->syml; + t_offs = offsetof(struct ol_sym, syml.tree); + } + + t = rb_search(head->tree, addr); + if (!t) + return NULL; + + return (struct ol_sym *)((char *)t - t_offs); +} diff --git a/vere/ext/nasm/output/outlib.h b/vere/ext/nasm/output/outlib.h new file mode 100644 index 0000000..7f6a789 --- /dev/null +++ b/vere/ext/nasm/output/outlib.h @@ -0,0 +1,313 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2020 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +#ifndef NASM_OUTLIB_H +#define NASM_OUTLIB_H + +#include "compiler.h" +#include "nasm.h" +#include "error.h" +#include "hashtbl.h" +#include "saa.h" +#include "rbtree.h" + +uint64_t realsize(enum out_type type, uint64_t size); + +/* Do-nothing versions of some output routines */ +enum directive_result +null_directive(enum directive directive, char *value); +void null_sectalign(int32_t seg, unsigned int value); +void null_reset(void); +int32_t null_segbase(int32_t seg); + +/* Do-nothing versions of all the debug routines */ +void null_debug_init(void); +void null_debug_linenum(const char *filename, int32_t linenumber, + int32_t segto); +void null_debug_deflabel(char *name, int32_t segment, int64_t offset, + int is_global, char *special); +void null_debug_directive(const char *directive, const char *params); +void null_debug_typevalue(int32_t type); +void null_debug_output(int type, void *param); +void null_debug_cleanup(void); +extern const struct dfmt * const null_debug_arr[2]; + +/* Wrapper for unported backends */ +void nasm_do_legacy_output(const struct out_data *data); + +/* + * Common routines for tasks that really should migrate into the core. + * This provides a common interface for maintaining sections and symbols, + * and provide quick lookups as well as declared-order sequential walks. + * + * These structures are intended to be embedded at the *top* of a + * backend-specific structure containing additional information. + * + * The tokens O_Section, O_Symbol and O_Reloc are intended to be + * defined as macros by the backend before including this file! + */ + +struct ol_sect; +struct ol_sym; + +#ifndef O_Section +typedef struct ol_sect O_Section; +#endif +#ifndef O_Symbol +typedef struct ol_sym O_Symbol; +#endif +#ifndef O_Reloc +typedef void * O_Reloc; +#endif + +/* Common section structure */ + +/* + * Common flags for sections and symbols; low values reserved for + * backend. Note that both ol_sect and ol_sym begin with a flags + * field, so if a section pointer points to an external symbol instead + * they can be trivially resolved. + */ +#define OF_SYMBOL 0x80000000 +#define OF_GLOBAL 0x40000000 +#define OF_IMPSEC 0x20000000 +#define OF_COMMON 0x10000000 + +struct ol_sym; + +struct ol_symlist { + struct ol_symlist *next; + struct rbtree tree; +}; +struct ol_symhead { + struct ol_symlist *head, **tail; + struct rbtree *tree; + uint64_t n; +}; + +struct ol_sect { + uint32_t flags; /* Section/symbol flags */ + struct ol_sect *next; /* Next section in declared order */ + const char *name; /* Name of section */ + struct ol_symhead syml; /* All symbols in this section */ + struct ol_symhead symg; /* Global symbols in this section */ + struct SAA *data; /* Contents of section */ + struct SAA *reloc; /* Section relocations */ + uint32_t index; /* Primary section index */ + uint32_t subindex; /* Current subsection index */ +}; + +/* Segment reference */ +enum ol_seg_type { + OS_NOSEG = 0, /* Plain number (no segment) */ + OS_SEGREF = 1, /* It is a segment reference */ + OS_ABS = 1, /* Absolute segment reference */ + OS_SECT = 2, /* It is a real section */ + OS_OFFS = OS_SECT, /* Offset reference in section */ + OS_SEG = OS_SECT|OS_SEGREF /* Section reference */ +}; + +union ol_segval { + struct ol_sect *sect; /* Section structure */ + struct ol_sym *sym; /* External symbol structure */ +}; + +struct ol_seg { + union ol_segval s; + enum ol_seg_type t; + + /* + * For a section: subsection index + * For a metasymbol: virtual segment index + * For an absolute symbol: absolute value + */ + uint32_t index; +}; + +/* seg:offs representing the full location value and type */ +struct ol_loc { + int64_t offs; + struct ol_seg seg; +}; + +/* Common symbol structure */ +struct ol_sym { + uint32_t flags; /* Section/symbol flags */ + uint32_t size; /* Size value (for backend) */ + struct ol_sym *next; /* Next symbol in declared order */ + const char *name; /* Symbol name */ + struct ol_symlist syml; /* Section-local symbol list */ + struct ol_symlist symg; /* Section-local global symbol list */ + struct ol_loc p; /* Symbol position ("where") */ + struct ol_loc v; /* Symbol value ("what") */ +}; + +/* + * Operations + */ +void ol_init(void); +void ol_cleanup(void); + +/* Convert offs:seg to a location structure */ +extern void +ol_mkloc(struct ol_loc *loc, int64_t offs, int32_t seg); + +/* Get the section or external symbol from a struct ol_seg */ +static inline O_Section *seg_sect(struct ol_seg *seg) +{ + return (O_Section *)seg->s.sect; +} +static inline O_Symbol *seg_xsym(struct ol_seg *seg) +{ + return (O_Symbol *)seg->s.sym; +} + +/* + * Return a pointer to the symbol structure if and only if a section is + * really a symbol of some kind (extern, common...) + */ +static inline struct ol_sym *_seg_extsym(struct ol_sect *sect) +{ + return (sect->flags & OF_SYMBOL) ? (struct ol_sym *)sect : NULL; +} +static inline O_Symbol *seg_extsym(O_Section *sect) +{ + return (O_Symbol *)_seg_extsym((struct ol_sect *)sect); +} + +/* + * Find a section or create a new section structure if it does not exist + * and allocate it an index value via seg_alloc(). + */ +extern struct ol_sect * +_ol_get_sect(const char *name, size_t ssize, size_t rsize); +static inline O_Section *ol_get_sect(const char *name) +{ + return (O_Section *)_ol_get_sect(name, sizeof(O_Section), sizeof(O_Reloc)); +} + +/* Find a section by name without creating one */ +extern struct ol_sect *_ol_sect_by_name(const char *); +static inline O_Section *ol_sect_by_name(const char *name) +{ + return (O_Section *)_ol_sect_by_name(name); +} + +/* Find a section or external symbol by index; NULL if not valid */ +extern struct ol_sect *_ol_sect_by_index(int32_t index); +static inline O_Section *ol_sect_by_index(int32_t index) +{ + return (O_Section *)_ol_sect_by_index(index); +} + +/* Global list of sections (not including external symbols) */ +extern struct ol_sect *_ol_sect_list; +static inline O_Section *ol_sect_list(void) +{ + return (O_Section *)_ol_sect_list; +} + +/* Count of sections (not including external symbols) */ +extern uint64_t _ol_nsects; +static inline uint64_t ol_nsects(void) +{ + return _ol_nsects; +} + +/* + * Start a new subsection for the given section. At the moment, once a + * subsection has been created, it is not possible to revert to an + * earlier subsection. ol_sect_by_index() will return the main section + * structure. Returns the new section index. This is used to prevent + * the front end from optimizing across subsection boundaries. + */ +extern int32_t _ol_new_subsection(struct ol_sect *sect); +static inline int32_t ol_new_subsection(O_Section *sect) +{ + return _ol_new_subsection((struct ol_sect *)sect); +} + +/* + * Create a new symbol. If this symbol is OS_OFFS, add it to the relevant + * section, too. If the symbol already exists, return NULL; this is + * different from ol_get_section() as a single section may be invoked + * many times. On the contrary, the front end will prevent a single symbol + * from being defined more than once. + * + * If flags has OF_GLOBAL set, add it to the global symbol hash for the + * containing section. If flags has OF_IMPSEC set, allocate a segment + * index for it via seg_alloc() and add it to the section by index list. + */ +extern struct ol_sym *_ol_new_sym(const char *name, const struct ol_loc *v, + uint32_t flags, size_t size); +static inline O_Symbol *ol_new_sym(const char *name, const struct ol_loc *v, + uint32_t flags) +{ + return (O_Symbol *)_ol_new_sym(name, v, flags, sizeof(O_Symbol)); +} + +/* Find a symbol by name in the global namespace */ +extern struct ol_sym *_ol_sym_by_name(const char *name); +static inline O_Symbol *ol_sym_by_name(const char *name) +{ + return (O_Symbol *)_ol_sym_by_name(name); +} + +/* + * Find a symbol by address in a specific section. If no symbol is defined + * at that exact address, return the immediately previously defined one. + * If global is set, then only return global symbols. + */ +extern struct ol_sym *_ol_sym_by_address(struct ol_sect *sect, int64_t addr, + bool global); +static inline O_Symbol *ol_sym_by_address(O_Section *sect, int64_t addr, + bool global) +{ + return (O_Symbol *)_ol_sym_by_address((struct ol_sect *)sect, addr, global); +} + +/* Global list of symbols */ +extern struct ol_sym *_ol_sym_list; +static inline O_Symbol *ol_sym_list(void) +{ + return (O_Symbol *)_ol_sym_list; +} + +/* Global count of symbols */ +extern uint64_t _ol_nsyms; +static inline uint64_t ol_nsyms(void) +{ + return _ol_nsyms; +} + +#endif /* NASM_OUTLIB_H */ diff --git a/vere/ext/nasm/output/outmacho.c b/vere/ext/nasm/output/outmacho.c new file mode 100644 index 0000000..1e776f5 --- /dev/null +++ b/vere/ext/nasm/output/outmacho.c @@ -0,0 +1,2411 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2018 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outmacho.c output routines for the Netwide Assembler to produce + * NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X object files + */ + +#include "compiler.h" + +#include "nctype.h" + +#include "nasm.h" +#include "nasmlib.h" +#include "ilog2.h" +#include "labels.h" +#include "error.h" +#include "saa.h" +#include "raa.h" +#include "rbtree.h" +#include "hashtbl.h" +#include "outform.h" +#include "outlib.h" +#include "ver.h" +#include "dwarf.h" +#include "macho.h" + +#if defined(OF_MACHO) || defined(OF_MACHO64) + +/* Mach-O in-file header structure sizes */ +#define MACHO_HEADER_SIZE 28 +#define MACHO_SEGCMD_SIZE 56 +#define MACHO_SECTCMD_SIZE 68 +#define MACHO_SYMCMD_SIZE 24 +#define MACHO_NLIST_SIZE 12 +#define MACHO_RELINFO_SIZE 8 + +#define MACHO_HEADER64_SIZE 32 +#define MACHO_SEGCMD64_SIZE 72 +#define MACHO_SECTCMD64_SIZE 80 +#define MACHO_NLIST64_SIZE 16 + +/* Mach-O relocations numbers */ + +#define VM_PROT_DEFAULT (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE) +#define VM_PROT_ALL (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE) + +/* Our internal relocation types */ +enum reltype { + RL_ABS, /* Absolute relocation */ + RL_REL, /* Relative relocation */ + RL_TLV, /* Thread local */ + RL_BRANCH, /* Relative direct branch */ + RL_SUB, /* X86_64_RELOC_SUBTRACT */ + RL_GOT, /* X86_64_RELOC_GOT */ + RL_GOTLOAD /* X86_64_RELOC_GOT_LOAD */ +}; +#define RL_MAX_32 RL_TLV +#define RL_MAX_64 RL_GOTLOAD + +struct macho_fmt { + uint32_t ptrsize; /* Pointer size in bytes */ + uint32_t mh_magic; /* Which magic number to use */ + uint32_t cpu_type; /* Which CPU type */ + uint32_t lc_segment; /* Which segment load command */ + uint32_t header_size; /* Header size */ + uint32_t segcmd_size; /* Segment command size */ + uint32_t sectcmd_size; /* Section command size */ + uint32_t nlist_size; /* Nlist (symbol) size */ + enum reltype maxreltype; /* Maximum entry in enum reltype permitted */ + uint32_t reloc_abs; /* Absolute relocation type */ + uint32_t reloc_rel; /* Relative relocation type */ + uint32_t reloc_tlv; /* Thread local relocation type */ + bool forcesym; /* Always use "external" (symbol-relative) relocations */ +}; + +static struct macho_fmt fmt; + +static void fwriteptr(uint64_t data, FILE * fp) +{ + fwriteaddr(data, fmt.ptrsize, fp); +} + +struct section { + /* nasm internal data */ + struct section *next; + struct SAA *data; + int32_t index; /* Main section index */ + int32_t subsection; /* Current subsection index */ + int32_t fileindex; + struct reloc *relocs; + struct rbtree *syms[2]; /* All/global symbols symbols in section */ + int align; + bool by_name; /* This section was specified by full MachO name */ + char namestr[34]; /* segment,section as a C string */ + + /* data that goes into the file */ + char sectname[16]; /* what this section is called */ + char segname[16]; /* segment this section will be in */ + uint64_t addr; /* in-memory address (subject to alignment) */ + uint64_t size; /* in-memory and -file size */ + uint64_t offset; /* in-file offset */ + uint32_t pad; /* padding bytes before section */ + uint32_t nreloc; /* relocation entry count */ + uint32_t flags; /* type and attributes (masked) */ + uint32_t extreloc; /* external relocations */ +}; + +#define S_NASM_TYPE_MASK 0x800004ff /* we consider these bits "section type" */ + +/* fake section for absolute symbols, *not* part of the section linked list */ +static struct section absolute_sect; + +struct reloc { + /* nasm internal data */ + struct reloc *next; + + /* data that goes into the file */ + int32_t addr; /* op's offset in section */ + uint32_t snum:24, /* contains symbol index if + ** ext otherwise in-file + ** section number */ + pcrel:1, /* relative relocation */ + length:2, /* 0=byte, 1=word, 2=int32_t, 3=int64_t */ + ext:1, /* external symbol referenced */ + type:4; /* reloc type */ +}; + +struct symbol { + /* nasm internal data */ + struct rbtree symv[2]; /* All/global symbol rbtrees; "key" contains the + symbol offset. */ + struct symbol *next; /* next symbol in the list */ + char *name; /* name of this symbol */ + int32_t initial_snum; /* symbol number used above in reloc */ + int32_t snum; /* true snum for reloc */ + + /* data that goes into the file */ + uint32_t strx; /* string table index */ + uint8_t type; /* symbol type */ + uint8_t sect; /* NO_SECT or section number */ + uint16_t desc; /* for stab debugging, 0 for us */ +}; + +#define DEFAULT_SECTION_ALIGNMENT 0 /* byte (i.e. no) alignment */ + +static struct section *sects, **sectstail, **sectstab; +static struct symbol *syms, **symstail; +static uint32_t nsyms; + +/* These variables are set by macho_layout_symbols() to organize + the symbol table and string table in order the dynamic linker + expects. They are then used in macho_write() to put out the + symbols and strings in that order. + + The order of the symbol table is: + local symbols + defined external symbols (sorted by name) + undefined external symbols (sorted by name) + + The order of the string table is: + strings for external symbols + strings for local symbols + */ +static uint32_t ilocalsym = 0; +static uint32_t iextdefsym = 0; +static uint32_t iundefsym = 0; +static uint32_t nlocalsym; +static uint32_t nextdefsym; +static uint32_t nundefsym; +static struct symbol **extdefsyms = NULL; +static struct symbol **undefsyms = NULL; + +static struct RAA *extsyms; +static struct SAA *strs; +static uint32_t strslen; + +/* Global file information. This should be cleaned up into either + a structure or as function arguments. */ +static uint32_t head_ncmds = 0; +static uint32_t head_sizeofcmds = 0; +static uint32_t head_flags = 0; +static uint64_t seg_filesize = 0; +static uint64_t seg_vmsize = 0; +static uint32_t seg_nsects = 0; +static uint64_t rel_padcnt = 0; + +/* + * Functions for handling fixed-length zero-padded string + * fields, that may or may not be null-terminated. + */ + +/* Copy a string into a zero-padded fixed-length field */ +#define xstrncpy(xdst, xsrc) strncpy(xdst, xsrc, sizeof(xdst)) + +/* Compare a fixed-length field with a string */ +#define xstrncmp(xdst, xsrc) strncmp(xdst, xsrc, sizeof(xdst)) + +#define alignint32_t(x) \ + ALIGN(x, sizeof(int32_t)) /* align x to int32_t boundary */ + +#define alignint64_t(x) \ + ALIGN(x, sizeof(int64_t)) /* align x to int64_t boundary */ + +#define alignptr(x) \ + ALIGN(x, fmt.ptrsize) /* align x to output format width */ + +static struct hash_table section_by_name; +static struct RAA *section_by_index; + +static struct section * never_null +find_or_add_section(const char *segname, const char *sectname) +{ + struct hash_insert hi; + void **sp; + struct section *s; + char sect[34]; + + snprintf(sect, sizeof sect, "%-16s,%-16s", segname, sectname); + + sp = hash_find(§ion_by_name, sect, &hi); + if (sp) + return (struct section *)(*sp); + + s = nasm_zalloc(sizeof *s); + xstrncpy(s->segname, segname); + xstrncpy(s->sectname, sectname); + xstrncpy(s->namestr, sect); + hash_add(&hi, s->namestr, s); + + s->index = s->subsection = seg_alloc(); + section_by_index = raa_write_ptr(section_by_index, s->index >> 1, s); + + return s; +} + +static inline bool is_new_section(const struct section *s) +{ + return !s->data; +} + +static struct section *get_section_by_name(const char *segname, + const char *sectname) +{ + char sect[34]; + void **sp; + + snprintf(sect, sizeof sect, "%-16s,%-16s", segname, sectname); + + sp = hash_find(§ion_by_name, sect, NULL); + return sp ? (struct section *)(*sp) : NULL; +} + +static struct section *get_section_by_index(int32_t index) +{ + if (index < 0 || index >= SEG_ABS || (index & 1)) + return NULL; + + return raa_read_ptr(section_by_index, index >> 1); +} + +struct dir_list { + struct dir_list *next; + struct dir_list *last; + const char *dir_name; + uint32_t dir; +}; + +struct file_list { + struct file_list *next; + struct file_list *last; + const char *file_name; + uint32_t file; + struct dir_list *dir; +}; + +struct dw_sect_list { + struct SAA *psaa; + int32_t section; + uint32_t line; + uint64_t offset; + uint32_t file; + struct dw_sect_list *next; + struct dw_sect_list *last; +}; + +struct section_info { + uint64_t size; + int32_t secto; +}; + +#define DW_LN_BASE (-5) +#define DW_LN_RANGE 14 +#define DW_OPCODE_BASE 13 +#define DW_MAX_LN (DW_LN_BASE + DW_LN_RANGE) +#define DW_MAX_SP_OPCODE 256 + +static struct file_list *dw_head_file = 0, *dw_cur_file = 0, **dw_last_file_next = NULL; +static struct dir_list *dw_head_dir = 0, **dw_last_dir_next = NULL; +static struct dw_sect_list *dw_head_sect = 0, *dw_cur_sect = 0, *dw_last_sect = 0; +static uint32_t cur_line = 0, dw_num_files = 0, dw_num_dirs = 0, dw_num_sects = 0; +static bool dbg_immcall = false; +static const char *module_name = NULL; + +/* + * Special section numbers which are used to define Mach-O special + * symbols, which can be used with WRT to provide PIC relocation + * types. + */ +static int32_t macho_tlvp_sect; +static int32_t macho_gotpcrel_sect; + +static void macho_init(void) +{ + module_name = inname; + sects = NULL; + sectstail = §s; + + /* Fake section for absolute symbols */ + absolute_sect.index = NO_SEG; + + syms = NULL; + symstail = &syms; + nsyms = 0; + nlocalsym = 0; + nextdefsym = 0; + nundefsym = 0; + + extsyms = raa_init(); + strs = saa_init(1L); + + section_by_index = raa_init(); + + /* string table starts with a zero byte so index 0 is an empty string */ + saa_wbytes(strs, zero_buffer, 1); + strslen = 1; + + /* add special symbol for TLVP */ + macho_tlvp_sect = seg_alloc() + 1; + backend_label("..tlvp", macho_tlvp_sect, 0L); +} + +static void sect_write(struct section *sect, + const uint8_t *data, uint32_t len) +{ + saa_wbytes(sect->data, data, len); + sect->size += len; +} + +/* + * Find a suitable global symbol for a ..gotpcrel or ..tlvp reference + */ +static struct symbol *macho_find_sym(struct section *s, uint64_t offset, + bool global, bool exact) +{ + struct rbtree *srb; + + srb = rb_search(s->syms[global], offset); + + if (!srb || (exact && srb->key != offset)) { + nasm_error(ERR_NONFATAL, "unable to find a suitable%s%s symbol" + " for this reference", + global ? " global" : "", + s == &absolute_sect ? " absolute " : ""); + return NULL; + } + + return container_of(srb - global, struct symbol, symv); +} + +static int64_t add_reloc(struct section *sect, int32_t section, + int64_t offset, + enum reltype reltype, int bytes) +{ + struct reloc *r; + struct section *s; + int32_t fi; + int64_t adjust; + + /* Double check this is a valid relocation type for this platform */ + nasm_assert(reltype <= fmt.maxreltype); + + /* the current end of the section will be the symbol's address for + ** now, might have to be fixed by macho_fixup_relocs() later on. make + ** sure we don't make the symbol scattered by setting the highest + ** bit by accident */ + r = nasm_malloc(sizeof(struct reloc)); + r->addr = sect->size & ~R_SCATTERED; + r->ext = 1; + adjust = 0; + + /* match byte count 1, 2, 4, 8 to length codes 0, 1, 2, 3 respectively */ + r->length = ilog2_32(bytes); + + /* set default relocation values */ + r->type = fmt.reloc_abs; + r->pcrel = 0; + r->snum = R_ABS; + + s = get_section_by_index(section); + fi = s ? s->fileindex : NO_SECT; + + /* absolute relocation */ + switch (reltype) { + case RL_ABS: + if (section == NO_SEG) { + /* absolute (can this even happen?) */ + r->ext = 0; + } else if (fi == NO_SECT) { + /* external */ + r->snum = raa_read(extsyms, section); + } else { + /* local */ + r->ext = 0; + r->snum = fi; + } + break; + + case RL_REL: + case RL_BRANCH: + r->type = fmt.reloc_rel; + r->pcrel = 1; + if (section == NO_SEG) { + /* may optionally be converted below by fmt.forcesym */ + r->ext = 0; + } else if (fi == NO_SECT) { + /* external */ + sect->extreloc = 1; + r->snum = raa_read(extsyms, section); + if (reltype == RL_BRANCH) + r->type = X86_64_RELOC_BRANCH; + } else { + /* local */ + r->ext = 0; + r->snum = fi; + if (reltype == RL_BRANCH) + r->type = X86_64_RELOC_BRANCH; + } + break; + + case RL_SUB: /* obsolete */ + nasm_warn(WARN_OTHER, "relcation with subtraction" + "becomes to be obsolete"); + r->ext = 0; + r->type = X86_64_RELOC_SUBTRACTOR; + break; + + case RL_GOT: + r->type = X86_64_RELOC_GOT; + goto needsym; + + case RL_GOTLOAD: + r->type = X86_64_RELOC_GOT_LOAD; + goto needsym; + + case RL_TLV: + r->type = fmt.reloc_tlv; + goto needsym; + + needsym: + r->pcrel = (fmt.ptrsize == 8 ? 1 : 0); + if (section == NO_SEG) { + nasm_error(ERR_NONFATAL, "Unsupported use of use of WRT"); + goto bail; + } else if (fi == NO_SECT) { + /* external */ + r->snum = raa_read(extsyms, section); + } else { + /* internal - GOTPCREL doesn't need to be in global */ + struct symbol *sym = macho_find_sym(s, offset, + false, /* reltype != RL_TLV */ + true); + if (!sym) { + nasm_error(ERR_NONFATAL, "Symbol for WRT not found"); + goto bail; + } + + adjust -= sym->symv[0].key; + r->snum = sym->initial_snum; + } + break; + } + + /* + * For 64-bit Mach-O, force a symbol reference if at all possible + * Allow for r->snum == R_ABS by searching absolute_sect + */ + if (!r->ext && fmt.forcesym) { + struct symbol *sym = macho_find_sym(s ? s : &absolute_sect, + offset, false, false); + if (sym) { + adjust -= sym->symv[0].key; + r->snum = sym->initial_snum; + r->ext = 1; + } + } + + if (r->pcrel) + adjust += ((r->ext && fmt.ptrsize == 8) ? bytes : -(int64_t)sect->size); + + /* NeXT as puts relocs in reversed order (address-wise) into the + ** files, so we do the same, doesn't seem to make much of a + ** difference either way */ + r->next = sect->relocs; + sect->relocs = r; + if (r->ext) + sect->extreloc = 1; + ++sect->nreloc; + + return adjust; + + bail: + nasm_free(r); + return 0; +} + +static void macho_output(int32_t secto, const void *data, + enum out_type type, uint64_t size, + int32_t section, int32_t wrt) +{ + struct section *s; + int64_t addr, offset; + uint8_t mydata[16], *p; + bool is_bss; + enum reltype reltype; + + s = get_section_by_index(secto); + if (!s) { + nasm_warn(WARN_OTHER, "attempt to assemble code in" + " section %d: defaulting to `.text'", secto); + s = get_section_by_name("__TEXT", "__text"); + + /* should never happen */ + if (!s) + nasm_panic("text section not found"); + } + + /* debug code generation only for sections tagged with + * instruction attribute */ + if (s->flags & S_ATTR_SOME_INSTRUCTIONS) + { + struct section_info sinfo; + sinfo.size = s->size; + sinfo.secto = secto; + dfmt->debug_output(0, &sinfo); + } + + is_bss = (s->flags & SECTION_TYPE) == S_ZEROFILL; + + if (is_bss && type != OUT_RESERVE) { + nasm_warn(WARN_OTHER, "attempt to initialize memory in " + "BSS section: ignored"); + /* FIXME */ + nasm_warn(WARN_OTHER, "section size may be negative" + "with address symbols"); + s->size += realsize(type, size); + return; + } + + memset(mydata, 0, sizeof(mydata)); + + switch (type) { + case OUT_RESERVE: + if (!is_bss) { + nasm_warn(WARN_ZEROING, "uninitialized space declared in" + " %s,%s section: zeroing", s->segname, s->sectname); + + sect_write(s, NULL, size); + } else + s->size += size; + + break; + + case OUT_RAWDATA: + sect_write(s, data, size); + break; + + case OUT_ADDRESS: + { + int asize = abs((int)size); + + addr = *(int64_t *)data; + if (section != NO_SEG) { + if (section % 2) { + nasm_error(ERR_NONFATAL, "Mach-O format does not support" + " section base references"); + } else if (wrt == NO_SEG) { + if (fmt.ptrsize == 8 && asize != 8) { + nasm_error(ERR_NONFATAL, + "Mach-O 64-bit format does not support" + " 32-bit absolute addresses"); + } else { + addr += add_reloc(s, section, addr, RL_ABS, asize); + } + } else if (wrt == macho_tlvp_sect && fmt.ptrsize != 8 && + asize == (int) fmt.ptrsize) { + addr += add_reloc(s, section, addr, RL_TLV, asize); + } else { + nasm_error(ERR_NONFATAL, "Mach-O format does not support" + " this use of WRT"); + } + } + + p = mydata; + WRITEADDR(p, addr, asize); + sect_write(s, mydata, asize); + break; + } + + case OUT_REL1ADR: + case OUT_REL2ADR: + + p = mydata; + offset = *(int64_t *)data; + addr = offset - size; + + if (section != NO_SEG && section % 2) { + nasm_error(ERR_NONFATAL, "Mach-O format does not support" + " section base references"); + } else if (fmt.ptrsize == 8) { + nasm_error(ERR_NONFATAL, "Unsupported non-32-bit" + " Macho-O relocation [2]"); + } else if (wrt != NO_SEG) { + nasm_error(ERR_NONFATAL, "Mach-O format does not support" + " this use of WRT"); + wrt = NO_SEG; /* we can at least _try_ to continue */ + } else { + addr += add_reloc(s, section, addr+size, RL_REL, + type == OUT_REL1ADR ? 1 : 2); + } + + WRITESHORT(p, addr); + sect_write(s, mydata, type == OUT_REL1ADR ? 1 : 2); + break; + + case OUT_REL4ADR: + case OUT_REL8ADR: + + p = mydata; + offset = *(int64_t *)data; + addr = offset - size; + reltype = RL_REL; + + if (section != NO_SEG && section % 2) { + nasm_error(ERR_NONFATAL, "Mach-O format does not support" + " section base references"); + } else if (wrt == NO_SEG) { + if (fmt.ptrsize == 8 && + (s->flags & S_ATTR_SOME_INSTRUCTIONS)) { + uint8_t opcode[2]; + + opcode[0] = opcode[1] = 0; + + /* HACK: Retrieve instruction opcode */ + if (likely(s->data->datalen >= 2)) { + saa_fread(s->data, s->data->datalen-2, opcode, 2); + } else if (s->data->datalen == 1) { + saa_fread(s->data, 0, opcode+1, 1); + } + + if ((opcode[0] != 0x0f && (opcode[1] & 0xfe) == 0xe8) || + (opcode[0] == 0x0f && (opcode[1] & 0xf0) == 0x80)) { + /* Direct call, jmp, or jcc */ + reltype = RL_BRANCH; + } + } + } else if (wrt == macho_gotpcrel_sect) { + reltype = RL_GOT; + + if ((s->flags & S_ATTR_SOME_INSTRUCTIONS) && + s->data->datalen >= 3) { + uint8_t gotload[3]; + + /* HACK: Retrieve instruction opcode */ + saa_fread(s->data, s->data->datalen-3, gotload, 3); + if ((gotload[0] & 0xf8) == 0x48 && + gotload[1] == 0x8b && + (gotload[2] & 0307) == 0005) { + /* movq <reg>,[rel sym wrt ..gotpcrel] */ + reltype = RL_GOTLOAD; + } + } + } else if (wrt == macho_tlvp_sect && fmt.ptrsize == 8) { + reltype = RL_TLV; + } else { + nasm_error(ERR_NONFATAL, "Mach-O format does not support" + " this use of WRT"); + /* continue with RL_REL */ + } + + addr += add_reloc(s, section, offset, reltype, + type == OUT_REL4ADR ? 4 : 8); + WRITELONG(p, addr); + sect_write(s, mydata, type == OUT_REL4ADR ? 4 : 8); + break; + + default: + nasm_error(ERR_NONFATAL, "Unrepresentable relocation in Mach-O"); + break; + } +} + +#define S_CODE (S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS) +#define NO_TYPE S_NASM_TYPE_MASK + +/* Translation table from traditional Unix section names to Mach-O */ +static const struct macho_known_section { + const char *nasmsect; + const char *segname; + const char *sectname; + const uint32_t flags; +} known_sections[] = { + { ".text", "__TEXT", "__text", S_CODE }, + { ".data", "__DATA", "__data", S_REGULAR }, + { ".rodata", "__DATA", "__const", S_REGULAR }, + { ".bss", "__DATA", "__bss", S_ZEROFILL }, + { ".debug_abbrev", "__DWARF", "__debug_abbrev", S_ATTR_DEBUG }, + { ".debug_info", "__DWARF", "__debug_info", S_ATTR_DEBUG }, + { ".debug_line", "__DWARF", "__debug_line", S_ATTR_DEBUG }, + { ".debug_str", "__DWARF", "__debug_str", S_ATTR_DEBUG }, +}; + +/* Section type or attribute directives */ +static const struct macho_known_section_attr { + const char *name; + uint32_t flags; +} sect_attribs[] = { + { "data", S_REGULAR }, + { "code", S_CODE }, + { "mixed", S_REGULAR | S_ATTR_SOME_INSTRUCTIONS }, + { "bss", S_ZEROFILL }, + { "zerofill", S_ZEROFILL }, + { "no_dead_strip", NO_TYPE | S_ATTR_NO_DEAD_STRIP }, + { "live_support", NO_TYPE | S_ATTR_LIVE_SUPPORT }, + { "strip_static_syms", NO_TYPE | S_ATTR_STRIP_STATIC_SYMS }, + { "debug", NO_TYPE | S_ATTR_DEBUG }, + { NULL, 0 } +}; + +static const struct macho_known_section * +lookup_known_section(const char *name, bool by_sectname) +{ + size_t i; + + if (name && name[0]) { + for (i = 0; i < ARRAY_SIZE(known_sections); i++) { + const char *p = by_sectname ? + known_sections[i].sectname : + known_sections[i].nasmsect; + if (!strcmp(name, p)) + return &known_sections[i]; + } + } + + return NULL; +} + +static int32_t macho_section(char *name, int *bits) +{ + const struct macho_known_section *known_section; + const struct macho_known_section_attr *sa; + char *sectionAttributes; + struct section *s; + const char *section, *segment; + uint32_t flags; + char *currentAttribute; + char *comma; + + bool new_seg; + + /* Default to the appropriate number of bits. */ + if (!name) { + *bits = fmt.ptrsize << 3; + name = ".text"; + sectionAttributes = NULL; + } else { + sectionAttributes = name; + name = nasm_strsep(§ionAttributes, " \t"); + } + + section = segment = NULL; + flags = 0; + + comma = strchr(name, ','); + if (comma) { + int len; + + *comma = '\0'; + segment = name; + section = comma+1; + + len = strlen(segment); + if (len == 0) { + nasm_error(ERR_NONFATAL, "empty segment name\n"); + } else if (len > 16) { + nasm_error(ERR_NONFATAL, "segment name %s too long\n", segment); + } + + len = strlen(section); + if (len == 0) { + nasm_error(ERR_NONFATAL, "empty section name\n"); + } else if (len > 16) { + nasm_error(ERR_NONFATAL, "section name %s too long\n", section); + } + + known_section = lookup_known_section(section, true); + if (known_section) + flags = known_section->flags; + else + flags = S_REGULAR; + } else { + known_section = lookup_known_section(name, false); + if (!known_section) { + nasm_error(ERR_NONFATAL, "unknown section name %s\n", name); + return NO_SEG; + } + + segment = known_section->segname; + section = known_section->sectname; + flags = known_section->flags; + } + + /* try to find section with that name, or create it */ + s = find_or_add_section(segment, section); + new_seg = is_new_section(s); + + /* initialize it if it is a brand new section */ + if (new_seg) { + *sectstail = s; + sectstail = &s->next; + + s->data = saa_init(1L); + s->fileindex = ++seg_nsects; + s->align = -1; + s->pad = -1; + s->offset = -1; + s->by_name = false; + + s->size = 0; + s->nreloc = 0; + s->flags = flags; + } + + if (comma) + *comma = ','; /* Restore comma */ + + s->by_name = s->by_name || comma; /* Was specified by name */ + + flags = NO_TYPE; + + while (sectionAttributes && + (currentAttribute = nasm_strsep(§ionAttributes, " \t"))) { + if (!*currentAttribute) + continue; + + if (!nasm_strnicmp("align=", currentAttribute, 6)) { + char *end; + int newAlignment, value; + + value = strtoul(currentAttribute + 6, (char**)&end, 0); + newAlignment = alignlog2_32(value); + + if (0 != *end) { + nasm_error(ERR_NONFATAL, + "unknown or missing alignment value \"%s\" " + "specified for section \"%s\"", + currentAttribute + 6, + name); + } else if (0 > newAlignment) { + nasm_error(ERR_NONFATAL, + "alignment of %d (for section \"%s\") is not " + "a power of two", + value, + name); + } + + if (s->align < newAlignment) + s->align = newAlignment; + } else { + for (sa = sect_attribs; sa->name; sa++) { + if (!nasm_stricmp(sa->name, currentAttribute)) { + if ((sa->flags & S_NASM_TYPE_MASK) != NO_TYPE) { + flags = (flags & ~S_NASM_TYPE_MASK) + | (sa->flags & S_NASM_TYPE_MASK); + } + flags |= sa->flags & ~S_NASM_TYPE_MASK; + break; + } + } + + if (!sa->name) { + nasm_error(ERR_NONFATAL, + "unknown section attribute %s for section %s", + currentAttribute, name); + } + } + } + + if ((flags & S_NASM_TYPE_MASK) != NO_TYPE) { + if (!new_seg && ((s->flags ^ flags) & S_NASM_TYPE_MASK)) { + nasm_error(ERR_NONFATAL, + "inconsistent section attributes for section %s\n", + name); + } else { + s->flags = (s->flags & ~S_NASM_TYPE_MASK) | flags; + } + } else { + s->flags |= flags & ~S_NASM_TYPE_MASK; + } + + return s->subsection; +} + +static int32_t macho_herelabel(const char *name, enum label_type type, + int32_t section, int32_t *subsection, + bool *copyoffset) +{ + struct section *s; + int32_t subsec; + (void)name; + + if (!(head_flags & MH_SUBSECTIONS_VIA_SYMBOLS)) + return section; + + /* No subsection only for local labels */ + if (type == LBL_LOCAL) + return section; + + s = get_section_by_index(section); + if (!s) + return section; + + subsec = *subsection; + if (subsec == NO_SEG) { + /* Allocate a new subsection index */ + subsec = *subsection = seg_alloc(); + section_by_index = raa_write_ptr(section_by_index, subsec >> 1, s); + } + + s->subsection = subsec; + *copyoffset = true; /* Maintain previous offset */ + return subsec; +} + +static void macho_symdef(char *name, int32_t section, int64_t offset, + int is_global, char *special) +{ + struct symbol *sym; + struct section *s; + bool special_used = false; + +#if defined(DEBUG) && DEBUG>2 + nasm_error(ERR_DEBUG, + " macho_symdef: %s, pass=%"PRId64" type %s, sec=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n", + name, pass_count(), pass_types[pass_type()], + section, offset, is_global, special); +#endif + + if (is_global == 3) { + if (special) { + int n = strcspn(special, " \t"); + + if (!nasm_strnicmp(special, "private_extern", n)) { + for (sym = syms; sym != NULL; sym = sym->next) { + if (!strcmp(name, sym->name)) { + if (sym->type & N_PEXT) + return; /* nothing to be done */ + else + break; + } + } + } + } + nasm_error(ERR_NONFATAL, "The Mach-O format does not " + "(yet) support forward reference fixups."); + return; + } + + if (name[0] == '.' && name[1] == '.' && name[2] != '@') { + /* + * This is a NASM special symbol. We never allow it into + * the Macho-O symbol table, even if it's a valid one. If it + * _isn't_ a valid one, we should barf immediately. + */ + if (strcmp(name, "..gotpcrel") && strcmp(name, "..tlvp")) + nasm_error(ERR_NONFATAL, "unrecognized special symbol `%s'", name); + return; + } + + sym = *symstail = nasm_zalloc(sizeof(struct symbol)); + sym->next = NULL; + symstail = &sym->next; + + sym->name = name; + sym->strx = strslen; + sym->type = 0; + sym->desc = 0; + sym->symv[0].key = offset; + sym->symv[1].key = offset; + sym->initial_snum = -1; + + /* external and common symbols get N_EXT */ + if (is_global != 0) { + sym->type |= N_EXT; + } + if (is_global == 1) { + /* check special to see if the global symbol shall be marked as private external: N_PEXT */ + if (special) { + int n = strcspn(special, " \t"); + + if (!nasm_strnicmp(special, "private_extern", n)) + sym->type |= N_PEXT; + else + nasm_error(ERR_NONFATAL, "unrecognised symbol type `%.*s'", n, special); + } + special_used = true; + } + + /* track the initially allocated symbol number for use in future fix-ups */ + sym->initial_snum = nsyms; + + if (section == NO_SEG) { + /* symbols in no section get absolute */ + sym->type |= N_ABS; + sym->sect = NO_SECT; + + s = &absolute_sect; + } else { + s = get_section_by_index(section); + + sym->type |= N_SECT; + + /* get the in-file index of the section the symbol was defined in */ + sym->sect = s ? s->fileindex : NO_SECT; + + if (!s) { + /* remember symbol number of references to external + ** symbols, this works because every external symbol gets + ** its own section number allocated internally by nasm and + ** can so be used as a key */ + extsyms = raa_write(extsyms, section, nsyms); + + switch (is_global) { + case 1: + case 2: + /* there isn't actually a difference between global + ** and common symbols, both even have their size in + ** sym->symv[0].key */ + sym->type = N_EXT; + break; + + default: + /* give an error on unfound section if it's not an + ** external or common symbol (assemble_file() does a + ** seg_alloc() on every call for them) */ + nasm_panic("in-file index for section %d not found, is_global = %d", section, is_global); + break; + } + } + } + + if (s) { + s->syms[0] = rb_insert(s->syms[0], &sym->symv[0]); + if (is_global) + s->syms[1] = rb_insert(s->syms[1], &sym->symv[1]); + } + + ++nsyms; + + if (special && !special_used) + nasm_error(ERR_NONFATAL, "no special symbol features supported here"); +} + +static void macho_sectalign(int32_t seg, unsigned int value) +{ + struct section *s; + int align; + + nasm_assert(!(seg & 1)); + + s = get_section_by_index(seg); + + if (!s || !is_power2(value)) + return; + + align = alignlog2_32(value); + if (s->align < align) + s->align = align; +} + +extern macros_t macho_stdmac[]; + +/* Comparison function for qsort symbol layout. */ +static int layout_compare (const struct symbol **s1, + const struct symbol **s2) +{ + return (strcmp ((*s1)->name, (*s2)->name)); +} + +/* The native assembler does a few things in a similar function + + * Remove temporary labels + * Sort symbols according to local, external, undefined (by name) + * Order the string table + + We do not remove temporary labels right now. + + numsyms is the total number of symbols we have. strtabsize is the + number entries in the string table. */ + +static void macho_layout_symbols (uint32_t *numsyms, + uint32_t *strtabsize) +{ + struct symbol *sym, **symp; + uint32_t i,j; + + *numsyms = 0; + *strtabsize = sizeof (char); + + symp = &syms; + + while ((sym = *symp)) { + /* Undefined symbols are now external. */ + if (sym->type == N_UNDF) + sym->type |= N_EXT; + + if ((sym->type & N_EXT) == 0) { + sym->snum = *numsyms; + *numsyms = *numsyms + 1; + nlocalsym++; + } + else { + if ((sym->type & N_TYPE) != N_UNDF) { + nextdefsym++; + } else { + nundefsym++; + } + + /* If we handle debug info we'll want + to check for it here instead of just + adding the symbol to the string table. */ + sym->strx = *strtabsize; + saa_wbytes (strs, sym->name, (int32_t)(strlen(sym->name) + 1)); + *strtabsize += strlen(sym->name) + 1; + } + symp = &(sym->next); + } + + /* Next, sort the symbols. Most of this code is a direct translation from + the Apple cctools symbol layout. We need to keep compatibility with that. */ + /* Set the indexes for symbol groups into the symbol table */ + ilocalsym = 0; + iextdefsym = nlocalsym; + iundefsym = nlocalsym + nextdefsym; + + /* allocate arrays for sorting externals by name */ + extdefsyms = nasm_malloc(nextdefsym * sizeof(struct symbol *)); + undefsyms = nasm_malloc(nundefsym * sizeof(struct symbol *)); + + i = 0; + j = 0; + + symp = &syms; + + while ((sym = *symp)) { + + if((sym->type & N_EXT) == 0) { + sym->strx = *strtabsize; + saa_wbytes (strs, sym->name, (int32_t)(strlen (sym->name) + 1)); + *strtabsize += strlen(sym->name) + 1; + } + else { + if ((sym->type & N_TYPE) != N_UNDF) { + extdefsyms[i++] = sym; + } else { + undefsyms[j++] = sym; + } + } + symp = &(sym->next); + } + + qsort(extdefsyms, nextdefsym, sizeof(struct symbol *), + (int (*)(const void *, const void *))layout_compare); + qsort(undefsyms, nundefsym, sizeof(struct symbol *), + (int (*)(const void *, const void *))layout_compare); + + for(i = 0; i < nextdefsym; i++) { + extdefsyms[i]->snum = *numsyms; + *numsyms += 1; + } + for(j = 0; j < nundefsym; j++) { + undefsyms[j]->snum = *numsyms; + *numsyms += 1; + } +} + +/* Calculate some values we'll need for writing later. */ + +static void macho_calculate_sizes (void) +{ + struct section *s; + int fi; + + /* count sections and calculate in-memory and in-file offsets */ + for (s = sects; s != NULL; s = s->next) { + uint64_t newaddr; + + /* recalculate segment address based on alignment and vm size */ + s->addr = seg_vmsize; + + /* we need section alignment to calculate final section address */ + if (s->align == -1) + s->align = DEFAULT_SECTION_ALIGNMENT; + + newaddr = ALIGN(s->addr, UINT64_C(1) << s->align); + s->addr = newaddr; + + seg_vmsize = newaddr + s->size; + + /* zerofill sections aren't actually written to the file */ + if ((s->flags & SECTION_TYPE) != S_ZEROFILL) { + /* + * LLVM/Xcode as always aligns the section data to 4 + * bytes; there is a comment in the LLVM source code that + * perhaps aligning to pointer size would be better. + */ + s->pad = ALIGN(seg_filesize, 4) - seg_filesize; + s->offset = seg_filesize + s->pad; + seg_filesize += s->size + s->pad; + + /* filesize and vmsize needs to be aligned */ + seg_vmsize += s->pad; + } + } + + /* calculate size of all headers, load commands and sections to + ** get a pointer to the start of all the raw data */ + if (seg_nsects > 0) { + ++head_ncmds; + head_sizeofcmds += fmt.segcmd_size + seg_nsects * fmt.sectcmd_size; + } + + if (nsyms > 0) { + ++head_ncmds; + head_sizeofcmds += MACHO_SYMCMD_SIZE; + } + + if (seg_nsects > MAX_SECT) { + nasm_fatal("MachO output is limited to %d sections\n", + MAX_SECT); + } + + /* Create a table of sections by file index to avoid linear search */ + sectstab = nasm_malloc((seg_nsects + 1) * sizeof(*sectstab)); + sectstab[NO_SECT] = &absolute_sect; + for (s = sects, fi = 1; s != NULL; s = s->next, fi++) + sectstab[fi] = s; +} + +/* Write out the header information for the file. */ + +static void macho_write_header (void) +{ + fwriteint32_t(fmt.mh_magic, ofile); /* magic */ + fwriteint32_t(fmt.cpu_type, ofile); /* CPU type */ + fwriteint32_t(CPU_SUBTYPE_I386_ALL, ofile); /* CPU subtype */ + fwriteint32_t(MH_OBJECT, ofile); /* Mach-O file type */ + fwriteint32_t(head_ncmds, ofile); /* number of load commands */ + fwriteint32_t(head_sizeofcmds, ofile); /* size of load commands */ + fwriteint32_t(head_flags, ofile); /* flags, if any */ + fwritezero(fmt.header_size - 7*4, ofile); /* reserved fields */ +} + +/* Write out the segment load command at offset. */ + +static uint32_t macho_write_segment (uint64_t offset) +{ + uint64_t rel_base = alignptr(offset + seg_filesize); + uint32_t s_reloff = 0; + struct section *s; + + fwriteint32_t(fmt.lc_segment, ofile); /* cmd == LC_SEGMENT_64 */ + + /* size of load command including section load commands */ + fwriteint32_t(fmt.segcmd_size + seg_nsects * fmt.sectcmd_size, + ofile); + + /* in an MH_OBJECT file all sections are in one unnamed (name + ** all zeros) segment */ + fwritezero(16, ofile); + fwriteptr(0, ofile); /* in-memory offset */ + fwriteptr(seg_vmsize, ofile); /* in-memory size */ + fwriteptr(offset, ofile); /* in-file offset to data */ + fwriteptr(seg_filesize, ofile); /* in-file size */ + fwriteint32_t(VM_PROT_DEFAULT, ofile); /* maximum vm protection */ + fwriteint32_t(VM_PROT_DEFAULT, ofile); /* initial vm protection */ + fwriteint32_t(seg_nsects, ofile); /* number of sections */ + fwriteint32_t(0, ofile); /* no flags */ + + /* emit section headers */ + for (s = sects; s != NULL; s = s->next) { + if (s->nreloc) { + nasm_assert((s->flags & SECTION_TYPE) != S_ZEROFILL); + s->flags |= S_ATTR_LOC_RELOC; + if (s->extreloc) + s->flags |= S_ATTR_EXT_RELOC; + } else if (!xstrncmp(s->segname, "__DATA") && + !xstrncmp(s->sectname, "__const") && + !s->by_name && + !get_section_by_name("__TEXT", "__const")) { + /* + * The MachO equivalent to .rodata can be either + * __DATA,__const or __TEXT,__const; the latter only if + * there are no relocations. However, when mixed it is + * better to specify the segments explicitly. + */ + xstrncpy(s->segname, "__TEXT"); + } + + nasm_write(s->sectname, sizeof(s->sectname), ofile); + nasm_write(s->segname, sizeof(s->segname), ofile); + fwriteptr(s->addr, ofile); + fwriteptr(s->size, ofile); + + /* dummy data for zerofill sections or proper values */ + if ((s->flags & SECTION_TYPE) != S_ZEROFILL) { + nasm_assert(s->pad != (uint32_t)-1); + offset += s->pad; + fwriteint32_t(offset, ofile); + offset += s->size; + /* Write out section alignment, as a power of two. + e.g. 32-bit word alignment would be 2 (2^2 = 4). */ + fwriteint32_t(s->align, ofile); + /* To be compatible with cctools as we emit + a zero reloff if we have no relocations. */ + fwriteint32_t(s->nreloc ? rel_base + s_reloff : 0, ofile); + fwriteint32_t(s->nreloc, ofile); + + s_reloff += s->nreloc * MACHO_RELINFO_SIZE; + } else { + fwriteint32_t(0, ofile); + fwriteint32_t(s->align, ofile); + fwriteint32_t(0, ofile); + fwriteint32_t(0, ofile); + } + + fwriteint32_t(s->flags, ofile); /* flags */ + fwriteint32_t(0, ofile); /* reserved */ + fwriteptr(0, ofile); /* reserved */ + } + + rel_padcnt = rel_base - offset; + offset = rel_base + s_reloff; + + return offset; +} + +/* For a given chain of relocs r, write out the entire relocation + chain to the object file. */ + +static void macho_write_relocs (struct reloc *r) +{ + while (r) { + uint32_t word2; + + fwriteint32_t(r->addr, ofile); /* reloc offset */ + + word2 = r->snum; + word2 |= r->pcrel << 24; + word2 |= r->length << 25; + word2 |= r->ext << 27; + word2 |= r->type << 28; + fwriteint32_t(word2, ofile); /* reloc data */ + r = r->next; + } +} + +/* Write out the section data. */ +static void macho_write_section (void) +{ + struct section *s; + struct reloc *r; + uint8_t *p; + int32_t len; + int64_t l; + union offset { + uint64_t val; + uint8_t buf[8]; + } blk; + + for (s = sects; s != NULL; s = s->next) { + if ((s->flags & SECTION_TYPE) == S_ZEROFILL) + continue; + + /* Like a.out Mach-O references things in the data or bss + * sections by addresses which are actually relative to the + * start of the _text_ section, in the _file_. See outaout.c + * for more information. */ + saa_rewind(s->data); + for (r = s->relocs; r != NULL; r = r->next) { + len = (uint32_t)1 << r->length; + if (len > 4) /* Can this ever be an issue?! */ + len = 8; + blk.val = 0; + saa_fread(s->data, r->addr, blk.buf, len); + + /* get offset based on relocation type */ +#ifdef WORDS_LITTLEENDIAN + l = blk.val; +#else + l = blk.buf[0]; + l += ((int64_t)blk.buf[1]) << 8; + l += ((int64_t)blk.buf[2]) << 16; + l += ((int64_t)blk.buf[3]) << 24; + l += ((int64_t)blk.buf[4]) << 32; + l += ((int64_t)blk.buf[5]) << 40; + l += ((int64_t)blk.buf[6]) << 48; + l += ((int64_t)blk.buf[7]) << 56; +#endif + + /* If the relocation is internal add to the current section + offset. Otherwise the only value we need is the symbol + offset which we already have. The linker takes care + of the rest of the address. */ + if (!r->ext) { + /* generate final address by section address and offset */ + nasm_assert(r->snum <= seg_nsects); + l += sectstab[r->snum]->addr; + if (r->pcrel) + l -= s->addr; + } else if (r->pcrel && r->type == GENERIC_RELOC_VANILLA) { + l -= s->addr; + } + + /* write new offset back */ + p = blk.buf; + WRITEDLONG(p, l); + saa_fwrite(s->data, r->addr, blk.buf, len); + } + + /* dump the section data to file */ + fwritezero(s->pad, ofile); + saa_fpwrite(s->data, ofile); + } + + /* pad last section up to reloc entries on pointer boundary */ + fwritezero(rel_padcnt, ofile); + + /* emit relocation entries */ + for (s = sects; s != NULL; s = s->next) + macho_write_relocs (s->relocs); +} + +/* Write out the symbol table. We should already have sorted this + before now. */ +static void macho_write_symtab (void) +{ + struct symbol *sym; + uint64_t i; + + /* we don't need to pad here since MACHO_RELINFO_SIZE == 8 */ + + for (sym = syms; sym != NULL; sym = sym->next) { + if ((sym->type & N_EXT) == 0) { + fwriteint32_t(sym->strx, ofile); /* string table entry number */ + nasm_write(&sym->type, 1, ofile); /* symbol type */ + nasm_write(&sym->sect, 1, ofile); /* section */ + fwriteint16_t(sym->desc, ofile); /* description */ + + /* Fix up the symbol value now that we know the final section + sizes. */ + if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) { + nasm_assert(sym->sect <= seg_nsects); + sym->symv[0].key += sectstab[sym->sect]->addr; + } + + fwriteptr(sym->symv[0].key, ofile); /* value (i.e. offset) */ + } + } + + for (i = 0; i < nextdefsym; i++) { + sym = extdefsyms[i]; + fwriteint32_t(sym->strx, ofile); + nasm_write(&sym->type, 1, ofile); /* symbol type */ + nasm_write(&sym->sect, 1, ofile); /* section */ + fwriteint16_t(sym->desc, ofile); /* description */ + + /* Fix up the symbol value now that we know the final section + sizes. */ + if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) { + nasm_assert(sym->sect <= seg_nsects); + sym->symv[0].key += sectstab[sym->sect]->addr; + } + + fwriteptr(sym->symv[0].key, ofile); /* value (i.e. offset) */ + } + + for (i = 0; i < nundefsym; i++) { + sym = undefsyms[i]; + fwriteint32_t(sym->strx, ofile); + nasm_write(&sym->type, 1, ofile); /* symbol type */ + nasm_write(&sym->sect, 1, ofile); /* section */ + fwriteint16_t(sym->desc, ofile); /* description */ + + /* Fix up the symbol value now that we know the final section + sizes. */ + if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) { + nasm_assert(sym->sect <= seg_nsects); + sym->symv[0].key += sectstab[sym->sect]->addr; + } + + fwriteptr(sym->symv[0].key, ofile); /* value (i.e. offset) */ + } + +} + +/* Fixup the snum in the relocation entries, we should be + doing this only for externally referenced symbols. */ +static void macho_fixup_relocs (struct reloc *r) +{ + struct symbol *sym; + + while (r != NULL) { + if (r->ext) { + for (sym = syms; sym != NULL; sym = sym->next) { + if (sym->initial_snum == r->snum) { + r->snum = sym->snum; + break; + } + } + } + r = r->next; + } +} + +/* Write out the object file. */ + +static void macho_write (void) +{ + uint64_t offset = 0; + + /* mach-o object file structure: + ** + ** mach header + ** uint32_t magic + ** int cpu type + ** int cpu subtype + ** uint32_t mach file type + ** uint32_t number of load commands + ** uint32_t size of all load commands + ** (includes section struct size of segment command) + ** uint32_t flags + ** + ** segment command + ** uint32_t command type == LC_SEGMENT[_64] + ** uint32_t size of load command + ** (including section load commands) + ** char[16] segment name + ** pointer in-memory offset + ** pointer in-memory size + ** pointer in-file offset to data area + ** pointer in-file size + ** (in-memory size excluding zerofill sections) + ** int maximum vm protection + ** int initial vm protection + ** uint32_t number of sections + ** uint32_t flags + ** + ** section commands + ** char[16] section name + ** char[16] segment name + ** pointer in-memory offset + ** pointer in-memory size + ** uint32_t in-file offset + ** uint32_t alignment + ** (irrelevant in MH_OBJECT) + ** uint32_t in-file offset of relocation entries + ** uint32_t number of relocations + ** uint32_t flags + ** uint32_t reserved + ** uint32_t reserved + ** + ** symbol table command + ** uint32_t command type == LC_SYMTAB + ** uint32_t size of load command + ** uint32_t symbol table offset + ** uint32_t number of symbol table entries + ** uint32_t string table offset + ** uint32_t string table size + ** + ** raw section data + ** + ** padding to pointer boundary + ** + ** relocation data (struct reloc) + ** int32_t offset + ** uint data (symbolnum, pcrel, length, extern, type) + ** + ** symbol table data (struct nlist) + ** int32_t string table entry number + ** uint8_t type + ** (extern, absolute, defined in section) + ** uint8_t section + ** (0 for global symbols, section number of definition (>= 1, <= + ** 254) for local symbols, size of variable for common symbols + ** [type == extern]) + ** int16_t description + ** (for stab debugging format) + ** pointer value (i.e. file offset) of symbol or stab offset + ** + ** string table data + ** list of null-terminated strings + */ + + /* Emit the Mach-O header. */ + macho_write_header(); + + offset = fmt.header_size + head_sizeofcmds; + + /* emit the segment load command */ + if (seg_nsects > 0) + offset = macho_write_segment (offset); + else + nasm_warn(WARN_OTHER, "no sections?"); + + if (nsyms > 0) { + /* write out symbol command */ + fwriteint32_t(LC_SYMTAB, ofile); /* cmd == LC_SYMTAB */ + fwriteint32_t(MACHO_SYMCMD_SIZE, ofile); /* size of load command */ + fwriteint32_t(offset, ofile); /* symbol table offset */ + fwriteint32_t(nsyms, ofile); /* number of symbol + ** table entries */ + offset += nsyms * fmt.nlist_size; + fwriteint32_t(offset, ofile); /* string table offset */ + fwriteint32_t(strslen, ofile); /* string table size */ + } + + /* emit section data */ + if (seg_nsects > 0) + macho_write_section (); + + /* emit symbol table if we have symbols */ + if (nsyms > 0) + macho_write_symtab (); + + /* we don't need to pad here, we are already aligned */ + + /* emit string table */ + saa_fpwrite(strs, ofile); +} +/* We do quite a bit here, starting with finalizing all of the data + for the object file, writing, and then freeing all of the data from + the file. */ + +static void macho_cleanup(void) +{ + struct section *s; + struct reloc *r; + struct symbol *sym; + + dfmt->cleanup(); + + /* Sort all symbols. */ + macho_layout_symbols (&nsyms, &strslen); + + /* Fixup relocation entries */ + for (s = sects; s != NULL; s = s->next) { + macho_fixup_relocs (s->relocs); + } + + /* First calculate and finalize needed values. */ + macho_calculate_sizes(); + macho_write(); + + /* free up everything */ + while (sects->next) { + s = sects; + sects = sects->next; + + saa_free(s->data); + while (s->relocs != NULL) { + r = s->relocs; + s->relocs = s->relocs->next; + nasm_free(r); + } + + nasm_free(s); + } + + saa_free(strs); + + raa_free(extsyms); + + while (syms) { + sym = syms; + syms = syms->next; + nasm_free (sym); + } + + nasm_free(extdefsyms); + nasm_free(undefsyms); + nasm_free(sectstab); + raa_free(section_by_index); + hash_free(§ion_by_name); +} + +static bool macho_set_section_attribute_by_symbol(const char *label, uint32_t flags) +{ + struct section *s; + int32_t nasm_seg; + int64_t offset; + + if (lookup_label(label, &nasm_seg, &offset) == LBL_none) { + nasm_error(ERR_NONFATAL, "unknown symbol `%s' in no_dead_strip", label); + return false; + } + + s = get_section_by_index(nasm_seg); + if (!s) { + nasm_error(ERR_NONFATAL, "symbol `%s' is external or absolute", label); + return false; + } + + s->flags |= flags; + return true; +} + +/* + * Mark a symbol for no dead stripping + */ +static enum directive_result macho_no_dead_strip(const char *labels) +{ + char *s, *p, *ep; + char ec; + enum directive_result rv = DIRR_ERROR; + + p = s = nasm_strdup(labels); + while (*p) { + ep = nasm_skip_identifier(p); + if (!ep) { + nasm_error(ERR_NONFATAL, "invalid symbol in NO_DEAD_STRIP"); + goto err; + } + ec = *ep; + if (ec && ec != ',' && !nasm_isspace(ec)) { + nasm_error(ERR_NONFATAL, "cannot parse contents after symbol"); + goto err; + } + *ep = '\0'; + if (!pass_first()) { + if (!macho_set_section_attribute_by_symbol(p, S_ATTR_NO_DEAD_STRIP)) + rv = DIRR_ERROR; + } + *ep = ec; + p = nasm_skip_spaces(ep); + if (*p == ',') + p = nasm_skip_spaces(++p); + } + + rv = DIRR_OK; + +err: + nasm_free(s); + return rv; +} + +/* + * Mach-O pragmas + */ +static enum directive_result +macho_pragma(const struct pragma *pragma) +{ + switch (pragma->opcode) { + case D_SUBSECTIONS_VIA_SYMBOLS: + if (*pragma->tail) + return DIRR_BADPARAM; + + if (!pass_first()) + head_flags |= MH_SUBSECTIONS_VIA_SYMBOLS; + + /* Jmp-match optimization conflicts */ + optimizing.flag |= OPTIM_DISABLE_JMP_MATCH; + + return DIRR_OK; + + case D_NO_DEAD_STRIP: + return macho_no_dead_strip(pragma->tail); + + default: + return DIRR_UNKNOWN; /* Not a Mach-O directive */ + } +} + +static const struct pragma_facility macho_pragma_list[] = { + { "macho", macho_pragma }, + { NULL, macho_pragma } /* Implements macho32/macho64 namespaces */ +}; + +static void macho_dbg_generate(void) +{ + uint8_t *p_buf = NULL, *p_buf_base = NULL; + size_t saa_len = 0, high_addr = 0, total_len = 0; + struct section *p_section = NULL; + /* calculated at debug_str and referenced at debug_info */ + uint32_t producer_str_offset = 0, module_str_offset = 0, dir_str_offset = 0; + + /* debug section defines */ + { + int bits = 0; + macho_section(".debug_abbrev", &bits); + macho_section(".debug_info", &bits); + macho_section(".debug_line", &bits); + macho_section(".debug_str", &bits); + } + + /* dw section walk to find high_addr and total_len */ + { + struct dw_sect_list *p_sect; + + list_for_each(p_sect, dw_head_sect) { + uint64_t offset = get_section_by_index(p_sect->section)->size; + struct SAA *p_linep = p_sect->psaa; + + saa_write8(p_linep, 2); /* std op 2 */ + saa_write8(p_linep, offset - p_sect->offset); + saa_write8(p_linep, DW_LNS_extended_op); + saa_write8(p_linep, 1); /* operand length */ + saa_write8(p_linep, DW_LNE_end_sequence); + + total_len += p_linep->datalen; + high_addr += offset; + } + } + + /* debug line */ + { + struct dw_sect_list *p_sect; + size_t linep_off, buf_size; + struct SAA *p_lines = saa_init(1L); + struct dir_list *p_dir; + struct file_list *p_file; + + p_section = get_section_by_name("__DWARF", "__debug_line"); + nasm_assert(p_section != NULL); + + saa_write8(p_lines, 1); /* minimum instruction length */ + saa_write8(p_lines, 1); /* initial value of "is_stmt" */ + saa_write8(p_lines, DW_LN_BASE); /* line base */ + saa_write8(p_lines, DW_LN_RANGE); /* line range */ + saa_write8(p_lines, DW_OPCODE_BASE); /* opcode base */ + saa_write8(p_lines, 0); /* std opcode 1 length */ + saa_write8(p_lines, 1); /* std opcode 2 length */ + saa_write8(p_lines, 1); /* std opcode 3 length */ + saa_write8(p_lines, 1); /* std opcode 4 length */ + saa_write8(p_lines, 1); /* std opcode 5 length */ + saa_write8(p_lines, 0); /* std opcode 6 length */ + saa_write8(p_lines, 0); /* std opcode 7 length */ + saa_write8(p_lines, 0); /* std opcode 8 length */ + saa_write8(p_lines, 1); /* std opcode 9 length */ + saa_write8(p_lines, 0); /* std opcode 10 length */ + saa_write8(p_lines, 0); /* std opcode 11 length */ + saa_write8(p_lines, 1); /* std opcode 12 length */ + list_for_each(p_dir, dw_head_dir) { + saa_wcstring(p_lines, p_dir->dir_name); + } + saa_write8(p_lines, 0); /* end of table */ + + list_for_each(p_file, dw_head_file) { + saa_wcstring(p_lines, p_file->file_name); + saa_write8(p_lines, p_file->dir->dir); /* directory id */ + saa_write8(p_lines, 0); /* time */ + saa_write8(p_lines, 0); /* size */ + } + saa_write8(p_lines, 0); /* end of table */ + + linep_off = p_lines->datalen; + /* 10 bytes for initial & prolong length, and dwarf version info */ + buf_size = saa_len = linep_off + total_len + 10; + p_buf_base = p_buf = nasm_malloc(buf_size); + + WRITELONG(p_buf, saa_len - 4); /* initial length; size excluding itself */ + WRITESHORT(p_buf, 2); /* dwarf version */ + WRITELONG(p_buf, linep_off); /* prolong length */ + + saa_rnbytes(p_lines, p_buf, linep_off); + p_buf += linep_off; + saa_free(p_lines); + + list_for_each(p_sect, dw_head_sect) { + struct SAA *p_linep = p_sect->psaa; + + saa_len = p_linep->datalen; + saa_rnbytes(p_linep, p_buf, saa_len); + p_buf += saa_len; + + saa_free(p_linep); + } + + macho_output(p_section->index, p_buf_base, OUT_RAWDATA, buf_size, NO_SEG, 0); + + nasm_free(p_buf_base); + } + + /* string section */ + { + struct SAA *p_str = saa_init(1L); + char *cur_path = nasm_realpath(module_name); + char *cur_file = nasm_basename(cur_path); + char *cur_dir = nasm_dirname(cur_path); + + p_section = get_section_by_name("__DWARF", "__debug_str"); + nasm_assert(p_section != NULL); + + producer_str_offset = 0; + module_str_offset = dir_str_offset = saa_wcstring(p_str, nasm_signature()); + dir_str_offset += saa_wcstring(p_str, cur_file); + saa_wcstring(p_str, cur_dir); + + saa_len = p_str->datalen; + p_buf = nasm_malloc(saa_len); + saa_rnbytes(p_str, p_buf, saa_len); + macho_output(p_section->index, p_buf, OUT_RAWDATA, saa_len, NO_SEG, 0); + + nasm_free(cur_path); + nasm_free(cur_file); + nasm_free(cur_dir); + saa_free(p_str); + nasm_free(p_buf); + } + + /* debug info */ + { + struct SAA *p_info = saa_init(1L); + + p_section = get_section_by_name("__DWARF", "__debug_info"); + nasm_assert(p_section != NULL); + + /* size will be overwritten once determined, so skip in p_info layout */ + saa_write16(p_info, 2); /* dwarf version */ + saa_write32(p_info, 0); /* offset info abbrev */ + saa_write8(p_info, (ofmt == &of_macho64) ? 8 : 4); /* pointer size */ + + saa_write8(p_info, 1); /* abbrev entry number */ + + saa_write32(p_info, producer_str_offset); /* offset from string table for DW_AT_producer */ + saa_write16(p_info, DW_LANG_Mips_Assembler); /* DW_AT_language */ + saa_write32(p_info, module_str_offset); /* offset from string table for DW_AT_name */ + saa_write32(p_info, dir_str_offset); /* offset from string table for DW_AT_comp_dir */ + saa_write32(p_info, 0); /* DW_AT_stmt_list */ + + if (ofmt == &of_macho64) { + saa_write64(p_info, 0); /* DW_AT_low_pc */ + saa_write64(p_info, high_addr); /* DW_AT_high_pc */ + } else { + saa_write32(p_info, 0); /* DW_AT_low_pc */ + saa_write32(p_info, high_addr); /* DW_AT_high_pc */ + } + + saa_write8(p_info, 2); /* abbrev entry number */ + + if (ofmt == &of_macho64) { + saa_write64(p_info, 0); /* DW_AT_low_pc */ + saa_write64(p_info, 0); /* DW_AT_frame_base */ + } else { + saa_write32(p_info, 0); /* DW_AT_low_pc */ + saa_write32(p_info, 0); /* DW_AT_frame_base */ + } + saa_write8(p_info, DW_END_default); + + saa_len = p_info->datalen; + p_buf_base = p_buf = nasm_malloc(saa_len + 4); /* 4B for size info */ + + WRITELONG(p_buf, saa_len); + saa_rnbytes(p_info, p_buf, saa_len); + macho_output(p_section->index, p_buf_base, OUT_RAWDATA, saa_len + 4, NO_SEG, 0); + + saa_free(p_info); + nasm_free(p_buf_base); + } + + /* abbrev section */ + { + struct SAA *p_abbrev = saa_init(1L); + + p_section = get_section_by_name("__DWARF", "__debug_abbrev"); + nasm_assert(p_section != NULL); + + saa_write8(p_abbrev, 1); /* entry number */ + + saa_write8(p_abbrev, DW_TAG_compile_unit); + saa_write8(p_abbrev, DW_CHILDREN_yes); + + saa_write8(p_abbrev, DW_AT_producer); + saa_write8(p_abbrev, DW_FORM_strp); + + saa_write8(p_abbrev, DW_AT_language); + saa_write8(p_abbrev, DW_FORM_data2); + + saa_write8(p_abbrev, DW_AT_name); + saa_write8(p_abbrev, DW_FORM_strp); + + saa_write8(p_abbrev, DW_AT_comp_dir); + saa_write8(p_abbrev, DW_FORM_strp); + + saa_write8(p_abbrev, DW_AT_stmt_list); + saa_write8(p_abbrev, DW_FORM_data4); + + saa_write8(p_abbrev, DW_AT_low_pc); + saa_write8(p_abbrev, DW_FORM_addr); + + saa_write8(p_abbrev, DW_AT_high_pc); + saa_write8(p_abbrev, DW_FORM_addr); + + saa_write16(p_abbrev, DW_END_default); + + saa_write8(p_abbrev, 2); /* entry number */ + + saa_write8(p_abbrev, DW_TAG_subprogram); + saa_write8(p_abbrev, DW_CHILDREN_no); + + saa_write8(p_abbrev, DW_AT_low_pc); + saa_write8(p_abbrev, DW_FORM_addr); + + saa_write8(p_abbrev, DW_AT_frame_base); + saa_write8(p_abbrev, DW_FORM_addr); + + saa_write16(p_abbrev, DW_END_default); + + saa_write8(p_abbrev, 0); /* Terminal zero entry */ + + saa_len = p_abbrev->datalen; + + p_buf = nasm_malloc(saa_len); + + saa_rnbytes(p_abbrev, p_buf, saa_len); + macho_output(p_section->index, p_buf, OUT_RAWDATA, saa_len, NO_SEG, 0); + + saa_free(p_abbrev); + nasm_free(p_buf); + } +} + +static void new_file_list (const char *file_name, const char *dir_name) +{ + struct dir_list *dir_list; + bool need_new_dir_list = true; + + nasm_new(dw_cur_file); + dw_cur_file->file = ++dw_num_files; + dw_cur_file->file_name = file_name; + if(!dw_head_file) { + dw_head_file = dw_cur_file; + } else { + *dw_last_file_next = dw_cur_file; + } + dw_last_file_next = &(dw_cur_file->next); + + if(dw_head_dir) { + list_for_each(dir_list, dw_head_dir) { + if(!(strcmp(dir_name, dir_list->dir_name))) { + dw_cur_file->dir = dir_list; + need_new_dir_list = false; + break; + } + } + } + + if(need_new_dir_list) + { + nasm_new(dir_list); + dir_list->dir = dw_num_dirs++; + dir_list->dir_name = dir_name; + if(!dw_head_dir) { + dw_head_dir = dir_list; + } else { + *dw_last_dir_next = dir_list; + } + dw_last_dir_next = &(dir_list->next); + dw_cur_file->dir = dir_list; + } +} + +static void macho_dbg_init(void) +{ +} + +static void macho_dbg_linenum(const char *file_name, int32_t line_num, int32_t segto) +{ + bool need_new_list = true; + const char *cur_file = nasm_basename(file_name); + const char *cur_dir = nasm_dirname(file_name); + (void)segto; + + if(!dw_cur_file || strcmp(cur_file, dw_cur_file->file_name) || + strcmp(cur_dir, dw_cur_file->dir->dir_name)) { + if(dw_head_file) { + struct file_list *match; + + list_for_each(match, dw_head_file) { + if(!(strcmp(cur_file, match->file_name)) && + !(strcmp(cur_dir, match->dir->dir_name))) { + dw_cur_file = match; + dw_cur_file->dir = match->dir; + need_new_list = false; + break; + } + } + } + + if (need_new_list) + new_file_list(cur_file, cur_dir); + } + + if (!need_new_list) { + nasm_free((void *)cur_file); + nasm_free((void *)cur_dir); + } + + dbg_immcall = true; + cur_line = line_num; +} + +static void macho_dbg_output(int type, void *param) +{ + struct section_info *sinfo_param = (struct section_info *)param; + int32_t secto = sinfo_param->secto; + bool need_new_sect = false; + struct SAA *p_linep = NULL; + (void)type; + + if(!(dw_cur_sect && (dw_cur_sect->section == secto))) { + need_new_sect = true; + if(dw_head_sect) { + struct dw_sect_list *match = dw_head_sect; + uint32_t idx = 0; + + for(; idx < dw_num_sects; idx++) { + if(match->section == secto) { + dw_cur_sect = match; + need_new_sect = false; + break; + } + match = match->next; + } + } + } + + if(need_new_sect) { + nasm_new(dw_cur_sect); + dw_num_sects ++; + p_linep = dw_cur_sect->psaa = saa_init(1L); + dw_cur_sect->line = dw_cur_sect->file = 1; + dw_cur_sect->offset = 0; + dw_cur_sect->next = NULL; + dw_cur_sect->section = secto; + + saa_write8(p_linep, DW_LNS_extended_op); + saa_write8(p_linep, (ofmt == &of_macho64) ? 9 : 5); + saa_write8(p_linep, DW_LNE_set_address); + if (ofmt == &of_macho64) { + saa_write64(p_linep, 0); + } else { + saa_write32(p_linep, 0); + } + + if(!dw_head_sect) { + dw_head_sect = dw_last_sect = dw_cur_sect; + } else { + dw_last_sect->next = dw_cur_sect; + dw_last_sect = dw_cur_sect; + } + } + + if(dbg_immcall == true) { + int32_t line_delta = cur_line - dw_cur_sect->line; + int32_t offset_delta = sinfo_param->size - dw_cur_sect->offset; + uint32_t cur_file = dw_cur_file->file; + p_linep = dw_cur_sect->psaa; + + if(cur_file != dw_cur_sect->file) { + saa_write8(p_linep, DW_LNS_set_file); + saa_write8(p_linep, cur_file); + dw_cur_sect->file = cur_file; + } + + if(line_delta) { + int special_opcode = (line_delta - DW_LN_BASE) + (DW_LN_RANGE * offset_delta) + + DW_OPCODE_BASE; + + if((line_delta >= DW_LN_BASE) && (line_delta < DW_MAX_LN) && + (special_opcode < DW_MAX_SP_OPCODE)) { + saa_write8(p_linep, special_opcode); + } else { + saa_write8(p_linep, DW_LNS_advance_line); + saa_wleb128s(p_linep, line_delta); + if(offset_delta) { + saa_write8(p_linep, DW_LNS_advance_pc); + saa_wleb128u(p_linep, offset_delta); + } + saa_write8(p_linep, DW_LNS_copy); + } + + dw_cur_sect->line = cur_line; + dw_cur_sect->offset = sinfo_param->size; + } + + dbg_immcall = false; + } +} + +static void macho_dbg_cleanup(void) +{ + /* dwarf sectors generation */ + macho_dbg_generate(); + + { + struct dw_sect_list *p_sect = dw_head_sect; + struct file_list *p_file = dw_head_file; + uint32_t idx = 0; + + for(; idx < dw_num_sects; idx++) { + struct dw_sect_list *next = p_sect->next; + nasm_free(p_sect); + p_sect = next; + } + + for(idx = 0; idx < dw_num_files; idx++) { + struct file_list *next = p_file->next; + nasm_free(p_file); + p_file = next; + } + } +} + +#ifdef OF_MACHO32 +static const struct macho_fmt macho32_fmt = { + 4, + MH_MAGIC, + CPU_TYPE_I386, + LC_SEGMENT, + MACHO_HEADER_SIZE, + MACHO_SEGCMD_SIZE, + MACHO_SECTCMD_SIZE, + MACHO_NLIST_SIZE, + RL_MAX_32, + GENERIC_RELOC_VANILLA, + GENERIC_RELOC_VANILLA, + GENERIC_RELOC_TLV, + false /* Allow segment-relative relocations */ +}; + +static void macho32_init(void) +{ + fmt = macho32_fmt; + macho_init(); + + macho_gotpcrel_sect = NO_SEG; +} + +static const struct dfmt macho32_df_dwarf = { + "Mach-O i386 dwarf for Darwin/MacOS", + "dwarf", + macho_dbg_init, + macho_dbg_linenum, + null_debug_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + null_debug_typevalue, + macho_dbg_output, + macho_dbg_cleanup, + NULL /*pragma list*/ +}; + +static const struct dfmt * const macho32_df_arr[2] = + { &macho32_df_dwarf, NULL }; + +const struct ofmt of_macho32 = { + "Mach-O i386 (Mach, including MacOS X and variants)", + "macho32", + ".o", + 0, + 32, + macho32_df_arr, + &macho32_df_dwarf, + macho_stdmac, + macho32_init, + null_reset, + nasm_do_legacy_output, + macho_output, + macho_symdef, + macho_section, + macho_herelabel, + macho_sectalign, + null_segbase, + null_directive, + macho_cleanup, + macho_pragma_list +}; +#endif + +#ifdef OF_MACHO64 +static const struct macho_fmt macho64_fmt = { + 8, + MH_MAGIC_64, + CPU_TYPE_X86_64, + LC_SEGMENT_64, + MACHO_HEADER64_SIZE, + MACHO_SEGCMD64_SIZE, + MACHO_SECTCMD64_SIZE, + MACHO_NLIST64_SIZE, + RL_MAX_64, + X86_64_RELOC_UNSIGNED, + X86_64_RELOC_SIGNED, + X86_64_RELOC_TLV, + true /* Force symbol-relative relocations */ +}; + +static void macho64_init(void) +{ + fmt = macho64_fmt; + macho_init(); + + /* add special symbol for ..gotpcrel */ + macho_gotpcrel_sect = seg_alloc() + 1; + backend_label("..gotpcrel", macho_gotpcrel_sect, 0L); +} + +static const struct dfmt macho64_df_dwarf = { + "Mach-O x86-64 dwarf for Darwin/MacOS", + "dwarf", + macho_dbg_init, + macho_dbg_linenum, + null_debug_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + null_debug_typevalue, + macho_dbg_output, + macho_dbg_cleanup, + NULL /*pragma list*/ +}; + +static const struct dfmt * const macho64_df_arr[2] = + { &macho64_df_dwarf, NULL }; + +const struct ofmt of_macho64 = { + "Mach-O x86-64 (Mach, including MacOS X and variants)", + "macho64", + ".o", + 0, + 64, + macho64_df_arr, + &macho64_df_dwarf, + macho_stdmac, + macho64_init, + null_reset, + nasm_do_legacy_output, + macho_output, + macho_symdef, + macho_section, + macho_herelabel, + macho_sectalign, + null_segbase, + null_directive, + macho_cleanup, + macho_pragma_list, +}; +#endif + +#endif + +/* + * Local Variables: + * mode:c + * c-basic-offset:4 + * End: + * + * end of file */ diff --git a/vere/ext/nasm/output/outmacho.mac b/vere/ext/nasm/output/outmacho.mac new file mode 100644 index 0000000..001bb3a --- /dev/null +++ b/vere/ext/nasm/output/outmacho.mac @@ -0,0 +1,49 @@ +;; -------------------------------------------------------------------------- +;; +;; Copyright 1996-2017 The NASM Authors - All Rights Reserved +;; See the file AUTHORS included with the NASM distribution for +;; the specific copyright holders. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following +;; conditions are met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following +;; disclaimer in the documentation and/or other materials provided +;; with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +;; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +;; EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;; +;; -------------------------------------------------------------------------- + +OUT: macho macho32 macho64 +%define __?SECT?__ [section .text] +%macro __?NASM_CDecl?__ 1 +%endmacro + +; This directive sets the MH_SUBSECTIONS_VIA_SYMBOLS header flag +%imacro subsections_via_symbols 0.nolist + %pragma __?OUTPUT_FORMAT?__ %? +%endmacro + +%imacro no_dead_strip 1-*.nolist + %rep %0 + %pragma __?OUTPUT_FORMAT?__ %? %1 + %rotate 1 + %endrep +%endmacro diff --git a/vere/ext/nasm/output/outobj.c b/vere/ext/nasm/output/outobj.c new file mode 100644 index 0000000..281839d --- /dev/null +++ b/vere/ext/nasm/output/outobj.c @@ -0,0 +1,2701 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2017 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +/* + * outobj.c output routines for the Netwide Assembler to produce + * .OBJ object files + */ + +#include "compiler.h" + +#include <ctype.h> /* For toupper() */ +#include "nctype.h" + +#include "nasm.h" +#include "nasmlib.h" +#include "error.h" +#include "stdscan.h" +#include "eval.h" +#include "ver.h" + +#include "outform.h" +#include "outlib.h" + +#ifdef OF_OBJ + +/* + * outobj.c is divided into two sections. The first section is low level + * routines for creating obj records; It has nearly zero NASM specific + * code. The second section is high level routines for processing calls and + * data structures from the rest of NASM into obj format. + * + * It should be easy (though not zero work) to lift the first section out for + * use as an obj file writer for some other assembler or compiler. + */ + +/* + * These routines are built around the ObjRecord data structure. An ObjRecord + * holds an object file record that may be under construction or complete. + * + * A major function of these routines is to support continuation of an obj + * record into the next record when the maximum record size is exceeded. The + * high level code does not need to worry about where the record breaks occur. + * It does need to do some minor extra steps to make the automatic continuation + * work. Those steps may be skipped for records where the high level knows no + * continuation could be required. + * + * 1) An ObjRecord is allocated and cleared by obj_new, or an existing ObjRecord + * is cleared by obj_clear. + * + * 2) The caller should fill in .type. + * + * 3) If the record is continuable and there is processing that must be done at + * the start of each record then the caller should fill in .ori with the + * address of the record initializer routine. + * + * 4) If the record is continuable and it should be saved (rather than emitted + * immediately) as each record is done, the caller should set .up to be a + * pointer to a location in which the caller keeps the master pointer to the + * ObjRecord. When the record is continued, the obj_bump routine will then + * allocate a new ObjRecord structure and update the master pointer. + * + * 5) If the .ori field was used then the caller should fill in the .parm with + * any data required by the initializer. + * + * 6) The caller uses the routines: obj_byte, obj_word, obj_rword, obj_dword, + * obj_x, obj_index, obj_value and obj_name to fill in the various kinds of + * data required for this record. + * + * 7) If the record is continuable, the caller should call obj_commit at each + * point where breaking the record is permitted. + * + * 8) To write out the record, the caller should call obj_emit2. If the + * caller has called obj_commit for all data written then he can get slightly + * faster code by calling obj_emit instead of obj_emit2. + * + * Most of these routines return an ObjRecord pointer. This will be the input + * pointer most of the time and will be the new location if the ObjRecord + * moved as a result of the call. The caller may ignore the return value in + * three cases: It is a "Never Reallocates" routine; or The caller knows + * continuation is not possible; or The caller uses the master pointer for the + * next operation. + */ + +#define RECORD_MAX (1024-3) /* maximal size of any record except type+reclen */ +#define OBJ_PARMS 3 /* maximum .parm used by any .ori routine */ + +#define FIX_08_LOW 0x8000 /* location type for various fixup subrecords */ +#define FIX_16_OFFSET 0x8400 +#define FIX_16_SELECTOR 0x8800 +#define FIX_32_POINTER 0x8C00 +#define FIX_08_HIGH 0x9000 +#define FIX_32_OFFSET 0xA400 +#define FIX_48_POINTER 0xAC00 + +enum RecordID { /* record ID codes */ + + THEADR = 0x80, /* module header */ + COMENT = 0x88, /* comment record */ + + LINNUM = 0x94, /* line number record */ + LNAMES = 0x96, /* list of names */ + + SEGDEF = 0x98, /* segment definition */ + GRPDEF = 0x9A, /* group definition */ + EXTDEF = 0x8C, /* external definition */ + PUBDEF = 0x90, /* public definition */ + COMDEF = 0xB0, /* common definition */ + + LEDATA = 0xA0, /* logical enumerated data */ + FIXUPP = 0x9C, /* fixups (relocations) */ + FIXU32 = 0x9D, /* 32-bit fixups (relocations) */ + + MODEND = 0x8A, /* module end */ + MODE32 = 0x8B /* module end for 32-bit objects */ +}; + +enum ComentID { /* ID codes for comment records */ + dTRANSL = 0x0000, /* translator comment */ + dOMFEXT = 0xC0A0, /* "OMF extension" */ + dEXTENDED = 0xC0A1, /* translator-specific extensions */ + dLINKPASS = 0x40A2, /* link pass 2 marker */ + dTYPEDEF = 0xC0E3, /* define a type */ + dSYM = 0xC0E6, /* symbol debug record */ + dFILNAME = 0xC0E8, /* file name record */ + dDEPFILE = 0xC0E9, /* dependency file */ + dCOMPDEF = 0xC0EA /* compiler type info */ +}; + +typedef struct ObjRecord ObjRecord; +typedef void ORI(ObjRecord * orp); + +struct ObjRecord { + ORI *ori; /* Initialization routine */ + int used; /* Current data size */ + int committed; /* Data size at last boundary */ + int x_size; /* (see obj_x) */ + unsigned int type; /* Record type */ + ObjRecord *child; /* Associated record below this one */ + ObjRecord **up; /* Master pointer to this ObjRecord */ + ObjRecord *back; /* Previous part of this record */ + uint32_t parm[OBJ_PARMS]; /* Parameters for ori routine */ + uint8_t buf[RECORD_MAX + 3]; +}; + +static void obj_fwrite(ObjRecord * orp); +static void ori_ledata(ObjRecord * orp); +static void ori_pubdef(ObjRecord * orp); +static void ori_null(ObjRecord * orp); +static ObjRecord *obj_commit(ObjRecord * orp); + +static bool obj_uppercase; /* Flag: all names in uppercase */ +static bool obj_use32; /* Flag: at least one segment is 32-bit */ +static bool obj_nodepend; /* Flag: don't emit file dependencies */ + +/* + * Clear an ObjRecord structure. (Never reallocates). + * To simplify reuse of ObjRecord's, .type, .ori and .parm are not cleared. + */ +static ObjRecord *obj_clear(ObjRecord * orp) +{ + orp->used = 0; + orp->committed = 0; + orp->x_size = 0; + orp->child = NULL; + orp->up = NULL; + orp->back = NULL; + return (orp); +} + +/* + * Emit an ObjRecord structure. (Never reallocates). + * The record is written out proceeded (recursively) by its previous part (if + * any) and followed (recursively) by its child (if any). + * The previous part and the child are freed. The main ObjRecord is cleared, + * not freed. + */ +static ObjRecord *obj_emit(ObjRecord * orp) +{ + if (orp->back) { + obj_emit(orp->back); + nasm_free(orp->back); + } + + if (orp->committed) + obj_fwrite(orp); + + if (orp->child) { + obj_emit(orp->child); + nasm_free(orp->child); + } + + return (obj_clear(orp)); +} + +/* + * Commit and Emit a record. (Never reallocates). + */ +static ObjRecord *obj_emit2(ObjRecord * orp) +{ + obj_commit(orp); + return (obj_emit(orp)); +} + +/* + * Allocate and clear a new ObjRecord; Also sets .ori to ori_null + */ +static ObjRecord *obj_new(void) +{ + ObjRecord *orp; + + orp = obj_clear(nasm_malloc(sizeof(ObjRecord))); + orp->ori = ori_null; + return (orp); +} + +/* + * Advance to the next record because the existing one is full or its x_size + * is incompatible. + * Any uncommitted data is moved into the next record. + */ +static ObjRecord *obj_bump(ObjRecord * orp) +{ + ObjRecord *nxt; + int used = orp->used; + int committed = orp->committed; + + if (orp->up) { + *orp->up = nxt = obj_new(); + nxt->ori = orp->ori; + nxt->type = orp->type; + nxt->up = orp->up; + nxt->back = orp; + memcpy(nxt->parm, orp->parm, sizeof(orp->parm)); + } else + nxt = obj_emit(orp); + + used -= committed; + if (used) { + nxt->committed = 1; + nxt->ori(nxt); + nxt->committed = nxt->used; + memcpy(nxt->buf + nxt->committed, orp->buf + committed, used); + nxt->used = nxt->committed + used; + } + + return (nxt); +} + +/* + * Advance to the next record if necessary to allow the next field to fit. + */ +static ObjRecord *obj_check(ObjRecord * orp, int size) +{ + if (orp->used + size > RECORD_MAX) + orp = obj_bump(orp); + + if (!orp->committed) { + orp->committed = 1; + orp->ori(orp); + orp->committed = orp->used; + } + + return (orp); +} + +/* + * All data written so far is committed to the current record (won't be moved to + * the next record in case of continuation). + */ +static ObjRecord *obj_commit(ObjRecord * orp) +{ + orp->committed = orp->used; + return (orp); +} + +/* + * Write a byte + */ +static ObjRecord *obj_byte(ObjRecord * orp, uint8_t val) +{ + orp = obj_check(orp, 1); + orp->buf[orp->used] = val; + orp->used++; + return (orp); +} + +/* + * Write a word + */ +static ObjRecord *obj_word(ObjRecord * orp, unsigned int val) +{ + orp = obj_check(orp, 2); + orp->buf[orp->used] = val; + orp->buf[orp->used + 1] = val >> 8; + orp->used += 2; + return (orp); +} + +/* + * Write a reversed word + */ +static ObjRecord *obj_rword(ObjRecord * orp, unsigned int val) +{ + orp = obj_check(orp, 2); + orp->buf[orp->used] = val >> 8; + orp->buf[orp->used + 1] = val; + orp->used += 2; + return (orp); +} + +/* + * Write a dword + */ +static ObjRecord *obj_dword(ObjRecord * orp, uint32_t val) +{ + orp = obj_check(orp, 4); + orp->buf[orp->used] = val; + orp->buf[orp->used + 1] = val >> 8; + orp->buf[orp->used + 2] = val >> 16; + orp->buf[orp->used + 3] = val >> 24; + orp->used += 4; + return (orp); +} + +/* + * All fields of "size x" in one obj record must be the same size (either 16 + * bits or 32 bits). There is a one bit flag in each record which specifies + * which. + * This routine is used to force the current record to have the desired + * x_size. x_size is normally automatic (using obj_x), so that this + * routine should be used outside obj_x, only to provide compatibility with + * linkers that have bugs in their processing of the size bit. + */ + +static ObjRecord *obj_force(ObjRecord * orp, int x) +{ + if (orp->x_size == (x ^ 48)) + orp = obj_bump(orp); + orp->x_size = x; + return (orp); +} + +/* + * This routine writes a field of size x. The caller does not need to worry at + * all about whether 16-bits or 32-bits are required. + */ +static ObjRecord *obj_x(ObjRecord * orp, uint32_t val) +{ + if (orp->type & 1) + orp->x_size = 32; + if (val > 0xFFFF) + orp = obj_force(orp, 32); + if (orp->x_size == 32) { + ObjRecord *nxt = obj_dword(orp, val); + nxt->x_size = 32; /* x_size is cleared when a record overflows */ + return nxt; + } + orp->x_size = 16; + return (obj_word(orp, val)); +} + +/* + * Writes an index + */ +static ObjRecord *obj_index(ObjRecord * orp, unsigned int val) +{ + if (val < 128) + return (obj_byte(orp, val)); + return (obj_word(orp, (val >> 8) | (val << 8) | 0x80)); +} + +/* + * Writes a variable length value + */ +static ObjRecord *obj_value(ObjRecord * orp, uint32_t val) +{ + if (val <= 128) + return (obj_byte(orp, val)); + if (val <= 0xFFFF) { + orp = obj_byte(orp, 129); + return (obj_word(orp, val)); + } + if (val <= 0xFFFFFF) + return (obj_dword(orp, (val << 8) + 132)); + orp = obj_byte(orp, 136); + return (obj_dword(orp, val)); +} + +/* + * Writes a counted string + */ +static ObjRecord *obj_name(ObjRecord * orp, const char *name) +{ + int len = strlen(name); + uint8_t *ptr; + + if (len > UINT8_MAX) { + nasm_warn(WARN_OTHER, "truncating object name `%.64s...' to %u bytes", + name, UINT8_MAX); + len = UINT8_MAX; + } + + orp = obj_check(orp, len + 1); + ptr = orp->buf + orp->used; + *ptr++ = len; + orp->used += len + 1; + if (obj_uppercase) + while (--len >= 0) { + *ptr++ = toupper(*name); + name++; + } else + memcpy(ptr, name, len); + return (orp); +} + +/* + * Initializer for an LEDATA record. + * parm[0] = offset + * parm[1] = segment index + * During the use of a LEDATA ObjRecord, parm[0] is constantly updated to + * represent the offset that would be required if the record were split at the + * last commit point. + * parm[2] is a copy of parm[0] as it was when the current record was initted. + */ +static void ori_ledata(ObjRecord * orp) +{ + obj_index(orp, orp->parm[1]); + orp->parm[2] = orp->parm[0]; + obj_x(orp, orp->parm[0]); +} + +/* + * Initializer for a PUBDEF record. + * parm[0] = group index + * parm[1] = segment index + * parm[2] = frame (only used when both indexes are zero) + */ +static void ori_pubdef(ObjRecord * orp) +{ + obj_index(orp, orp->parm[0]); + obj_index(orp, orp->parm[1]); + if (!(orp->parm[0] | orp->parm[1])) + obj_word(orp, orp->parm[2]); +} + +/* + * Initializer for a LINNUM record. + * parm[0] = group index + * parm[1] = segment index + */ +static void ori_linnum(ObjRecord * orp) +{ + obj_index(orp, orp->parm[0]); + obj_index(orp, orp->parm[1]); +} + +/* + * Initializer for a local vars record. + */ +static void ori_local(ObjRecord * orp) +{ + obj_rword(orp, dSYM); +} + +/* + * Null initializer for records that continue without any header info + */ +static void ori_null(ObjRecord * orp) +{ + (void)orp; /* Do nothing */ +} + +/* + * This concludes the low level section of outobj.c + */ + +static char obj_infile[FILENAME_MAX]; + +static int32_t first_seg; +static bool any_segs; +static int passtwo; +static int arrindex; + +#define GROUP_MAX 256 /* we won't _realistically_ have more + * than this many segs in a group */ +#define EXT_BLKSIZ 256 /* block size for externals list */ + +struct Segment; /* need to know these structs exist */ +struct Group; + +struct LineNumber { + struct LineNumber *next; + struct Segment *segment; + int32_t offset; + int32_t lineno; +}; + +static struct FileName { + struct FileName *next; + char *name; + struct LineNumber *lnhead, **lntail; + int index; +} *fnhead, **fntail; + +static struct Array { + struct Array *next; + unsigned size; + int basetype; +} *arrhead, **arrtail; + +#define ARRAYBOT 31 /* magic number for first array index */ + +static struct Public { + struct Public *next; + char *name; + int32_t offset; + int32_t segment; /* only if it's far-absolute */ + int type; /* only for local debug syms */ +} *fpubhead, **fpubtail, *last_defined; + +static struct External { + struct External *next; + char *name; + int32_t commonsize; + int32_t commonelem; /* element size if FAR, else zero */ + int index; /* OBJ-file external index */ + enum { + DEFWRT_NONE, /* no unusual default-WRT */ + DEFWRT_STRING, /* a string we don't yet understand */ + DEFWRT_SEGMENT, /* a segment */ + DEFWRT_GROUP /* a group */ + } defwrt_type; + union { + char *string; + struct Segment *seg; + struct Group *grp; + } defwrt_ptr; + struct External *next_dws; /* next with DEFWRT_STRING */ +} *exthead, **exttail, *dws; + +static int externals; + +static struct ExtBack { + struct ExtBack *next; + struct External *exts[EXT_BLKSIZ]; +} *ebhead, **ebtail; + +static struct Segment { + struct Segment *next; + char *name; + int32_t index; /* the NASM segment id */ + int32_t obj_index; /* the OBJ-file segment index */ + struct Group *grp; /* the group it beint32_ts to */ + uint32_t currentpos; + int32_t align; /* can be SEG_ABS + absolute addr */ + int64_t pass_last_seen; + struct Public *pubhead, **pubtail, *lochead, **loctail; + char *segclass, *overlay; /* `class' is a C++ keyword :-) */ + ObjRecord *orp; + enum { + CMB_PRIVATE = 0, + CMB_PUBLIC = 2, + CMB_STACK = 5, + CMB_COMMON = 6 + } combine; + bool use32; /* is this segment 32-bit? */ +} *seghead, **segtail, *obj_seg_needs_update; + +static struct Group { + struct Group *next; + char *name; + int32_t index; /* NASM segment id */ + int32_t obj_index; /* OBJ-file group index */ + int32_t nentries; /* number of elements... */ + int32_t nindices; /* ...and number of index elts... */ + union { + int32_t index; + char *name; + } segs[GROUP_MAX]; /* ...in this */ +} *grphead, **grptail, *obj_grp_needs_update; + +static struct ImpDef { + struct ImpDef *next; + char *extname; + char *libname; + unsigned int impindex; + char *impname; +} *imphead, **imptail; + +static struct ExpDef { + struct ExpDef *next; + char *intname; + char *extname; + unsigned int ordinal; + int flags; +} *exphead, **exptail; + +#define EXPDEF_FLAG_ORDINAL 0x80 +#define EXPDEF_FLAG_RESIDENT 0x40 +#define EXPDEF_FLAG_NODATA 0x20 +#define EXPDEF_MASK_PARMCNT 0x1F + +static int32_t obj_entry_seg, obj_entry_ofs; + +const struct ofmt of_obj; +static const struct dfmt borland_debug_form; + +/* The current segment */ +static struct Segment *current_seg; + +static int32_t obj_segment(char *, int *); +static void obj_write_file(void); +static enum directive_result obj_directive(enum directive, char *); + +static void obj_init(void) +{ + strlcpy(obj_infile, inname, sizeof(obj_infile)); + first_seg = seg_alloc(); + any_segs = false; + fpubhead = NULL; + fpubtail = &fpubhead; + exthead = NULL; + exttail = &exthead; + imphead = NULL; + imptail = &imphead; + exphead = NULL; + exptail = &exphead; + dws = NULL; + externals = 0; + ebhead = NULL; + ebtail = &ebhead; + seghead = obj_seg_needs_update = NULL; + segtail = &seghead; + grphead = obj_grp_needs_update = NULL; + grptail = &grphead; + obj_entry_seg = NO_SEG; + obj_uppercase = false; + obj_use32 = false; + passtwo = 0; + current_seg = NULL; +} + +static void obj_cleanup(void) +{ + obj_write_file(); + dfmt->cleanup(); + while (seghead) { + struct Segment *segtmp = seghead; + seghead = seghead->next; + while (segtmp->pubhead) { + struct Public *pubtmp = segtmp->pubhead; + segtmp->pubhead = pubtmp->next; + nasm_free(pubtmp->name); + nasm_free(pubtmp); + } + nasm_free(segtmp->segclass); + nasm_free(segtmp->overlay); + nasm_free(segtmp); + } + while (fpubhead) { + struct Public *pubtmp = fpubhead; + fpubhead = fpubhead->next; + nasm_free(pubtmp->name); + nasm_free(pubtmp); + } + while (exthead) { + struct External *exttmp = exthead; + exthead = exthead->next; + nasm_free(exttmp); + } + while (imphead) { + struct ImpDef *imptmp = imphead; + imphead = imphead->next; + nasm_free(imptmp->extname); + nasm_free(imptmp->libname); + nasm_free(imptmp->impname); /* nasm_free won't mind if it's NULL */ + nasm_free(imptmp); + } + while (exphead) { + struct ExpDef *exptmp = exphead; + exphead = exphead->next; + nasm_free(exptmp->extname); + nasm_free(exptmp->intname); + nasm_free(exptmp); + } + while (ebhead) { + struct ExtBack *ebtmp = ebhead; + ebhead = ebhead->next; + nasm_free(ebtmp); + } + while (grphead) { + struct Group *grptmp = grphead; + grphead = grphead->next; + nasm_free(grptmp); + } +} + +static void obj_ext_set_defwrt(struct External *ext, char *id) +{ + struct Segment *seg; + struct Group *grp; + + for (seg = seghead; seg; seg = seg->next) + if (!strcmp(seg->name, id)) { + ext->defwrt_type = DEFWRT_SEGMENT; + ext->defwrt_ptr.seg = seg; + nasm_free(id); + return; + } + + for (grp = grphead; grp; grp = grp->next) + if (!strcmp(grp->name, id)) { + ext->defwrt_type = DEFWRT_GROUP; + ext->defwrt_ptr.grp = grp; + nasm_free(id); + return; + } + + ext->defwrt_type = DEFWRT_STRING; + ext->defwrt_ptr.string = id; + ext->next_dws = dws; + dws = ext; +} + +static void obj_deflabel(char *name, int32_t segment, + int64_t offset, int is_global, char *special) +{ + /* + * We have three cases: + * + * (i) `segment' is a segment-base. If so, set the name field + * for the segment or group structure it refers to, and then + * return. + * + * (ii) `segment' is one of our segments, or a SEG_ABS segment. + * Save the label position for later output of a PUBDEF record. + * (Or a MODPUB, if we work out how.) + * + * (iii) `segment' is not one of our segments. Save the label + * position for later output of an EXTDEF, and also store a + * back-reference so that we can map later references to this + * segment number to the external index. + */ + struct External *ext; + struct ExtBack *eb; + struct Segment *seg; + int i; + bool used_special = false; /* have we used the special text? */ + + if (debug_level(2)) + nasm_debug(" obj_deflabel: %s, seg=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n", + name, segment, offset, is_global, special); + + /* + * If it's a special-retry from pass two, discard it. + */ + if (is_global == 3) + return; + + /* + * First check for the double-period, signifying something + * unusual. + */ + if (name[0] == '.' && name[1] == '.' && name[2] != '@') { + if (!strcmp(name, "..start")) { + obj_entry_seg = segment; + obj_entry_ofs = offset; + return; + } + nasm_nonfatal("unrecognised special symbol `%s'", name); + } + + /* + * Case (i): + */ + if (obj_seg_needs_update) { + obj_seg_needs_update->name = name; + return; + } else if (obj_grp_needs_update) { + obj_grp_needs_update->name = name; + return; + } + if (segment < SEG_ABS && segment != NO_SEG && segment % 2) + return; + + if (segment >= SEG_ABS || segment == NO_SEG) { + /* + * SEG_ABS subcase of (ii). + */ + if (is_global) { + struct Public *pub; + + pub = *fpubtail = nasm_malloc(sizeof(*pub)); + fpubtail = &pub->next; + pub->next = NULL; + pub->name = nasm_strdup(name); + pub->offset = offset; + pub->segment = (segment == NO_SEG ? 0 : segment & ~SEG_ABS); + } + if (special) + nasm_nonfatal("OBJ supports no special symbol features" + " for this symbol type"); + return; + } + + /* + * If `any_segs' is still false, we might need to define a + * default segment, if they're trying to declare a label in + * `first_seg'. + */ + if (!any_segs && segment == first_seg) { + int tempint = 0; + if (segment != obj_segment("__NASMDEFSEG", &tempint)) + nasm_panic("strange segment conditions in OBJ driver"); + } + + for (seg = seghead; seg && is_global; seg = seg->next) + if (seg->index == segment) { + struct Public *loc = nasm_malloc(sizeof(*loc)); + /* + * Case (ii). Maybe MODPUB someday? + */ + *seg->pubtail = loc; + seg->pubtail = &loc->next; + loc->next = NULL; + loc->name = nasm_strdup(name); + loc->offset = offset; + + if (special) + nasm_nonfatal("OBJ supports no special symbol features" + " for this symbol type"); + return; + } + + /* + * Case (iii). + */ + if (is_global) { + ext = *exttail = nasm_malloc(sizeof(*ext)); + ext->next = NULL; + exttail = &ext->next; + ext->name = name; + /* Place by default all externs into the current segment */ + ext->defwrt_type = DEFWRT_NONE; + +/* 28-Apr-2002 - John Coffman + The following code was introduced on 12-Aug-2000, and breaks fixups + on code passed thru the MSC 5.1 linker (3.66) and MSC 6.00A linker + (5.10). It was introduced after FIXUP32 was added, and may be needed + for 32-bit segments. The following will get 16-bit segments working + again, and maybe someone can correct the 'if' condition which is + actually needed. +*/ +#if 0 + if (current_seg) { +#else + if (current_seg && current_seg->use32) { + if (current_seg->grp) { + ext->defwrt_type = DEFWRT_GROUP; + ext->defwrt_ptr.grp = current_seg->grp; + } else { + ext->defwrt_type = DEFWRT_SEGMENT; + ext->defwrt_ptr.seg = current_seg; + } + } +#endif + + if (is_global == 2) { + ext->commonsize = offset; + ext->commonelem = 1; /* default FAR */ + } else + ext->commonsize = 0; + } else + return; + + /* + * Now process the special text, if any, to find default-WRT + * specifications and common-variable element-size and near/far + * specifications. + */ + while (special && *special) { + used_special = true; + + /* + * We might have a default-WRT specification. + */ + if (!nasm_strnicmp(special, "wrt", 3)) { + char *p; + int len; + special += 3; + special += strspn(special, " \t"); + p = nasm_strndup(special, len = strcspn(special, ":")); + obj_ext_set_defwrt(ext, p); + special += len; + if (*special && *special != ':') + nasm_nonfatal("`:' expected in special symbol" + " text for `%s'", ext->name); + else if (*special == ':') + special++; + } + + /* + * The NEAR or FAR keywords specify nearness or + * farness. FAR gives default element size 1. + */ + if (!nasm_strnicmp(special, "far", 3)) { + if (ext->commonsize) + ext->commonelem = 1; + else + nasm_nonfatal("`%s': `far' keyword may only be applied" + " to common variables\n", ext->name); + special += 3; + special += strspn(special, " \t"); + } else if (!nasm_strnicmp(special, "near", 4)) { + if (ext->commonsize) + ext->commonelem = 0; + else + nasm_nonfatal("`%s': `far' keyword may only be applied" + " to common variables\n", ext->name); + special += 4; + special += strspn(special, " \t"); + } + + /* + * If it's a common, and anything else remains on the line + * before a further colon, evaluate it as an expression and + * use that as the element size. Forward references aren't + * allowed. + */ + if (*special == ':') + special++; + else if (*special) { + if (ext->commonsize) { + expr *e; + struct tokenval tokval; + + stdscan_reset(); + stdscan_set(special); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, NULL, 1, NULL); + if (e) { + if (!is_simple(e)) + nasm_nonfatal("cannot use relocatable" + " expression as common-variable element size"); + else + ext->commonelem = reloc_value(e); + } + special = stdscan_get(); + } else { + nasm_nonfatal("`%s': element-size specifications only" + " apply to common variables", ext->name); + while (*special && *special != ':') + special++; + if (*special == ':') + special++; + } + } + } + + i = segment / 2; + eb = ebhead; + if (!eb) { + eb = *ebtail = nasm_zalloc(sizeof(*eb)); + eb->next = NULL; + ebtail = &eb->next; + } + while (i >= EXT_BLKSIZ) { + if (eb && eb->next) + eb = eb->next; + else { + eb = *ebtail = nasm_zalloc(sizeof(*eb)); + eb->next = NULL; + ebtail = &eb->next; + } + i -= EXT_BLKSIZ; + } + eb->exts[i] = ext; + ext->index = ++externals; + + if (special && !used_special) + nasm_nonfatal("OBJ supports no special symbol features" + " for this symbol type"); +} + +/* forward declaration */ +static void obj_write_fixup(ObjRecord * orp, int bytes, + int segrel, int32_t seg, int32_t wrt, + struct Segment *segto); + +static void obj_out(int32_t segto, const void *data, + enum out_type type, uint64_t size, + int32_t segment, int32_t wrt) +{ + const uint8_t *ucdata; + int32_t ldata; + struct Segment *seg; + ObjRecord *orp; + + /* + * If `any_segs' is still false, we must define a default + * segment. + */ + if (!any_segs) { + int tempint = 0; + if (segto != obj_segment("__NASMDEFSEG", &tempint)) + nasm_panic("strange segment conditions in OBJ driver"); + } + + /* + * Find the segment we are targeting. + */ + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segto) + break; + if (!seg) + nasm_panic("code directed to nonexistent segment?"); + + orp = seg->orp; + orp->parm[0] = seg->currentpos; + + switch (type) { + case OUT_RAWDATA: + ucdata = data; + while (size > 0) { + unsigned int len; + orp = obj_check(seg->orp, 1); + len = RECORD_MAX - orp->used; + if (len > size) + len = size; + memcpy(orp->buf + orp->used, ucdata, len); + orp->committed = orp->used += len; + orp->parm[0] = seg->currentpos += len; + ucdata += len; + size -= len; + } + break; + + case OUT_ADDRESS: + case OUT_REL1ADR: + case OUT_REL2ADR: + case OUT_REL4ADR: + case OUT_REL8ADR: + { + int rsize; + + if (type == OUT_ADDRESS) + size = abs((int)size); + + if (segment == NO_SEG && type != OUT_ADDRESS) + nasm_nonfatal("relative call to absolute address not" + " supported by OBJ format"); + if (segment >= SEG_ABS) + nasm_nonfatal("far-absolute relocations not supported" + " by OBJ format"); + + ldata = *(int64_t *)data; + if (type != OUT_ADDRESS) { + /* + * For 16-bit and 32-bit x86 code, the size and realsize() always + * matches as only jumps, calls and loops uses PC relative + * addressing and the address isn't followed by any other opcode + * bytes. In 64-bit mode there is RIP relative addressing which + * means the fixup location can be followed by an immediate value, + * meaning that size > realsize(). + * + * When the CPU is calculating the effective address, it takes the + * RIP at the end of the instruction and adds the fixed up relative + * address value to it. + * + * The linker's point of reference is the end of the fixup location + * (which is the end of the instruction for Jcc, CALL, LOOP[cc]). + * It is calculating distance between the target symbol and the end + * of the fixup location, and add this to the displacement value we + * are calculating here and storing at the fixup location. + * + * To get the right effect, we need to _reduce_ the displacement + * value by the number of bytes following the fixup. + * + * Example: + * data at address 0x100; REL4ADR at 0x050, 4 byte immediate, + * end of fixup at 0x054, end of instruction at 0x058. + * => size = 8. + * => realsize() -> 4 + * => CPU needs a value of: 0x100 - 0x058 = 0x0a8 + * => linker/loader will add: 0x100 - 0x054 = 0x0ac + * => We must add an addend of -4. + * => realsize() - size = -4. + * + * The code used to do size - realsize() at least since v0.90, + * probably because it wasn't needed... + */ + ldata -= size; + size = realsize(type, size); + ldata += size; + } + + switch (size) { + default: + nasm_nonfatal("OBJ format can only handle 16- or " + "32-byte relocations"); + segment = NO_SEG; /* Don't actually generate a relocation */ + break; + case 2: + orp = obj_word(orp, ldata); + break; + case 4: + orp = obj_dword(orp, ldata); + break; + } + + rsize = size; + if (segment < SEG_ABS && (segment != NO_SEG && segment % 2) && + size == 4) { + /* + * This is a 4-byte segment-base relocation such as + * `MOV EAX,SEG foo'. OBJ format can't actually handle + * these, but if the constant term has the 16 low bits + * zero, we can just apply a 2-byte segment-base + * relocation to the low word instead. + */ + rsize = 2; + if (ldata & 0xFFFF) + nasm_nonfatal("OBJ format cannot handle complex" + " dword-size segment base references"); + } + if (segment != NO_SEG) + obj_write_fixup(orp, rsize, + (type == OUT_ADDRESS ? 0x4000 : 0), + segment, wrt, seg); + seg->currentpos += size; + break; + } + + default: + nasm_nonfatal("Relocation type not supported by output format"); + /* fall through */ + + case OUT_RESERVE: + if (orp->committed) + orp = obj_bump(orp); + seg->currentpos += size; + break; + } + obj_commit(orp); +} + +static void obj_write_fixup(ObjRecord * orp, int bytes, + int segrel, int32_t seg, int32_t wrt, + struct Segment *segto) +{ + unsigned locat; + int method; + int base; + int32_t tidx, fidx; + struct Segment *s = NULL; + struct Group *g = NULL; + struct External *e = NULL; + ObjRecord *forp; + + if (bytes != 2 && bytes != 4) { + nasm_nonfatal("`obj' output driver does not support" + " %d-bit relocations", bytes << 3); + return; + } + + forp = orp->child; + if (forp == NULL) { + orp->child = forp = obj_new(); + forp->up = &(orp->child); + /* We should choose between FIXUPP and FIXU32 record type */ + /* If we're targeting a 32-bit segment, use a FIXU32 record */ + if (segto->use32) + forp->type = FIXU32; + else + forp->type = FIXUPP; + } + + if (seg % 2) { + base = true; + locat = FIX_16_SELECTOR; + seg--; + if (bytes != 2) + nasm_panic("OBJ: 4-byte segment base fixup got" + " through sanity check"); + } else { + base = false; + locat = (bytes == 2) ? FIX_16_OFFSET : FIX_32_OFFSET; + if (!segrel) + /* + * There is a bug in tlink that makes it process self relative + * fixups incorrectly if the x_size doesn't match the location + * size. + */ + forp = obj_force(forp, bytes << 3); + } + + forp = obj_rword(forp, locat | segrel | (orp->parm[0] - orp->parm[2])); + + tidx = fidx = -1, method = 0; /* placate optimisers */ + + /* + * See if we can find the segment ID in our segment list. If + * so, we have a T4 (LSEG) target. + */ + for (s = seghead; s; s = s->next) + if (s->index == seg) + break; + if (s) + method = 4, tidx = s->obj_index; + else { + for (g = grphead; g; g = g->next) + if (g->index == seg) + break; + if (g) + method = 5, tidx = g->obj_index; + else { + int32_t i = seg / 2; + struct ExtBack *eb = ebhead; + while (i >= EXT_BLKSIZ) { + if (eb) + eb = eb->next; + else + break; + i -= EXT_BLKSIZ; + } + if (eb) + method = 6, e = eb->exts[i], tidx = e->index; + else + nasm_panic("unrecognised segment value in obj_write_fixup"); + } + } + + /* + * If no WRT given, assume the natural default, which is method + * F5 unless: + * + * - we are doing an OFFSET fixup for a grouped segment, in + * which case we require F1 (group). + * + * - we are doing an OFFSET fixup for an external with a + * default WRT, in which case we must honour the default WRT. + */ + if (wrt == NO_SEG) { + if (!base && s && s->grp) + method |= 0x10, fidx = s->grp->obj_index; + else if (!base && e && e->defwrt_type != DEFWRT_NONE) { + if (e->defwrt_type == DEFWRT_SEGMENT) + method |= 0x00, fidx = e->defwrt_ptr.seg->obj_index; + else if (e->defwrt_type == DEFWRT_GROUP) + method |= 0x10, fidx = e->defwrt_ptr.grp->obj_index; + else { + nasm_nonfatal("default WRT specification for" + " external `%s' unresolved", e->name); + method |= 0x50, fidx = -1; /* got to do _something_ */ + } + } else + method |= 0x50, fidx = -1; + } else { + /* + * See if we can find the WRT-segment ID in our segment + * list. If so, we have a F0 (LSEG) frame. + */ + for (s = seghead; s; s = s->next) + if (s->index == wrt - 1) + break; + if (s) + method |= 0x00, fidx = s->obj_index; + else { + for (g = grphead; g; g = g->next) + if (g->index == wrt - 1) + break; + if (g) + method |= 0x10, fidx = g->obj_index; + else { + int32_t i = wrt / 2; + struct ExtBack *eb = ebhead; + while (i >= EXT_BLKSIZ) { + if (eb) + eb = eb->next; + else + break; + i -= EXT_BLKSIZ; + } + if (eb) + method |= 0x20, fidx = eb->exts[i]->index; + else + nasm_panic("unrecognised WRT value in obj_write_fixup"); + } + } + } + + forp = obj_byte(forp, method); + if (fidx != -1) + forp = obj_index(forp, fidx); + forp = obj_index(forp, tidx); + obj_commit(forp); +} + +static int32_t obj_segment(char *name, int *bits) +{ + /* + * We call the label manager here to define a name for the new + * segment, and when our _own_ label-definition stub gets + * called in return, it should register the new segment name + * using the pointer it gets passed. That way we save memory, + * by sponging off the label manager. + */ + if (debug_level(3)) + nasm_debug(" obj_segment: < %s >, *bits=%d\n", name, *bits); + + if (!name) { + *bits = 16; + current_seg = NULL; + return first_seg; + } else { + struct Segment *seg; + struct Group *grp; + struct External **extp; + int obj_idx, i, attrs; + bool rn_error; + char *p; + + /* + * Look for segment attributes. + */ + attrs = 0; + while (*name == '.') + name++; /* hack, but a documented one */ + p = name; + while (*p && !nasm_isspace(*p)) + p++; + if (*p) { + *p++ = '\0'; + while (*p && nasm_isspace(*p)) + *p++ = '\0'; + } + while (*p) { + while (*p && !nasm_isspace(*p)) + p++; + if (*p) { + *p++ = '\0'; + while (*p && nasm_isspace(*p)) + *p++ = '\0'; + } + + attrs++; + } + + for (seg = seghead, obj_idx = 1; ; seg = seg->next, obj_idx++) { + if (!seg) + break; + + if (!strcmp(seg->name, name)) { + if (attrs > 0 && seg->pass_last_seen == pass_count()) + nasm_warn(WARN_OTHER, "segment attributes specified on" + " redeclaration of segment: ignoring"); + if (seg->use32) + *bits = 32; + else + *bits = 16; + current_seg = seg; + seg->pass_last_seen = pass_count(); + return seg->index; + } + } + + *segtail = seg = nasm_malloc(sizeof(*seg)); + seg->next = NULL; + segtail = &seg->next; + seg->index = (any_segs ? seg_alloc() : first_seg); + seg->obj_index = obj_idx; + seg->grp = NULL; + any_segs = true; + seg->name = nasm_strdup(name); + seg->currentpos = 0; + seg->align = 1; /* default */ + seg->use32 = false; /* default */ + seg->combine = CMB_PUBLIC; /* default */ + seg->segclass = seg->overlay = NULL; + seg->pubhead = NULL; + seg->pubtail = &seg->pubhead; + seg->lochead = NULL; + seg->loctail = &seg->lochead; + seg->orp = obj_new(); + seg->orp->up = &(seg->orp); + seg->orp->ori = ori_ledata; + seg->orp->type = LEDATA; + seg->orp->parm[1] = obj_idx; + + /* + * Process the segment attributes. + */ + p = name; + while (attrs--) { + p += strlen(p); + while (!*p) + p++; + + /* + * `p' contains a segment attribute. + */ + if (!nasm_stricmp(p, "private")) + seg->combine = CMB_PRIVATE; + else if (!nasm_stricmp(p, "public")) + seg->combine = CMB_PUBLIC; + else if (!nasm_stricmp(p, "common")) + seg->combine = CMB_COMMON; + else if (!nasm_stricmp(p, "stack")) + seg->combine = CMB_STACK; + else if (!nasm_stricmp(p, "use16")) + seg->use32 = false; + else if (!nasm_stricmp(p, "use32")) + seg->use32 = true; + else if (!nasm_stricmp(p, "flat")) { + /* + * This segment is an OS/2 FLAT segment. That means + * that its default group is group FLAT, even if + * the group FLAT does not explicitly _contain_ the + * segment. + * + * When we see this, we must create the group + * `FLAT', containing no segments, if it does not + * already exist; then we must set the default + * group of this segment to be the FLAT group. + */ + struct Group *grp; + for (grp = grphead; grp; grp = grp->next) + if (!strcmp(grp->name, "FLAT")) + break; + if (!grp) { + obj_directive(D_GROUP, "FLAT"); + for (grp = grphead; grp; grp = grp->next) + if (!strcmp(grp->name, "FLAT")) + break; + if (!grp) + nasm_panic("failure to define FLAT?!"); + } + seg->grp = grp; + } else if (!nasm_strnicmp(p, "class=", 6)) + seg->segclass = nasm_strdup(p + 6); + else if (!nasm_strnicmp(p, "overlay=", 8)) + seg->overlay = nasm_strdup(p + 8); + else if (!nasm_strnicmp(p, "align=", 6)) { + seg->align = readnum(p + 6, &rn_error); + if (rn_error) { + seg->align = 1; + nasm_nonfatal("segment alignment should be numeric"); + } + switch (seg->align) { + case 1: /* BYTE */ + case 2: /* WORD */ + case 4: /* DWORD */ + case 16: /* PARA */ + case 256: /* PAGE */ + case 4096: /* PharLap extension */ + break; + case 8: + nasm_warn(WARN_OTHER, "OBJ format does not support alignment" + " of 8: rounding up to 16"); + seg->align = 16; + break; + case 32: + case 64: + case 128: + nasm_warn(WARN_OTHER, "OBJ format does not support alignment" + " of %d: rounding up to 256", seg->align); + seg->align = 256; + break; + case 512: + case 1024: + case 2048: + nasm_warn(WARN_OTHER, "OBJ format does not support alignment" + " of %d: rounding up to 4096", seg->align); + seg->align = 4096; + break; + default: + nasm_nonfatal("invalid alignment value %d", + seg->align); + seg->align = 1; + break; + } + } else if (!nasm_strnicmp(p, "absolute=", 9)) { + seg->align = SEG_ABS + readnum(p + 9, &rn_error); + if (rn_error) + nasm_nonfatal("argument to `absolute' segment" + " attribute should be numeric"); + } + } + + /* We need to know whenever we have at least one 32-bit segment */ + obj_use32 |= seg->use32; + + obj_seg_needs_update = seg; + if (seg->align >= SEG_ABS) + define_label(name, NO_SEG, seg->align - SEG_ABS, false); + else + define_label(name, seg->index + 1, 0L, false); + obj_seg_needs_update = NULL; + + /* + * See if this segment is defined in any groups. + */ + for (grp = grphead; grp; grp = grp->next) { + for (i = grp->nindices; i < grp->nentries; i++) { + if (!strcmp(grp->segs[i].name, seg->name)) { + nasm_free(grp->segs[i].name); + grp->segs[i] = grp->segs[grp->nindices]; + grp->segs[grp->nindices++].index = seg->obj_index; + if (seg->grp) + nasm_warn(WARN_OTHER, "segment `%s' is already part of" + " a group: first one takes precedence", + seg->name); + else + seg->grp = grp; + } + } + } + + /* + * Walk through the list of externals with unresolved + * default-WRT clauses, and resolve any that point at this + * segment. + */ + extp = &dws; + while (*extp) { + if ((*extp)->defwrt_type == DEFWRT_STRING && + !strcmp((*extp)->defwrt_ptr.string, seg->name)) { + nasm_free((*extp)->defwrt_ptr.string); + (*extp)->defwrt_type = DEFWRT_SEGMENT; + (*extp)->defwrt_ptr.seg = seg; + *extp = (*extp)->next_dws; + } else + extp = &(*extp)->next_dws; + } + + if (seg->use32) + *bits = 32; + else + *bits = 16; + current_seg = seg; + return seg->index; + } +} + +static enum directive_result +obj_directive(enum directive directive, char *value) +{ + switch (directive) { + case D_GROUP: + { + char *p, *q, *v; + if (pass_first()) { /* XXX */ + struct Group *grp; + struct Segment *seg; + struct External **extp; + int obj_idx; + + q = value; + while (*q == '.') + q++; /* hack, but a documented one */ + v = q; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + /* + * Here we used to sanity-check the group directive to + * ensure nobody tried to declare a group containing no + * segments. However, OS/2 does this as standard + * practice, so the sanity check has been removed. + * + * if (!*q) { + * nasm_error(ERR_NONFATAL,"GROUP directive contains no segments"); + * return DIRR_ERROR; + * } + */ + + obj_idx = 1; + for (grp = grphead; grp; grp = grp->next) { + obj_idx++; + if (!strcmp(grp->name, v)) { + nasm_nonfatal("group `%s' defined twice", v); + return DIRR_ERROR; + } + } + + *grptail = grp = nasm_malloc(sizeof(*grp)); + grp->next = NULL; + grptail = &grp->next; + grp->index = seg_alloc(); + grp->obj_index = obj_idx; + grp->nindices = grp->nentries = 0; + grp->name = NULL; + + obj_grp_needs_update = grp; + backend_label(v, grp->index + 1, 0L); + obj_grp_needs_update = NULL; + + while (*q) { + p = q; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + /* + * Now p contains a segment name. Find it. + */ + for (seg = seghead; seg; seg = seg->next) + if (!strcmp(seg->name, p)) + break; + if (seg) { + /* + * We have a segment index. Shift a name entry + * to the end of the array to make room. + */ + grp->segs[grp->nentries++] = grp->segs[grp->nindices]; + grp->segs[grp->nindices++].index = seg->obj_index; + if (seg->grp) + nasm_warn(WARN_OTHER, "segment `%s' is already part of" + " a group: first one takes precedence", + seg->name); + else + seg->grp = grp; + } else { + /* + * We have an as-yet undefined segment. + * Remember its name, for later. + */ + grp->segs[grp->nentries++].name = nasm_strdup(p); + } + } + + /* + * Walk through the list of externals with unresolved + * default-WRT clauses, and resolve any that point at + * this group. + */ + extp = &dws; + while (*extp) { + if ((*extp)->defwrt_type == DEFWRT_STRING && + !strcmp((*extp)->defwrt_ptr.string, grp->name)) { + nasm_free((*extp)->defwrt_ptr.string); + (*extp)->defwrt_type = DEFWRT_GROUP; + (*extp)->defwrt_ptr.grp = grp; + *extp = (*extp)->next_dws; + } else + extp = &(*extp)->next_dws; + } + } + return DIRR_OK; + } + case D_UPPERCASE: + obj_uppercase = true; + return DIRR_OK; + + case D_IMPORT: + { + char *q, *extname, *libname, *impname; + + if (!pass_first()) /* XXX */ + return DIRR_OK; + extname = q = value; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + + libname = q; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + + impname = q; + + if (!*extname || !*libname) + nasm_nonfatal("`import' directive requires symbol name" + " and library name"); + else { + struct ImpDef *imp; + bool err = false; + + imp = *imptail = nasm_malloc(sizeof(struct ImpDef)); + imptail = &imp->next; + imp->next = NULL; + imp->extname = nasm_strdup(extname); + imp->libname = nasm_strdup(libname); + imp->impindex = readnum(impname, &err); + if (!*impname || err) + imp->impname = nasm_strdup(impname); + else + imp->impname = NULL; + } + + return DIRR_OK; + } + case D_EXPORT: + { + char *q, *extname, *intname, *v; + struct ExpDef *export; + int flags = 0; + unsigned int ordinal = 0; + + if (!pass_first()) + return DIRR_OK; /* ignore in pass two */ + intname = q = value; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + + extname = q; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + + if (!*intname) { + nasm_nonfatal("`export' directive requires export name"); + return DIRR_OK; + } + if (!*extname) { + extname = intname; + intname = ""; + } + while (*q) { + v = q; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + if (!nasm_stricmp(v, "resident")) + flags |= EXPDEF_FLAG_RESIDENT; + else if (!nasm_stricmp(v, "nodata")) + flags |= EXPDEF_FLAG_NODATA; + else if (!nasm_strnicmp(v, "parm=", 5)) { + bool err = false; + flags |= EXPDEF_MASK_PARMCNT & readnum(v + 5, &err); + if (err) { + nasm_nonfatal("value `%s' for `parm' is non-numeric", v + 5); + return DIRR_ERROR; + } + } else { + bool err = false; + ordinal = readnum(v, &err); + if (err) { + nasm_nonfatal("unrecognised export qualifier `%s'", v); + return DIRR_ERROR; + } + flags |= EXPDEF_FLAG_ORDINAL; + } + } + + export = *exptail = nasm_malloc(sizeof(struct ExpDef)); + exptail = &export->next; + export->next = NULL; + export->extname = nasm_strdup(extname); + export->intname = nasm_strdup(intname); + export->ordinal = ordinal; + export->flags = flags; + + return DIRR_OK; + } + default: + return DIRR_UNKNOWN; + } +} + +static void obj_sectalign(int32_t seg, unsigned int value) +{ + struct Segment *s; + + list_for_each(s, seghead) { + if (s->index == seg) + break; + } + + /* + * it should not be too big value + * and applied on non-absolute sections + */ + if (!s || !is_power2(value) || + value > 4096 || s->align >= SEG_ABS) + return; + + /* + * FIXME: No code duplication please + * consider making helper for this + * mapping since section handler has + * to do the same + */ + switch (value) { + case 8: + value = 16; + break; + case 32: + case 64: + case 128: + value = 256; + break; + case 512: + case 1024: + case 2048: + value = 4096; + break; + } + + if (s->align < (int)value) + s->align = value; +} + +static int32_t obj_segbase(int32_t segment) +{ + struct Segment *seg; + + /* + * Find the segment in our list. + */ + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segment - 1) + break; + + if (!seg) { + /* + * Might be an external with a default WRT. + */ + int32_t i = segment / 2; + struct ExtBack *eb = ebhead; + struct External *e; + + while (i >= EXT_BLKSIZ) { + if (eb) + eb = eb->next; + else + break; + i -= EXT_BLKSIZ; + } + if (eb) { + e = eb->exts[i]; + if (!e) { + /* Not available yet, probably a forward reference */ + nasm_assert(!pass_final()); + return NO_SEG; + } + + switch (e->defwrt_type) { + case DEFWRT_NONE: + return segment; /* fine */ + case DEFWRT_SEGMENT: + return e->defwrt_ptr.seg->index + 1; + case DEFWRT_GROUP: + return e->defwrt_ptr.grp->index + 1; + default: + return NO_SEG; /* can't tell what it is */ + } + } + + return segment; /* not one of ours - leave it alone */ + } + + if (seg->align >= SEG_ABS) + return seg->align; /* absolute segment */ + if (seg->grp) + return seg->grp->index + 1; /* grouped segment */ + + return segment; /* no special treatment */ +} + +/* Get a file timestamp in MS-DOS format */ +static uint32_t obj_file_timestamp(const char *pathname) +{ + time_t t; + const struct tm *lt; + + if (!nasm_file_time(&t, pathname)) + return 0; + + lt = localtime(&t); + if (!lt) + return 0; + + if (lt->tm_year < 80 || lt->tm_year > 207) + return 0; /* Only years 1980-2107 representable */ + + return + ((uint32_t)lt->tm_sec >> 1) + + ((uint32_t)lt->tm_min << 5) + + ((uint32_t)lt->tm_hour << 11) + + ((uint32_t)lt->tm_mday << 16) + + (((uint32_t)lt->tm_mon + 1) << 21) + + (((uint32_t)lt->tm_year - 80) << 25); +} + +static void obj_write_file(void) +{ + struct Segment *seg, *entry_seg_ptr = 0; + struct FileName *fn; + struct LineNumber *ln; + struct Group *grp; + struct Public *pub, *loc; + struct External *ext; + struct ImpDef *imp; + struct ExpDef *export; + int lname_idx; + ObjRecord *orp; + const struct strlist_entry *depfile; + const bool debuginfo = (dfmt == &borland_debug_form); + + /* + * Write the THEADR module header. + */ + orp = obj_new(); + orp->type = THEADR; + obj_name(orp, obj_infile); + obj_emit2(orp); + + /* + * Write the NASM boast comment. + */ + orp->type = COMENT; + obj_rword(orp, dTRANSL); + obj_name(orp, nasm_comment()); + obj_emit2(orp); + + /* + * Output file dependency information + */ + if (!obj_nodepend && depend_list) { + strlist_for_each(depfile, depend_list) { + uint32_t ts; + + ts = obj_file_timestamp(depfile->str); + if (ts) { + orp->type = COMENT; + obj_rword(orp, dDEPFILE); + obj_dword(orp, ts); + obj_name(orp, depfile->str); + obj_emit2(orp); + } + } + } + + orp->type = COMENT; + /* + * Write the IMPDEF records, if any. + */ + for (imp = imphead; imp; imp = imp->next) { + obj_rword(orp, dOMFEXT); + obj_byte(orp, 1); /* subfunction 1: IMPDEF */ + if (imp->impname) + obj_byte(orp, 0); /* import by name */ + else + obj_byte(orp, 1); /* import by ordinal */ + obj_name(orp, imp->extname); + obj_name(orp, imp->libname); + if (imp->impname) + obj_name(orp, imp->impname); + else + obj_word(orp, imp->impindex); + obj_emit2(orp); + } + + /* + * Write the EXPDEF records, if any. + */ + for (export = exphead; export; export = export->next) { + obj_rword(orp, dOMFEXT); + obj_byte(orp, 2); /* subfunction 2: EXPDEF */ + obj_byte(orp, export->flags); + obj_name(orp, export->extname); + obj_name(orp, export->intname); + if (export->flags & EXPDEF_FLAG_ORDINAL) + obj_word(orp, export->ordinal); + obj_emit2(orp); + } + + /* we're using extended OMF if we put in debug info */ + if (debuginfo) { + orp->type = COMENT; + obj_rword(orp, dEXTENDED); + obj_emit2(orp); + } + + /* + * Write the first LNAMES record, containing LNAME one, which + * is null. Also initialize the LNAME counter. + */ + orp->type = LNAMES; + obj_byte(orp, 0); + lname_idx = 1; + /* + * Write some LNAMES for the segment names + */ + for (seg = seghead; seg; seg = seg->next) { + orp = obj_name(orp, seg->name); + if (seg->segclass) + orp = obj_name(orp, seg->segclass); + if (seg->overlay) + orp = obj_name(orp, seg->overlay); + obj_commit(orp); + } + /* + * Write some LNAMES for the group names + */ + for (grp = grphead; grp; grp = grp->next) { + orp = obj_name(orp, grp->name); + obj_commit(orp); + } + obj_emit(orp); + + /* + * Write the SEGDEF records. + */ + orp->type = SEGDEF; + for (seg = seghead; seg; seg = seg->next) { + int acbp; + uint32_t seglen = seg->currentpos; + + acbp = (seg->combine << 2); /* C field */ + + if (seg->use32) + acbp |= 0x01; /* P bit is Use32 flag */ + else if (seglen == 0x10000L) { + seglen = 0; /* This special case may be needed for old linkers */ + acbp |= 0x02; /* B bit */ + } + + /* A field */ + if (seg->align >= SEG_ABS) + /* acbp |= 0x00 */ ; + else if (seg->align >= 4096) { + if (seg->align > 4096) + nasm_nonfatal("segment `%s' requires more alignment" + " than OBJ format supports", seg->name); + acbp |= 0xC0; /* PharLap extension */ + } else if (seg->align >= 256) { + acbp |= 0x80; + } else if (seg->align >= 16) { + acbp |= 0x60; + } else if (seg->align >= 4) { + acbp |= 0xA0; + } else if (seg->align >= 2) { + acbp |= 0x40; + } else + acbp |= 0x20; + + obj_byte(orp, acbp); + if (seg->align & SEG_ABS) { + obj_x(orp, seg->align - SEG_ABS); /* Frame */ + obj_byte(orp, 0); /* Offset */ + } + obj_x(orp, seglen); + obj_index(orp, ++lname_idx); + obj_index(orp, seg->segclass ? ++lname_idx : 1); + obj_index(orp, seg->overlay ? ++lname_idx : 1); + obj_emit2(orp); + } + + /* + * Write the GRPDEF records. + */ + orp->type = GRPDEF; + for (grp = grphead; grp; grp = grp->next) { + int i; + + if (grp->nindices != grp->nentries) { + for (i = grp->nindices; i < grp->nentries; i++) { + nasm_nonfatal("group `%s' contains undefined segment" + " `%s'", grp->name, grp->segs[i].name); + nasm_free(grp->segs[i].name); + grp->segs[i].name = NULL; + } + } + obj_index(orp, ++lname_idx); + for (i = 0; i < grp->nindices; i++) { + obj_byte(orp, 0xFF); + obj_index(orp, grp->segs[i].index); + } + obj_emit2(orp); + } + + /* + * Write the PUBDEF records: first the ones in the segments, + * then the far-absolutes. + */ + orp->type = PUBDEF; + orp->ori = ori_pubdef; + for (seg = seghead; seg; seg = seg->next) { + orp->parm[0] = seg->grp ? seg->grp->obj_index : 0; + orp->parm[1] = seg->obj_index; + for (pub = seg->pubhead; pub; pub = pub->next) { + orp = obj_name(orp, pub->name); + orp = obj_x(orp, pub->offset); + orp = obj_byte(orp, 0); /* type index */ + obj_commit(orp); + } + obj_emit(orp); + } + orp->parm[0] = 0; + orp->parm[1] = 0; + for (pub = fpubhead; pub; pub = pub->next) { /* pub-crawl :-) */ + if (orp->parm[2] != (uint32_t)pub->segment) { + obj_emit(orp); + orp->parm[2] = pub->segment; + } + orp = obj_name(orp, pub->name); + orp = obj_x(orp, pub->offset); + orp = obj_byte(orp, 0); /* type index */ + obj_commit(orp); + } + obj_emit(orp); + + /* + * Write the EXTDEF and COMDEF records, in order. + */ + orp->ori = ori_null; + for (ext = exthead; ext; ext = ext->next) { + if (ext->commonsize == 0) { + if (orp->type != EXTDEF) { + obj_emit(orp); + orp->type = EXTDEF; + } + orp = obj_name(orp, ext->name); + orp = obj_index(orp, 0); + } else { + if (orp->type != COMDEF) { + obj_emit(orp); + orp->type = COMDEF; + } + orp = obj_name(orp, ext->name); + orp = obj_index(orp, 0); + if (ext->commonelem) { + orp = obj_byte(orp, 0x61); /* far communal */ + orp = obj_value(orp, (ext->commonsize / ext->commonelem)); + orp = obj_value(orp, ext->commonelem); + } else { + orp = obj_byte(orp, 0x62); /* near communal */ + orp = obj_value(orp, ext->commonsize); + } + } + obj_commit(orp); + } + obj_emit(orp); + + /* + * Write a COMENT record stating that the linker's first pass + * may stop processing at this point. Exception is if our + * MODEND record specifies a start point, in which case, + * according to some variants of the documentation, this COMENT + * should be omitted. So we'll omit it just in case. + * But, TASM puts it in all the time so if we are using + * TASM debug stuff we are putting it in + */ + if (debuginfo || obj_entry_seg == NO_SEG) { + orp->type = COMENT; + obj_rword(orp, dLINKPASS); + obj_byte(orp, 1); + obj_emit2(orp); + } + + /* + * 1) put out the compiler type + * 2) Put out the type info. The only type we are using is near label #19 + */ + if (debuginfo) { + int i; + struct Array *arrtmp = arrhead; + orp->type = COMENT; + obj_rword(orp, dCOMPDEF); + obj_byte(orp, 4); + obj_byte(orp, 0); + obj_emit2(orp); + + obj_rword(orp, dTYPEDEF); + obj_word(orp, 0x18); /* type # for linking */ + obj_word(orp, 6); /* size of type */ + obj_byte(orp, 0x2a); /* absolute type for debugging */ + obj_emit2(orp); + obj_rword(orp, dTYPEDEF); + obj_word(orp, 0x19); /* type # for linking */ + obj_word(orp, 0); /* size of type */ + obj_byte(orp, 0x24); /* absolute type for debugging */ + obj_byte(orp, 0); /* near/far specifier */ + obj_emit2(orp); + obj_rword(orp, dTYPEDEF); + obj_word(orp, 0x1A); /* type # for linking */ + obj_word(orp, 0); /* size of type */ + obj_byte(orp, 0x24); /* absolute type for debugging */ + obj_byte(orp, 1); /* near/far specifier */ + obj_emit2(orp); + obj_rword(orp, dTYPEDEF); + obj_word(orp, 0x1b); /* type # for linking */ + obj_word(orp, 0); /* size of type */ + obj_byte(orp, 0x23); /* absolute type for debugging */ + obj_byte(orp, 0); + obj_byte(orp, 0); + obj_byte(orp, 0); + obj_emit2(orp); + obj_rword(orp, dTYPEDEF); + obj_word(orp, 0x1c); /* type # for linking */ + obj_word(orp, 0); /* size of type */ + obj_byte(orp, 0x23); /* absolute type for debugging */ + obj_byte(orp, 0); + obj_byte(orp, 4); + obj_byte(orp, 0); + obj_emit2(orp); + obj_rword(orp, dTYPEDEF); + obj_word(orp, 0x1d); /* type # for linking */ + obj_word(orp, 0); /* size of type */ + obj_byte(orp, 0x23); /* absolute type for debugging */ + obj_byte(orp, 0); + obj_byte(orp, 1); + obj_byte(orp, 0); + obj_emit2(orp); + obj_rword(orp, dTYPEDEF); + obj_word(orp, 0x1e); /* type # for linking */ + obj_word(orp, 0); /* size of type */ + obj_byte(orp, 0x23); /* absolute type for debugging */ + obj_byte(orp, 0); + obj_byte(orp, 5); + obj_byte(orp, 0); + obj_emit2(orp); + + /* put out the array types */ + for (i = ARRAYBOT; i < arrindex; i++) { + obj_rword(orp, dTYPEDEF); + obj_word(orp, i); /* type # for linking */ + obj_word(orp, arrtmp->size); /* size of type */ + obj_byte(orp, 0x1A); /* absolute type for debugging (array) */ + obj_byte(orp, arrtmp->basetype); /* base type */ + obj_emit2(orp); + arrtmp = arrtmp->next; + } + } + /* + * write out line number info with a LINNUM record + * switch records when we switch segments, and output the + * file in a pseudo-TASM fashion. The record switch is naive; that + * is that one file may have many records for the same segment + * if there are lots of segment switches + */ + if (fnhead && debuginfo) { + seg = fnhead->lnhead->segment; + + for (fn = fnhead; fn; fn = fn->next) { + /* write out current file name */ + orp->type = COMENT; + orp->ori = ori_null; + obj_rword(orp, dFILNAME); + obj_byte(orp, 0); + obj_name(orp, fn->name); + obj_dword(orp, 0); + obj_emit2(orp); + + /* write out line numbers this file */ + + orp->type = LINNUM; + orp->ori = ori_linnum; + for (ln = fn->lnhead; ln; ln = ln->next) { + if (seg != ln->segment) { + /* if we get here have to flush the buffer and start + * a new record for a new segment + */ + seg = ln->segment; + obj_emit(orp); + } + orp->parm[0] = seg->grp ? seg->grp->obj_index : 0; + orp->parm[1] = seg->obj_index; + orp = obj_word(orp, ln->lineno); + orp = obj_x(orp, ln->offset); + obj_commit(orp); + } + obj_emit(orp); + } + } + /* + * we are going to locate the entry point segment now + * rather than wait until the MODEND record, because, + * then we can output a special symbol to tell where the + * entry point is. + * + */ + if (obj_entry_seg != NO_SEG) { + for (seg = seghead; seg; seg = seg->next) { + if (seg->index == obj_entry_seg) { + entry_seg_ptr = seg; + break; + } + } + if (!seg) + nasm_nonfatal("entry point is not in this module"); + } + + /* + * get ready to put out symbol records + */ + orp->type = COMENT; + orp->ori = ori_local; + + /* + * put out a symbol for the entry point + * no dots in this symbol, because, borland does + * not (officially) support dots in label names + * and I don't know what various versions of TLINK will do + */ + if (debuginfo && obj_entry_seg != NO_SEG) { + orp = obj_name(orp, "start_of_program"); + orp = obj_word(orp, 0x19); /* type: near label */ + orp = obj_index(orp, seg->grp ? seg->grp->obj_index : 0); + orp = obj_index(orp, seg->obj_index); + orp = obj_x(orp, obj_entry_ofs); + obj_commit(orp); + } + + /* + * put out the local labels + */ + for (seg = seghead; seg && debuginfo; seg = seg->next) { + /* labels this seg */ + for (loc = seg->lochead; loc; loc = loc->next) { + orp = obj_name(orp, loc->name); + orp = obj_word(orp, loc->type); + orp = obj_index(orp, seg->grp ? seg->grp->obj_index : 0); + orp = obj_index(orp, seg->obj_index); + orp = obj_x(orp, loc->offset); + obj_commit(orp); + } + } + if (orp->used) + obj_emit(orp); + + /* + * Write the LEDATA/FIXUPP pairs. + */ + for (seg = seghead; seg; seg = seg->next) { + obj_emit(seg->orp); + nasm_free(seg->orp); + } + + /* + * Write the MODEND module end marker. + */ + orp->type = obj_use32 ? MODE32 : MODEND; + orp->ori = ori_null; + if (entry_seg_ptr) { + orp->type = entry_seg_ptr->use32 ? MODE32 : MODEND; + obj_byte(orp, 0xC1); + seg = entry_seg_ptr; + if (seg->grp) { + obj_byte(orp, 0x10); + obj_index(orp, seg->grp->obj_index); + } else { + /* + * the below changed to prevent TLINK crashing. + * Previous more efficient version read: + * + * obj_byte (orp, 0x50); + */ + obj_byte(orp, 0x00); + obj_index(orp, seg->obj_index); + } + obj_index(orp, seg->obj_index); + obj_x(orp, obj_entry_ofs); + } else + obj_byte(orp, 0); + obj_emit2(orp); + nasm_free(orp); +} + +static void obj_fwrite(ObjRecord * orp) +{ + unsigned int cksum, len; + uint8_t *ptr; + + cksum = orp->type; + if (orp->x_size == 32) + cksum |= 1; + fputc(cksum, ofile); + len = orp->committed + 1; + cksum += (len & 0xFF) + ((len >> 8) & 0xFF); + fwriteint16_t(len, ofile); + nasm_write(orp->buf, len-1, ofile); + for (ptr = orp->buf; --len; ptr++) + cksum += *ptr; + fputc((-cksum) & 0xFF, ofile); +} + +static enum directive_result +obj_pragma(const struct pragma *pragma) +{ + switch (pragma->opcode) { + case D_NODEPEND: + obj_nodepend = true; + break; + + default: + break; + } + + return DIRR_OK; +} + +extern macros_t obj_stdmac[]; + +static void dbgbi_init(void) +{ + fnhead = NULL; + fntail = &fnhead; + arrindex = ARRAYBOT; + arrhead = NULL; + arrtail = &arrhead; +} +static void dbgbi_cleanup(void) +{ + struct Segment *segtmp; + while (fnhead) { + struct FileName *fntemp = fnhead; + while (fnhead->lnhead) { + struct LineNumber *lntemp = fnhead->lnhead; + fnhead->lnhead = lntemp->next; + nasm_free(lntemp); + } + fnhead = fnhead->next; + nasm_free(fntemp->name); + nasm_free(fntemp); + } + for (segtmp = seghead; segtmp; segtmp = segtmp->next) { + while (segtmp->lochead) { + struct Public *loctmp = segtmp->lochead; + segtmp->lochead = loctmp->next; + nasm_free(loctmp->name); + nasm_free(loctmp); + } + } + while (arrhead) { + struct Array *arrtmp = arrhead; + arrhead = arrhead->next; + nasm_free(arrtmp); + } +} + +static void dbgbi_linnum(const char *lnfname, int32_t lineno, int32_t segto) +{ + struct FileName *fn; + struct LineNumber *ln; + struct Segment *seg; + + if (segto == NO_SEG) + return; + + /* + * If `any_segs' is still false, we must define a default + * segment. + */ + if (!any_segs) { + int tempint = 0; + if (segto != obj_segment("__NASMDEFSEG", &tempint)) + nasm_panic("strange segment conditions in OBJ driver"); + } + + /* + * Find the segment we are targeting. + */ + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segto) + break; + if (!seg) + nasm_panic("lineno directed to nonexistent segment?"); + +/* for (fn = fnhead; fn; fn = fnhead->next) */ + for (fn = fnhead; fn; fn = fn->next) /* fbk - Austin Lunnen - John Fine */ + if (!nasm_stricmp(lnfname, fn->name)) + break; + if (!fn) { + fn = nasm_malloc(sizeof(*fn)); + fn->name = nasm_malloc(strlen(lnfname) + 1); + strcpy(fn->name, lnfname); + fn->lnhead = NULL; + fn->lntail = &fn->lnhead; + fn->next = NULL; + *fntail = fn; + fntail = &fn->next; + } + ln = nasm_malloc(sizeof(*ln)); + ln->segment = seg; + ln->offset = seg->currentpos; + ln->lineno = lineno; + ln->next = NULL; + *fn->lntail = ln; + fn->lntail = &ln->next; + +} +static void dbgbi_deflabel(char *name, int32_t segment, + int64_t offset, int is_global, char *special) +{ + struct Segment *seg; + + (void)special; + + /* + * Note: ..[^@] special symbols are filtered in labels.c + */ + + /* + * If it's a special-retry from pass two, discard it. + */ + if (is_global == 3) + return; + + /* + * Case (i): + */ + if (obj_seg_needs_update) { + return; + } else if (obj_grp_needs_update) { + return; + } + if (segment < SEG_ABS && segment != NO_SEG && segment % 2) + return; + + if (segment >= SEG_ABS || segment == NO_SEG) { + return; + } + + /* + * If `any_segs' is still false, we might need to define a + * default segment, if they're trying to declare a label in + * `first_seg'. But the label should exist due to a prior + * call to obj_deflabel so we can skip that. + */ + + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segment) { + struct Public *loc = nasm_malloc(sizeof(*loc)); + /* + * Case (ii). Maybe MODPUB someday? + */ + last_defined = *seg->loctail = loc; + seg->loctail = &loc->next; + loc->next = NULL; + loc->name = nasm_strdup(name); + loc->offset = offset; + } +} +static void dbgbi_typevalue(int32_t type) +{ + int vsize; + int elem = TYM_ELEMENTS(type); + type = TYM_TYPE(type); + + if (!last_defined) + return; + + switch (type) { + case TY_BYTE: + last_defined->type = 8; /* uint8_t */ + vsize = 1; + break; + case TY_WORD: + last_defined->type = 10; /* unsigned word */ + vsize = 2; + break; + case TY_DWORD: + last_defined->type = 12; /* unsigned dword */ + vsize = 4; + break; + case TY_FLOAT: + last_defined->type = 14; /* float */ + vsize = 4; + break; + case TY_QWORD: + last_defined->type = 15; /* qword */ + vsize = 8; + break; + case TY_TBYTE: + last_defined->type = 16; /* TBYTE */ + vsize = 10; + break; + default: + last_defined->type = 0x19; /* label */ + vsize = 0; + break; + } + + if (elem > 1) { + struct Array *arrtmp = nasm_malloc(sizeof(*arrtmp)); + int vtype = last_defined->type; + arrtmp->size = vsize * elem; + arrtmp->basetype = vtype; + arrtmp->next = NULL; + last_defined->type = arrindex++; + *arrtail = arrtmp; + arrtail = &(arrtmp->next); + } + last_defined = NULL; +} +static void dbgbi_output(int output_type, void *param) +{ + (void)output_type; + (void)param; +} +static const struct dfmt borland_debug_form = { + "Borland Debug Records", + "borland", + dbgbi_init, + dbgbi_linnum, + dbgbi_deflabel, + NULL, /* .debug_smacros */ + NULL, /* .debug_include */ + NULL, /* .debug_mmacros */ + null_debug_directive, + dbgbi_typevalue, + dbgbi_output, + dbgbi_cleanup, + NULL /* pragma list */ +}; + +static const struct dfmt * const borland_debug_arr[3] = { + &borland_debug_form, + &null_debug_form, + NULL +}; + +static const struct pragma_facility obj_pragma_list[] = { + { NULL, obj_pragma } +}; + +const struct ofmt of_obj = { + "Intel/Microsoft OMF (MS-DOS, OS/2, Win16)", + "obj", + ".obj", + 0, + 32, + borland_debug_arr, + &borland_debug_form, + obj_stdmac, + obj_init, + null_reset, + nasm_do_legacy_output, + obj_out, + obj_deflabel, + obj_segment, + NULL, + obj_sectalign, + obj_segbase, + obj_directive, + obj_cleanup, + obj_pragma_list +}; +#endif /* OF_OBJ */ diff --git a/vere/ext/nasm/output/outobj.mac b/vere/ext/nasm/output/outobj.mac new file mode 100644 index 0000000..09158f2 --- /dev/null +++ b/vere/ext/nasm/output/outobj.mac @@ -0,0 +1,49 @@ +;; -------------------------------------------------------------------------- +;; +;; Copyright 1996-2009 The NASM Authors - All Rights Reserved +;; See the file AUTHORS included with the NASM distribution for +;; the specific copyright holders. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following +;; conditions are met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above +;; copyright notice, this list of conditions and the following +;; disclaimer in the documentation and/or other materials provided +;; with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +;; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +;; EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;; +;; -------------------------------------------------------------------------- + +OUT: obj +%define __?SECT?__ [section .text] +%imacro group 1+.nolist +[group %1] +%endmacro +%imacro uppercase 0+.nolist +[uppercase %1] +%endmacro +%imacro export 1+.nolist +[export %1] +%endmacro +%imacro import 1+.nolist +[import %1] +%endmacro +%macro __?NASM_CDecl?__ 1 +%endmacro diff --git a/vere/ext/nasm/output/pecoff.h b/vere/ext/nasm/output/pecoff.h new file mode 100644 index 0000000..b99bed0 --- /dev/null +++ b/vere/ext/nasm/output/pecoff.h @@ -0,0 +1,542 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2020 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +#ifndef PECOFF_H +#define PECOFF_H + +/* + * Microsoft Portable Executable and Common Object + * File Format Specification + * + * Revision 8.1 – February 15, 2008 + */ + +/* + * Machine types + */ +#define IMAGE_FILE_MACHINE_UNKNOWN 0x0000 +#define IMAGE_FILE_MACHINE_AM33 0x01d3 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_EBC 0x0ebc +#define IMAGE_FILE_MACHINE_M32R 0x9041 +#define IMAGE_FILE_MACHINE_ALPHA 0x0184 +#define IMAGE_FILE_MACHINE_ARM 0x01c0 +#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_IA64 0x0200 +#define IMAGE_FILE_MACHINE_M68K 0x0268 +#define IMAGE_FILE_MACHINE_MIPS16 0x0266 +#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 +#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 +#define IMAGE_FILE_MACHINE_POWERPC 0x01f0 +#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 +#define IMAGE_FILE_MACHINE_R3000 0x0162 +#define IMAGE_FILE_MACHINE_R4000 0x0166 +#define IMAGE_FILE_MACHINE_R10000 0x0168 +#define IMAGE_FILE_MACHINE_SH3 0x01a2 +#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 +#define IMAGE_FILE_MACHINE_SH4 0x01a6 +#define IMAGE_FILE_MACHINE_SH5 0x01a8 +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 +#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 +#define IMAGE_FILE_MACHINE_MASK 0xffff + +/* + * Characteristics + */ +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010 +#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 +#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 +#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 +#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 +#define IMAGE_FILE_SYSTEM 0x1000 +#define IMAGE_FILE_DLL 0x2000 +#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 +#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 + +/* + * Windows subsystem + */ +#define IMAGE_SUBSYSTEM_UNKNOWN 0 +#define IMAGE_SUBSYSTEM_NATIVE 1 +#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define IMAGE_SUBSYSTEM_POSIX_CUI 7 +#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 +#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 +#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 +#define IMAGE_SUBSYSTEM_EFI_ROM 13 +#define IMAGE_SUBSYSTEM_XBOX 14 + +/* + * DLL characteristics + */ +#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 0x0040 +#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY 0x0080 +#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT 0x0100 +#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 +#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 +#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 +#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 +#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 + +/* + * Section flags + */ +#define IMAGE_SCN_TYPE_REG 0x00000000 +#define IMAGE_SCN_TYPE_DSECT 0x00000001 +#define IMAGE_SCN_TYPE_NOLOAD 0x00000002 +#define IMAGE_SCN_TYPE_GROUP 0x00000004 +#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 +#define IMAGE_SCN_TYPE_COPY 0x00000010 + +#define IMAGE_SCN_CNT_CODE 0x00000020 +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 + +#define IMAGE_SCN_LNK_OTHER 0x00000100 +#define IMAGE_SCN_LNK_INFO 0x00000200 +#define IMAGE_SCN_TYPE_OVER 0x00000400 +#define IMAGE_SCN_LNK_REMOVE 0x00000800 +#define IMAGE_SCN_LNK_COMDAT 0x00001000 + +#define IMAGE_SCN_MAX_RELOC 0xffff + +#define IMAGE_SCN_MEM_FARDATA 0x00008000 +#define IMAGE_SCN_MEM_PURGEABLE 0x00020000 +#define IMAGE_SCN_MEM_16BIT 0x00020000 +#define IMAGE_SCN_MEM_LOCKED 0x00040000 +#define IMAGE_SCN_MEM_PRELOAD 0x00080000 + +#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 +#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 +#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 +#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 +#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 +#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 +#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 +#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 +#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 +#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000 +#define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000 +#define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000 +#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000 +#define IMAGE_SCN_ALIGN_8192BYTES 0x00e00000 +#define IMAGE_SCN_ALIGN_MASK 0x00f00000 + +#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 +#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 +#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 +#define IMAGE_SCN_MEM_SHARED 0x10000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +/* + * Relocation type x86-64 + */ +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 +#define IMAGE_REL_AMD64_ADDR64 0x0001 +#define IMAGE_REL_AMD64_ADDR32 0x0002 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +#define IMAGE_REL_AMD64_REL32 0x0004 +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000a +#define IMAGE_REL_AMD64_SECREL 0x000b +#define IMAGE_REL_AMD64_SECREL7 0x000c +#define IMAGE_REL_AMD64_TOKEN 0x000d +#define IMAGE_REL_AMD64_SREL32 0x000e +#define IMAGE_REL_AMD64_PAIR 0x000f +#define IMAGE_REL_AMD64_SSPAN32 0x0010 + +/* + * Relocation types i386 + */ +#define IMAGE_REL_I386_ABSOLUTE 0x0000 +#define IMAGE_REL_I386_DIR16 0x0001 +#define IMAGE_REL_I386_REL16 0x0002 +#define IMAGE_REL_I386_DIR32 0x0006 +#define IMAGE_REL_I386_DIR32NB 0x0007 +#define IMAGE_REL_I386_SEG12 0x0009 +#define IMAGE_REL_I386_SECTION 0x000a +#define IMAGE_REL_I386_SECREL 0x000b +#define IMAGE_REL_I386_TOKEN 0x000c +#define IMAGE_REL_I386_SECREL7 0x000d +#define IMAGE_REL_I386_REL32 0x0014 + +/* + * Relocation types ARM + */ +#define IMAGE_REL_ARM_ABSOLUTE 0x0000 +#define IMAGE_REL_ARM_ADDR32 0x0001 +#define IMAGE_REL_ARM_ADDR32NB 0x0002 +#define IMAGE_REL_ARM_BRANCH24 0x0003 +#define IMAGE_REL_ARM_BRANCH11 0x0004 +#define IMAGE_REL_ARM_SECTION 0x000e +#define IMAGE_REL_ARM_SECREL 0x000f + +/* + * Relocation types Hitachi SuperH + */ +#define IMAGE_REL_SH3_ABSOLUTE 0x0000 +#define IMAGE_REL_SH3_DIRECT16 0x0001 +#define IMAGE_REL_SH3_DIRECT32 0x0002 +#define IMAGE_REL_SH3_DIRECT8 0x0003 +#define IMAGE_REL_SH3_DIRECT8_WORD 0x0004 +#define IMAGE_REL_SH3_DIRECT8_LONG 0x0005 +#define IMAGE_REL_SH3_DIRECT4 0x0006 +#define IMAGE_REL_SH3_DIRECT4_WORD 0x0007 +#define IMAGE_REL_SH3_DIRECT4_LONG 0x0008 +#define IMAGE_REL_SH3_PCREL8_WORD 0x0009 +#define IMAGE_REL_SH3_PCREL8_LONG 0x000a +#define IMAGE_REL_SH3_PCREL12_WORD 0x000b +#define IMAGE_REL_SH3_STARTOF_SECTION 0x000c +#define IMAGE_REL_SH3_SIZEOF_SECTION 0x000d +#define IMAGE_REL_SH3_SECTION 0x000e +#define IMAGE_REL_SH3_SECREL 0x000f +#define IMAGE_REL_SH3_DIRECT32_NB 0x0010 +#define IMAGE_REL_SH3_GPREL4_LONG 0x0011 +#define IMAGE_REL_SH3_TOKEN 0x0012 +#define IMAGE_REL_SHM_PCRELPT 0x0013 +#define IMAGE_REL_SHM_REFLO 0x0014 +#define IMAGE_REL_SHM_REFHALF 0x0015 +#define IMAGE_REL_SHM_RELLO 0x0016 +#define IMAGE_REL_SHM_RELHALF 0x0017 +#define IMAGE_REL_SHM_PAIR 0x0018 +#define IMAGE_REL_SHM_NOMODE 0x8000 + +/* + * Relocation types IBM PowerPC processors + */ +#define IMAGE_REL_PPC_ABSOLUTE 0x0000 +#define IMAGE_REL_PPC_ADDR64 0x0001 +#define IMAGE_REL_PPC_ADDR32 0x0002 +#define IMAGE_REL_PPC_ADDR24 0x0003 +#define IMAGE_REL_PPC_ADDR16 0x0004 +#define IMAGE_REL_PPC_ADDR14 0x0005 +#define IMAGE_REL_PPC_REL24 0x0006 +#define IMAGE_REL_PPC_REL14 0x0007 +#define IMAGE_REL_PPC_ADDR32NB 0x000a +#define IMAGE_REL_PPC_SECREL 0x000b +#define IMAGE_REL_PPC_SECTION 0x000c +#define IMAGE_REL_PPC_SECREL16 0x000f +#define IMAGE_REL_PPC_REFHI 0x0010 +#define IMAGE_REL_PPC_REFLO 0x0011 +#define IMAGE_REL_PPC_PAIR 0x0012 +#define IMAGE_REL_PPC_SECRELLO 0x0013 +#define IMAGE_REL_PPC_GPREL 0x0015 +#define IMAGE_REL_PPC_TOKEN 0x0016 + +/* + * Relocation types Intel Itanium processor family (IPF) + */ +#define IMAGE_REL_IA64_ABSOLUTE 0x0000 +#define IMAGE_REL_IA64_IMM14 0x0001 +#define IMAGE_REL_IA64_IMM22 0x0002 +#define IMAGE_REL_IA64_IMM64 0x0003 +#define IMAGE_REL_IA64_DIR32 0x0004 +#define IMAGE_REL_IA64_DIR64 0x0005 +#define IMAGE_REL_IA64_PCREL21B 0x0006 +#define IMAGE_REL_IA64_PCREL21M 0x0007 +#define IMAGE_REL_IA64_PCREL21F 0x0008 +#define IMAGE_REL_IA64_GPREL22 0x0009 +#define IMAGE_REL_IA64_LTOFF22 0x000a +#define IMAGE_REL_IA64_SECTION 0x000b +#define IMAGE_REL_IA64_SECREL22 0x000c +#define IMAGE_REL_IA64_SECREL64I 0x000d +#define IMAGE_REL_IA64_SECREL32 0x000e +#define IMAGE_REL_IA64_DIR32NB 0x0010 +#define IMAGE_REL_IA64_SREL14 0x0011 +#define IMAGE_REL_IA64_SREL22 0x0012 +#define IMAGE_REL_IA64_SREL32 0x0013 +#define IMAGE_REL_IA64_UREL32 0x0014 +#define IMAGE_REL_IA64_PCREL60X 0x0015 +#define IMAGE_REL_IA64_PCREL 60B 0x0016 +#define IMAGE_REL_IA64_PCREL60F 0x0017 +#define IMAGE_REL_IA64_PCREL60I 0x0018 +#define IMAGE_REL_IA64_PCREL60M 0x0019 +#define IMAGE_REL_IA64_IMMGPREL64 0x001a +#define IMAGE_REL_IA64_TOKEN 0x001b +#define IMAGE_REL_IA64_GPREL32 0x001c +#define IMAGE_REL_IA64_ADDEND 0x001f + +/* + * Relocation types MIPS Processors + */ +#define IMAGE_REL_MIPS_ABSOLUTE 0x0000 +#define IMAGE_REL_MIPS_REFHALF 0x0001 +#define IMAGE_REL_MIPS_REFWORD 0x0002 +#define IMAGE_REL_MIPS_JMPADDR 0x0003 +#define IMAGE_REL_MIPS_REFHI 0x0004 +#define IMAGE_REL_MIPS_REFLO 0x0005 +#define IMAGE_REL_MIPS_GPREL 0x0006 +#define IMAGE_REL_MIPS_LITERAL 0x0007 +#define IMAGE_REL_MIPS_SECTION 0x000a +#define IMAGE_REL_MIPS_SECREL 0x000b +#define IMAGE_REL_MIPS_SECRELLO 0x000c +#define IMAGE_REL_MIPS_SECRELHI 0x000d +#define IMAGE_REL_MIPS_JMPADDR16 0x0010 +#define IMAGE_REL_MIPS_REFWORDNB 0x0022 +#define IMAGE_REL_MIPS_PAIR 0x0025 + +/* + * Relocation types Mitsubishi M32R + */ +#define IMAGE_REL_M32R_ABSOLUTE 0x0000 +#define IMAGE_REL_M32R_ADDR32 0x0001 +#define IMAGE_REL_M32R_ADDR32NB 0x0002 +#define IMAGE_REL_M32R_ADDR24 0x0003 +#define IMAGE_REL_M32R_GPREL16 0x0004 +#define IMAGE_REL_M32R_PCREL24 0x0005 +#define IMAGE_REL_M32R_PCREL16 0x0006 +#define IMAGE_REL_M32R_PCREL8 0x0007 +#define IMAGE_REL_M32R_REFHALF 0x0008 +#define IMAGE_REL_M32R_REFHI 0x0009 +#define IMAGE_REL_M32R_REFLO 0x000a +#define IMAGE_REL_M32R_PAIR 0x000b +#define IMAGE_REL_M32R_SECTION 0x000c +#define IMAGE_REL_M32R_SECREL 0x000d +#define IMAGE_REL_M32R_TOKEN 0x000e + +/* + * Section number values + */ +#define IMAGE_SYM_UNDEFINED 0 +#define IMAGE_SYM_ABSOLUTE -1 +#define IMAGE_SYM_DEBUG -2 + +/* + * Type representation + */ +#define IMAGE_SYM_TYPE_NULL 0 +#define IMAGE_SYM_TYPE_VOID 1 +#define IMAGE_SYM_TYPE_CHAR 2 +#define IMAGE_SYM_TYPE_SHORT 3 +#define IMAGE_SYM_TYPE_INT 4 +#define IMAGE_SYM_TYPE_LONG 5 +#define IMAGE_SYM_TYPE_FLOAT 6 +#define IMAGE_SYM_TYPE_DOUBLE 7 +#define IMAGE_SYM_TYPE_STRUCT 8 +#define IMAGE_SYM_TYPE_UNION 9 +#define IMAGE_SYM_TYPE_ENUM 10 +#define IMAGE_SYM_TYPE_MOE 11 +#define IMAGE_SYM_TYPE_BYTE 12 +#define IMAGE_SYM_TYPE_WORD 13 +#define IMAGE_SYM_TYPE_UINT 14 +#define IMAGE_SYM_TYPE_DWORD 15 + +#define IMAGE_SYM_DTYPE_NULL 0 +#define IMAGE_SYM_DTYPE_POINTER 1 +#define IMAGE_SYM_DTYPE_FUNCTION 2 +#define IMAGE_SYM_DTYPE_ARRAY 3 + +/* + * Storage class + */ +#define IMAGE_SYM_CLASS_END_OF_FUNCTION -1 +#define IMAGE_SYM_CLASS_NULL 0 +#define IMAGE_SYM_CLASS_AUTOMATIC 1 +#define IMAGE_SYM_CLASS_EXTERNAL 2 +#define IMAGE_SYM_CLASS_STATIC 3 +#define IMAGE_SYM_CLAS S_REGISTER 4 +#define IMAGE_SYM_CLASS_EXTERNAL_DEF 5 +#define IMAGE_SYM_CLASS_LABEL 6 +#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 +#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 +#define IMAGE_SYM_CLASS_ARGUMENT 9 +#define IMAGE_SYM_CLASS_STRUCT_TAG 10 +#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 +#define IMAGE_SYM_CLASS_UNION_TAG 12 +#define IMAGE_SYM_CLASS_TYPE_DEFINITION 13 +#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 +#define IMAGE_SYM_CLASS_ENUM_TAG 15 +#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 +#define IMAGE_SYM_CLASS_REGISTER_PARAM 17 +#define IMAGE_SYM_CLASS_BIT_FIELD 18 +#define IMAGE_SYM_CLASS_BLOCK 100 +#define IMAGE_SYM_CLASS_FUNCTION 101 +#define IMAGE_SYM_CLASS_END_OF_STRUCT 102 +#define IMAGE_SYM_CLASS_FILE 103 +#define IMAGE_SYM_CLASS_SECTION 104 +#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 +#define IMAGE_SYM_CLASS_CLR_TOKEN 107 + +/* + * COMDAT sections + */ +#define IMAGE_COMDAT_SELECT_NODUPLICATES 1 +#define IMAGE_COMDAT_SELECT_ANY 2 +#define IMAGE_COMDAT_SELECT_SAME_SIZE 3 +#define IMAGE_COMDAT_SELECT_EXACT_MATCH 4 +#define IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 +#define IMAGE_COMDAT_SELECT_LARGEST 6 + +/* + * Attribute certificate table + */ +#define WIN_CERT_REVISION_1_0 0x0100 +#define WIN_CERT_REVISION_2_0 0x0200 +#define WIN_CERT_TYPE_X509 0x0001 +#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 +#define WIN_CERT_TYPE_RESERVED_1 0x0003 +#define WIN_CERT_TYPE_TS_STACK_SIGNED 0x0004 + +/* + * Debug type + */ +#define IMAGE_DEBUG_TYPE_UNKNOWN 0 +#define IMAGE_DEBUG_TYPE_COFF 1 +#define IMAGE_DEBUG_TYPE_CODEVIEW 2 +#define IMAGE_DEBUG_TYPE_FPO 3 +#define IMAGE_DEBUG_TYPE_MISC 4 +#define IMAGE_DEBUG_TYPE_EXCEPTION 5 +#define IMAGE_DEBUG_TYPE_FIXUP 6 +#define IMAGE_DEBUG_TYPE_OMAP_TO_SRC 7 +#define IMAGE_DEBUG_TYPE_OMAP_FROM_SRC 8 +#define IMAGE_DEBUG_TYP E_BORLAND 9 +#define IMAGE_DEBUG_TYPE_RESERVED10 10 +#define IMAGE_DEBUG_TYPE_CLSID 11 + +/* + * Base relocation types + */ +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGH 1 +#define IMAGE_REL_BASED_LOW 2 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_HIGHADJ 4 +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 +#define IMAGE_REL_BASED_DIR64 10 + +/* + * TLS callback functions + */ +#define DLL_PROCESS_ATTACH 1 +#define DLL_THREAD_ATTACH 2 +#define DLL_THREAD_DETACH 3 +#define DLL_PROCESS_DETACH 0 + +/* + * Import Type + */ +#define IMPORT_CODE 0 +#define IMPORT_DATA 1 +#define IMPORT_CONST 2 + +/* + * Import name type + */ +#define IMPORT_ORDINAL 0 +#define IMPORT_NAME 1 +#define IMPORT_NAME_NOPREFIX 2 +#define IMPORT_NAME_UNDECORATE 3 + +struct coff_Section { + struct SAA *data; + uint32_t len; + int nrelocs; + int32_t index; + struct coff_Reloc *head, **tail; + uint32_t flags; /* section flags */ + uint32_t align_flags; /* user-specified alignment flags */ + uint32_t sectalign_flags; /* minimum alignment from sectalign */ + char *name; + int32_t namepos; /* Offset of name into the strings table */ + int32_t pos, relpos; + int64_t pass_last_seen; + + /* comdat-related members */ + char *comdat_name; + uint32_t checksum; /* set only for comdat sections */ + int8_t comdat_selection; + int8_t comdat_symbol; /* is the "comdat name" in symbol table? */ + int32_t comdat_associated; /* associated section for selection==5 */ +}; + +struct coff_Reloc { + struct coff_Reloc *next; + int32_t address; /* relative to _start_ of section */ + int32_t symbol; /* symbol number */ + enum { + SECT_SYMBOLS, + ABS_SYMBOL, + REAL_SYMBOLS + } symbase; /* relocation for symbol number :) */ + int16_t type; +}; + +struct coff_Symbol { + char name[9]; + int32_t strpos; /* string table position of name */ + int32_t value; /* address, or COMMON variable size */ + int section; /* section number where it's defined + * - in COFF codes, not NASM codes */ + bool is_global; /* is it a global symbol or not? */ + int16_t type; /* 0 - notype, 0x20 - function */ + int32_t namlen; /* full name length */ +}; + +struct coff_DebugInfo { + int32_t segto; + int32_t seg; + uint64_t size; + struct coff_Section *section; +}; + +extern struct coff_Section **coff_sects; +extern int coff_nsects; +extern struct SAA *coff_syms; +extern uint32_t coff_nsyms; +extern struct SAA *coff_strs; +extern bool win32, win64; + +extern char coff_infile[FILENAME_MAX]; +extern char coff_outfile[FILENAME_MAX]; + +extern int coff_make_section(char *name, uint32_t flags); + + +#endif /* PECOFF_H */ diff --git a/vere/ext/nasm/output/stabs.h b/vere/ext/nasm/output/stabs.h new file mode 100644 index 0000000..dbc4986 --- /dev/null +++ b/vere/ext/nasm/output/stabs.h @@ -0,0 +1,150 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2018 The NASM Authors - All Rights Reserved + * See the file AUTHORS included with the NASM distribution for + * the specific copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ----------------------------------------------------------------------- */ + +#ifndef STABS_H_ +#define STABS_H_ + +#include "nctype.h" + +#include "compiler.h" +#include "nasmlib.h" +#include "nasm.h" + +/* offsets */ +enum stab_offsets { + STAB_strdxoff = 0, + STAB_typeoff = 4, + STAB_otheroff = 5, + STAB_descoff = 6, + STAB_valoff = 8, + STAB_stabsize = 12 +}; + +/* stab/non-stab types */ +enum stab_types { + N_UNDF = 0x00, /* Undefined symbol */ + N_EXT = 0x01, /* External symbol */ + N_ABS = 0x02, /* Absolute symbol */ + N_ABS_EXT = 0x03, /* Absolute external symbol */ + N_TEXT = 0x04, /* Symbol in text segment */ + N_TEXT_EXT = 0x05, /* Symbol in external text segment */ + N_DATA = 0x06, + N_DATA_EXT = 0x07, + N_BSS = 0x08, + N_BSS_EXT = 0x09, + N_INDR = 0x0a, + N_FN_SEQ = 0x0c, /* N_FN from Sequent compilers */ + N_WEAKU = 0x0d, /* Weak undefined symbol */ + N_WEAKA = 0x0e, /* Weak absolute symbl */ + N_WEAKT = 0x0f, /* Weak text symbol */ + N_WEAKD = 0x10, /* Weak data symbol */ + N_WEAKB = 0x11, /* Weak bss symbol */ + N_COMM = 0x12, /* Common symbol */ + N_SETA = 0x14, /* Absolute set element symbol */ + N_SETA_EXT = 0x15, + N_SETT = 0x16, /* Text set element symbol */ + N_SETT_EXT = 0x17, + N_SETD = 0x18, /* Data set element symbol */ + N_SETD_EXT = 0x19, + N_SETB = 0x1a, /* BSS set element symbol */ + N_SETB_EXT = 0x1b, + N_SETV = 0x1c, /* Pointer to set vector in data area */ + N_SETV_EXT = 0x1d, + N_WARNING = 0x1e, /* Warning symbol */ + N_FN = 0x1f, /* Filename of .o file */ + N_GSYM = 0x20, /* Global variable */ + N_FNAME = 0x22, /* Function name for BSD Fortran */ + N_FUN = 0x24, /* Function name or text segment variable for C */ + N_STSYM = 0x26, /* Data-segment variable with internal linkage */ + N_LCSYM = 0x28, /* BSS-segment variable with internal linkage */ + N_MAIN = 0x2a, /* Name of main routine */ + N_ROSYM = 0x2c, /* Read-only data symbols */ + N_BNSYM = 0x2e, /* The beginning of a relocatable function block */ + N_PC = 0x30, /* Global symbol in Pascal */ + N_NSYMS = 0x32, /* Number of symbols */ + N_NOMAP = 0x34, /* No DST map for sym */ + N_OBJ = 0x38, /* Like N_SO, but for the object file */ + N_OPT = 0x3c, /* Options for the debugger */ + N_RSYM = 0x40, /* Register variable */ + N_M2C = 0x42, /* Modula-2 compilation unit */ + N_SLINE = 0x44, /* Line number in text segment */ + N_DSLINE = 0x46, /* Line number in data segment */ + N_BSLINE = 0x48, /* Line number in bss segment */ + N_BROWS = 0x48, /* Sun's source-code browser stabs */ + N_DEFD = 0x4a, /* GNU Modula-2 definition module dependency */ + N_FLINE = 0x4c, /* Function start/body/end line numbers */ + N_ENSYM = 0x4e, /* This tells the end of a relocatable function */ + N_EHDECL = 0x50, /* GNU C++ exception variable */ + N_MOD2 = 0x50, /* Modula2 info "for imc" */ + N_CATCH = 0x54, /* GNU C++ `catch' clause */ + N_SSYM = 0x60, /* Structure or union element */ + N_ENDM = 0x62, /* Last stab emitted for module */ + N_SO = 0x64, /* ID for main source file */ + N_OSO = 0x66, /* Apple: This is the stab that associated the .o file */ + N_ALIAS = 0x6c, /* SunPro F77: Name of alias */ + N_LSYM = 0x80, /* Automatic variable in the stack */ + N_BINCL = 0x82, /* Beginning of an include file */ + N_SOL = 0x84, /* ID for sub-source file */ + N_PSYM = 0xa0, /* Parameter variable */ + N_EINCL = 0xa2, /* End of an include file */ + N_ENTRY = 0xa4, /* Alternate entry point */ + N_LBRAC = 0xc0, /* Beginning of lexical block */ + N_EXCL = 0xc2, /* Place holder for deleted include file */ + N_SCOPE = 0xc4, /* Modula-2 scope information */ + N_PATCH = 0xd0, /* Solaris2: Patch Run Time Checker */ + N_RBRAC = 0xe0, /* End of a lexical block */ + N_BCOMM = 0xe2, /* Begin named common block */ + N_ECOMM = 0xe4, /* End named common block */ + N_ECOML = 0xe8, /* Member of a common block */ + N_WITH = 0xea, /* Solaris2: Pascal "with" statement */ + N_NBTEXT = 0xf0, + N_NBDATA = 0xf2, + N_NBBSS = 0xf4, + N_NBSTS = 0xf6, + N_NBLCS = 0xf8, + N_LENG = 0xfe /* Second symbol entry whih a length-value for the preceding entry */ +}; + +enum stab_source_file { + N_SO_AS = 0x01, + N_SO_C = 0x02, + N_SO_ANSI_C = 0x03, + N_SO_CC = 0x04, + N_SO_FORTRAN = 0x05, + N_SO_PASCAL = 0x06, + N_SO_FORTRAN90 = 0x07, + N_SO_OBJC = 0x32, + N_SO_OBJCPLUS = 0x33 +}; + +#endif /* STABS_H_ */ |