diff options
author | somov <somov@yandex-team.ru> | 2022-02-10 16:45:47 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:47 +0300 |
commit | a5950576e397b1909261050b8c7da16db58f10b1 (patch) | |
tree | 7ba7677f6a4c3e19e2cefab34d16df2c8963b4d4 /contrib/tools/yasm/modules/objfmts | |
parent | 81eddc8c0b55990194e112b02d127b87d54164a9 (diff) | |
download | ydb-a5950576e397b1909261050b8c7da16db58f10b1.tar.gz |
Restoring authorship annotation for <somov@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/tools/yasm/modules/objfmts')
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/bin/bin-objfmt.c | 3944 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c | 5032 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h | 154 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/coff/win64-except.c | 1118 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/dbg/dbg-objfmt.c | 366 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/elf/elf-machine.h | 216 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/elf/elf-objfmt.c | 2782 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/elf/elf-x86-amd64.c | 512 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/elf/elf-x86-x32.c | 502 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/elf/elf-x86-x86.c | 484 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/elf/elf.c | 1920 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/elf/elf.h | 1064 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/macho/macho-objfmt.c | 3240 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/rdf/rdf-objfmt.c | 2176 | ||||
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/xdf/xdf-objfmt.c | 1684 |
15 files changed, 12597 insertions, 12597 deletions
diff --git a/contrib/tools/yasm/modules/objfmts/bin/bin-objfmt.c b/contrib/tools/yasm/modules/objfmts/bin/bin-objfmt.c index 5f422245d3..702bda1590 100644 --- a/contrib/tools/yasm/modules/objfmts/bin/bin-objfmt.c +++ b/contrib/tools/yasm/modules/objfmts/bin/bin-objfmt.c @@ -1,1972 +1,1972 @@ -/* - * Flat-format binary object format - * - * Copyright (C) 2002-2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include <util.h> - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#include <libyasm.h> - - -#define REGULAR_OUTBUF_SIZE 1024 - -typedef struct bin_section_data { - int bss; /* aka nobits */ - - /* User-provided alignment */ - yasm_intnum *align, *valign; - - /* User-provided starts */ - /*@null@*/ /*@owned@*/ yasm_expr *start, *vstart; - - /* User-provided follows */ - /*@null@*/ /*@owned@*/ char *follows, *vfollows; - - /* Calculated (final) starts, used only during output() */ - /*@null@*/ /*@owned@*/ yasm_intnum *istart, *ivstart; - - /* Calculated (final) length, used only during output() */ - /*@null@*/ /*@owned@*/ yasm_intnum *length; -} bin_section_data; - -typedef struct yasm_objfmt_bin { - yasm_objfmt_base objfmt; /* base structure */ - - enum { - NO_MAP = 0, - MAP_NONE = 0x01, - MAP_BRIEF = 0x02, - MAP_SECTIONS = 0x04, - MAP_SYMBOLS = 0x08 - } map_flags; - /*@null@*/ /*@only@*/ char *map_filename; - - /*@null@*/ /*@only@*/ yasm_expr *org; -} yasm_objfmt_bin; - -/* symrec data is used only for the special symbols section<sectname>.start, - * section<sectname>.vstart, and section<sectname>.length - */ -typedef struct bin_symrec_data { - yasm_section *section; /* referenced section */ - enum bin_ssym { - SSYM_START, - SSYM_VSTART, - SSYM_LENGTH - } which; -} bin_symrec_data; - -static void bin_section_data_destroy(/*@only@*/ void *d); -static void bin_section_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback bin_section_data_cb = { - bin_section_data_destroy, - bin_section_data_print -}; - -static void bin_symrec_data_destroy(/*@only@*/ void *d); -static void bin_symrec_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback bin_symrec_data_cb = { - bin_symrec_data_destroy, - bin_symrec_data_print -}; - -yasm_objfmt_module yasm_bin_LTX_objfmt; - - -static yasm_objfmt * -bin_objfmt_create(yasm_object *object) -{ - yasm_objfmt_bin *objfmt_bin = yasm_xmalloc(sizeof(yasm_objfmt_bin)); - objfmt_bin->objfmt.module = &yasm_bin_LTX_objfmt; - - objfmt_bin->map_flags = NO_MAP; - objfmt_bin->map_filename = NULL; - objfmt_bin->org = NULL; - - return (yasm_objfmt *)objfmt_bin; -} - -typedef TAILQ_HEAD(bin_group_head, bin_group) bin_groups; - -typedef struct bin_group { - TAILQ_ENTRY(bin_group) link; - yasm_section *section; - bin_section_data *bsd; - - /* Groups that (in parallel) logically come immediately after this - * group's section. - */ - bin_groups follow_groups; -} bin_group; - -/* Recursive function to find group containing named section. */ -static bin_group * -find_group_by_name(bin_groups *groups, const char *name) -{ - bin_group *group, *found; - TAILQ_FOREACH(group, groups, link) { - if (strcmp(yasm_section_get_name(group->section), name) == 0) - return group; - /* Recurse to loop through follow groups */ - found = find_group_by_name(&group->follow_groups, name); - if (found) - return found; - } - return NULL; -} - -/* Recursive function to find group. Returns NULL if not found. */ -static bin_group * -find_group_by_section(bin_groups *groups, yasm_section *section) -{ - bin_group *group, *found; - TAILQ_FOREACH(group, groups, link) { - if (group->section == section) - return group; - /* Recurse to loop through follow groups */ - found = find_group_by_section(&group->follow_groups, section); - if (found) - return found; - } - return NULL; -} - -#if 0 -/* Debugging function */ -static void -print_groups(const bin_groups *groups, int indent_level) -{ - bin_group *group; - TAILQ_FOREACH(group, groups, link) { - printf("%*sSection `%s':\n", indent_level, "", - yasm_section_get_name(group->section)); - bin_section_data_print(group->bsd, stdout, indent_level+1); - if (!TAILQ_EMPTY(&group->follow_groups)) { - printf("%*sFollowing groups:\n", indent_level, ""); - print_groups(&group->follow_groups, indent_level+1); - } - } -} -#endif - -static void -bin_group_destroy(/*@only@*/ bin_group *group) -{ - bin_group *follow, *group_temp; - TAILQ_FOREACH_SAFE(follow, &group->follow_groups, link, group_temp) - bin_group_destroy(follow); - yasm_xfree(group); -} - -typedef struct bin_objfmt_output_info { - yasm_object *object; - yasm_errwarns *errwarns; - /*@dependent@*/ FILE *f; - /*@only@*/ unsigned char *buf; - /*@observer@*/ const yasm_section *sect; - unsigned long start; /* what normal variables go against */ - - yasm_intnum *origin; - yasm_intnum *tmp_intn; /* temporary working intnum */ - - bin_groups lma_groups, vma_groups; -} bin_objfmt_output_info; - -static int -bin_objfmt_check_sym(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - assert(info != NULL); - - /* Don't check internally-generated symbols. Only internally generated - * symbols have symrec data, so simply check for its presence. - */ - if (yasm_symrec_get_data(sym, &bin_symrec_data_cb)) - return 0; - - if (vis & YASM_SYM_EXTERN) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("binary object format does not support extern variables")); - yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); - } else if (vis & YASM_SYM_GLOBAL) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("binary object format does not support global variables")); - yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); - } else if (vis & YASM_SYM_COMMON) { - yasm_error_set(YASM_ERROR_TYPE, - N_("binary object format does not support common variables")); - yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); - } - return 0; -} - -static int -bin_lma_create_group(yasm_section *sect, /*@null@*/ void *d) -{ - bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; - bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb); - unsigned long align = yasm_section_get_align(sect); - bin_group *group; - - assert(info != NULL); - assert(bsd != NULL); - - group = yasm_xmalloc(sizeof(bin_group)); - group->section = sect; - group->bsd = bsd; - TAILQ_INIT(&group->follow_groups); - - /* Determine section alignment as necessary. */ - if (!bsd->align) - bsd->align = yasm_intnum_create_uint(align > 4 ? align : 4); - else { - yasm_intnum *align_intn = yasm_intnum_create_uint(align); - if (yasm_intnum_compare(align_intn, bsd->align) > 0) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("section `%s' internal align of %lu is greater than `%s' of %lu; using `%s'"), - yasm_section_get_name(sect), - yasm_intnum_get_uint(align_intn), - N_("align"), - yasm_intnum_get_uint(bsd->align), - N_("align")); - yasm_errwarn_propagate(info->errwarns, 0); - } - yasm_intnum_destroy(align_intn); - } - - /* Calculate section integer start. */ - if (bsd->start) { - bsd->istart = yasm_expr_get_intnum(&bsd->start, 0); - if (!bsd->istart) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("start expression is too complex")); - yasm_errwarn_propagate(info->errwarns, bsd->start->line); - return 1; - } else - bsd->istart = yasm_intnum_copy(bsd->istart); - } else - bsd->istart = NULL; - - /* Calculate section integer vstart. */ - if (bsd->vstart) { - bsd->ivstart = yasm_expr_get_intnum(&bsd->vstart, 0); - if (!bsd->ivstart) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("vstart expression is too complex")); - yasm_errwarn_propagate(info->errwarns, bsd->vstart->line); - return 1; - } else - bsd->ivstart = yasm_intnum_copy(bsd->ivstart); - } else - bsd->ivstart = NULL; - - /* Calculate section integer length. */ - bsd->length = yasm_calc_bc_dist(yasm_section_bcs_first(sect), - yasm_section_bcs_last(sect)); - - TAILQ_INSERT_TAIL(&info->lma_groups, group, link); - return 0; -} - -static int -bin_vma_create_group(yasm_section *sect, /*@null@*/ void *d) -{ - bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; - bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb); - bin_group *group; - - assert(info != NULL); - assert(bsd != NULL); - - group = yasm_xmalloc(sizeof(bin_group)); - group->section = sect; - group->bsd = bsd; - TAILQ_INIT(&group->follow_groups); - - TAILQ_INSERT_TAIL(&info->vma_groups, group, link); - return 0; -} - -/* Calculates new start address based on alignment constraint. - * Start is modified (rounded up) to the closest aligned value greater than - * what was passed in. - * Align must be a power of 2. - */ -static void -bin_objfmt_align(yasm_intnum *start, const yasm_intnum *align) -{ - /* Because alignment is always a power of two, we can use some bit - * trickery to do this easily. - */ - yasm_intnum *align_intn = - yasm_intnum_create_uint(yasm_intnum_get_uint(align)-1); - yasm_intnum_calc(align_intn, YASM_EXPR_AND, start); - if (!yasm_intnum_is_zero(align_intn)) { - /* start = (start & ~(align-1)) + align; */ - yasm_intnum_set_uint(align_intn, yasm_intnum_get_uint(align)-1); - yasm_intnum_calc(align_intn, YASM_EXPR_NOT, NULL); - yasm_intnum_calc(align_intn, YASM_EXPR_AND, start); - yasm_intnum_set(start, align); - yasm_intnum_calc(start, YASM_EXPR_ADD, align_intn); - } - yasm_intnum_destroy(align_intn); -} - -/* Recursive function to assign start addresses. - * Updates start, last, and vdelta parameters as it goes along. - * The tmp parameter is just a working intnum so one doesn't have to be - * locally allocated for this purpose. - */ -static void -group_assign_start_recurse(bin_group *group, yasm_intnum *start, - yasm_intnum *last, yasm_intnum *vdelta, - yasm_intnum *tmp, yasm_errwarns *errwarns) -{ - bin_group *follow_group; - - /* Determine LMA */ - if (group->bsd->istart) { - yasm_intnum_set(group->bsd->istart, start); - if (group->bsd->align) { - bin_objfmt_align(group->bsd->istart, group->bsd->align); - if (yasm_intnum_compare(start, group->bsd->istart) != 0) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("start inconsistent with align; using aligned value")); - yasm_errwarn_propagate(errwarns, group->bsd->start->line); - } - } - } else { - group->bsd->istart = yasm_intnum_copy(start); - if (group->bsd->align != 0) - bin_objfmt_align(group->bsd->istart, group->bsd->align); - } - - /* Determine VMA if either just valign specified or if no v* specified */ - if (!group->bsd->vstart) { - if (!group->bsd->vfollows && !group->bsd->valign) { - /* No v* specified, set VMA=LMA+vdelta. */ - group->bsd->ivstart = yasm_intnum_copy(group->bsd->istart); - yasm_intnum_calc(group->bsd->ivstart, YASM_EXPR_ADD, vdelta); - } else if (!group->bsd->vfollows) { - /* Just valign specified: set VMA=LMA+vdelta, align VMA, then add - * delta between unaligned and aligned to vdelta parameter. - */ - group->bsd->ivstart = yasm_intnum_copy(group->bsd->istart); - yasm_intnum_calc(group->bsd->ivstart, YASM_EXPR_ADD, vdelta); - yasm_intnum_set(tmp, group->bsd->ivstart); - bin_objfmt_align(group->bsd->ivstart, group->bsd->valign); - yasm_intnum_calc(vdelta, YASM_EXPR_ADD, group->bsd->ivstart); - yasm_intnum_calc(vdelta, YASM_EXPR_SUB, tmp); - } - } - - /* Find the maximum end value */ - yasm_intnum_set(tmp, group->bsd->istart); - yasm_intnum_calc(tmp, YASM_EXPR_ADD, group->bsd->length); - if (yasm_intnum_compare(tmp, last) > 0) /* tmp > last */ - yasm_intnum_set(last, tmp); - - /* Recurse for each following group. */ - TAILQ_FOREACH(follow_group, &group->follow_groups, link) { - /* Following sections have to follow this one, - * so add length to start. - */ - yasm_intnum_set(start, group->bsd->istart); - yasm_intnum_calc(start, YASM_EXPR_ADD, group->bsd->length); - - group_assign_start_recurse(follow_group, start, last, vdelta, tmp, - errwarns); - } -} - -/* Recursive function to assign start addresses. - * Updates start parameter as it goes along. - * The tmp parameter is just a working intnum so one doesn't have to be - * locally allocated for this purpose. - */ -static void -group_assign_vstart_recurse(bin_group *group, yasm_intnum *start, - yasm_errwarns *errwarns) -{ - bin_group *follow_group; - - /* Determine VMA section alignment as necessary. - * Default to LMA alignment if not specified. - */ - if (!group->bsd->valign) - group->bsd->valign = yasm_intnum_copy(group->bsd->align); - else { - unsigned long align = yasm_section_get_align(group->section); - yasm_intnum *align_intn = yasm_intnum_create_uint(align); - if (yasm_intnum_compare(align_intn, group->bsd->valign) > 0) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("section `%s' internal align of %lu is greater than `%s' of %lu; using `%s'"), - yasm_section_get_name(group->section), - yasm_intnum_get_uint(align_intn), - N_("valign"), - yasm_intnum_get_uint(group->bsd->valign), - N_("valign")); - yasm_errwarn_propagate(errwarns, 0); - } - yasm_intnum_destroy(align_intn); - } - - /* Determine VMA */ - if (group->bsd->ivstart) { - yasm_intnum_set(group->bsd->ivstart, start); - if (group->bsd->valign) { - bin_objfmt_align(group->bsd->ivstart, group->bsd->valign); - if (yasm_intnum_compare(start, group->bsd->ivstart) != 0) { - yasm_error_set(YASM_ERROR_VALUE, - N_("vstart inconsistent with valign")); - yasm_errwarn_propagate(errwarns, group->bsd->vstart->line); - } - } - } else { - group->bsd->ivstart = yasm_intnum_copy(start); - if (group->bsd->valign) - bin_objfmt_align(group->bsd->ivstart, group->bsd->valign); - } - - /* Recurse for each following group. */ - TAILQ_FOREACH(follow_group, &group->follow_groups, link) { - /* Following sections have to follow this one, - * so add length to start. - */ - yasm_intnum_set(start, group->bsd->ivstart); - yasm_intnum_calc(start, YASM_EXPR_ADD, group->bsd->length); - - group_assign_vstart_recurse(follow_group, start, errwarns); - } -} - -static /*@null@*/ const yasm_intnum * -get_ssym_value(yasm_symrec *sym) -{ - bin_symrec_data *bsymd = yasm_symrec_get_data(sym, &bin_symrec_data_cb); - bin_section_data *bsd; - - if (!bsymd) - return NULL; - - bsd = yasm_section_get_data(bsymd->section, &bin_section_data_cb); - assert(bsd != NULL); - - switch (bsymd->which) { - case SSYM_START: return bsd->istart; - case SSYM_VSTART: return bsd->ivstart; - case SSYM_LENGTH: return bsd->length; - } - return NULL; -} - -static /*@only@*/ yasm_expr * -bin_objfmt_expr_xform(/*@returned@*/ /*@only@*/ yasm_expr *e, - /*@unused@*/ /*@null@*/ void *d) -{ - int i; - for (i=0; i<e->numterms; i++) { - /*@dependent@*/ yasm_section *sect; - /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; - /*@null@*/ yasm_intnum *dist; - /*@null@*/ const yasm_intnum *ssymval; - - /* Transform symrecs or precbcs that reference sections into - * vstart + intnum(dist). - */ - if (((e->terms[i].type == YASM_EXPR_SYM && - yasm_symrec_get_label(e->terms[i].data.sym, &precbc)) || - (e->terms[i].type == YASM_EXPR_PRECBC && - (precbc = e->terms[i].data.precbc))) && - (sect = yasm_bc_get_section(precbc)) && - (dist = yasm_calc_bc_dist(yasm_section_bcs_first(sect), precbc))) { - bin_section_data *bsd; - bsd = yasm_section_get_data(sect, &bin_section_data_cb); - assert(bsd != NULL); - yasm_intnum_calc(dist, YASM_EXPR_ADD, bsd->ivstart); - e->terms[i].type = YASM_EXPR_INT; - e->terms[i].data.intn = dist; - } - - /* Transform our special symrecs into the appropriate value */ - if (e->terms[i].type == YASM_EXPR_SYM && - (ssymval = get_ssym_value(e->terms[i].data.sym))) { - e->terms[i].type = YASM_EXPR_INT; - e->terms[i].data.intn = yasm_intnum_copy(ssymval); - } - } - - return e; -} - -typedef struct map_output_info { - /* address width */ - int bytes; - - /* intnum output static data areas */ - unsigned char *buf; - yasm_intnum *intn; - - /* symrec output information */ - unsigned long count; - yasm_section *section; /* NULL for EQUs */ - - yasm_object *object; /* object */ - FILE *f; /* map output file */ -} map_output_info; - -static int -map_prescan_bytes(yasm_section *sect, void *d) -{ - bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb); - map_output_info *info = (map_output_info *)d; - - assert(bsd != NULL); - assert(info != NULL); - - while (!yasm_intnum_check_size(bsd->length, info->bytes * 8, 0, 0)) - info->bytes *= 2; - while (!yasm_intnum_check_size(bsd->istart, info->bytes * 8, 0, 0)) - info->bytes *= 2; - while (!yasm_intnum_check_size(bsd->ivstart, info->bytes * 8, 0, 0)) - info->bytes *= 2; - - return 0; -} - -static void -map_print_intnum(const yasm_intnum *intn, map_output_info *info) -{ - size_t i; - yasm_intnum_get_sized(intn, info->buf, info->bytes, info->bytes*8, 0, 0, - 0); - for (i=info->bytes; i != 0; i--) - fprintf(info->f, "%02X", info->buf[i-1]); -} - -static void -map_sections_summary(bin_groups *groups, map_output_info *info) -{ - bin_group *group; - TAILQ_FOREACH(group, groups, link) { - bin_section_data *bsd = group->bsd; - - assert(bsd != NULL); - assert(info != NULL); - - map_print_intnum(bsd->ivstart, info); - fprintf(info->f, " "); - - yasm_intnum_set(info->intn, bsd->ivstart); - yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->length); - map_print_intnum(info->intn, info); - fprintf(info->f, " "); - - map_print_intnum(bsd->istart, info); - fprintf(info->f, " "); - - yasm_intnum_set(info->intn, bsd->istart); - yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->length); - map_print_intnum(info->intn, info); - fprintf(info->f, " "); - - map_print_intnum(bsd->length, info); - fprintf(info->f, " "); - - fprintf(info->f, "%-*s", 10, bsd->bss ? "nobits" : "progbits"); - fprintf(info->f, "%s\n", yasm_section_get_name(group->section)); - - /* Recurse to loop through follow groups */ - map_sections_summary(&group->follow_groups, info); - } -} - -static void -map_sections_detail(bin_groups *groups, map_output_info *info) -{ - bin_group *group; - TAILQ_FOREACH(group, groups, link) { - bin_section_data *bsd = group->bsd; - size_t i; - const char *s; - - s = yasm_section_get_name(group->section); - fprintf(info->f, "---- Section %s ", s); - for (i=0; i<(65-strlen(s)); i++) - fputc('-', info->f); - - fprintf(info->f, "\n\nclass: %s", - bsd->bss ? "nobits" : "progbits"); - fprintf(info->f, "\nlength: "); - map_print_intnum(bsd->length, info); - fprintf(info->f, "\nstart: "); - map_print_intnum(bsd->istart, info); - fprintf(info->f, "\nalign: "); - map_print_intnum(bsd->align, info); - fprintf(info->f, "\nfollows: %s", - bsd->follows ? bsd->follows : "not defined"); - fprintf(info->f, "\nvstart: "); - map_print_intnum(bsd->ivstart, info); - fprintf(info->f, "\nvalign: "); - map_print_intnum(bsd->valign, info); - fprintf(info->f, "\nvfollows: %s\n\n", - bsd->vfollows ? bsd->vfollows : "not defined"); - - /* Recurse to loop through follow groups */ - map_sections_detail(&group->follow_groups, info); - } -} - -static int -map_symrec_count(yasm_symrec *sym, void *d) -{ - map_output_info *info = (map_output_info *)d; - /*@dependent@*/ yasm_bytecode *precbc; - - assert(info != NULL); - - /* TODO: autodetect wider size */ - if (!info->section && yasm_symrec_get_equ(sym)) { - info->count++; - } else if (yasm_symrec_get_label(sym, &precbc) && - yasm_bc_get_section(precbc) == info->section) { - info->count++; - } - return 0; -} - -static int -map_symrec_output(yasm_symrec *sym, void *d) -{ - map_output_info *info = (map_output_info *)d; - const yasm_expr *equ; - /*@dependent@*/ yasm_bytecode *precbc; - /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); - - assert(info != NULL); - - if (!info->section && (equ = yasm_symrec_get_equ(sym))) { - yasm_expr *realequ = yasm_expr_copy(equ); - realequ = yasm_expr__level_tree - (realequ, 1, 1, 1, 0, bin_objfmt_expr_xform, NULL); - yasm_intnum_set(info->intn, yasm_expr_get_intnum(&realequ, 0)); - yasm_expr_destroy(realequ); - map_print_intnum(info->intn, info); - fprintf(info->f, " %s\n", name); - } else if (yasm_symrec_get_label(sym, &precbc) && - yasm_bc_get_section(precbc) == info->section) { - bin_section_data *bsd = - yasm_section_get_data(info->section, &bin_section_data_cb); - - /* Real address */ - yasm_intnum_set_uint(info->intn, yasm_bc_next_offset(precbc)); - yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->istart); - map_print_intnum(info->intn, info); - fprintf(info->f, " "); - - /* Virtual address */ - yasm_intnum_set_uint(info->intn, yasm_bc_next_offset(precbc)); - yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->ivstart); - map_print_intnum(info->intn, info); - - /* Name */ - fprintf(info->f, " %s\n", name); - } - yasm_xfree(name); - return 0; -} - -static void -map_sections_symbols(bin_groups *groups, map_output_info *info) -{ - bin_group *group; - TAILQ_FOREACH(group, groups, link) { - info->count = 0; - info->section = group->section; - yasm_symtab_traverse(info->object->symtab, info, map_symrec_count); - - if (info->count > 0) { - const char *s = yasm_section_get_name(group->section); - size_t i; - fprintf(info->f, "---- Section %s ", s); - for (i=0; i<(65-strlen(s)); i++) - fputc('-', info->f); - fprintf(info->f, "\n\n%-*s%-*s%s\n", - info->bytes*2+2, "Real", - info->bytes*2+2, "Virtual", - "Name"); - yasm_symtab_traverse(info->object->symtab, info, - map_symrec_output); - fprintf(info->f, "\n\n"); - } - - /* Recurse to loop through follow groups */ - map_sections_symbols(&group->follow_groups, info); - } -} - -static void -output_map(bin_objfmt_output_info *info) -{ - yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)info->object->objfmt; - FILE *f; - int i; - map_output_info mapinfo; - - if (objfmt_bin->map_flags == NO_MAP) - return; - - if (objfmt_bin->map_flags == MAP_NONE) - objfmt_bin->map_flags = MAP_BRIEF; /* default to brief */ - - if (!objfmt_bin->map_filename) - f = stdout; /* default to stdout */ - else { - f = fopen(objfmt_bin->map_filename, "wt"); - if (!f) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("unable to open map file `%s'"), - objfmt_bin->map_filename); - yasm_errwarn_propagate(info->errwarns, 0); - return; - } - } - - mapinfo.object = info->object; - mapinfo.f = f; - - /* Temporary intnum */ - mapinfo.intn = info->tmp_intn; - - /* Prescan all values to figure out what width we should make the output - * fields. Start with a minimum of 4. - */ - mapinfo.bytes = 4; - while (!yasm_intnum_check_size(info->origin, mapinfo.bytes * 8, 0, 0)) - mapinfo.bytes *= 2; - yasm_object_sections_traverse(info->object, &mapinfo, map_prescan_bytes); - mapinfo.buf = yasm_xmalloc(mapinfo.bytes); - - fprintf(f, "\n- YASM Map file "); - for (i=0; i<63; i++) - fputc('-', f); - fprintf(f, "\n\nSource file: %s\n", info->object->src_filename); - fprintf(f, "Output file: %s\n\n", info->object->obj_filename); - - fprintf(f, "-- Program origin "); - for (i=0; i<61; i++) - fputc('-', f); - fprintf(f, "\n\n"); - map_print_intnum(info->origin, &mapinfo); - fprintf(f, "\n\n"); - - if (objfmt_bin->map_flags & MAP_BRIEF) { - fprintf(f, "-- Sections (summary) "); - for (i=0; i<57; i++) - fputc('-', f); - fprintf(f, "\n\n%-*s%-*s%-*s%-*s%-*s%-*s%s\n", - mapinfo.bytes*2+2, "Vstart", - mapinfo.bytes*2+2, "Vstop", - mapinfo.bytes*2+2, "Start", - mapinfo.bytes*2+2, "Stop", - mapinfo.bytes*2+2, "Length", - 10, "Class", "Name"); - - map_sections_summary(&info->lma_groups, &mapinfo); - fprintf(f, "\n"); - } - - if (objfmt_bin->map_flags & MAP_SECTIONS) { - fprintf(f, "-- Sections (detailed) "); - for (i=0; i<56; i++) - fputc('-', f); - fprintf(f, "\n\n"); - map_sections_detail(&info->lma_groups, &mapinfo); - } - - if (objfmt_bin->map_flags & MAP_SYMBOLS) { - fprintf(f, "-- Symbols "); - for (i=0; i<68; i++) - fputc('-', f); - fprintf(f, "\n\n"); - - /* We do two passes for EQU and each section; the first pass - * determines the byte width to use for the value and whether any - * symbols are present, the second pass actually outputs the text. - */ - - /* EQUs */ - mapinfo.count = 0; - mapinfo.section = NULL; - yasm_symtab_traverse(info->object->symtab, &mapinfo, map_symrec_count); - - if (mapinfo.count > 0) { - fprintf(f, "---- No Section "); - for (i=0; i<63; i++) - fputc('-', f); - fprintf(f, "\n\n%-*s%s\n", mapinfo.bytes*2+2, "Value", "Name"); - yasm_symtab_traverse(info->object->symtab, &mapinfo, - map_symrec_output); - fprintf(f, "\n\n"); - } - - /* Other sections */ - map_sections_symbols(&info->lma_groups, &mapinfo); - } - - if (f != stdout) - fclose(f); - - yasm_xfree(mapinfo.buf); -} - -/* Check for LMA overlap using a simple N^2 algorithm. */ -static int -check_lma_overlap(yasm_section *sect, /*@null@*/ void *d) -{ - bin_section_data *bsd, *bsd2; - yasm_section *other = (yasm_section *)d; - yasm_intnum *overlap; - - if (!d) - return yasm_object_sections_traverse(yasm_section_get_object(sect), - sect, check_lma_overlap); - if (sect == other) - return 0; - - bsd = yasm_section_get_data(sect, &bin_section_data_cb); - bsd2 = yasm_section_get_data(other, &bin_section_data_cb); - - if (yasm_intnum_is_zero(bsd->length) || - yasm_intnum_is_zero(bsd2->length)) - return 0; - - if (yasm_intnum_compare(bsd->istart, bsd2->istart) <= 0) { - overlap = yasm_intnum_copy(bsd->istart); - yasm_intnum_calc(overlap, YASM_EXPR_ADD, bsd->length); - yasm_intnum_calc(overlap, YASM_EXPR_SUB, bsd2->istart); - } else { - overlap = yasm_intnum_copy(bsd2->istart); - yasm_intnum_calc(overlap, YASM_EXPR_ADD, bsd2->length); - yasm_intnum_calc(overlap, YASM_EXPR_SUB, bsd->istart); - } - - if (yasm_intnum_sign(overlap) > 0) { - yasm_error_set(YASM_ERROR_GENERAL, - N_("sections `%s' and `%s' overlap by %lu bytes"), - yasm_section_get_name(sect), - yasm_section_get_name(other), - yasm_intnum_get_uint(overlap)); - yasm_intnum_destroy(overlap); - return -1; - } - - yasm_intnum_destroy(overlap); - return 0; -} - -static int -bin_objfmt_output_value(yasm_value *value, unsigned char *buf, - unsigned int destsize, - /*@unused@*/ unsigned long offset, yasm_bytecode *bc, - int warn, /*@null@*/ void *d) -{ - /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; - /*@dependent@*/ yasm_section *sect; - - assert(info != NULL); - - /* Binary objects we need to resolve against object, not against section. */ - if (value->rel) { - unsigned int rshift = (unsigned int)value->rshift; - yasm_expr *syme; - /*@null@*/ const yasm_intnum *ssymval; - - if (yasm_symrec_is_abs(value->rel)) { - syme = yasm_expr_create_ident(yasm_expr_int( - yasm_intnum_create_uint(0)), bc->line); - } else if (yasm_symrec_get_label(value->rel, &precbc) - && (sect = yasm_bc_get_section(precbc))) { - syme = yasm_expr_create_ident(yasm_expr_sym(value->rel), bc->line); - } else if ((ssymval = get_ssym_value(value->rel))) { - syme = yasm_expr_create_ident(yasm_expr_int( - yasm_intnum_copy(ssymval)), bc->line); - } else - goto done; - - /* Handle PC-relative */ - if (value->curpos_rel) { - yasm_expr *sube; - sube = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(bc), - yasm_expr_int(yasm_intnum_create_uint(bc->len*bc->mult_int)), - bc->line); - syme = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(syme), - yasm_expr_expr(sube), bc->line); - value->curpos_rel = 0; - value->ip_rel = 0; - } - - if (value->rshift > 0) - syme = yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr(syme), - yasm_expr_int(yasm_intnum_create_uint(rshift)), bc->line); - - /* Add into absolute portion */ - if (!value->abs) - value->abs = syme; - else - value->abs = - yasm_expr_create(YASM_EXPR_ADD, yasm_expr_expr(value->abs), - yasm_expr_expr(syme), bc->line); - value->rel = NULL; - value->rshift = 0; - } -done: - /* Simplify absolute portion of value, transforming symrecs */ - if (value->abs) - value->abs = yasm_expr__level_tree - (value->abs, 1, 1, 1, 0, bin_objfmt_expr_xform, NULL); - - /* Output */ - switch (yasm_value_output_basic(value, buf, destsize, bc, warn, - info->object->arch)) { - case -1: - return 1; - case 0: - break; - default: - return 0; - } - - /* Couldn't output, assume it contains an external reference. */ - yasm_error_set(YASM_ERROR_GENERAL, - N_("binary object format does not support external references")); - return 1; -} - -static int -bin_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) -{ - /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; - /*@null@*/ /*@only@*/ unsigned char *bigbuf; - unsigned long size = REGULAR_OUTBUF_SIZE; - int gap; - - assert(info != NULL); - - bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, - bin_objfmt_output_value, NULL); - - /* Don't bother doing anything else if size ended up being 0. */ - if (size == 0) { - if (bigbuf) - yasm_xfree(bigbuf); - return 0; - } - - /* Warn that gaps are converted to 0 and write out the 0's. */ - if (gap) { - unsigned long left; - yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, - N_("uninitialized space declared in code/data section: zeroing")); - /* Write out in chunks */ - memset(info->buf, 0, REGULAR_OUTBUF_SIZE); - left = size; - while (left > REGULAR_OUTBUF_SIZE) { - fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); - left -= REGULAR_OUTBUF_SIZE; - } - fwrite(info->buf, left, 1, info->f); - } else { - /* Output buf (or bigbuf if non-NULL) to file */ - fwrite(bigbuf ? bigbuf : info->buf, (size_t)size, 1, info->f); - } - - /* If bigbuf was allocated, free it */ - if (bigbuf) - yasm_xfree(bigbuf); - - return 0; -} - -/* Check to ensure bytecode is res* (for BSS sections) */ -static int -bin_objfmt_no_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) -{ - /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; - /*@null@*/ /*@only@*/ unsigned char *bigbuf; - unsigned long size = REGULAR_OUTBUF_SIZE; - int gap; - - assert(info != NULL); - - bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, - bin_objfmt_output_value, NULL); - - /* If bigbuf was allocated, free it */ - if (bigbuf) - yasm_xfree(bigbuf); - - /* Don't bother doing anything else if size ended up being 0. */ - if (size == 0) - return 0; - - /* Warn if not a gap. */ - if (!gap) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("initialized space declared in nobits section: ignoring")); - } - - return 0; -} - -static int -bin_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) -{ - bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb); - /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; - - assert(bsd != NULL); - assert(info != NULL); - - if (bsd->bss) { - yasm_section_bcs_traverse(sect, info->errwarns, - info, bin_objfmt_no_output_bytecode); - } else { - yasm_intnum_set(info->tmp_intn, bsd->istart); - yasm_intnum_calc(info->tmp_intn, YASM_EXPR_SUB, info->origin); - if (yasm_intnum_sign(info->tmp_intn) < 0) { - yasm_error_set(YASM_ERROR_VALUE, - N_("section `%s' starts before origin (ORG)"), - yasm_section_get_name(sect)); - yasm_errwarn_propagate(info->errwarns, 0); - return 0; - } - if (!yasm_intnum_check_size(info->tmp_intn, sizeof(long)*8, 0, 1)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("section `%s' start value too large"), - yasm_section_get_name(sect)); - yasm_errwarn_propagate(info->errwarns, 0); - return 0; - } - if (fseek(info->f, yasm_intnum_get_int(info->tmp_intn) + info->start, - SEEK_SET) < 0) - yasm__fatal(N_("could not seek on output file")); - yasm_section_bcs_traverse(sect, info->errwarns, - info, bin_objfmt_output_bytecode); - } - - return 0; -} - -static void -bin_objfmt_cleanup(bin_objfmt_output_info *info) -{ - bin_group *group, *group_temp; - - yasm_xfree(info->buf); - yasm_intnum_destroy(info->origin); - yasm_intnum_destroy(info->tmp_intn); - - TAILQ_FOREACH_SAFE(group, &info->lma_groups, link, group_temp) - bin_group_destroy(group); - - TAILQ_FOREACH_SAFE(group, &info->vma_groups, link, group_temp) - bin_group_destroy(group); -} - -static void -bin_objfmt_output(yasm_object *object, FILE *f, /*@unused@*/ int all_syms, - yasm_errwarns *errwarns) -{ - yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt; - bin_objfmt_output_info info; - bin_group *group, *lma_group, *vma_group, *group_temp; - yasm_intnum *start, *last, *vdelta; - bin_groups unsorted_groups, bss_groups; - - info.start = ftell(f); - - /* Set ORG to 0 unless otherwise specified */ - if (objfmt_bin->org) { - info.origin = yasm_expr_get_intnum(&objfmt_bin->org, 0); - if (!info.origin) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("ORG expression is too complex")); - yasm_errwarn_propagate(errwarns, objfmt_bin->org->line); - return; - } - if (yasm_intnum_sign(info.origin) < 0) { - yasm_error_set(YASM_ERROR_VALUE, N_("ORG expression is negative")); - yasm_errwarn_propagate(errwarns, objfmt_bin->org->line); - return; - } - info.origin = yasm_intnum_copy(info.origin); - } else - info.origin = yasm_intnum_create_uint(0); - - info.object = object; - info.errwarns = errwarns; - info.f = f; - info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); - info.tmp_intn = yasm_intnum_create_uint(0); - TAILQ_INIT(&info.lma_groups); - TAILQ_INIT(&info.vma_groups); - - /* Check symbol table */ - yasm_symtab_traverse(object->symtab, &info, bin_objfmt_check_sym); - - /* Create section groups */ - if (yasm_object_sections_traverse(object, &info, bin_lma_create_group)) { - bin_objfmt_cleanup(&info); - return; /* error detected */ - } - - /* Determine section order according to LMA. - * Sections can be ordered either by (priority): - * - follows - * - start - * - progbits/nobits setting - * - order in the input file - */ - - /* Look at each group with follows specified, and find the section - * that group is supposed to follow. - */ - TAILQ_FOREACH_SAFE(lma_group, &info.lma_groups, link, group_temp) { - if (lma_group->bsd->follows) { - bin_group *found; - /* Need to find group containing section this section follows. */ - found = - find_group_by_name(&info.lma_groups, lma_group->bsd->follows); - if (!found) { - yasm_error_set(YASM_ERROR_VALUE, - N_("section `%s' follows an invalid or unknown section `%s'"), - yasm_section_get_name(lma_group->section), - lma_group->bsd->follows); - yasm_errwarn_propagate(errwarns, 0); - bin_objfmt_cleanup(&info); - return; - } - - /* Check for loops */ - if (lma_group->section == found->section || - find_group_by_section(&lma_group->follow_groups, - found->section)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("follows loop between section `%s' and section `%s'"), - yasm_section_get_name(lma_group->section), - yasm_section_get_name(found->section)); - yasm_errwarn_propagate(errwarns, 0); - bin_objfmt_cleanup(&info); - return; - } - - /* Remove this section from main lma groups list */ - TAILQ_REMOVE(&info.lma_groups, lma_group, link); - /* Add it after the section it's supposed to follow. */ - TAILQ_INSERT_TAIL(&found->follow_groups, lma_group, link); - } - } - - /* Sort the top-level groups according to their start address. - * Use Shell sort for ease of implementation. - * If no start address is specified for a section, don't change the order, - * and move BSS sections to a separate list so they can be moved to the - * end of the lma list after all other sections are sorted. - */ - unsorted_groups = info.lma_groups; /* structure copy */ - TAILQ_INIT(&info.lma_groups); - TAILQ_INIT(&bss_groups); - TAILQ_FOREACH_SAFE(lma_group, &unsorted_groups, link, group_temp) { - bin_group *before; - - if (!lma_group->bsd->istart) { - if (lma_group->bsd->bss) - TAILQ_INSERT_TAIL(&bss_groups, lma_group, link); - else - TAILQ_INSERT_TAIL(&info.lma_groups, lma_group, link); - continue; - } - - before = NULL; - TAILQ_FOREACH(group, &info.lma_groups, link) { - if (!group->bsd->istart) - continue; - if (yasm_intnum_compare(group->bsd->istart, - lma_group->bsd->istart) > 0) { - before = group; - break; - } - } - if (before) - TAILQ_INSERT_BEFORE(before, lma_group, link); - else - TAILQ_INSERT_TAIL(&info.lma_groups, lma_group, link); - } - - /* Move the pure-BSS sections to the end of the LMA list. */ - TAILQ_FOREACH_SAFE(group, &bss_groups, link, group_temp) - TAILQ_INSERT_TAIL(&info.lma_groups, group, link); - TAILQ_INIT(&bss_groups); /* For sanity */ - - /* Assign a LMA start address to every section. - * Also assign VMA=LMA unless otherwise specified. - * - * We need to assign VMA=LMA here (while walking the tree) for the case: - * sect1 start=0 (size=0x11) - * sect2 follows=sect1 valign=16 (size=0x104) - * sect3 follows=sect2 valign=16 - * Where the valign of sect2 will result in a sect3 vaddr higher than a - * naive segment-by-segment interpretation (where sect3 and sect2 would - * have a VMA overlap). - * - * Algorithm for VMA=LMA setting: - * Start with delta=0. - * If there's no virtual attributes, we simply set VMA = LMA+delta. - * If there's only valign specified, we set VMA = aligned LMA, and add - * any new alignment difference to delta. - * - * We could do the LMA start and VMA=LMA steps in two separate steps, - * but it's easier to just recurse once. - */ - start = yasm_intnum_copy(info.origin); - last = yasm_intnum_copy(info.origin); - vdelta = yasm_intnum_create_uint(0); - TAILQ_FOREACH(lma_group, &info.lma_groups, link) { - if (lma_group->bsd->istart) - yasm_intnum_set(start, lma_group->bsd->istart); - group_assign_start_recurse(lma_group, start, last, vdelta, - info.tmp_intn, errwarns); - yasm_intnum_set(start, last); - } - yasm_intnum_destroy(last); - yasm_intnum_destroy(vdelta); - - /* - * Determine section order according to VMA - */ - - /* Create section groups */ - if (yasm_object_sections_traverse(object, &info, bin_vma_create_group)) { - yasm_intnum_destroy(start); - bin_objfmt_cleanup(&info); - return; /* error detected */ - } - - /* Look at each group with vfollows specified, and find the section - * that group is supposed to follow. - */ - TAILQ_FOREACH_SAFE(vma_group, &info.vma_groups, link, group_temp) { - if (vma_group->bsd->vfollows) { - bin_group *found; - /* Need to find group containing section this section follows. */ - found = find_group_by_name(&info.vma_groups, - vma_group->bsd->vfollows); - if (!found) { - yasm_error_set(YASM_ERROR_VALUE, - N_("section `%s' vfollows an invalid or unknown section `%s'"), - yasm_section_get_name(vma_group->section), - vma_group->bsd->vfollows); - yasm_errwarn_propagate(errwarns, 0); - yasm_intnum_destroy(start); - bin_objfmt_cleanup(&info); - return; - } - - /* Check for loops */ - if (vma_group->section == found->section || - find_group_by_section(&vma_group->follow_groups, - found->section)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("vfollows loop between section `%s' and section `%s'"), - yasm_section_get_name(vma_group->section), - yasm_section_get_name(found->section)); - yasm_errwarn_propagate(errwarns, 0); - bin_objfmt_cleanup(&info); - return; - } - - /* Remove this section from main lma groups list */ - TAILQ_REMOVE(&info.vma_groups, vma_group, link); - /* Add it after the section it's supposed to follow. */ - TAILQ_INSERT_TAIL(&found->follow_groups, vma_group, link); - } - } - - /* Due to the combination of steps above, we now know that all top-level - * groups have integer ivstart: - * Vstart Vfollows Valign Handled by - * No No No group_assign_start_recurse() - * No No Yes group_assign_start_recurse() - * No Yes - vfollows loop (above) - * Yes - - bin_lma_create_group() - */ - TAILQ_FOREACH(vma_group, &info.vma_groups, link) { - yasm_intnum_set(start, vma_group->bsd->ivstart); - group_assign_vstart_recurse(vma_group, start, errwarns); - } - - /* Output map file */ - output_map(&info); - - /* Ensure we don't have overlapping progbits LMAs. - * Use a dumb O(N^2) algorithm as the number of sections is essentially - * always low. - */ - if (yasm_object_sections_traverse(object, NULL, check_lma_overlap)) { - yasm_errwarn_propagate(errwarns, 0); - yasm_intnum_destroy(start); - bin_objfmt_cleanup(&info); - return; - } - - /* Output sections */ - yasm_object_sections_traverse(object, &info, bin_objfmt_output_section); - - /* Clean up */ - yasm_intnum_destroy(start); - bin_objfmt_cleanup(&info); -} - -static void -bin_objfmt_destroy(yasm_objfmt *objfmt) -{ - yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)objfmt; - if (objfmt_bin->map_filename) - yasm_xfree(objfmt_bin->map_filename); - yasm_expr_destroy(objfmt_bin->org); - yasm_xfree(objfmt); -} - -static void -define_section_symbol(yasm_symtab *symtab, yasm_section *sect, - const char *sectname, const char *suffix, - enum bin_ssym which, unsigned long line) -{ - yasm_symrec *sym; - bin_symrec_data *bsymd = yasm_xmalloc(sizeof(bin_symrec_data)); - char *symname = yasm_xmalloc(8+strlen(sectname)+strlen(suffix)+1); - - strcpy(symname, "section."); - strcat(symname, sectname); - strcat(symname, suffix); - - bsymd->section = sect; - bsymd->which = which; - - sym = yasm_symtab_declare(symtab, symname, YASM_SYM_EXTERN, line); - yasm_xfree(symname); - yasm_symrec_add_data(sym, &bin_symrec_data_cb, bsymd); -} - -static void -bin_objfmt_init_new_section(yasm_section *sect, unsigned long line) -{ - yasm_object *object = yasm_section_get_object(sect); - const char *sectname = yasm_section_get_name(sect); - /*yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt;*/ - bin_section_data *data; - - data = yasm_xmalloc(sizeof(bin_section_data)); - data->bss = 0; - data->align = NULL; - data->valign = NULL; - data->start = NULL; - data->vstart = NULL; - data->follows = NULL; - data->vfollows = NULL; - data->istart = NULL; - data->ivstart = NULL; - data->length = NULL; - yasm_section_add_data(sect, &bin_section_data_cb, data); - - define_section_symbol(object->symtab, sect, sectname, ".start", - SSYM_START, line); - define_section_symbol(object->symtab, sect, sectname, ".vstart", - SSYM_VSTART, line); - define_section_symbol(object->symtab, sect, sectname, ".length", - SSYM_LENGTH, line); -} - -static yasm_section * -bin_objfmt_add_default_section(yasm_object *object) -{ - yasm_section *retval; - int isnew; - - retval = yasm_object_get_general(object, ".text", 0, 1, 0, &isnew, 0); - if (isnew) - yasm_section_set_default(retval, 1); - return retval; -} - -/* GAS-style flags */ -static int -bin_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d, - /*@unused@*/ uintptr_t arg) -{ - /* TODO */ - return 0; -} - -static /*@observer@*/ /*@null@*/ yasm_section * -bin_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, - /*@unused@*/ /*@null@*/ - yasm_valparamhead *objext_valparams, - unsigned long line) -{ - yasm_valparam *vp; - yasm_section *retval; - int isnew; - int flags_override = 0; - const char *sectname; - bin_section_data *bsd = NULL; - - struct bin_section_switch_data { - /*@only@*/ /*@null@*/ char *follows; - /*@only@*/ /*@null@*/ char *vfollows; - /*@only@*/ /*@null@*/ yasm_expr *start; - /*@only@*/ /*@null@*/ yasm_expr *vstart; - /*@only@*/ /*@null@*/ yasm_intnum *align; - /*@only@*/ /*@null@*/ yasm_intnum *valign; - unsigned long bss; - unsigned long code; - } data; - - static const yasm_dir_help help[] = { - { "follows", 1, yasm_dir_helper_string, - offsetof(struct bin_section_switch_data, follows), 0 }, - { "vfollows", 1, yasm_dir_helper_string, - offsetof(struct bin_section_switch_data, vfollows), 0 }, - { "start", 1, yasm_dir_helper_expr, - offsetof(struct bin_section_switch_data, start), 0 }, - { "vstart", 1, yasm_dir_helper_expr, - offsetof(struct bin_section_switch_data, vstart), 0 }, - { "align", 1, yasm_dir_helper_intn, - offsetof(struct bin_section_switch_data, align), 0 }, - { "valign", 1, yasm_dir_helper_intn, - offsetof(struct bin_section_switch_data, valign), 0 }, - { "nobits", 0, yasm_dir_helper_flag_set, - offsetof(struct bin_section_switch_data, bss), 1 }, - { "progbits", 0, yasm_dir_helper_flag_set, - offsetof(struct bin_section_switch_data, bss), 0 }, - { "code", 0, yasm_dir_helper_flag_set, - offsetof(struct bin_section_switch_data, code), 1 }, - { "data", 0, yasm_dir_helper_flag_set, - offsetof(struct bin_section_switch_data, code), 0 }, - { "execute", 0, yasm_dir_helper_flag_set, - offsetof(struct bin_section_switch_data, code), 1 }, - { "noexecute", 0, yasm_dir_helper_flag_set, - offsetof(struct bin_section_switch_data, code), 0 }, - { "gasflags", 1, bin_helper_gasflags, 0, 0 } - }; - - vp = yasm_vps_first(valparams); - sectname = yasm_vp_string(vp); - if (!sectname) - return NULL; - vp = yasm_vps_next(vp); - - retval = yasm_object_find_general(object, sectname); - if (retval) { - bsd = yasm_section_get_data(retval, &bin_section_data_cb); - assert(bsd != NULL); - data.follows = bsd->follows; - data.vfollows = bsd->vfollows; - data.start = bsd->start; - data.vstart = bsd->vstart; - data.align = NULL; - data.valign = NULL; - data.bss = bsd->bss; - data.code = yasm_section_is_code(retval); - } else { - data.follows = NULL; - data.vfollows = NULL; - data.start = NULL; - data.vstart = NULL; - data.align = NULL; - data.valign = NULL; - data.bss = strcmp(sectname, ".bss") == 0; - data.code = strcmp(sectname, ".text") == 0; - } - - flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), - &data, yasm_dir_helper_valparam_warn); - if (flags_override < 0) - return NULL; /* error occurred */ - - if (data.start && data.follows) { - yasm_error_set(YASM_ERROR_GENERAL, - N_("cannot combine `start' and `follows' section attributes")); - return NULL; - } - - if (data.vstart && data.vfollows) { - yasm_error_set(YASM_ERROR_GENERAL, - N_("cannot combine `vstart' and `vfollows' section attributes")); - return NULL; - } - - if (data.align) { - unsigned long align = yasm_intnum_get_uint(data.align); - - /* Alignments must be a power of two. */ - if (!is_exp2(align)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("argument to `%s' is not a power of two"), - "align"); - return NULL; - } - } else - data.align = bsd ? bsd->align : NULL; - - if (data.valign) { - unsigned long valign = yasm_intnum_get_uint(data.valign); - - /* Alignments must be a power of two. */ - if (!is_exp2(valign)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("argument to `%s' is not a power of two"), - "valign"); - return NULL; - } - } else - data.valign = bsd ? bsd->valign : NULL; - - retval = yasm_object_get_general(object, sectname, 0, (int)data.code, - (int)data.bss, &isnew, line); - - bsd = yasm_section_get_data(retval, &bin_section_data_cb); - - if (isnew || yasm_section_is_default(retval)) { - yasm_section_set_default(retval, 0); - } - - /* Update section flags */ - bsd->bss = data.bss; - bsd->align = data.align; - bsd->valign = data.valign; - bsd->start = data.start; - bsd->vstart = data.vstart; - bsd->follows = data.follows; - bsd->vfollows = data.vfollows; - - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -bin_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - return NULL; -} - -static void -bin_objfmt_dir_org(yasm_object *object, - /*@null@*/ yasm_valparamhead *valparams, - /*@unused@*/ /*@null@*/ - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt; - yasm_valparam *vp; - - /* We only allow a single ORG in a program. */ - if (objfmt_bin->org) { - yasm_error_set(YASM_ERROR_GENERAL, N_("program origin redefined")); - return; - } - - /* ORG takes just a simple expression as param */ - vp = yasm_vps_first(valparams); - objfmt_bin->org = yasm_vp_expr(vp, object->symtab, line); - if (!objfmt_bin->org) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("argument to ORG must be expression")); - return; - } -} - -struct bin_dir_map_data { - unsigned long flags; - /*@only@*/ /*@null@*/ char *filename; -}; - -static int -dir_map_filename(void *obj, yasm_valparam *vp, unsigned long line, void *data) -{ - struct bin_dir_map_data *mdata = (struct bin_dir_map_data *)data; - const char *filename; - - if (mdata->filename) { - yasm_warn_set(YASM_WARN_GENERAL, N_("map file already specified")); - return 0; - } - - filename = yasm_vp_string(vp); - if (!filename) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("unexpected expression in [map]")); - return -1; - } - mdata->filename = yasm__xstrdup(filename); - - return 1; -} - -static void -bin_objfmt_dir_map(yasm_object *object, - /*@null@*/ yasm_valparamhead *valparams, - /*@unused@*/ /*@null@*/ - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt; - - struct bin_dir_map_data data; - - static const yasm_dir_help help[] = { - { "all", 0, yasm_dir_helper_flag_or, - offsetof(struct bin_dir_map_data, flags), - MAP_BRIEF|MAP_SECTIONS|MAP_SYMBOLS }, - { "brief", 0, yasm_dir_helper_flag_or, - offsetof(struct bin_dir_map_data, flags), MAP_BRIEF }, - { "sections", 0, yasm_dir_helper_flag_or, - offsetof(struct bin_dir_map_data, flags), MAP_SECTIONS }, - { "segments", 0, yasm_dir_helper_flag_or, - offsetof(struct bin_dir_map_data, flags), MAP_SECTIONS }, - { "symbols", 0, yasm_dir_helper_flag_or, - offsetof(struct bin_dir_map_data, flags), MAP_SYMBOLS } - }; - - data.flags = objfmt_bin->map_flags | MAP_NONE; - data.filename = objfmt_bin->map_filename; - - if (valparams && yasm_dir_helper(object, yasm_vps_first(valparams), line, help, - NELEMS(help), &data, dir_map_filename) < 0) - return; /* error occurred */ - - objfmt_bin->map_flags = data.flags; - objfmt_bin->map_filename = data.filename; -} - -static void -bin_section_data_destroy(void *data) -{ - bin_section_data *bsd = (bin_section_data *)data; - if (bsd->start) - yasm_expr_destroy(bsd->start); - if (bsd->vstart) - yasm_expr_destroy(bsd->vstart); - if (bsd->follows) - yasm_xfree(bsd->follows); - if (bsd->vfollows) - yasm_xfree(bsd->vfollows); - if (bsd->istart) - yasm_intnum_destroy(bsd->istart); - if (bsd->ivstart) - yasm_intnum_destroy(bsd->ivstart); - if (bsd->length) - yasm_intnum_destroy(bsd->length); - yasm_xfree(data); -} - -static void -bin_section_data_print(void *data, FILE *f, int indent_level) -{ - bin_section_data *bsd = (bin_section_data *)data; - - fprintf(f, "%*sbss=%d\n", indent_level, "", bsd->bss); - - fprintf(f, "%*salign=", indent_level, ""); - if (bsd->align) - yasm_intnum_print(bsd->align, f); - else - fprintf(f, "(nil)"); - fprintf(f, "\n%*svalign=", indent_level, ""); - if (bsd->valign) - yasm_intnum_print(bsd->valign, f); - else - fprintf(f, "(nil)"); - - fprintf(f, "\n%*sstart=", indent_level, ""); - yasm_expr_print(bsd->start, f); - fprintf(f, "\n%*svstart=", indent_level, ""); - yasm_expr_print(bsd->vstart, f); - - fprintf(f, "\n%*sfollows=", indent_level, ""); - if (bsd->follows) - fprintf(f, "\"%s\"", bsd->follows); - else - fprintf(f, "(nil)"); - fprintf(f, "\n%*svfollows=", indent_level, ""); - if (bsd->vfollows) - fprintf(f, "\"%s\"", bsd->vfollows); - else - fprintf(f, "(nil)"); - - fprintf(f, "\n%*sistart=", indent_level, ""); - if (bsd->istart) - yasm_intnum_print(bsd->istart, f); - else - fprintf(f, "(nil)"); - fprintf(f, "\n%*sivstart=", indent_level, ""); - if (bsd->ivstart) - yasm_intnum_print(bsd->ivstart, f); - else - fprintf(f, "(nil)"); - - fprintf(f, "\n%*slength=", indent_level, ""); - if (bsd->length) - yasm_intnum_print(bsd->length, f); - else - fprintf(f, "(nil)"); - fprintf(f, "\n"); -} - -static void -bin_symrec_data_destroy(void *data) -{ - yasm_xfree(data); -} - -static void -bin_symrec_data_print(void *data, FILE *f, int indent_level) -{ - bin_symrec_data *bsymd = (bin_symrec_data *)data; - - fprintf(f, "%*ssection=\"%s\"\n", indent_level, "", - yasm_section_get_name(bsymd->section)); - fprintf(f, "%*swhich=", indent_level, ""); - switch (bsymd->which) { - case SSYM_START: fprintf(f, "START"); break; - case SSYM_VSTART: fprintf(f, "VSTART"); break; - case SSYM_LENGTH: fprintf(f, "LENGTH"); break; - } - fprintf(f, "\n"); -} - - -/* Define valid debug formats to use with this object format */ -static const char *bin_objfmt_dbgfmt_keywords[] = { - "null", - NULL -}; - -static const yasm_directive bin_objfmt_directives[] = { - { "org", "nasm", bin_objfmt_dir_org, YASM_DIR_ARG_REQUIRED }, - { "map", "nasm", bin_objfmt_dir_map, YASM_DIR_ANY }, - { NULL, NULL, NULL, 0 } -}; - -static const char *bin_nasm_stdmac[] = { - "%imacro org 1+.nolist", - "[org %1]", - "%endmacro", - NULL -}; - -static const yasm_stdmac bin_objfmt_stdmacs[] = { - { "nasm", "nasm", bin_nasm_stdmac }, - { "tasm", "tasm", bin_nasm_stdmac }, - { NULL, NULL, NULL } -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_bin_LTX_objfmt = { - "Flat format binary", - "bin", - NULL, - 16, - 0, - bin_objfmt_dbgfmt_keywords, - "null", - bin_objfmt_directives, - bin_objfmt_stdmacs, - bin_objfmt_create, - bin_objfmt_output, - bin_objfmt_destroy, - bin_objfmt_add_default_section, - bin_objfmt_init_new_section, - bin_objfmt_section_switch, - bin_objfmt_get_special_sym -}; - -#define EXE_HEADER_SIZE 0x200 - -/* DOS .EXE binaries are just raw binaries with a header */ -yasm_objfmt_module yasm_dosexe_LTX_objfmt; - -static yasm_objfmt * -dosexe_objfmt_create(yasm_object *object) -{ - yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *) bin_objfmt_create(object); - objfmt_bin->objfmt.module = &yasm_dosexe_LTX_objfmt; - return (yasm_objfmt *)objfmt_bin; -} - -static unsigned long -get_sym(yasm_object *object, const char *name) { - yasm_symrec *symrec = yasm_symtab_get(object->symtab, name); - yasm_bytecode *prevbc; - if (!symrec) - return 0; - if (!yasm_symrec_get_label(symrec, &prevbc)) - return 0; - return prevbc->offset + prevbc->len; -} - -static void -dosexe_objfmt_output(yasm_object *object, FILE *f, /*@unused@*/ int all_syms, - yasm_errwarns *errwarns) -{ - unsigned long tot_size, size, bss_size; - unsigned long start, bss; - unsigned char c; - - fseek(f, EXE_HEADER_SIZE, SEEK_SET); - - bin_objfmt_output(object, f, all_syms, errwarns); - - tot_size = ftell(f); - - /* if there is a __bss_start symbol, data after it is 0, no need to write - * it. */ - bss = get_sym(object, "__bss_start"); - if (bss) - size = bss; - else - size = tot_size; - bss_size = tot_size - size; -#ifdef HAVE_FTRUNCATE - if (size != tot_size) - ftruncate(fileno(f), EXE_HEADER_SIZE + size); -#endif - fseek(f, 0, SEEK_SET); - - /* magic */ - fwrite("MZ", 1, 2, f); - - /* file size */ - c = size & 0xff; - fwrite(&c, 1, 1, f); - c = !!(size & 0x100); - fwrite(&c, 1, 1, f); - c = ((size + 511) >> 9) & 0xff; - fwrite(&c, 1, 1, f); - c = ((size + 511) >> 17) & 0xff; - fwrite(&c, 1, 1, f); - - /* relocation # */ - c = 0; - fwrite(&c, 1, 1, f); - fwrite(&c, 1, 1, f); - - /* header size */ - c = EXE_HEADER_SIZE / 16; - fwrite(&c, 1, 1, f); - c = 0; - fwrite(&c, 1, 1, f); - - /* minimum paragraph # */ - bss_size = (bss_size + 15) >> 4; - c = bss_size & 0xff; - fwrite(&c, 1, 1, f); - c = (bss_size >> 8) & 0xff; - fwrite(&c, 1, 1, f); - - /* maximum paragraph # */ - c = 0xFF; - fwrite(&c, 1, 1, f); - fwrite(&c, 1, 1, f); - - /* relative value of stack segment */ - c = 0; - fwrite(&c, 1, 1, f); - fwrite(&c, 1, 1, f); - - /* SP at start */ - c = 0; - fwrite(&c, 1, 1, f); - fwrite(&c, 1, 1, f); - - /* header checksum */ - c = 0; - fwrite(&c, 1, 1, f); - fwrite(&c, 1, 1, f); - - /* IP at start */ - start = get_sym(object, "start"); - if (!start) { - yasm_error_set(YASM_ERROR_GENERAL, - N_("%s: could not find symbol `start'")); - return; - } - c = start & 0xff; - fwrite(&c, 1, 1, f); - c = (start >> 8) & 0xff; - fwrite(&c, 1, 1, f); - - /* CS start */ - c = 0; - fwrite(&c, 1, 1, f); - fwrite(&c, 1, 1, f); - - /* reloc start */ - c = 0x22; - fwrite(&c, 1, 1, f); - c = 0; - fwrite(&c, 1, 1, f); - - /* Overlay number */ - c = 0; - fwrite(&c, 1, 1, f); - fwrite(&c, 1, 1, f); -} - - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_dosexe_LTX_objfmt = { - "DOS .EXE format binary", - "dosexe", - "exe", - 16, - 0, - bin_objfmt_dbgfmt_keywords, - "null", - bin_objfmt_directives, - bin_objfmt_stdmacs, - dosexe_objfmt_create, - dosexe_objfmt_output, - bin_objfmt_destroy, - bin_objfmt_add_default_section, - bin_objfmt_init_new_section, - bin_objfmt_section_switch, - bin_objfmt_get_special_sym -}; +/* + * Flat-format binary object format + * + * Copyright (C) 2002-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <util.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <libyasm.h> + + +#define REGULAR_OUTBUF_SIZE 1024 + +typedef struct bin_section_data { + int bss; /* aka nobits */ + + /* User-provided alignment */ + yasm_intnum *align, *valign; + + /* User-provided starts */ + /*@null@*/ /*@owned@*/ yasm_expr *start, *vstart; + + /* User-provided follows */ + /*@null@*/ /*@owned@*/ char *follows, *vfollows; + + /* Calculated (final) starts, used only during output() */ + /*@null@*/ /*@owned@*/ yasm_intnum *istart, *ivstart; + + /* Calculated (final) length, used only during output() */ + /*@null@*/ /*@owned@*/ yasm_intnum *length; +} bin_section_data; + +typedef struct yasm_objfmt_bin { + yasm_objfmt_base objfmt; /* base structure */ + + enum { + NO_MAP = 0, + MAP_NONE = 0x01, + MAP_BRIEF = 0x02, + MAP_SECTIONS = 0x04, + MAP_SYMBOLS = 0x08 + } map_flags; + /*@null@*/ /*@only@*/ char *map_filename; + + /*@null@*/ /*@only@*/ yasm_expr *org; +} yasm_objfmt_bin; + +/* symrec data is used only for the special symbols section<sectname>.start, + * section<sectname>.vstart, and section<sectname>.length + */ +typedef struct bin_symrec_data { + yasm_section *section; /* referenced section */ + enum bin_ssym { + SSYM_START, + SSYM_VSTART, + SSYM_LENGTH + } which; +} bin_symrec_data; + +static void bin_section_data_destroy(/*@only@*/ void *d); +static void bin_section_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback bin_section_data_cb = { + bin_section_data_destroy, + bin_section_data_print +}; + +static void bin_symrec_data_destroy(/*@only@*/ void *d); +static void bin_symrec_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback bin_symrec_data_cb = { + bin_symrec_data_destroy, + bin_symrec_data_print +}; + +yasm_objfmt_module yasm_bin_LTX_objfmt; + + +static yasm_objfmt * +bin_objfmt_create(yasm_object *object) +{ + yasm_objfmt_bin *objfmt_bin = yasm_xmalloc(sizeof(yasm_objfmt_bin)); + objfmt_bin->objfmt.module = &yasm_bin_LTX_objfmt; + + objfmt_bin->map_flags = NO_MAP; + objfmt_bin->map_filename = NULL; + objfmt_bin->org = NULL; + + return (yasm_objfmt *)objfmt_bin; +} + +typedef TAILQ_HEAD(bin_group_head, bin_group) bin_groups; + +typedef struct bin_group { + TAILQ_ENTRY(bin_group) link; + yasm_section *section; + bin_section_data *bsd; + + /* Groups that (in parallel) logically come immediately after this + * group's section. + */ + bin_groups follow_groups; +} bin_group; + +/* Recursive function to find group containing named section. */ +static bin_group * +find_group_by_name(bin_groups *groups, const char *name) +{ + bin_group *group, *found; + TAILQ_FOREACH(group, groups, link) { + if (strcmp(yasm_section_get_name(group->section), name) == 0) + return group; + /* Recurse to loop through follow groups */ + found = find_group_by_name(&group->follow_groups, name); + if (found) + return found; + } + return NULL; +} + +/* Recursive function to find group. Returns NULL if not found. */ +static bin_group * +find_group_by_section(bin_groups *groups, yasm_section *section) +{ + bin_group *group, *found; + TAILQ_FOREACH(group, groups, link) { + if (group->section == section) + return group; + /* Recurse to loop through follow groups */ + found = find_group_by_section(&group->follow_groups, section); + if (found) + return found; + } + return NULL; +} + +#if 0 +/* Debugging function */ +static void +print_groups(const bin_groups *groups, int indent_level) +{ + bin_group *group; + TAILQ_FOREACH(group, groups, link) { + printf("%*sSection `%s':\n", indent_level, "", + yasm_section_get_name(group->section)); + bin_section_data_print(group->bsd, stdout, indent_level+1); + if (!TAILQ_EMPTY(&group->follow_groups)) { + printf("%*sFollowing groups:\n", indent_level, ""); + print_groups(&group->follow_groups, indent_level+1); + } + } +} +#endif + +static void +bin_group_destroy(/*@only@*/ bin_group *group) +{ + bin_group *follow, *group_temp; + TAILQ_FOREACH_SAFE(follow, &group->follow_groups, link, group_temp) + bin_group_destroy(follow); + yasm_xfree(group); +} + +typedef struct bin_objfmt_output_info { + yasm_object *object; + yasm_errwarns *errwarns; + /*@dependent@*/ FILE *f; + /*@only@*/ unsigned char *buf; + /*@observer@*/ const yasm_section *sect; + unsigned long start; /* what normal variables go against */ + + yasm_intnum *origin; + yasm_intnum *tmp_intn; /* temporary working intnum */ + + bin_groups lma_groups, vma_groups; +} bin_objfmt_output_info; + +static int +bin_objfmt_check_sym(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + assert(info != NULL); + + /* Don't check internally-generated symbols. Only internally generated + * symbols have symrec data, so simply check for its presence. + */ + if (yasm_symrec_get_data(sym, &bin_symrec_data_cb)) + return 0; + + if (vis & YASM_SYM_EXTERN) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("binary object format does not support extern variables")); + yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); + } else if (vis & YASM_SYM_GLOBAL) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("binary object format does not support global variables")); + yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); + } else if (vis & YASM_SYM_COMMON) { + yasm_error_set(YASM_ERROR_TYPE, + N_("binary object format does not support common variables")); + yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); + } + return 0; +} + +static int +bin_lma_create_group(yasm_section *sect, /*@null@*/ void *d) +{ + bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; + bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb); + unsigned long align = yasm_section_get_align(sect); + bin_group *group; + + assert(info != NULL); + assert(bsd != NULL); + + group = yasm_xmalloc(sizeof(bin_group)); + group->section = sect; + group->bsd = bsd; + TAILQ_INIT(&group->follow_groups); + + /* Determine section alignment as necessary. */ + if (!bsd->align) + bsd->align = yasm_intnum_create_uint(align > 4 ? align : 4); + else { + yasm_intnum *align_intn = yasm_intnum_create_uint(align); + if (yasm_intnum_compare(align_intn, bsd->align) > 0) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("section `%s' internal align of %lu is greater than `%s' of %lu; using `%s'"), + yasm_section_get_name(sect), + yasm_intnum_get_uint(align_intn), + N_("align"), + yasm_intnum_get_uint(bsd->align), + N_("align")); + yasm_errwarn_propagate(info->errwarns, 0); + } + yasm_intnum_destroy(align_intn); + } + + /* Calculate section integer start. */ + if (bsd->start) { + bsd->istart = yasm_expr_get_intnum(&bsd->start, 0); + if (!bsd->istart) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("start expression is too complex")); + yasm_errwarn_propagate(info->errwarns, bsd->start->line); + return 1; + } else + bsd->istart = yasm_intnum_copy(bsd->istart); + } else + bsd->istart = NULL; + + /* Calculate section integer vstart. */ + if (bsd->vstart) { + bsd->ivstart = yasm_expr_get_intnum(&bsd->vstart, 0); + if (!bsd->ivstart) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("vstart expression is too complex")); + yasm_errwarn_propagate(info->errwarns, bsd->vstart->line); + return 1; + } else + bsd->ivstart = yasm_intnum_copy(bsd->ivstart); + } else + bsd->ivstart = NULL; + + /* Calculate section integer length. */ + bsd->length = yasm_calc_bc_dist(yasm_section_bcs_first(sect), + yasm_section_bcs_last(sect)); + + TAILQ_INSERT_TAIL(&info->lma_groups, group, link); + return 0; +} + +static int +bin_vma_create_group(yasm_section *sect, /*@null@*/ void *d) +{ + bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; + bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb); + bin_group *group; + + assert(info != NULL); + assert(bsd != NULL); + + group = yasm_xmalloc(sizeof(bin_group)); + group->section = sect; + group->bsd = bsd; + TAILQ_INIT(&group->follow_groups); + + TAILQ_INSERT_TAIL(&info->vma_groups, group, link); + return 0; +} + +/* Calculates new start address based on alignment constraint. + * Start is modified (rounded up) to the closest aligned value greater than + * what was passed in. + * Align must be a power of 2. + */ +static void +bin_objfmt_align(yasm_intnum *start, const yasm_intnum *align) +{ + /* Because alignment is always a power of two, we can use some bit + * trickery to do this easily. + */ + yasm_intnum *align_intn = + yasm_intnum_create_uint(yasm_intnum_get_uint(align)-1); + yasm_intnum_calc(align_intn, YASM_EXPR_AND, start); + if (!yasm_intnum_is_zero(align_intn)) { + /* start = (start & ~(align-1)) + align; */ + yasm_intnum_set_uint(align_intn, yasm_intnum_get_uint(align)-1); + yasm_intnum_calc(align_intn, YASM_EXPR_NOT, NULL); + yasm_intnum_calc(align_intn, YASM_EXPR_AND, start); + yasm_intnum_set(start, align); + yasm_intnum_calc(start, YASM_EXPR_ADD, align_intn); + } + yasm_intnum_destroy(align_intn); +} + +/* Recursive function to assign start addresses. + * Updates start, last, and vdelta parameters as it goes along. + * The tmp parameter is just a working intnum so one doesn't have to be + * locally allocated for this purpose. + */ +static void +group_assign_start_recurse(bin_group *group, yasm_intnum *start, + yasm_intnum *last, yasm_intnum *vdelta, + yasm_intnum *tmp, yasm_errwarns *errwarns) +{ + bin_group *follow_group; + + /* Determine LMA */ + if (group->bsd->istart) { + yasm_intnum_set(group->bsd->istart, start); + if (group->bsd->align) { + bin_objfmt_align(group->bsd->istart, group->bsd->align); + if (yasm_intnum_compare(start, group->bsd->istart) != 0) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("start inconsistent with align; using aligned value")); + yasm_errwarn_propagate(errwarns, group->bsd->start->line); + } + } + } else { + group->bsd->istart = yasm_intnum_copy(start); + if (group->bsd->align != 0) + bin_objfmt_align(group->bsd->istart, group->bsd->align); + } + + /* Determine VMA if either just valign specified or if no v* specified */ + if (!group->bsd->vstart) { + if (!group->bsd->vfollows && !group->bsd->valign) { + /* No v* specified, set VMA=LMA+vdelta. */ + group->bsd->ivstart = yasm_intnum_copy(group->bsd->istart); + yasm_intnum_calc(group->bsd->ivstart, YASM_EXPR_ADD, vdelta); + } else if (!group->bsd->vfollows) { + /* Just valign specified: set VMA=LMA+vdelta, align VMA, then add + * delta between unaligned and aligned to vdelta parameter. + */ + group->bsd->ivstart = yasm_intnum_copy(group->bsd->istart); + yasm_intnum_calc(group->bsd->ivstart, YASM_EXPR_ADD, vdelta); + yasm_intnum_set(tmp, group->bsd->ivstart); + bin_objfmt_align(group->bsd->ivstart, group->bsd->valign); + yasm_intnum_calc(vdelta, YASM_EXPR_ADD, group->bsd->ivstart); + yasm_intnum_calc(vdelta, YASM_EXPR_SUB, tmp); + } + } + + /* Find the maximum end value */ + yasm_intnum_set(tmp, group->bsd->istart); + yasm_intnum_calc(tmp, YASM_EXPR_ADD, group->bsd->length); + if (yasm_intnum_compare(tmp, last) > 0) /* tmp > last */ + yasm_intnum_set(last, tmp); + + /* Recurse for each following group. */ + TAILQ_FOREACH(follow_group, &group->follow_groups, link) { + /* Following sections have to follow this one, + * so add length to start. + */ + yasm_intnum_set(start, group->bsd->istart); + yasm_intnum_calc(start, YASM_EXPR_ADD, group->bsd->length); + + group_assign_start_recurse(follow_group, start, last, vdelta, tmp, + errwarns); + } +} + +/* Recursive function to assign start addresses. + * Updates start parameter as it goes along. + * The tmp parameter is just a working intnum so one doesn't have to be + * locally allocated for this purpose. + */ +static void +group_assign_vstart_recurse(bin_group *group, yasm_intnum *start, + yasm_errwarns *errwarns) +{ + bin_group *follow_group; + + /* Determine VMA section alignment as necessary. + * Default to LMA alignment if not specified. + */ + if (!group->bsd->valign) + group->bsd->valign = yasm_intnum_copy(group->bsd->align); + else { + unsigned long align = yasm_section_get_align(group->section); + yasm_intnum *align_intn = yasm_intnum_create_uint(align); + if (yasm_intnum_compare(align_intn, group->bsd->valign) > 0) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("section `%s' internal align of %lu is greater than `%s' of %lu; using `%s'"), + yasm_section_get_name(group->section), + yasm_intnum_get_uint(align_intn), + N_("valign"), + yasm_intnum_get_uint(group->bsd->valign), + N_("valign")); + yasm_errwarn_propagate(errwarns, 0); + } + yasm_intnum_destroy(align_intn); + } + + /* Determine VMA */ + if (group->bsd->ivstart) { + yasm_intnum_set(group->bsd->ivstart, start); + if (group->bsd->valign) { + bin_objfmt_align(group->bsd->ivstart, group->bsd->valign); + if (yasm_intnum_compare(start, group->bsd->ivstart) != 0) { + yasm_error_set(YASM_ERROR_VALUE, + N_("vstart inconsistent with valign")); + yasm_errwarn_propagate(errwarns, group->bsd->vstart->line); + } + } + } else { + group->bsd->ivstart = yasm_intnum_copy(start); + if (group->bsd->valign) + bin_objfmt_align(group->bsd->ivstart, group->bsd->valign); + } + + /* Recurse for each following group. */ + TAILQ_FOREACH(follow_group, &group->follow_groups, link) { + /* Following sections have to follow this one, + * so add length to start. + */ + yasm_intnum_set(start, group->bsd->ivstart); + yasm_intnum_calc(start, YASM_EXPR_ADD, group->bsd->length); + + group_assign_vstart_recurse(follow_group, start, errwarns); + } +} + +static /*@null@*/ const yasm_intnum * +get_ssym_value(yasm_symrec *sym) +{ + bin_symrec_data *bsymd = yasm_symrec_get_data(sym, &bin_symrec_data_cb); + bin_section_data *bsd; + + if (!bsymd) + return NULL; + + bsd = yasm_section_get_data(bsymd->section, &bin_section_data_cb); + assert(bsd != NULL); + + switch (bsymd->which) { + case SSYM_START: return bsd->istart; + case SSYM_VSTART: return bsd->ivstart; + case SSYM_LENGTH: return bsd->length; + } + return NULL; +} + +static /*@only@*/ yasm_expr * +bin_objfmt_expr_xform(/*@returned@*/ /*@only@*/ yasm_expr *e, + /*@unused@*/ /*@null@*/ void *d) +{ + int i; + for (i=0; i<e->numterms; i++) { + /*@dependent@*/ yasm_section *sect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + /*@null@*/ yasm_intnum *dist; + /*@null@*/ const yasm_intnum *ssymval; + + /* Transform symrecs or precbcs that reference sections into + * vstart + intnum(dist). + */ + if (((e->terms[i].type == YASM_EXPR_SYM && + yasm_symrec_get_label(e->terms[i].data.sym, &precbc)) || + (e->terms[i].type == YASM_EXPR_PRECBC && + (precbc = e->terms[i].data.precbc))) && + (sect = yasm_bc_get_section(precbc)) && + (dist = yasm_calc_bc_dist(yasm_section_bcs_first(sect), precbc))) { + bin_section_data *bsd; + bsd = yasm_section_get_data(sect, &bin_section_data_cb); + assert(bsd != NULL); + yasm_intnum_calc(dist, YASM_EXPR_ADD, bsd->ivstart); + e->terms[i].type = YASM_EXPR_INT; + e->terms[i].data.intn = dist; + } + + /* Transform our special symrecs into the appropriate value */ + if (e->terms[i].type == YASM_EXPR_SYM && + (ssymval = get_ssym_value(e->terms[i].data.sym))) { + e->terms[i].type = YASM_EXPR_INT; + e->terms[i].data.intn = yasm_intnum_copy(ssymval); + } + } + + return e; +} + +typedef struct map_output_info { + /* address width */ + int bytes; + + /* intnum output static data areas */ + unsigned char *buf; + yasm_intnum *intn; + + /* symrec output information */ + unsigned long count; + yasm_section *section; /* NULL for EQUs */ + + yasm_object *object; /* object */ + FILE *f; /* map output file */ +} map_output_info; + +static int +map_prescan_bytes(yasm_section *sect, void *d) +{ + bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb); + map_output_info *info = (map_output_info *)d; + + assert(bsd != NULL); + assert(info != NULL); + + while (!yasm_intnum_check_size(bsd->length, info->bytes * 8, 0, 0)) + info->bytes *= 2; + while (!yasm_intnum_check_size(bsd->istart, info->bytes * 8, 0, 0)) + info->bytes *= 2; + while (!yasm_intnum_check_size(bsd->ivstart, info->bytes * 8, 0, 0)) + info->bytes *= 2; + + return 0; +} + +static void +map_print_intnum(const yasm_intnum *intn, map_output_info *info) +{ + size_t i; + yasm_intnum_get_sized(intn, info->buf, info->bytes, info->bytes*8, 0, 0, + 0); + for (i=info->bytes; i != 0; i--) + fprintf(info->f, "%02X", info->buf[i-1]); +} + +static void +map_sections_summary(bin_groups *groups, map_output_info *info) +{ + bin_group *group; + TAILQ_FOREACH(group, groups, link) { + bin_section_data *bsd = group->bsd; + + assert(bsd != NULL); + assert(info != NULL); + + map_print_intnum(bsd->ivstart, info); + fprintf(info->f, " "); + + yasm_intnum_set(info->intn, bsd->ivstart); + yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->length); + map_print_intnum(info->intn, info); + fprintf(info->f, " "); + + map_print_intnum(bsd->istart, info); + fprintf(info->f, " "); + + yasm_intnum_set(info->intn, bsd->istart); + yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->length); + map_print_intnum(info->intn, info); + fprintf(info->f, " "); + + map_print_intnum(bsd->length, info); + fprintf(info->f, " "); + + fprintf(info->f, "%-*s", 10, bsd->bss ? "nobits" : "progbits"); + fprintf(info->f, "%s\n", yasm_section_get_name(group->section)); + + /* Recurse to loop through follow groups */ + map_sections_summary(&group->follow_groups, info); + } +} + +static void +map_sections_detail(bin_groups *groups, map_output_info *info) +{ + bin_group *group; + TAILQ_FOREACH(group, groups, link) { + bin_section_data *bsd = group->bsd; + size_t i; + const char *s; + + s = yasm_section_get_name(group->section); + fprintf(info->f, "---- Section %s ", s); + for (i=0; i<(65-strlen(s)); i++) + fputc('-', info->f); + + fprintf(info->f, "\n\nclass: %s", + bsd->bss ? "nobits" : "progbits"); + fprintf(info->f, "\nlength: "); + map_print_intnum(bsd->length, info); + fprintf(info->f, "\nstart: "); + map_print_intnum(bsd->istart, info); + fprintf(info->f, "\nalign: "); + map_print_intnum(bsd->align, info); + fprintf(info->f, "\nfollows: %s", + bsd->follows ? bsd->follows : "not defined"); + fprintf(info->f, "\nvstart: "); + map_print_intnum(bsd->ivstart, info); + fprintf(info->f, "\nvalign: "); + map_print_intnum(bsd->valign, info); + fprintf(info->f, "\nvfollows: %s\n\n", + bsd->vfollows ? bsd->vfollows : "not defined"); + + /* Recurse to loop through follow groups */ + map_sections_detail(&group->follow_groups, info); + } +} + +static int +map_symrec_count(yasm_symrec *sym, void *d) +{ + map_output_info *info = (map_output_info *)d; + /*@dependent@*/ yasm_bytecode *precbc; + + assert(info != NULL); + + /* TODO: autodetect wider size */ + if (!info->section && yasm_symrec_get_equ(sym)) { + info->count++; + } else if (yasm_symrec_get_label(sym, &precbc) && + yasm_bc_get_section(precbc) == info->section) { + info->count++; + } + return 0; +} + +static int +map_symrec_output(yasm_symrec *sym, void *d) +{ + map_output_info *info = (map_output_info *)d; + const yasm_expr *equ; + /*@dependent@*/ yasm_bytecode *precbc; + /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); + + assert(info != NULL); + + if (!info->section && (equ = yasm_symrec_get_equ(sym))) { + yasm_expr *realequ = yasm_expr_copy(equ); + realequ = yasm_expr__level_tree + (realequ, 1, 1, 1, 0, bin_objfmt_expr_xform, NULL); + yasm_intnum_set(info->intn, yasm_expr_get_intnum(&realequ, 0)); + yasm_expr_destroy(realequ); + map_print_intnum(info->intn, info); + fprintf(info->f, " %s\n", name); + } else if (yasm_symrec_get_label(sym, &precbc) && + yasm_bc_get_section(precbc) == info->section) { + bin_section_data *bsd = + yasm_section_get_data(info->section, &bin_section_data_cb); + + /* Real address */ + yasm_intnum_set_uint(info->intn, yasm_bc_next_offset(precbc)); + yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->istart); + map_print_intnum(info->intn, info); + fprintf(info->f, " "); + + /* Virtual address */ + yasm_intnum_set_uint(info->intn, yasm_bc_next_offset(precbc)); + yasm_intnum_calc(info->intn, YASM_EXPR_ADD, bsd->ivstart); + map_print_intnum(info->intn, info); + + /* Name */ + fprintf(info->f, " %s\n", name); + } + yasm_xfree(name); + return 0; +} + +static void +map_sections_symbols(bin_groups *groups, map_output_info *info) +{ + bin_group *group; + TAILQ_FOREACH(group, groups, link) { + info->count = 0; + info->section = group->section; + yasm_symtab_traverse(info->object->symtab, info, map_symrec_count); + + if (info->count > 0) { + const char *s = yasm_section_get_name(group->section); + size_t i; + fprintf(info->f, "---- Section %s ", s); + for (i=0; i<(65-strlen(s)); i++) + fputc('-', info->f); + fprintf(info->f, "\n\n%-*s%-*s%s\n", + info->bytes*2+2, "Real", + info->bytes*2+2, "Virtual", + "Name"); + yasm_symtab_traverse(info->object->symtab, info, + map_symrec_output); + fprintf(info->f, "\n\n"); + } + + /* Recurse to loop through follow groups */ + map_sections_symbols(&group->follow_groups, info); + } +} + +static void +output_map(bin_objfmt_output_info *info) +{ + yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)info->object->objfmt; + FILE *f; + int i; + map_output_info mapinfo; + + if (objfmt_bin->map_flags == NO_MAP) + return; + + if (objfmt_bin->map_flags == MAP_NONE) + objfmt_bin->map_flags = MAP_BRIEF; /* default to brief */ + + if (!objfmt_bin->map_filename) + f = stdout; /* default to stdout */ + else { + f = fopen(objfmt_bin->map_filename, "wt"); + if (!f) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("unable to open map file `%s'"), + objfmt_bin->map_filename); + yasm_errwarn_propagate(info->errwarns, 0); + return; + } + } + + mapinfo.object = info->object; + mapinfo.f = f; + + /* Temporary intnum */ + mapinfo.intn = info->tmp_intn; + + /* Prescan all values to figure out what width we should make the output + * fields. Start with a minimum of 4. + */ + mapinfo.bytes = 4; + while (!yasm_intnum_check_size(info->origin, mapinfo.bytes * 8, 0, 0)) + mapinfo.bytes *= 2; + yasm_object_sections_traverse(info->object, &mapinfo, map_prescan_bytes); + mapinfo.buf = yasm_xmalloc(mapinfo.bytes); + + fprintf(f, "\n- YASM Map file "); + for (i=0; i<63; i++) + fputc('-', f); + fprintf(f, "\n\nSource file: %s\n", info->object->src_filename); + fprintf(f, "Output file: %s\n\n", info->object->obj_filename); + + fprintf(f, "-- Program origin "); + for (i=0; i<61; i++) + fputc('-', f); + fprintf(f, "\n\n"); + map_print_intnum(info->origin, &mapinfo); + fprintf(f, "\n\n"); + + if (objfmt_bin->map_flags & MAP_BRIEF) { + fprintf(f, "-- Sections (summary) "); + for (i=0; i<57; i++) + fputc('-', f); + fprintf(f, "\n\n%-*s%-*s%-*s%-*s%-*s%-*s%s\n", + mapinfo.bytes*2+2, "Vstart", + mapinfo.bytes*2+2, "Vstop", + mapinfo.bytes*2+2, "Start", + mapinfo.bytes*2+2, "Stop", + mapinfo.bytes*2+2, "Length", + 10, "Class", "Name"); + + map_sections_summary(&info->lma_groups, &mapinfo); + fprintf(f, "\n"); + } + + if (objfmt_bin->map_flags & MAP_SECTIONS) { + fprintf(f, "-- Sections (detailed) "); + for (i=0; i<56; i++) + fputc('-', f); + fprintf(f, "\n\n"); + map_sections_detail(&info->lma_groups, &mapinfo); + } + + if (objfmt_bin->map_flags & MAP_SYMBOLS) { + fprintf(f, "-- Symbols "); + for (i=0; i<68; i++) + fputc('-', f); + fprintf(f, "\n\n"); + + /* We do two passes for EQU and each section; the first pass + * determines the byte width to use for the value and whether any + * symbols are present, the second pass actually outputs the text. + */ + + /* EQUs */ + mapinfo.count = 0; + mapinfo.section = NULL; + yasm_symtab_traverse(info->object->symtab, &mapinfo, map_symrec_count); + + if (mapinfo.count > 0) { + fprintf(f, "---- No Section "); + for (i=0; i<63; i++) + fputc('-', f); + fprintf(f, "\n\n%-*s%s\n", mapinfo.bytes*2+2, "Value", "Name"); + yasm_symtab_traverse(info->object->symtab, &mapinfo, + map_symrec_output); + fprintf(f, "\n\n"); + } + + /* Other sections */ + map_sections_symbols(&info->lma_groups, &mapinfo); + } + + if (f != stdout) + fclose(f); + + yasm_xfree(mapinfo.buf); +} + +/* Check for LMA overlap using a simple N^2 algorithm. */ +static int +check_lma_overlap(yasm_section *sect, /*@null@*/ void *d) +{ + bin_section_data *bsd, *bsd2; + yasm_section *other = (yasm_section *)d; + yasm_intnum *overlap; + + if (!d) + return yasm_object_sections_traverse(yasm_section_get_object(sect), + sect, check_lma_overlap); + if (sect == other) + return 0; + + bsd = yasm_section_get_data(sect, &bin_section_data_cb); + bsd2 = yasm_section_get_data(other, &bin_section_data_cb); + + if (yasm_intnum_is_zero(bsd->length) || + yasm_intnum_is_zero(bsd2->length)) + return 0; + + if (yasm_intnum_compare(bsd->istart, bsd2->istart) <= 0) { + overlap = yasm_intnum_copy(bsd->istart); + yasm_intnum_calc(overlap, YASM_EXPR_ADD, bsd->length); + yasm_intnum_calc(overlap, YASM_EXPR_SUB, bsd2->istart); + } else { + overlap = yasm_intnum_copy(bsd2->istart); + yasm_intnum_calc(overlap, YASM_EXPR_ADD, bsd2->length); + yasm_intnum_calc(overlap, YASM_EXPR_SUB, bsd->istart); + } + + if (yasm_intnum_sign(overlap) > 0) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("sections `%s' and `%s' overlap by %lu bytes"), + yasm_section_get_name(sect), + yasm_section_get_name(other), + yasm_intnum_get_uint(overlap)); + yasm_intnum_destroy(overlap); + return -1; + } + + yasm_intnum_destroy(overlap); + return 0; +} + +static int +bin_objfmt_output_value(yasm_value *value, unsigned char *buf, + unsigned int destsize, + /*@unused@*/ unsigned long offset, yasm_bytecode *bc, + int warn, /*@null@*/ void *d) +{ + /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + /*@dependent@*/ yasm_section *sect; + + assert(info != NULL); + + /* Binary objects we need to resolve against object, not against section. */ + if (value->rel) { + unsigned int rshift = (unsigned int)value->rshift; + yasm_expr *syme; + /*@null@*/ const yasm_intnum *ssymval; + + if (yasm_symrec_is_abs(value->rel)) { + syme = yasm_expr_create_ident(yasm_expr_int( + yasm_intnum_create_uint(0)), bc->line); + } else if (yasm_symrec_get_label(value->rel, &precbc) + && (sect = yasm_bc_get_section(precbc))) { + syme = yasm_expr_create_ident(yasm_expr_sym(value->rel), bc->line); + } else if ((ssymval = get_ssym_value(value->rel))) { + syme = yasm_expr_create_ident(yasm_expr_int( + yasm_intnum_copy(ssymval)), bc->line); + } else + goto done; + + /* Handle PC-relative */ + if (value->curpos_rel) { + yasm_expr *sube; + sube = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(bc), + yasm_expr_int(yasm_intnum_create_uint(bc->len*bc->mult_int)), + bc->line); + syme = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(syme), + yasm_expr_expr(sube), bc->line); + value->curpos_rel = 0; + value->ip_rel = 0; + } + + if (value->rshift > 0) + syme = yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr(syme), + yasm_expr_int(yasm_intnum_create_uint(rshift)), bc->line); + + /* Add into absolute portion */ + if (!value->abs) + value->abs = syme; + else + value->abs = + yasm_expr_create(YASM_EXPR_ADD, yasm_expr_expr(value->abs), + yasm_expr_expr(syme), bc->line); + value->rel = NULL; + value->rshift = 0; + } +done: + /* Simplify absolute portion of value, transforming symrecs */ + if (value->abs) + value->abs = yasm_expr__level_tree + (value->abs, 1, 1, 1, 0, bin_objfmt_expr_xform, NULL); + + /* Output */ + switch (yasm_value_output_basic(value, buf, destsize, bc, warn, + info->object->arch)) { + case -1: + return 1; + case 0: + break; + default: + return 0; + } + + /* Couldn't output, assume it contains an external reference. */ + yasm_error_set(YASM_ERROR_GENERAL, + N_("binary object format does not support external references")); + return 1; +} + +static int +bin_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) +{ + /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; + /*@null@*/ /*@only@*/ unsigned char *bigbuf; + unsigned long size = REGULAR_OUTBUF_SIZE; + int gap; + + assert(info != NULL); + + bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, + bin_objfmt_output_value, NULL); + + /* Don't bother doing anything else if size ended up being 0. */ + if (size == 0) { + if (bigbuf) + yasm_xfree(bigbuf); + return 0; + } + + /* Warn that gaps are converted to 0 and write out the 0's. */ + if (gap) { + unsigned long left; + yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, + N_("uninitialized space declared in code/data section: zeroing")); + /* Write out in chunks */ + memset(info->buf, 0, REGULAR_OUTBUF_SIZE); + left = size; + while (left > REGULAR_OUTBUF_SIZE) { + fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); + left -= REGULAR_OUTBUF_SIZE; + } + fwrite(info->buf, left, 1, info->f); + } else { + /* Output buf (or bigbuf if non-NULL) to file */ + fwrite(bigbuf ? bigbuf : info->buf, (size_t)size, 1, info->f); + } + + /* If bigbuf was allocated, free it */ + if (bigbuf) + yasm_xfree(bigbuf); + + return 0; +} + +/* Check to ensure bytecode is res* (for BSS sections) */ +static int +bin_objfmt_no_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) +{ + /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; + /*@null@*/ /*@only@*/ unsigned char *bigbuf; + unsigned long size = REGULAR_OUTBUF_SIZE; + int gap; + + assert(info != NULL); + + bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, + bin_objfmt_output_value, NULL); + + /* If bigbuf was allocated, free it */ + if (bigbuf) + yasm_xfree(bigbuf); + + /* Don't bother doing anything else if size ended up being 0. */ + if (size == 0) + return 0; + + /* Warn if not a gap. */ + if (!gap) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("initialized space declared in nobits section: ignoring")); + } + + return 0; +} + +static int +bin_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) +{ + bin_section_data *bsd = yasm_section_get_data(sect, &bin_section_data_cb); + /*@null@*/ bin_objfmt_output_info *info = (bin_objfmt_output_info *)d; + + assert(bsd != NULL); + assert(info != NULL); + + if (bsd->bss) { + yasm_section_bcs_traverse(sect, info->errwarns, + info, bin_objfmt_no_output_bytecode); + } else { + yasm_intnum_set(info->tmp_intn, bsd->istart); + yasm_intnum_calc(info->tmp_intn, YASM_EXPR_SUB, info->origin); + if (yasm_intnum_sign(info->tmp_intn) < 0) { + yasm_error_set(YASM_ERROR_VALUE, + N_("section `%s' starts before origin (ORG)"), + yasm_section_get_name(sect)); + yasm_errwarn_propagate(info->errwarns, 0); + return 0; + } + if (!yasm_intnum_check_size(info->tmp_intn, sizeof(long)*8, 0, 1)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("section `%s' start value too large"), + yasm_section_get_name(sect)); + yasm_errwarn_propagate(info->errwarns, 0); + return 0; + } + if (fseek(info->f, yasm_intnum_get_int(info->tmp_intn) + info->start, + SEEK_SET) < 0) + yasm__fatal(N_("could not seek on output file")); + yasm_section_bcs_traverse(sect, info->errwarns, + info, bin_objfmt_output_bytecode); + } + + return 0; +} + +static void +bin_objfmt_cleanup(bin_objfmt_output_info *info) +{ + bin_group *group, *group_temp; + + yasm_xfree(info->buf); + yasm_intnum_destroy(info->origin); + yasm_intnum_destroy(info->tmp_intn); + + TAILQ_FOREACH_SAFE(group, &info->lma_groups, link, group_temp) + bin_group_destroy(group); + + TAILQ_FOREACH_SAFE(group, &info->vma_groups, link, group_temp) + bin_group_destroy(group); +} + +static void +bin_objfmt_output(yasm_object *object, FILE *f, /*@unused@*/ int all_syms, + yasm_errwarns *errwarns) +{ + yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt; + bin_objfmt_output_info info; + bin_group *group, *lma_group, *vma_group, *group_temp; + yasm_intnum *start, *last, *vdelta; + bin_groups unsorted_groups, bss_groups; + + info.start = ftell(f); + + /* Set ORG to 0 unless otherwise specified */ + if (objfmt_bin->org) { + info.origin = yasm_expr_get_intnum(&objfmt_bin->org, 0); + if (!info.origin) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("ORG expression is too complex")); + yasm_errwarn_propagate(errwarns, objfmt_bin->org->line); + return; + } + if (yasm_intnum_sign(info.origin) < 0) { + yasm_error_set(YASM_ERROR_VALUE, N_("ORG expression is negative")); + yasm_errwarn_propagate(errwarns, objfmt_bin->org->line); + return; + } + info.origin = yasm_intnum_copy(info.origin); + } else + info.origin = yasm_intnum_create_uint(0); + + info.object = object; + info.errwarns = errwarns; + info.f = f; + info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); + info.tmp_intn = yasm_intnum_create_uint(0); + TAILQ_INIT(&info.lma_groups); + TAILQ_INIT(&info.vma_groups); + + /* Check symbol table */ + yasm_symtab_traverse(object->symtab, &info, bin_objfmt_check_sym); + + /* Create section groups */ + if (yasm_object_sections_traverse(object, &info, bin_lma_create_group)) { + bin_objfmt_cleanup(&info); + return; /* error detected */ + } + + /* Determine section order according to LMA. + * Sections can be ordered either by (priority): + * - follows + * - start + * - progbits/nobits setting + * - order in the input file + */ + + /* Look at each group with follows specified, and find the section + * that group is supposed to follow. + */ + TAILQ_FOREACH_SAFE(lma_group, &info.lma_groups, link, group_temp) { + if (lma_group->bsd->follows) { + bin_group *found; + /* Need to find group containing section this section follows. */ + found = + find_group_by_name(&info.lma_groups, lma_group->bsd->follows); + if (!found) { + yasm_error_set(YASM_ERROR_VALUE, + N_("section `%s' follows an invalid or unknown section `%s'"), + yasm_section_get_name(lma_group->section), + lma_group->bsd->follows); + yasm_errwarn_propagate(errwarns, 0); + bin_objfmt_cleanup(&info); + return; + } + + /* Check for loops */ + if (lma_group->section == found->section || + find_group_by_section(&lma_group->follow_groups, + found->section)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("follows loop between section `%s' and section `%s'"), + yasm_section_get_name(lma_group->section), + yasm_section_get_name(found->section)); + yasm_errwarn_propagate(errwarns, 0); + bin_objfmt_cleanup(&info); + return; + } + + /* Remove this section from main lma groups list */ + TAILQ_REMOVE(&info.lma_groups, lma_group, link); + /* Add it after the section it's supposed to follow. */ + TAILQ_INSERT_TAIL(&found->follow_groups, lma_group, link); + } + } + + /* Sort the top-level groups according to their start address. + * Use Shell sort for ease of implementation. + * If no start address is specified for a section, don't change the order, + * and move BSS sections to a separate list so they can be moved to the + * end of the lma list after all other sections are sorted. + */ + unsorted_groups = info.lma_groups; /* structure copy */ + TAILQ_INIT(&info.lma_groups); + TAILQ_INIT(&bss_groups); + TAILQ_FOREACH_SAFE(lma_group, &unsorted_groups, link, group_temp) { + bin_group *before; + + if (!lma_group->bsd->istart) { + if (lma_group->bsd->bss) + TAILQ_INSERT_TAIL(&bss_groups, lma_group, link); + else + TAILQ_INSERT_TAIL(&info.lma_groups, lma_group, link); + continue; + } + + before = NULL; + TAILQ_FOREACH(group, &info.lma_groups, link) { + if (!group->bsd->istart) + continue; + if (yasm_intnum_compare(group->bsd->istart, + lma_group->bsd->istart) > 0) { + before = group; + break; + } + } + if (before) + TAILQ_INSERT_BEFORE(before, lma_group, link); + else + TAILQ_INSERT_TAIL(&info.lma_groups, lma_group, link); + } + + /* Move the pure-BSS sections to the end of the LMA list. */ + TAILQ_FOREACH_SAFE(group, &bss_groups, link, group_temp) + TAILQ_INSERT_TAIL(&info.lma_groups, group, link); + TAILQ_INIT(&bss_groups); /* For sanity */ + + /* Assign a LMA start address to every section. + * Also assign VMA=LMA unless otherwise specified. + * + * We need to assign VMA=LMA here (while walking the tree) for the case: + * sect1 start=0 (size=0x11) + * sect2 follows=sect1 valign=16 (size=0x104) + * sect3 follows=sect2 valign=16 + * Where the valign of sect2 will result in a sect3 vaddr higher than a + * naive segment-by-segment interpretation (where sect3 and sect2 would + * have a VMA overlap). + * + * Algorithm for VMA=LMA setting: + * Start with delta=0. + * If there's no virtual attributes, we simply set VMA = LMA+delta. + * If there's only valign specified, we set VMA = aligned LMA, and add + * any new alignment difference to delta. + * + * We could do the LMA start and VMA=LMA steps in two separate steps, + * but it's easier to just recurse once. + */ + start = yasm_intnum_copy(info.origin); + last = yasm_intnum_copy(info.origin); + vdelta = yasm_intnum_create_uint(0); + TAILQ_FOREACH(lma_group, &info.lma_groups, link) { + if (lma_group->bsd->istart) + yasm_intnum_set(start, lma_group->bsd->istart); + group_assign_start_recurse(lma_group, start, last, vdelta, + info.tmp_intn, errwarns); + yasm_intnum_set(start, last); + } + yasm_intnum_destroy(last); + yasm_intnum_destroy(vdelta); + + /* + * Determine section order according to VMA + */ + + /* Create section groups */ + if (yasm_object_sections_traverse(object, &info, bin_vma_create_group)) { + yasm_intnum_destroy(start); + bin_objfmt_cleanup(&info); + return; /* error detected */ + } + + /* Look at each group with vfollows specified, and find the section + * that group is supposed to follow. + */ + TAILQ_FOREACH_SAFE(vma_group, &info.vma_groups, link, group_temp) { + if (vma_group->bsd->vfollows) { + bin_group *found; + /* Need to find group containing section this section follows. */ + found = find_group_by_name(&info.vma_groups, + vma_group->bsd->vfollows); + if (!found) { + yasm_error_set(YASM_ERROR_VALUE, + N_("section `%s' vfollows an invalid or unknown section `%s'"), + yasm_section_get_name(vma_group->section), + vma_group->bsd->vfollows); + yasm_errwarn_propagate(errwarns, 0); + yasm_intnum_destroy(start); + bin_objfmt_cleanup(&info); + return; + } + + /* Check for loops */ + if (vma_group->section == found->section || + find_group_by_section(&vma_group->follow_groups, + found->section)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("vfollows loop between section `%s' and section `%s'"), + yasm_section_get_name(vma_group->section), + yasm_section_get_name(found->section)); + yasm_errwarn_propagate(errwarns, 0); + bin_objfmt_cleanup(&info); + return; + } + + /* Remove this section from main lma groups list */ + TAILQ_REMOVE(&info.vma_groups, vma_group, link); + /* Add it after the section it's supposed to follow. */ + TAILQ_INSERT_TAIL(&found->follow_groups, vma_group, link); + } + } + + /* Due to the combination of steps above, we now know that all top-level + * groups have integer ivstart: + * Vstart Vfollows Valign Handled by + * No No No group_assign_start_recurse() + * No No Yes group_assign_start_recurse() + * No Yes - vfollows loop (above) + * Yes - - bin_lma_create_group() + */ + TAILQ_FOREACH(vma_group, &info.vma_groups, link) { + yasm_intnum_set(start, vma_group->bsd->ivstart); + group_assign_vstart_recurse(vma_group, start, errwarns); + } + + /* Output map file */ + output_map(&info); + + /* Ensure we don't have overlapping progbits LMAs. + * Use a dumb O(N^2) algorithm as the number of sections is essentially + * always low. + */ + if (yasm_object_sections_traverse(object, NULL, check_lma_overlap)) { + yasm_errwarn_propagate(errwarns, 0); + yasm_intnum_destroy(start); + bin_objfmt_cleanup(&info); + return; + } + + /* Output sections */ + yasm_object_sections_traverse(object, &info, bin_objfmt_output_section); + + /* Clean up */ + yasm_intnum_destroy(start); + bin_objfmt_cleanup(&info); +} + +static void +bin_objfmt_destroy(yasm_objfmt *objfmt) +{ + yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)objfmt; + if (objfmt_bin->map_filename) + yasm_xfree(objfmt_bin->map_filename); + yasm_expr_destroy(objfmt_bin->org); + yasm_xfree(objfmt); +} + +static void +define_section_symbol(yasm_symtab *symtab, yasm_section *sect, + const char *sectname, const char *suffix, + enum bin_ssym which, unsigned long line) +{ + yasm_symrec *sym; + bin_symrec_data *bsymd = yasm_xmalloc(sizeof(bin_symrec_data)); + char *symname = yasm_xmalloc(8+strlen(sectname)+strlen(suffix)+1); + + strcpy(symname, "section."); + strcat(symname, sectname); + strcat(symname, suffix); + + bsymd->section = sect; + bsymd->which = which; + + sym = yasm_symtab_declare(symtab, symname, YASM_SYM_EXTERN, line); + yasm_xfree(symname); + yasm_symrec_add_data(sym, &bin_symrec_data_cb, bsymd); +} + +static void +bin_objfmt_init_new_section(yasm_section *sect, unsigned long line) +{ + yasm_object *object = yasm_section_get_object(sect); + const char *sectname = yasm_section_get_name(sect); + /*yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt;*/ + bin_section_data *data; + + data = yasm_xmalloc(sizeof(bin_section_data)); + data->bss = 0; + data->align = NULL; + data->valign = NULL; + data->start = NULL; + data->vstart = NULL; + data->follows = NULL; + data->vfollows = NULL; + data->istart = NULL; + data->ivstart = NULL; + data->length = NULL; + yasm_section_add_data(sect, &bin_section_data_cb, data); + + define_section_symbol(object->symtab, sect, sectname, ".start", + SSYM_START, line); + define_section_symbol(object->symtab, sect, sectname, ".vstart", + SSYM_VSTART, line); + define_section_symbol(object->symtab, sect, sectname, ".length", + SSYM_LENGTH, line); +} + +static yasm_section * +bin_objfmt_add_default_section(yasm_object *object) +{ + yasm_section *retval; + int isnew; + + retval = yasm_object_get_general(object, ".text", 0, 1, 0, &isnew, 0); + if (isnew) + yasm_section_set_default(retval, 1); + return retval; +} + +/* GAS-style flags */ +static int +bin_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d, + /*@unused@*/ uintptr_t arg) +{ + /* TODO */ + return 0; +} + +static /*@observer@*/ /*@null@*/ yasm_section * +bin_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, + /*@unused@*/ /*@null@*/ + yasm_valparamhead *objext_valparams, + unsigned long line) +{ + yasm_valparam *vp; + yasm_section *retval; + int isnew; + int flags_override = 0; + const char *sectname; + bin_section_data *bsd = NULL; + + struct bin_section_switch_data { + /*@only@*/ /*@null@*/ char *follows; + /*@only@*/ /*@null@*/ char *vfollows; + /*@only@*/ /*@null@*/ yasm_expr *start; + /*@only@*/ /*@null@*/ yasm_expr *vstart; + /*@only@*/ /*@null@*/ yasm_intnum *align; + /*@only@*/ /*@null@*/ yasm_intnum *valign; + unsigned long bss; + unsigned long code; + } data; + + static const yasm_dir_help help[] = { + { "follows", 1, yasm_dir_helper_string, + offsetof(struct bin_section_switch_data, follows), 0 }, + { "vfollows", 1, yasm_dir_helper_string, + offsetof(struct bin_section_switch_data, vfollows), 0 }, + { "start", 1, yasm_dir_helper_expr, + offsetof(struct bin_section_switch_data, start), 0 }, + { "vstart", 1, yasm_dir_helper_expr, + offsetof(struct bin_section_switch_data, vstart), 0 }, + { "align", 1, yasm_dir_helper_intn, + offsetof(struct bin_section_switch_data, align), 0 }, + { "valign", 1, yasm_dir_helper_intn, + offsetof(struct bin_section_switch_data, valign), 0 }, + { "nobits", 0, yasm_dir_helper_flag_set, + offsetof(struct bin_section_switch_data, bss), 1 }, + { "progbits", 0, yasm_dir_helper_flag_set, + offsetof(struct bin_section_switch_data, bss), 0 }, + { "code", 0, yasm_dir_helper_flag_set, + offsetof(struct bin_section_switch_data, code), 1 }, + { "data", 0, yasm_dir_helper_flag_set, + offsetof(struct bin_section_switch_data, code), 0 }, + { "execute", 0, yasm_dir_helper_flag_set, + offsetof(struct bin_section_switch_data, code), 1 }, + { "noexecute", 0, yasm_dir_helper_flag_set, + offsetof(struct bin_section_switch_data, code), 0 }, + { "gasflags", 1, bin_helper_gasflags, 0, 0 } + }; + + vp = yasm_vps_first(valparams); + sectname = yasm_vp_string(vp); + if (!sectname) + return NULL; + vp = yasm_vps_next(vp); + + retval = yasm_object_find_general(object, sectname); + if (retval) { + bsd = yasm_section_get_data(retval, &bin_section_data_cb); + assert(bsd != NULL); + data.follows = bsd->follows; + data.vfollows = bsd->vfollows; + data.start = bsd->start; + data.vstart = bsd->vstart; + data.align = NULL; + data.valign = NULL; + data.bss = bsd->bss; + data.code = yasm_section_is_code(retval); + } else { + data.follows = NULL; + data.vfollows = NULL; + data.start = NULL; + data.vstart = NULL; + data.align = NULL; + data.valign = NULL; + data.bss = strcmp(sectname, ".bss") == 0; + data.code = strcmp(sectname, ".text") == 0; + } + + flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), + &data, yasm_dir_helper_valparam_warn); + if (flags_override < 0) + return NULL; /* error occurred */ + + if (data.start && data.follows) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("cannot combine `start' and `follows' section attributes")); + return NULL; + } + + if (data.vstart && data.vfollows) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("cannot combine `vstart' and `vfollows' section attributes")); + return NULL; + } + + if (data.align) { + unsigned long align = yasm_intnum_get_uint(data.align); + + /* Alignments must be a power of two. */ + if (!is_exp2(align)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("argument to `%s' is not a power of two"), + "align"); + return NULL; + } + } else + data.align = bsd ? bsd->align : NULL; + + if (data.valign) { + unsigned long valign = yasm_intnum_get_uint(data.valign); + + /* Alignments must be a power of two. */ + if (!is_exp2(valign)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("argument to `%s' is not a power of two"), + "valign"); + return NULL; + } + } else + data.valign = bsd ? bsd->valign : NULL; + + retval = yasm_object_get_general(object, sectname, 0, (int)data.code, + (int)data.bss, &isnew, line); + + bsd = yasm_section_get_data(retval, &bin_section_data_cb); + + if (isnew || yasm_section_is_default(retval)) { + yasm_section_set_default(retval, 0); + } + + /* Update section flags */ + bsd->bss = data.bss; + bsd->align = data.align; + bsd->valign = data.valign; + bsd->start = data.start; + bsd->vstart = data.vstart; + bsd->follows = data.follows; + bsd->vfollows = data.vfollows; + + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +bin_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + return NULL; +} + +static void +bin_objfmt_dir_org(yasm_object *object, + /*@null@*/ yasm_valparamhead *valparams, + /*@unused@*/ /*@null@*/ + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt; + yasm_valparam *vp; + + /* We only allow a single ORG in a program. */ + if (objfmt_bin->org) { + yasm_error_set(YASM_ERROR_GENERAL, N_("program origin redefined")); + return; + } + + /* ORG takes just a simple expression as param */ + vp = yasm_vps_first(valparams); + objfmt_bin->org = yasm_vp_expr(vp, object->symtab, line); + if (!objfmt_bin->org) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("argument to ORG must be expression")); + return; + } +} + +struct bin_dir_map_data { + unsigned long flags; + /*@only@*/ /*@null@*/ char *filename; +}; + +static int +dir_map_filename(void *obj, yasm_valparam *vp, unsigned long line, void *data) +{ + struct bin_dir_map_data *mdata = (struct bin_dir_map_data *)data; + const char *filename; + + if (mdata->filename) { + yasm_warn_set(YASM_WARN_GENERAL, N_("map file already specified")); + return 0; + } + + filename = yasm_vp_string(vp); + if (!filename) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("unexpected expression in [map]")); + return -1; + } + mdata->filename = yasm__xstrdup(filename); + + return 1; +} + +static void +bin_objfmt_dir_map(yasm_object *object, + /*@null@*/ yasm_valparamhead *valparams, + /*@unused@*/ /*@null@*/ + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *)object->objfmt; + + struct bin_dir_map_data data; + + static const yasm_dir_help help[] = { + { "all", 0, yasm_dir_helper_flag_or, + offsetof(struct bin_dir_map_data, flags), + MAP_BRIEF|MAP_SECTIONS|MAP_SYMBOLS }, + { "brief", 0, yasm_dir_helper_flag_or, + offsetof(struct bin_dir_map_data, flags), MAP_BRIEF }, + { "sections", 0, yasm_dir_helper_flag_or, + offsetof(struct bin_dir_map_data, flags), MAP_SECTIONS }, + { "segments", 0, yasm_dir_helper_flag_or, + offsetof(struct bin_dir_map_data, flags), MAP_SECTIONS }, + { "symbols", 0, yasm_dir_helper_flag_or, + offsetof(struct bin_dir_map_data, flags), MAP_SYMBOLS } + }; + + data.flags = objfmt_bin->map_flags | MAP_NONE; + data.filename = objfmt_bin->map_filename; + + if (valparams && yasm_dir_helper(object, yasm_vps_first(valparams), line, help, + NELEMS(help), &data, dir_map_filename) < 0) + return; /* error occurred */ + + objfmt_bin->map_flags = data.flags; + objfmt_bin->map_filename = data.filename; +} + +static void +bin_section_data_destroy(void *data) +{ + bin_section_data *bsd = (bin_section_data *)data; + if (bsd->start) + yasm_expr_destroy(bsd->start); + if (bsd->vstart) + yasm_expr_destroy(bsd->vstart); + if (bsd->follows) + yasm_xfree(bsd->follows); + if (bsd->vfollows) + yasm_xfree(bsd->vfollows); + if (bsd->istart) + yasm_intnum_destroy(bsd->istart); + if (bsd->ivstart) + yasm_intnum_destroy(bsd->ivstart); + if (bsd->length) + yasm_intnum_destroy(bsd->length); + yasm_xfree(data); +} + +static void +bin_section_data_print(void *data, FILE *f, int indent_level) +{ + bin_section_data *bsd = (bin_section_data *)data; + + fprintf(f, "%*sbss=%d\n", indent_level, "", bsd->bss); + + fprintf(f, "%*salign=", indent_level, ""); + if (bsd->align) + yasm_intnum_print(bsd->align, f); + else + fprintf(f, "(nil)"); + fprintf(f, "\n%*svalign=", indent_level, ""); + if (bsd->valign) + yasm_intnum_print(bsd->valign, f); + else + fprintf(f, "(nil)"); + + fprintf(f, "\n%*sstart=", indent_level, ""); + yasm_expr_print(bsd->start, f); + fprintf(f, "\n%*svstart=", indent_level, ""); + yasm_expr_print(bsd->vstart, f); + + fprintf(f, "\n%*sfollows=", indent_level, ""); + if (bsd->follows) + fprintf(f, "\"%s\"", bsd->follows); + else + fprintf(f, "(nil)"); + fprintf(f, "\n%*svfollows=", indent_level, ""); + if (bsd->vfollows) + fprintf(f, "\"%s\"", bsd->vfollows); + else + fprintf(f, "(nil)"); + + fprintf(f, "\n%*sistart=", indent_level, ""); + if (bsd->istart) + yasm_intnum_print(bsd->istart, f); + else + fprintf(f, "(nil)"); + fprintf(f, "\n%*sivstart=", indent_level, ""); + if (bsd->ivstart) + yasm_intnum_print(bsd->ivstart, f); + else + fprintf(f, "(nil)"); + + fprintf(f, "\n%*slength=", indent_level, ""); + if (bsd->length) + yasm_intnum_print(bsd->length, f); + else + fprintf(f, "(nil)"); + fprintf(f, "\n"); +} + +static void +bin_symrec_data_destroy(void *data) +{ + yasm_xfree(data); +} + +static void +bin_symrec_data_print(void *data, FILE *f, int indent_level) +{ + bin_symrec_data *bsymd = (bin_symrec_data *)data; + + fprintf(f, "%*ssection=\"%s\"\n", indent_level, "", + yasm_section_get_name(bsymd->section)); + fprintf(f, "%*swhich=", indent_level, ""); + switch (bsymd->which) { + case SSYM_START: fprintf(f, "START"); break; + case SSYM_VSTART: fprintf(f, "VSTART"); break; + case SSYM_LENGTH: fprintf(f, "LENGTH"); break; + } + fprintf(f, "\n"); +} + + +/* Define valid debug formats to use with this object format */ +static const char *bin_objfmt_dbgfmt_keywords[] = { + "null", + NULL +}; + +static const yasm_directive bin_objfmt_directives[] = { + { "org", "nasm", bin_objfmt_dir_org, YASM_DIR_ARG_REQUIRED }, + { "map", "nasm", bin_objfmt_dir_map, YASM_DIR_ANY }, + { NULL, NULL, NULL, 0 } +}; + +static const char *bin_nasm_stdmac[] = { + "%imacro org 1+.nolist", + "[org %1]", + "%endmacro", + NULL +}; + +static const yasm_stdmac bin_objfmt_stdmacs[] = { + { "nasm", "nasm", bin_nasm_stdmac }, + { "tasm", "tasm", bin_nasm_stdmac }, + { NULL, NULL, NULL } +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_bin_LTX_objfmt = { + "Flat format binary", + "bin", + NULL, + 16, + 0, + bin_objfmt_dbgfmt_keywords, + "null", + bin_objfmt_directives, + bin_objfmt_stdmacs, + bin_objfmt_create, + bin_objfmt_output, + bin_objfmt_destroy, + bin_objfmt_add_default_section, + bin_objfmt_init_new_section, + bin_objfmt_section_switch, + bin_objfmt_get_special_sym +}; + +#define EXE_HEADER_SIZE 0x200 + +/* DOS .EXE binaries are just raw binaries with a header */ +yasm_objfmt_module yasm_dosexe_LTX_objfmt; + +static yasm_objfmt * +dosexe_objfmt_create(yasm_object *object) +{ + yasm_objfmt_bin *objfmt_bin = (yasm_objfmt_bin *) bin_objfmt_create(object); + objfmt_bin->objfmt.module = &yasm_dosexe_LTX_objfmt; + return (yasm_objfmt *)objfmt_bin; +} + +static unsigned long +get_sym(yasm_object *object, const char *name) { + yasm_symrec *symrec = yasm_symtab_get(object->symtab, name); + yasm_bytecode *prevbc; + if (!symrec) + return 0; + if (!yasm_symrec_get_label(symrec, &prevbc)) + return 0; + return prevbc->offset + prevbc->len; +} + +static void +dosexe_objfmt_output(yasm_object *object, FILE *f, /*@unused@*/ int all_syms, + yasm_errwarns *errwarns) +{ + unsigned long tot_size, size, bss_size; + unsigned long start, bss; + unsigned char c; + + fseek(f, EXE_HEADER_SIZE, SEEK_SET); + + bin_objfmt_output(object, f, all_syms, errwarns); + + tot_size = ftell(f); + + /* if there is a __bss_start symbol, data after it is 0, no need to write + * it. */ + bss = get_sym(object, "__bss_start"); + if (bss) + size = bss; + else + size = tot_size; + bss_size = tot_size - size; +#ifdef HAVE_FTRUNCATE + if (size != tot_size) + ftruncate(fileno(f), EXE_HEADER_SIZE + size); +#endif + fseek(f, 0, SEEK_SET); + + /* magic */ + fwrite("MZ", 1, 2, f); + + /* file size */ + c = size & 0xff; + fwrite(&c, 1, 1, f); + c = !!(size & 0x100); + fwrite(&c, 1, 1, f); + c = ((size + 511) >> 9) & 0xff; + fwrite(&c, 1, 1, f); + c = ((size + 511) >> 17) & 0xff; + fwrite(&c, 1, 1, f); + + /* relocation # */ + c = 0; + fwrite(&c, 1, 1, f); + fwrite(&c, 1, 1, f); + + /* header size */ + c = EXE_HEADER_SIZE / 16; + fwrite(&c, 1, 1, f); + c = 0; + fwrite(&c, 1, 1, f); + + /* minimum paragraph # */ + bss_size = (bss_size + 15) >> 4; + c = bss_size & 0xff; + fwrite(&c, 1, 1, f); + c = (bss_size >> 8) & 0xff; + fwrite(&c, 1, 1, f); + + /* maximum paragraph # */ + c = 0xFF; + fwrite(&c, 1, 1, f); + fwrite(&c, 1, 1, f); + + /* relative value of stack segment */ + c = 0; + fwrite(&c, 1, 1, f); + fwrite(&c, 1, 1, f); + + /* SP at start */ + c = 0; + fwrite(&c, 1, 1, f); + fwrite(&c, 1, 1, f); + + /* header checksum */ + c = 0; + fwrite(&c, 1, 1, f); + fwrite(&c, 1, 1, f); + + /* IP at start */ + start = get_sym(object, "start"); + if (!start) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("%s: could not find symbol `start'")); + return; + } + c = start & 0xff; + fwrite(&c, 1, 1, f); + c = (start >> 8) & 0xff; + fwrite(&c, 1, 1, f); + + /* CS start */ + c = 0; + fwrite(&c, 1, 1, f); + fwrite(&c, 1, 1, f); + + /* reloc start */ + c = 0x22; + fwrite(&c, 1, 1, f); + c = 0; + fwrite(&c, 1, 1, f); + + /* Overlay number */ + c = 0; + fwrite(&c, 1, 1, f); + fwrite(&c, 1, 1, f); +} + + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_dosexe_LTX_objfmt = { + "DOS .EXE format binary", + "dosexe", + "exe", + 16, + 0, + bin_objfmt_dbgfmt_keywords, + "null", + bin_objfmt_directives, + bin_objfmt_stdmacs, + dosexe_objfmt_create, + dosexe_objfmt_output, + bin_objfmt_destroy, + bin_objfmt_add_default_section, + bin_objfmt_init_new_section, + bin_objfmt_section_switch, + bin_objfmt_get_special_sym +}; diff --git a/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c b/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c index 388b09af32..80d9567e05 100644 --- a/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c +++ b/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.c @@ -1,2522 +1,2522 @@ -/* - * COFF (DJGPP) object format - * - * Copyright (C) 2002-2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include <util.h> -#include <time.h> - -#include <libyasm.h> - -#include "coff-objfmt.h" - - -#define REGULAR_OUTBUF_SIZE 1024 - -/* Defining this to 0 sets all section VMA's to 0 rather than as the same as - * the LMA. According to the DJGPP COFF Spec, this should be set to 1 - * (VMA=LMA), and indeed DJGPP's GCC output shows VMA=LMA. However, NASM - * outputs VMA=0 (as if this was 0), and GNU objdump output looks a lot nicer - * with VMA=0. Who's right? This is #defined as changing this setting affects - * several places in the code. - */ -#define COFF_SET_VMA (!objfmt_coff->win32) - -#define COFF_MACHINE_I386 0x014C -#define COFF_MACHINE_AMD64 0x8664 - -#define COFF_F_LNNO 0x0004 /* line number info NOT present */ -#define COFF_F_LSYMS 0x0008 /* local symbols NOT present */ -#define COFF_F_AR32WR 0x0100 /* 32-bit little endian file */ - -typedef struct coff_reloc { - yasm_reloc reloc; - enum { - COFF_RELOC_ABSOLUTE = 0, /* absolute, no reloc needed */ - - /* I386 relocations */ - COFF_RELOC_I386_ADDR16 = 0x1, /* 16-bit absolute reference */ - COFF_RELOC_I386_REL16 = 0x2, /* 16-bit PC-relative reference */ - COFF_RELOC_I386_ADDR32 = 0x6, /* 32-bit absolute reference */ - COFF_RELOC_I386_ADDR32NB = 0x7, /* 32-bit absolute ref w/o base */ - COFF_RELOC_I386_SEG12 = 0x9, /* 16-bit absolute segment ref */ - COFF_RELOC_I386_SECTION = 0xA, /* section index */ - COFF_RELOC_I386_SECREL = 0xB, /* offset from start of segment */ - COFF_RELOC_I386_TOKEN = 0xC, /* CLR metadata token */ - COFF_RELOC_I386_SECREL7 = 0xD, /* 7-bit offset from base of sect */ - COFF_RELOC_I386_REL32 = 0x14, /* 32-bit PC-relative reference */ - - /* AMD64 relocations */ - COFF_RELOC_AMD64_ADDR64 = 0x1, /* 64-bit address (VA) */ - COFF_RELOC_AMD64_ADDR32 = 0x2, /* 32-bit address (VA) */ - COFF_RELOC_AMD64_ADDR32NB = 0x3, /* 32-bit address w/o base (RVA) */ - COFF_RELOC_AMD64_REL32 = 0x4, /* 32-bit relative (0 byte dist) */ - COFF_RELOC_AMD64_REL32_1 = 0x5, /* 32-bit relative (1 byte dist) */ - COFF_RELOC_AMD64_REL32_2 = 0x6, /* 32-bit relative (2 byte dist) */ - COFF_RELOC_AMD64_REL32_3 = 0x7, /* 32-bit relative (3 byte dist) */ - COFF_RELOC_AMD64_REL32_4 = 0x8, /* 32-bit relative (4 byte dist) */ - COFF_RELOC_AMD64_REL32_5 = 0x9, /* 32-bit relative (5 byte dist) */ - COFF_RELOC_AMD64_SECTION = 0xA, /* section index */ - COFF_RELOC_AMD64_SECREL = 0xB, /* 32-bit offset from base of sect */ - COFF_RELOC_AMD64_SECREL7 = 0xC, /* 7-bit offset from base of sect */ - COFF_RELOC_AMD64_TOKEN = 0xD /* CLR metadata token */ - } type; /* type of relocation */ -} coff_reloc; - -#define COFF_STYP_TEXT 0x00000020UL -#define COFF_STYP_DATA 0x00000040UL -#define COFF_STYP_BSS 0x00000080UL -#define COFF_STYP_INFO 0x00000200UL -#define COFF_STYP_STD_MASK 0x000003FFUL -#define COFF_STYP_ALIGN_MASK 0x00F00000UL -#define COFF_STYP_ALIGN_SHIFT 20 -#define COFF_STYP_NRELOC_OVFL 0x01000000UL -#define COFF_STYP_DISCARD 0x02000000UL -#define COFF_STYP_NOCACHE 0x04000000UL -#define COFF_STYP_NOPAGE 0x08000000UL -#define COFF_STYP_SHARED 0x10000000UL -#define COFF_STYP_EXECUTE 0x20000000UL -#define COFF_STYP_READ 0x40000000UL -#define COFF_STYP_WRITE 0x80000000UL -#define COFF_STYP_WIN32_MASK 0xFF000000UL - -#define COFF_FLAG_NOBASE (1UL<<0) /* Use no-base (NB) relocs */ - -typedef struct coff_section_data { - /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ - unsigned int scnum; /* section number (1=first section) */ - unsigned long flags; /* section flags (see COFF_STYP_* above) */ - unsigned long addr; /* starting memory address (first section -> 0) */ - unsigned long scnptr; /* file ptr to raw data */ - unsigned long size; /* size of raw data (section data) in bytes */ - unsigned long relptr; /* file ptr to relocation */ - unsigned long nreloc; /* number of relocation entries >64k -> error */ - unsigned long flags2; /* internal flags (see COFF_FLAG_* above) */ - unsigned long strtab_name; /* strtab offset of name if name > 8 chars */ - int isdebug; /* is a debug section? */ -} coff_section_data; - -typedef enum coff_symrec_sclass { - COFF_SCL_EFCN = 0xff, /* physical end of function */ - COFF_SCL_NULL = 0, - COFF_SCL_AUTO = 1, /* automatic variable */ - COFF_SCL_EXT = 2, /* external symbol */ - COFF_SCL_STAT = 3, /* static */ - COFF_SCL_REG = 4, /* register variable */ - COFF_SCL_EXTDEF = 5, /* external definition */ - COFF_SCL_LABEL = 6, /* label */ - COFF_SCL_ULABEL = 7, /* undefined label */ - COFF_SCL_MOS = 8, /* member of structure */ - COFF_SCL_ARG = 9, /* function argument */ - COFF_SCL_STRTAG = 10, /* structure tag */ - COFF_SCL_MOU = 11, /* member of union */ - COFF_SCL_UNTAG = 12, /* union tag */ - COFF_SCL_TPDEF = 13, /* type definition */ - COFF_SCL_USTATIC = 14, /* undefined static */ - COFF_SCL_ENTAG = 15, /* enumeration tag */ - COFF_SCL_MOE = 16, /* member of enumeration */ - COFF_SCL_REGPARM = 17, /* register parameter */ - COFF_SCL_FIELD = 18, /* bit field */ - COFF_SCL_AUTOARG = 19, /* auto argument */ - COFF_SCL_LASTENT = 20, /* dummy entry (end of block) */ - COFF_SCL_BLOCK = 100, /* ".bb" or ".eb" */ - COFF_SCL_FCN = 101, /* ".bf" or ".ef" */ - COFF_SCL_EOS = 102, /* end of structure */ - COFF_SCL_FILE = 103, /* file name */ - COFF_SCL_LINE = 104, /* line # reformatted as symbol table entry */ - COFF_SCL_ALIAS = 105, /* duplicate tag */ - COFF_SCL_HIDDEN = 106 /* ext symbol in dmert public lib */ -} coff_symrec_sclass; - -typedef union coff_symtab_auxent { - /* no data needed for section symbol auxent, all info avail from sym */ - /*@owned@*/ char *fname; /* filename aux entry */ -} coff_symtab_auxent; - -typedef enum coff_symtab_auxtype { - COFF_SYMTAB_AUX_NONE = 0, - COFF_SYMTAB_AUX_SECT, - COFF_SYMTAB_AUX_FILE -} coff_symtab_auxtype; - -typedef struct coff_symrec_data { - int forcevis; /* force visibility in symbol table */ - unsigned long index; /* assigned COFF symbol table index */ - unsigned int type; /* type */ - coff_symrec_sclass sclass; /* storage class */ - - int numaux; /* number of auxiliary entries */ - coff_symtab_auxtype auxtype; /* type of aux entries */ - coff_symtab_auxent aux[1]; /* actually may be any size (including 0) */ -} coff_symrec_data; - -typedef struct yasm_objfmt_coff { - yasm_objfmt_base objfmt; /* base structure */ - - unsigned int parse_scnum; /* sect numbering in parser */ - int win32; /* nonzero for win32/64 output */ - int win64; /* nonzero for win64 output */ - - unsigned int machine; /* COFF machine to use */ - - coff_symrec_data *filesym_data; /* Data for .file symbol */ - - /* data for .def/.endef and related directives */ - coff_symrec_data *def_sym; /* symbol specified by .def */ - - /* data for win64 proc_frame and related directives */ - unsigned long proc_frame; /* Line number of start of proc, or 0 */ - unsigned long done_prolog; /* Line number of end of prologue, or 0 */ - /*@null@*/ coff_unwind_info *unwind; /* Unwind info */ - - yasm_symrec *ssym_imagebase; /* ..imagebase symbol for win64 */ -} yasm_objfmt_coff; - -typedef struct coff_objfmt_output_info { - yasm_object *object; - yasm_objfmt_coff *objfmt_coff; - yasm_errwarns *errwarns; - /*@dependent@*/ FILE *f; - /*@only@*/ unsigned char *buf; - yasm_section *sect; - /*@dependent@*/ coff_section_data *csd; - unsigned long addr; /* start of next section */ - - unsigned long indx; /* current symbol index */ - int all_syms; /* outputting all symbols? */ - unsigned long strtab_offset; /* current string table offset */ -} coff_objfmt_output_info; - -static void coff_section_data_destroy(/*@only@*/ void *d); -static void coff_section_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback coff_section_data_cb = { - coff_section_data_destroy, - coff_section_data_print -}; - -static void coff_symrec_data_destroy(/*@only@*/ void *d); -static void coff_symrec_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback coff_symrec_data_cb = { - coff_symrec_data_destroy, - coff_symrec_data_print -}; - -/* Bytecode callback function prototypes */ -static void win32_sxdata_bc_destroy(void *contents); -static void win32_sxdata_bc_print(const void *contents, FILE *f, - int indent_level); -static int win32_sxdata_bc_calc_len - (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); -static int win32_sxdata_bc_tobytes - (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - /*@null@*/ yasm_output_reloc_func output_reloc); - -/* Bytecode callback structures */ -static const yasm_bytecode_callback win32_sxdata_bc_callback = { - win32_sxdata_bc_destroy, - win32_sxdata_bc_print, - yasm_bc_finalize_common, - NULL, - win32_sxdata_bc_calc_len, - yasm_bc_expand_common, - win32_sxdata_bc_tobytes, - 0 -}; - -yasm_objfmt_module yasm_coff_LTX_objfmt; -yasm_objfmt_module yasm_win32_LTX_objfmt; -yasm_objfmt_module yasm_win64_LTX_objfmt; - - -static /*@dependent@*/ coff_symrec_data * -coff_objfmt_sym_set_data(yasm_symrec *sym, coff_symrec_sclass sclass, - int numaux, coff_symtab_auxtype auxtype) -{ - coff_symrec_data *sym_data; - - sym_data = yasm_xmalloc(sizeof(coff_symrec_data) + - (numaux-1)*sizeof(coff_symtab_auxent)); - sym_data->forcevis = 0; - sym_data->index = 0; - sym_data->type = 0; - sym_data->sclass = sclass; - sym_data->numaux = numaux; - sym_data->auxtype = auxtype; - - yasm_symrec_add_data(sym, &coff_symrec_data_cb, sym_data); - - return sym_data; -} - -static yasm_objfmt_coff * -coff_common_create(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = yasm_xmalloc(sizeof(yasm_objfmt_coff)); - yasm_symrec *filesym; - - /* Only support x86 arch */ - if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") != 0) { - yasm_xfree(objfmt_coff); - return NULL; - } - - objfmt_coff->parse_scnum = 1; /* section numbering starts at 1 */ - - /* FIXME: misuse of NULL bytecode here; it works, but only barely. */ - filesym = yasm_symtab_define_special(object->symtab, ".file", - YASM_SYM_GLOBAL); - objfmt_coff->filesym_data = - coff_objfmt_sym_set_data(filesym, COFF_SCL_FILE, 1, - COFF_SYMTAB_AUX_FILE); - /* Filename is set in coff_objfmt_output */ - objfmt_coff->filesym_data->aux[0].fname = NULL; - - objfmt_coff->proc_frame = 0; - objfmt_coff->done_prolog = 0; - objfmt_coff->unwind = NULL; - objfmt_coff->ssym_imagebase = NULL; - - return objfmt_coff; -} - -static yasm_objfmt * -coff_objfmt_create(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = coff_common_create(object); - - if (objfmt_coff) { - /* Support x86 and amd64 machines of x86 arch */ - if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), "x86") == 0) - objfmt_coff->machine = COFF_MACHINE_I386; - else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), - "amd64") == 0) - objfmt_coff->machine = COFF_MACHINE_AMD64; - else { - yasm_xfree(objfmt_coff); - return NULL; - } - - objfmt_coff->objfmt.module = &yasm_coff_LTX_objfmt; - objfmt_coff->win32 = 0; - objfmt_coff->win64 = 0; - } - return (yasm_objfmt *)objfmt_coff; -} - -static yasm_objfmt * -win32_objfmt_create(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = coff_common_create(object); - - if (objfmt_coff) { - /* Support x86 and amd64 machines of x86 arch. - * (amd64 machine supported for backwards compatibility) - */ - if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), - "x86") == 0) { - objfmt_coff->machine = COFF_MACHINE_I386; - objfmt_coff->objfmt.module = &yasm_win32_LTX_objfmt; - objfmt_coff->win64 = 0; - } else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), - "amd64") == 0) { - objfmt_coff->machine = COFF_MACHINE_AMD64; - objfmt_coff->objfmt.module = &yasm_win64_LTX_objfmt; - objfmt_coff->win64 = 1; - } else { - yasm_xfree(objfmt_coff); - return NULL; - } - - objfmt_coff->win32 = 1; - /* Define a @feat.00 symbol for win32 safeseh handling */ - if (!objfmt_coff->win64) { - yasm_symrec *feat00; - coff_symrec_data *sym_data; - feat00 = yasm_symtab_define_equ(object->symtab, "@feat.00", - yasm_expr_create_ident(yasm_expr_int( - yasm_intnum_create_uint(1)), 0), 0); - sym_data = coff_objfmt_sym_set_data(feat00, COFF_SCL_STAT, 0, - COFF_SYMTAB_AUX_NONE); - sym_data->forcevis = 1; - } - } - return (yasm_objfmt *)objfmt_coff; -} - -static yasm_objfmt * -win64_objfmt_create(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = coff_common_create(object); - - if (objfmt_coff) { - /* Support amd64 machine of x86 arch */ - if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), - "amd64") == 0) { - objfmt_coff->machine = COFF_MACHINE_AMD64; - } else { - yasm_xfree(objfmt_coff); - return NULL; - } - - objfmt_coff->objfmt.module = &yasm_win64_LTX_objfmt; - objfmt_coff->win32 = 1; - objfmt_coff->win64 = 1; - objfmt_coff->ssym_imagebase = - yasm_symtab_define_label(object->symtab, "..imagebase", NULL, 0, 0); - } - return (yasm_objfmt *)objfmt_coff; -} - -static void -coff_objfmt_init_new_section(yasm_section *sect, unsigned long line) -{ - yasm_object *object = yasm_section_get_object(sect); - const char *sectname = yasm_section_get_name(sect); - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - coff_section_data *data; - yasm_symrec *sym; - - data = yasm_xmalloc(sizeof(coff_section_data)); - data->scnum = objfmt_coff->parse_scnum++; - data->flags = 0; - data->addr = 0; - data->scnptr = 0; - data->size = 0; - data->relptr = 0; - data->nreloc = 0; - data->flags2 = 0; - data->strtab_name = 0; - data->isdebug = 0; - - if (yasm__strncasecmp(sectname, ".debug", 6)==0) { - data->flags = COFF_STYP_DATA; - if (objfmt_coff->win32) - data->flags |= COFF_STYP_DISCARD|COFF_STYP_READ; - data->isdebug = 1; - } else - data->flags = COFF_STYP_TEXT; - - yasm_section_add_data(sect, &coff_section_data_cb, data); - - sym = yasm_symtab_define_label(object->symtab, sectname, - yasm_section_bcs_first(sect), 1, line); - yasm_symrec_declare(sym, YASM_SYM_GLOBAL, line); - coff_objfmt_sym_set_data(sym, COFF_SCL_STAT, 1, COFF_SYMTAB_AUX_SECT); - data->sym = sym; -} - -static int -coff_objfmt_set_section_addr(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ coff_section_data *csd; - - assert(info != NULL); - csd = yasm_section_get_data(sect, &coff_section_data_cb); - assert(csd != NULL); - - csd->addr = info->addr; - info->addr += yasm_bc_next_offset(yasm_section_bcs_last(sect)); - - return 0; -} - -static int -coff_objfmt_output_value(yasm_value *value, unsigned char *buf, - unsigned int destsize, unsigned long offset, - yasm_bytecode *bc, int warn, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_objfmt_coff *objfmt_coff; - /*@only@*/ /*@null@*/ yasm_intnum *dist = NULL; - /*@dependent@*/ /*@null@*/ yasm_intnum *intn; - unsigned long intn_val, intn_minus; - int retval; - unsigned int valsize = value->size; - - assert(info != NULL); - objfmt_coff = info->objfmt_coff; - - if (value->abs) - value->abs = yasm_expr_simplify(value->abs, 1); - - /* Try to output constant and PC-relative section-local first. - * Note this does NOT output any value with a SEG, WRT, external, - * cross-section, or non-PC-relative reference (those are handled below). - */ - switch (yasm_value_output_basic(value, buf, destsize, bc, warn, - info->object->arch)) { - case -1: - return 1; - case 0: - break; - default: - return 0; - } - - /* Handle other expressions, with relocation if necessary */ - if (value->rshift > 0 - || (value->seg_of && (value->wrt || value->curpos_rel)) - || (value->section_rel && (value->wrt || value->curpos_rel))) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: relocation too complex")); - return 1; - } - - intn_val = 0; - intn_minus = 0; - if (value->rel) { - yasm_sym_vis vis = yasm_symrec_get_visibility(value->rel); - /*@dependent@*/ /*@null@*/ yasm_symrec *sym = value->rel; - unsigned long addr; - coff_reloc *reloc; - int nobase = info->csd->flags2 & COFF_FLAG_NOBASE; - - /* Sometimes we want the relocation to be generated against one - * symbol but the value generated correspond to a different symbol. - * This is done through (sym being referenced) WRT (sym used for - * reloc). Note both syms need to be in the same section! - */ - if (value->wrt && value->wrt == objfmt_coff->ssym_imagebase) - nobase = 1; - else if (value->wrt) { - /*@dependent@*/ /*@null@*/ yasm_bytecode *rel_precbc, *wrt_precbc; - - if (!yasm_symrec_get_label(sym, &rel_precbc) - || !yasm_symrec_get_label(value->wrt, &wrt_precbc)) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: wrt expression too complex")); - return 1; - } - dist = yasm_calc_bc_dist(wrt_precbc, rel_precbc); - if (!dist) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: cannot wrt across sections")); - return 1; - } - sym = value->wrt; - } - - if (vis & YASM_SYM_COMMON) { - /* In standard COFF, COMMON symbols have their length added in */ - if (!objfmt_coff->win32) { - /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; - /*@dependent@*/ /*@null@*/ yasm_intnum *common_size; - - csize_expr = yasm_symrec_get_common_size(sym); - assert(csize_expr != NULL); - common_size = yasm_expr_get_intnum(csize_expr, 1); - if (!common_size) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: common size too complex")); - return 1; - } - - if (yasm_intnum_sign(common_size) < 0) { - yasm_error_set(YASM_ERROR_VALUE, - N_("coff: common size is negative")); - return 1; - } - - intn_val += yasm_intnum_get_uint(common_size); - } - } else if (!(vis & YASM_SYM_EXTERN) && !objfmt_coff->win64) { - /*@dependent@*/ /*@null@*/ yasm_bytecode *sym_precbc; - - /* Local symbols need relocation to their section's start */ - if (yasm_symrec_get_label(sym, &sym_precbc)) { - yasm_section *sym_sect = yasm_bc_get_section(sym_precbc); - /*@null@*/ coff_section_data *sym_csd; - sym_csd = yasm_section_get_data(sym_sect, - &coff_section_data_cb); - assert(sym_csd != NULL); - sym = sym_csd->sym; - intn_val = yasm_bc_next_offset(sym_precbc); - if (COFF_SET_VMA) - intn_val += sym_csd->addr; - } - } - - if (value->curpos_rel) { - /* For standard COFF, need to adjust to start of section, e.g. - * subtract out the bytecode offset. - * For Win32 COFF, need to adjust based on value size and position. - * For Win64 COFF that's IP-relative, adjust to next bytecode; - * the difference between the offset+destsize and BC length is - * taken care of by special relocation types. - */ - if (objfmt_coff->win64 && value->ip_rel) - intn_val += bc->len*bc->mult_int; - else if (objfmt_coff->win32) - intn_val += offset+destsize; - else - intn_minus = bc->offset; - } - - if (value->seg_of) { - /* Segment generation; zero value. */ - intn_val = 0; - intn_minus = 0; - } - - /* Generate reloc */ - reloc = yasm_xmalloc(sizeof(coff_reloc)); - addr = bc->offset + offset; - if (COFF_SET_VMA) - addr += info->addr; - reloc->reloc.addr = yasm_intnum_create_uint(addr); - reloc->reloc.sym = sym; - - if (value->curpos_rel) { - if (objfmt_coff->machine == COFF_MACHINE_I386) { - if (valsize == 32) - reloc->type = COFF_RELOC_I386_REL32; - else { - yasm_error_set(YASM_ERROR_TYPE, - N_("coff: invalid relocation size")); - return 1; - } - } else if (objfmt_coff->machine == COFF_MACHINE_AMD64) { - if (valsize != 32) { - yasm_error_set(YASM_ERROR_TYPE, - N_("coff: invalid relocation size")); - return 1; - } - if (!value->ip_rel) - reloc->type = COFF_RELOC_AMD64_REL32; - else switch (bc->len*bc->mult_int - (offset+destsize)) { - case 0: - reloc->type = COFF_RELOC_AMD64_REL32; - break; - case 1: - reloc->type = COFF_RELOC_AMD64_REL32_1; - break; - case 2: - reloc->type = COFF_RELOC_AMD64_REL32_2; - break; - case 3: - reloc->type = COFF_RELOC_AMD64_REL32_3; - break; - case 4: - reloc->type = COFF_RELOC_AMD64_REL32_4; - break; - case 5: - reloc->type = COFF_RELOC_AMD64_REL32_5; - break; - default: - yasm_error_set(YASM_ERROR_TYPE, - N_("coff: invalid relocation size")); - return 1; - } - } else - yasm_internal_error(N_("coff objfmt: unrecognized machine")); - } else if (value->seg_of) { - if (objfmt_coff->machine == COFF_MACHINE_I386) - reloc->type = COFF_RELOC_I386_SECTION; - else if (objfmt_coff->machine == COFF_MACHINE_AMD64) - reloc->type = COFF_RELOC_AMD64_SECTION; - else - yasm_internal_error(N_("coff objfmt: unrecognized machine")); - } else if (value->section_rel) { - if (objfmt_coff->machine == COFF_MACHINE_I386) - reloc->type = COFF_RELOC_I386_SECREL; - else if (objfmt_coff->machine == COFF_MACHINE_AMD64) - reloc->type = COFF_RELOC_AMD64_SECREL; - else - yasm_internal_error(N_("coff objfmt: unrecognized machine")); - } else { - if (objfmt_coff->machine == COFF_MACHINE_I386) { - if (nobase) - reloc->type = COFF_RELOC_I386_ADDR32NB; - else - reloc->type = COFF_RELOC_I386_ADDR32; - } else if (objfmt_coff->machine == COFF_MACHINE_AMD64) { - if (valsize == 32) { - if (nobase) - reloc->type = COFF_RELOC_AMD64_ADDR32NB; - else - reloc->type = COFF_RELOC_AMD64_ADDR32; - } else if (valsize == 64) - reloc->type = COFF_RELOC_AMD64_ADDR64; - else { - yasm_error_set(YASM_ERROR_TYPE, - N_("coff: invalid relocation size")); - return 1; - } - } else - yasm_internal_error(N_("coff objfmt: unrecognized machine")); - } - info->csd->nreloc++; - yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); - } - - /* Build up final integer output from intn_val, intn_minus, value->abs, - * and dist. We do all this at the end to avoid creating temporary - * intnums above (except for dist). - */ - if (intn_minus <= intn_val) - intn = yasm_intnum_create_uint(intn_val-intn_minus); - else { - intn = yasm_intnum_create_uint(intn_minus-intn_val); - yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); - } - - if (value->abs) { - yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); - if (!intn2) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("coff: relocation too complex")); - yasm_intnum_destroy(intn); - if (dist) - yasm_intnum_destroy(dist); - return 1; - } - yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); - } - - if (dist) { - yasm_intnum_calc(intn, YASM_EXPR_ADD, dist); - yasm_intnum_destroy(dist); - } - - retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, - valsize, 0, bc, warn); - yasm_intnum_destroy(intn); - return retval; -} - -static int -coff_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - /*@null@*/ /*@only@*/ unsigned char *bigbuf; - unsigned long size = REGULAR_OUTBUF_SIZE; - int gap; - - assert(info != NULL); - - bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, - coff_objfmt_output_value, NULL); - - /* Don't bother doing anything else if size ended up being 0. */ - if (size == 0) { - if (bigbuf) - yasm_xfree(bigbuf); - return 0; - } - - info->csd->size += size; - - /* Warn that gaps are converted to 0 and write out the 0's. */ - if (gap) { - unsigned long left; - yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, - N_("uninitialized space declared in code/data section: zeroing")); - /* Write out in chunks */ - memset(info->buf, 0, REGULAR_OUTBUF_SIZE); - left = size; - while (left > REGULAR_OUTBUF_SIZE) { - fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); - left -= REGULAR_OUTBUF_SIZE; - } - fwrite(info->buf, left, 1, info->f); - } else { - /* Output buf (or bigbuf if non-NULL) to file */ - fwrite(bigbuf ? bigbuf : info->buf, (size_t)size, 1, info->f); - } - - /* If bigbuf was allocated, free it */ - if (bigbuf) - yasm_xfree(bigbuf); - - return 0; -} - -static int -coff_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ coff_section_data *csd; - long pos; - coff_reloc *reloc; - unsigned char *localbuf; - - assert(info != NULL); - csd = yasm_section_get_data(sect, &coff_section_data_cb); - assert(csd != NULL); - - /* Add to strtab if in win32 format and name > 8 chars */ - if (info->objfmt_coff->win32) { - size_t namelen = strlen(yasm_section_get_name(sect)); - if (namelen > 8) { - csd->strtab_name = info->strtab_offset; - info->strtab_offset += (unsigned long)(namelen + 1); - } - } - - if (!csd->isdebug) - csd->addr = info->addr; - - if ((csd->flags & COFF_STYP_STD_MASK) == COFF_STYP_BSS) { - /* Don't output BSS sections. - * TODO: Check for non-reserve bytecodes? - */ - pos = 0; /* position = 0 because it's not in the file */ - csd->size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); - } else { - pos = ftell(info->f); - if (pos == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return 1; - } - - info->sect = sect; - info->csd = csd; - yasm_section_bcs_traverse(sect, info->errwarns, info, - coff_objfmt_output_bytecode); - - /* Sanity check final section size */ - if (yasm_errwarns_num_errors(info->errwarns, 0) == 0 && - csd->size != yasm_bc_next_offset(yasm_section_bcs_last(sect))) - yasm_internal_error( - N_("coff: section computed size did not match actual size")); - } - - /* Empty? Go on to next section */ - if (csd->size == 0) - return 0; - - if (!csd->isdebug) - info->addr += csd->size; - csd->scnptr = (unsigned long)pos; - - /* No relocations to output? Go on to next section */ - if (csd->nreloc == 0) - return 0; - - pos = ftell(info->f); - if (pos == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return 1; - } - csd->relptr = (unsigned long)pos; - - /* If >=64K relocs (for Win32/64), we set a flag in the section header - * (NRELOC_OVFL) and the first relocation contains the number of relocs. - */ - if (csd->nreloc >= 64*1024 && info->objfmt_coff->win32) { - localbuf = info->buf; - YASM_WRITE_32_L(localbuf, csd->nreloc+1); /* address of relocation */ - YASM_WRITE_32_L(localbuf, 0); /* relocated symbol */ - YASM_WRITE_16_L(localbuf, 0); /* type of relocation */ - fwrite(info->buf, 10, 1, info->f); - } - - reloc = (coff_reloc *)yasm_section_relocs_first(sect); - while (reloc) { - /*@null@*/ coff_symrec_data *csymd; - localbuf = info->buf; - - csymd = yasm_symrec_get_data(reloc->reloc.sym, &coff_symrec_data_cb); - if (!csymd) - yasm_internal_error( - N_("coff: no symbol data for relocated symbol")); - - yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); - localbuf += 4; /* address of relocation */ - YASM_WRITE_32_L(localbuf, csymd->index); /* relocated symbol */ - YASM_WRITE_16_L(localbuf, reloc->type); /* type of relocation */ - fwrite(info->buf, 10, 1, info->f); - - reloc = (coff_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); - } - - return 0; -} - -static int -coff_objfmt_output_sectstr(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - const char *name; - size_t len; - - /* Add to strtab if in win32 format and name > 8 chars */ - if (!info->objfmt_coff->win32) - return 0; - - name = yasm_section_get_name(sect); - len = strlen(name); - if (len > 8) - fwrite(name, len+1, 1, info->f); - return 0; -} - -static int -coff_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_objfmt_coff *objfmt_coff; - /*@dependent@*/ /*@null@*/ coff_section_data *csd; - unsigned char *localbuf; - unsigned long align = yasm_section_get_align(sect); - - assert(info != NULL); - objfmt_coff = info->objfmt_coff; - csd = yasm_section_get_data(sect, &coff_section_data_cb); - assert(csd != NULL); - - /* Check to see if alignment is supported size */ - if (align > 8192) - align = 8192; - - /* Convert alignment into flags setting */ - csd->flags &= ~COFF_STYP_ALIGN_MASK; - while (align != 0) { - csd->flags += 1<<COFF_STYP_ALIGN_SHIFT; - align >>= 1; - } - - /* section name */ - localbuf = info->buf; - if (strlen(yasm_section_get_name(sect)) > 8) { - char namenum[30]; - sprintf(namenum, "/%ld", csd->strtab_name); - strncpy((char *)localbuf, namenum, 8); - } else - strncpy((char *)localbuf, yasm_section_get_name(sect), 8); - localbuf += 8; - if (csd->isdebug) { - YASM_WRITE_32_L(localbuf, 0); /* physical address */ - YASM_WRITE_32_L(localbuf, 0); /* virtual address */ - } else { - YASM_WRITE_32_L(localbuf, csd->addr); /* physical address */ - if (COFF_SET_VMA) - YASM_WRITE_32_L(localbuf, csd->addr);/* virtual address */ - else - YASM_WRITE_32_L(localbuf, 0); /* virtual address */ - } - YASM_WRITE_32_L(localbuf, csd->size); /* section size */ - YASM_WRITE_32_L(localbuf, csd->scnptr); /* file ptr to data */ - YASM_WRITE_32_L(localbuf, csd->relptr); /* file ptr to relocs */ - YASM_WRITE_32_L(localbuf, 0); /* file ptr to line nums */ - if (csd->nreloc >= 64*1024) { - /* Win32/64 has special handling for this case. */ - if (objfmt_coff->win32) - csd->flags |= COFF_STYP_NRELOC_OVFL; - else { - yasm_warn_set(YASM_WARN_GENERAL, - N_("too many relocations in section `%s'"), - yasm_section_get_name(sect)); - yasm_errwarn_propagate(info->errwarns, 0); - } - YASM_WRITE_16_L(localbuf, 0xFFFF); /* max out */ - } else - YASM_WRITE_16_L(localbuf, csd->nreloc); /* num of relocation entries */ - YASM_WRITE_16_L(localbuf, 0); /* num of line number entries */ - YASM_WRITE_32_L(localbuf, csd->flags); /* flags */ - fwrite(info->buf, 40, 1, info->f); - - return 0; -} - -static int -coff_objfmt_count_sym(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - coff_symrec_data *sym_data; - - assert(info != NULL); - - sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - - if (info->all_syms || vis != YASM_SYM_LOCAL || yasm_symrec_is_abs(sym) || - (sym_data && sym_data->forcevis)) { - /* Save index in symrec data */ - if (!sym_data) - sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, - COFF_SYMTAB_AUX_NONE); - /* Set storage class based on visibility if not already set */ - if (sym_data->sclass == COFF_SCL_NULL) { - if (vis & (YASM_SYM_EXTERN|YASM_SYM_GLOBAL|YASM_SYM_COMMON)) - sym_data->sclass = COFF_SCL_EXT; - else - sym_data->sclass = COFF_SCL_STAT; - } - - sym_data->index = info->indx; - - info->indx += sym_data->numaux + 1; - } - return 0; -} - -static int -coff_objfmt_output_sym(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - int is_abs = yasm_symrec_is_abs(sym); - /*@dependent@*/ /*@null@*/ coff_symrec_data *csymd; - yasm_valparamhead *objext_valparams = - yasm_symrec_get_objext_valparams(sym); - csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - - assert(info != NULL); - - /* Look for "function" flag on global syms */ - if (csymd && csymd->type == 0 && (vis & YASM_SYM_GLOBAL) != 0) { - if (objext_valparams) { - const char *id = yasm_vp_id(yasm_vps_first(objext_valparams)); - if (yasm__strcasecmp(id, "function") == 0) - csymd->type = 0x20; - } - } - - /* Don't output local syms unless outputting all syms */ - if (info->all_syms || vis != YASM_SYM_LOCAL || is_abs || - (csymd && csymd->forcevis)) { - /*@only*/ char *name; - const yasm_expr *equ_val; - const yasm_intnum *intn; - unsigned char *localbuf; - size_t len; - int aux; - unsigned long value = 0; - unsigned int scnum = 0xfffe; /* -2 = debugging symbol */ - /*@dependent@*/ /*@null@*/ yasm_section *sect; - /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; - unsigned long scnlen = 0; /* for sect auxent */ - unsigned long nreloc = 0; /* for sect auxent */ - yasm_objfmt_coff *objfmt_coff = info->objfmt_coff; - - if (is_abs) - name = yasm__xstrdup(".absolut"); - else - name = yasm_symrec_get_global_name(sym, info->object); - len = strlen(name); - - /* Get symrec's of_data (needed for storage class) */ - if (!csymd) - yasm_internal_error(N_("coff: expected sym data to be present")); - - /* Look at symrec for value/scnum/etc. */ - if (yasm_symrec_get_label(sym, &precbc)) { - if (precbc) - sect = yasm_bc_get_section(precbc); - else - sect = NULL; - /* it's a label: get value and offset. - * If there is not a section, leave as debugging symbol. - */ - if (sect) { - /*@dependent@*/ /*@null@*/ coff_section_data *csectd; - csectd = yasm_section_get_data(sect, &coff_section_data_cb); - if (csectd) { - scnum = csectd->scnum; - scnlen = csectd->size; - nreloc = csectd->nreloc; - if (COFF_SET_VMA) - value = csectd->addr; - } else - yasm_internal_error(N_("didn't understand section")); - if (precbc) - value += yasm_bc_next_offset(precbc); - } - } else if ((equ_val = yasm_symrec_get_equ(sym))) { - yasm_expr *equ_val_copy = yasm_expr_copy(equ_val); - intn = yasm_expr_get_intnum(&equ_val_copy, 1); - if (!intn) { - if (vis & YASM_SYM_GLOBAL) { - yasm_error_set(YASM_ERROR_NOT_CONSTANT, - N_("global EQU value not an integer expression")); - yasm_errwarn_propagate(info->errwarns, equ_val->line); - } - } else - value = yasm_intnum_get_uint(intn); - yasm_expr_destroy(equ_val_copy); - - scnum = 0xffff; /* -1 = absolute symbol */ - } else { - if (vis & YASM_SYM_COMMON) { - /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; - csize_expr = yasm_symrec_get_common_size(sym); - assert(csize_expr != NULL); - intn = yasm_expr_get_intnum(csize_expr, 1); - if (!intn) { - yasm_error_set(YASM_ERROR_NOT_CONSTANT, - N_("COMMON data size not an integer expression")); - yasm_errwarn_propagate(info->errwarns, - (*csize_expr)->line); - } else - value = yasm_intnum_get_uint(intn); - scnum = 0; - } - if (vis & YASM_SYM_EXTERN) - scnum = 0; - } - - localbuf = info->buf; - if (len > 8) { - YASM_WRITE_32_L(localbuf, 0); /* "zeros" field */ - YASM_WRITE_32_L(localbuf, info->strtab_offset); /* strtab offset */ - info->strtab_offset += (unsigned long)(len+1); - } else { - /* <8 chars, so no string table entry needed */ - strncpy((char *)localbuf, name, 8); - localbuf += 8; - } - YASM_WRITE_32_L(localbuf, value); /* value */ - YASM_WRITE_16_L(localbuf, scnum); /* section number */ - YASM_WRITE_16_L(localbuf, csymd->type); /* type */ - YASM_WRITE_8(localbuf, csymd->sclass); /* storage class */ - YASM_WRITE_8(localbuf, csymd->numaux); /* number of aux entries */ - fwrite(info->buf, 18, 1, info->f); - for (aux=0; aux<csymd->numaux; aux++) { - localbuf = info->buf; - memset(localbuf, 0, 18); - switch (csymd->auxtype) { - case COFF_SYMTAB_AUX_NONE: - break; - case COFF_SYMTAB_AUX_SECT: - YASM_WRITE_32_L(localbuf, scnlen); /* section length */ - YASM_WRITE_16_L(localbuf, nreloc); /* number relocs */ - YASM_WRITE_16_L(localbuf, 0); /* number line nums */ - break; - case COFF_SYMTAB_AUX_FILE: - len = strlen(csymd->aux[0].fname); - if (len > 14) { - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, info->strtab_offset); - info->strtab_offset += (unsigned long)(len+1); - } else - strncpy((char *)localbuf, csymd->aux[0].fname, 14); - break; - default: - yasm_internal_error( - N_("coff: unrecognized aux symtab type")); - } - fwrite(info->buf, 18, 1, info->f); - } - yasm_xfree(name); - } - return 0; -} - -static int -coff_objfmt_output_str(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - /*@dependent@*/ /*@null@*/ coff_symrec_data *csymd; - csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - - assert(info != NULL); - - /* Don't output local syms unless outputting all syms */ - if (info->all_syms || vis != YASM_SYM_LOCAL || - (csymd && csymd->forcevis)) { - /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); - size_t len = strlen(name); - int aux; - - if (!csymd) - yasm_internal_error(N_("coff: expected sym data to be present")); - - if (len > 8) - fwrite(name, len+1, 1, info->f); - for (aux=0; aux<csymd->numaux; aux++) { - switch (csymd->auxtype) { - case COFF_SYMTAB_AUX_FILE: - len = strlen(csymd->aux[0].fname); - if (len > 14) - fwrite(csymd->aux[0].fname, len+1, 1, info->f); - break; - default: - break; - } - } - yasm_xfree(name); - } - return 0; -} - -static void -coff_objfmt_output(yasm_object *object, FILE *f, int all_syms, - yasm_errwarns *errwarns) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - coff_objfmt_output_info info; - unsigned char *localbuf; - long pos; - unsigned long symtab_pos; - unsigned long symtab_count; - unsigned int flags; - unsigned long ts; - - if (objfmt_coff->proc_frame) { - yasm_error_set_xref(objfmt_coff->proc_frame, - N_("procedure started here")); - yasm_error_set(YASM_ERROR_GENERAL, - N_("end of file in procedure frame")); - yasm_errwarn_propagate(errwarns, 0); - return; - } - - if (objfmt_coff->filesym_data->aux[0].fname) - yasm_xfree(objfmt_coff->filesym_data->aux[0].fname); +/* + * COFF (DJGPP) object format + * + * Copyright (C) 2002-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <util.h> +#include <time.h> + +#include <libyasm.h> + +#include "coff-objfmt.h" + + +#define REGULAR_OUTBUF_SIZE 1024 + +/* Defining this to 0 sets all section VMA's to 0 rather than as the same as + * the LMA. According to the DJGPP COFF Spec, this should be set to 1 + * (VMA=LMA), and indeed DJGPP's GCC output shows VMA=LMA. However, NASM + * outputs VMA=0 (as if this was 0), and GNU objdump output looks a lot nicer + * with VMA=0. Who's right? This is #defined as changing this setting affects + * several places in the code. + */ +#define COFF_SET_VMA (!objfmt_coff->win32) + +#define COFF_MACHINE_I386 0x014C +#define COFF_MACHINE_AMD64 0x8664 + +#define COFF_F_LNNO 0x0004 /* line number info NOT present */ +#define COFF_F_LSYMS 0x0008 /* local symbols NOT present */ +#define COFF_F_AR32WR 0x0100 /* 32-bit little endian file */ + +typedef struct coff_reloc { + yasm_reloc reloc; + enum { + COFF_RELOC_ABSOLUTE = 0, /* absolute, no reloc needed */ + + /* I386 relocations */ + COFF_RELOC_I386_ADDR16 = 0x1, /* 16-bit absolute reference */ + COFF_RELOC_I386_REL16 = 0x2, /* 16-bit PC-relative reference */ + COFF_RELOC_I386_ADDR32 = 0x6, /* 32-bit absolute reference */ + COFF_RELOC_I386_ADDR32NB = 0x7, /* 32-bit absolute ref w/o base */ + COFF_RELOC_I386_SEG12 = 0x9, /* 16-bit absolute segment ref */ + COFF_RELOC_I386_SECTION = 0xA, /* section index */ + COFF_RELOC_I386_SECREL = 0xB, /* offset from start of segment */ + COFF_RELOC_I386_TOKEN = 0xC, /* CLR metadata token */ + COFF_RELOC_I386_SECREL7 = 0xD, /* 7-bit offset from base of sect */ + COFF_RELOC_I386_REL32 = 0x14, /* 32-bit PC-relative reference */ + + /* AMD64 relocations */ + COFF_RELOC_AMD64_ADDR64 = 0x1, /* 64-bit address (VA) */ + COFF_RELOC_AMD64_ADDR32 = 0x2, /* 32-bit address (VA) */ + COFF_RELOC_AMD64_ADDR32NB = 0x3, /* 32-bit address w/o base (RVA) */ + COFF_RELOC_AMD64_REL32 = 0x4, /* 32-bit relative (0 byte dist) */ + COFF_RELOC_AMD64_REL32_1 = 0x5, /* 32-bit relative (1 byte dist) */ + COFF_RELOC_AMD64_REL32_2 = 0x6, /* 32-bit relative (2 byte dist) */ + COFF_RELOC_AMD64_REL32_3 = 0x7, /* 32-bit relative (3 byte dist) */ + COFF_RELOC_AMD64_REL32_4 = 0x8, /* 32-bit relative (4 byte dist) */ + COFF_RELOC_AMD64_REL32_5 = 0x9, /* 32-bit relative (5 byte dist) */ + COFF_RELOC_AMD64_SECTION = 0xA, /* section index */ + COFF_RELOC_AMD64_SECREL = 0xB, /* 32-bit offset from base of sect */ + COFF_RELOC_AMD64_SECREL7 = 0xC, /* 7-bit offset from base of sect */ + COFF_RELOC_AMD64_TOKEN = 0xD /* CLR metadata token */ + } type; /* type of relocation */ +} coff_reloc; + +#define COFF_STYP_TEXT 0x00000020UL +#define COFF_STYP_DATA 0x00000040UL +#define COFF_STYP_BSS 0x00000080UL +#define COFF_STYP_INFO 0x00000200UL +#define COFF_STYP_STD_MASK 0x000003FFUL +#define COFF_STYP_ALIGN_MASK 0x00F00000UL +#define COFF_STYP_ALIGN_SHIFT 20 +#define COFF_STYP_NRELOC_OVFL 0x01000000UL +#define COFF_STYP_DISCARD 0x02000000UL +#define COFF_STYP_NOCACHE 0x04000000UL +#define COFF_STYP_NOPAGE 0x08000000UL +#define COFF_STYP_SHARED 0x10000000UL +#define COFF_STYP_EXECUTE 0x20000000UL +#define COFF_STYP_READ 0x40000000UL +#define COFF_STYP_WRITE 0x80000000UL +#define COFF_STYP_WIN32_MASK 0xFF000000UL + +#define COFF_FLAG_NOBASE (1UL<<0) /* Use no-base (NB) relocs */ + +typedef struct coff_section_data { + /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ + unsigned int scnum; /* section number (1=first section) */ + unsigned long flags; /* section flags (see COFF_STYP_* above) */ + unsigned long addr; /* starting memory address (first section -> 0) */ + unsigned long scnptr; /* file ptr to raw data */ + unsigned long size; /* size of raw data (section data) in bytes */ + unsigned long relptr; /* file ptr to relocation */ + unsigned long nreloc; /* number of relocation entries >64k -> error */ + unsigned long flags2; /* internal flags (see COFF_FLAG_* above) */ + unsigned long strtab_name; /* strtab offset of name if name > 8 chars */ + int isdebug; /* is a debug section? */ +} coff_section_data; + +typedef enum coff_symrec_sclass { + COFF_SCL_EFCN = 0xff, /* physical end of function */ + COFF_SCL_NULL = 0, + COFF_SCL_AUTO = 1, /* automatic variable */ + COFF_SCL_EXT = 2, /* external symbol */ + COFF_SCL_STAT = 3, /* static */ + COFF_SCL_REG = 4, /* register variable */ + COFF_SCL_EXTDEF = 5, /* external definition */ + COFF_SCL_LABEL = 6, /* label */ + COFF_SCL_ULABEL = 7, /* undefined label */ + COFF_SCL_MOS = 8, /* member of structure */ + COFF_SCL_ARG = 9, /* function argument */ + COFF_SCL_STRTAG = 10, /* structure tag */ + COFF_SCL_MOU = 11, /* member of union */ + COFF_SCL_UNTAG = 12, /* union tag */ + COFF_SCL_TPDEF = 13, /* type definition */ + COFF_SCL_USTATIC = 14, /* undefined static */ + COFF_SCL_ENTAG = 15, /* enumeration tag */ + COFF_SCL_MOE = 16, /* member of enumeration */ + COFF_SCL_REGPARM = 17, /* register parameter */ + COFF_SCL_FIELD = 18, /* bit field */ + COFF_SCL_AUTOARG = 19, /* auto argument */ + COFF_SCL_LASTENT = 20, /* dummy entry (end of block) */ + COFF_SCL_BLOCK = 100, /* ".bb" or ".eb" */ + COFF_SCL_FCN = 101, /* ".bf" or ".ef" */ + COFF_SCL_EOS = 102, /* end of structure */ + COFF_SCL_FILE = 103, /* file name */ + COFF_SCL_LINE = 104, /* line # reformatted as symbol table entry */ + COFF_SCL_ALIAS = 105, /* duplicate tag */ + COFF_SCL_HIDDEN = 106 /* ext symbol in dmert public lib */ +} coff_symrec_sclass; + +typedef union coff_symtab_auxent { + /* no data needed for section symbol auxent, all info avail from sym */ + /*@owned@*/ char *fname; /* filename aux entry */ +} coff_symtab_auxent; + +typedef enum coff_symtab_auxtype { + COFF_SYMTAB_AUX_NONE = 0, + COFF_SYMTAB_AUX_SECT, + COFF_SYMTAB_AUX_FILE +} coff_symtab_auxtype; + +typedef struct coff_symrec_data { + int forcevis; /* force visibility in symbol table */ + unsigned long index; /* assigned COFF symbol table index */ + unsigned int type; /* type */ + coff_symrec_sclass sclass; /* storage class */ + + int numaux; /* number of auxiliary entries */ + coff_symtab_auxtype auxtype; /* type of aux entries */ + coff_symtab_auxent aux[1]; /* actually may be any size (including 0) */ +} coff_symrec_data; + +typedef struct yasm_objfmt_coff { + yasm_objfmt_base objfmt; /* base structure */ + + unsigned int parse_scnum; /* sect numbering in parser */ + int win32; /* nonzero for win32/64 output */ + int win64; /* nonzero for win64 output */ + + unsigned int machine; /* COFF machine to use */ + + coff_symrec_data *filesym_data; /* Data for .file symbol */ + + /* data for .def/.endef and related directives */ + coff_symrec_data *def_sym; /* symbol specified by .def */ + + /* data for win64 proc_frame and related directives */ + unsigned long proc_frame; /* Line number of start of proc, or 0 */ + unsigned long done_prolog; /* Line number of end of prologue, or 0 */ + /*@null@*/ coff_unwind_info *unwind; /* Unwind info */ + + yasm_symrec *ssym_imagebase; /* ..imagebase symbol for win64 */ +} yasm_objfmt_coff; + +typedef struct coff_objfmt_output_info { + yasm_object *object; + yasm_objfmt_coff *objfmt_coff; + yasm_errwarns *errwarns; + /*@dependent@*/ FILE *f; + /*@only@*/ unsigned char *buf; + yasm_section *sect; + /*@dependent@*/ coff_section_data *csd; + unsigned long addr; /* start of next section */ + + unsigned long indx; /* current symbol index */ + int all_syms; /* outputting all symbols? */ + unsigned long strtab_offset; /* current string table offset */ +} coff_objfmt_output_info; + +static void coff_section_data_destroy(/*@only@*/ void *d); +static void coff_section_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback coff_section_data_cb = { + coff_section_data_destroy, + coff_section_data_print +}; + +static void coff_symrec_data_destroy(/*@only@*/ void *d); +static void coff_symrec_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback coff_symrec_data_cb = { + coff_symrec_data_destroy, + coff_symrec_data_print +}; + +/* Bytecode callback function prototypes */ +static void win32_sxdata_bc_destroy(void *contents); +static void win32_sxdata_bc_print(const void *contents, FILE *f, + int indent_level); +static int win32_sxdata_bc_calc_len + (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); +static int win32_sxdata_bc_tobytes + (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +/* Bytecode callback structures */ +static const yasm_bytecode_callback win32_sxdata_bc_callback = { + win32_sxdata_bc_destroy, + win32_sxdata_bc_print, + yasm_bc_finalize_common, + NULL, + win32_sxdata_bc_calc_len, + yasm_bc_expand_common, + win32_sxdata_bc_tobytes, + 0 +}; + +yasm_objfmt_module yasm_coff_LTX_objfmt; +yasm_objfmt_module yasm_win32_LTX_objfmt; +yasm_objfmt_module yasm_win64_LTX_objfmt; + + +static /*@dependent@*/ coff_symrec_data * +coff_objfmt_sym_set_data(yasm_symrec *sym, coff_symrec_sclass sclass, + int numaux, coff_symtab_auxtype auxtype) +{ + coff_symrec_data *sym_data; + + sym_data = yasm_xmalloc(sizeof(coff_symrec_data) + + (numaux-1)*sizeof(coff_symtab_auxent)); + sym_data->forcevis = 0; + sym_data->index = 0; + sym_data->type = 0; + sym_data->sclass = sclass; + sym_data->numaux = numaux; + sym_data->auxtype = auxtype; + + yasm_symrec_add_data(sym, &coff_symrec_data_cb, sym_data); + + return sym_data; +} + +static yasm_objfmt_coff * +coff_common_create(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = yasm_xmalloc(sizeof(yasm_objfmt_coff)); + yasm_symrec *filesym; + + /* Only support x86 arch */ + if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") != 0) { + yasm_xfree(objfmt_coff); + return NULL; + } + + objfmt_coff->parse_scnum = 1; /* section numbering starts at 1 */ + + /* FIXME: misuse of NULL bytecode here; it works, but only barely. */ + filesym = yasm_symtab_define_special(object->symtab, ".file", + YASM_SYM_GLOBAL); + objfmt_coff->filesym_data = + coff_objfmt_sym_set_data(filesym, COFF_SCL_FILE, 1, + COFF_SYMTAB_AUX_FILE); + /* Filename is set in coff_objfmt_output */ + objfmt_coff->filesym_data->aux[0].fname = NULL; + + objfmt_coff->proc_frame = 0; + objfmt_coff->done_prolog = 0; + objfmt_coff->unwind = NULL; + objfmt_coff->ssym_imagebase = NULL; + + return objfmt_coff; +} + +static yasm_objfmt * +coff_objfmt_create(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = coff_common_create(object); + + if (objfmt_coff) { + /* Support x86 and amd64 machines of x86 arch */ + if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), "x86") == 0) + objfmt_coff->machine = COFF_MACHINE_I386; + else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), + "amd64") == 0) + objfmt_coff->machine = COFF_MACHINE_AMD64; + else { + yasm_xfree(objfmt_coff); + return NULL; + } + + objfmt_coff->objfmt.module = &yasm_coff_LTX_objfmt; + objfmt_coff->win32 = 0; + objfmt_coff->win64 = 0; + } + return (yasm_objfmt *)objfmt_coff; +} + +static yasm_objfmt * +win32_objfmt_create(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = coff_common_create(object); + + if (objfmt_coff) { + /* Support x86 and amd64 machines of x86 arch. + * (amd64 machine supported for backwards compatibility) + */ + if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), + "x86") == 0) { + objfmt_coff->machine = COFF_MACHINE_I386; + objfmt_coff->objfmt.module = &yasm_win32_LTX_objfmt; + objfmt_coff->win64 = 0; + } else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), + "amd64") == 0) { + objfmt_coff->machine = COFF_MACHINE_AMD64; + objfmt_coff->objfmt.module = &yasm_win64_LTX_objfmt; + objfmt_coff->win64 = 1; + } else { + yasm_xfree(objfmt_coff); + return NULL; + } + + objfmt_coff->win32 = 1; + /* Define a @feat.00 symbol for win32 safeseh handling */ + if (!objfmt_coff->win64) { + yasm_symrec *feat00; + coff_symrec_data *sym_data; + feat00 = yasm_symtab_define_equ(object->symtab, "@feat.00", + yasm_expr_create_ident(yasm_expr_int( + yasm_intnum_create_uint(1)), 0), 0); + sym_data = coff_objfmt_sym_set_data(feat00, COFF_SCL_STAT, 0, + COFF_SYMTAB_AUX_NONE); + sym_data->forcevis = 1; + } + } + return (yasm_objfmt *)objfmt_coff; +} + +static yasm_objfmt * +win64_objfmt_create(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = coff_common_create(object); + + if (objfmt_coff) { + /* Support amd64 machine of x86 arch */ + if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), + "amd64") == 0) { + objfmt_coff->machine = COFF_MACHINE_AMD64; + } else { + yasm_xfree(objfmt_coff); + return NULL; + } + + objfmt_coff->objfmt.module = &yasm_win64_LTX_objfmt; + objfmt_coff->win32 = 1; + objfmt_coff->win64 = 1; + objfmt_coff->ssym_imagebase = + yasm_symtab_define_label(object->symtab, "..imagebase", NULL, 0, 0); + } + return (yasm_objfmt *)objfmt_coff; +} + +static void +coff_objfmt_init_new_section(yasm_section *sect, unsigned long line) +{ + yasm_object *object = yasm_section_get_object(sect); + const char *sectname = yasm_section_get_name(sect); + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + coff_section_data *data; + yasm_symrec *sym; + + data = yasm_xmalloc(sizeof(coff_section_data)); + data->scnum = objfmt_coff->parse_scnum++; + data->flags = 0; + data->addr = 0; + data->scnptr = 0; + data->size = 0; + data->relptr = 0; + data->nreloc = 0; + data->flags2 = 0; + data->strtab_name = 0; + data->isdebug = 0; + + if (yasm__strncasecmp(sectname, ".debug", 6)==0) { + data->flags = COFF_STYP_DATA; + if (objfmt_coff->win32) + data->flags |= COFF_STYP_DISCARD|COFF_STYP_READ; + data->isdebug = 1; + } else + data->flags = COFF_STYP_TEXT; + + yasm_section_add_data(sect, &coff_section_data_cb, data); + + sym = yasm_symtab_define_label(object->symtab, sectname, + yasm_section_bcs_first(sect), 1, line); + yasm_symrec_declare(sym, YASM_SYM_GLOBAL, line); + coff_objfmt_sym_set_data(sym, COFF_SCL_STAT, 1, COFF_SYMTAB_AUX_SECT); + data->sym = sym; +} + +static int +coff_objfmt_set_section_addr(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ coff_section_data *csd; + + assert(info != NULL); + csd = yasm_section_get_data(sect, &coff_section_data_cb); + assert(csd != NULL); + + csd->addr = info->addr; + info->addr += yasm_bc_next_offset(yasm_section_bcs_last(sect)); + + return 0; +} + +static int +coff_objfmt_output_value(yasm_value *value, unsigned char *buf, + unsigned int destsize, unsigned long offset, + yasm_bytecode *bc, int warn, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_objfmt_coff *objfmt_coff; + /*@only@*/ /*@null@*/ yasm_intnum *dist = NULL; + /*@dependent@*/ /*@null@*/ yasm_intnum *intn; + unsigned long intn_val, intn_minus; + int retval; + unsigned int valsize = value->size; + + assert(info != NULL); + objfmt_coff = info->objfmt_coff; + + if (value->abs) + value->abs = yasm_expr_simplify(value->abs, 1); + + /* Try to output constant and PC-relative section-local first. + * Note this does NOT output any value with a SEG, WRT, external, + * cross-section, or non-PC-relative reference (those are handled below). + */ + switch (yasm_value_output_basic(value, buf, destsize, bc, warn, + info->object->arch)) { + case -1: + return 1; + case 0: + break; + default: + return 0; + } + + /* Handle other expressions, with relocation if necessary */ + if (value->rshift > 0 + || (value->seg_of && (value->wrt || value->curpos_rel)) + || (value->section_rel && (value->wrt || value->curpos_rel))) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: relocation too complex")); + return 1; + } + + intn_val = 0; + intn_minus = 0; + if (value->rel) { + yasm_sym_vis vis = yasm_symrec_get_visibility(value->rel); + /*@dependent@*/ /*@null@*/ yasm_symrec *sym = value->rel; + unsigned long addr; + coff_reloc *reloc; + int nobase = info->csd->flags2 & COFF_FLAG_NOBASE; + + /* Sometimes we want the relocation to be generated against one + * symbol but the value generated correspond to a different symbol. + * This is done through (sym being referenced) WRT (sym used for + * reloc). Note both syms need to be in the same section! + */ + if (value->wrt && value->wrt == objfmt_coff->ssym_imagebase) + nobase = 1; + else if (value->wrt) { + /*@dependent@*/ /*@null@*/ yasm_bytecode *rel_precbc, *wrt_precbc; + + if (!yasm_symrec_get_label(sym, &rel_precbc) + || !yasm_symrec_get_label(value->wrt, &wrt_precbc)) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: wrt expression too complex")); + return 1; + } + dist = yasm_calc_bc_dist(wrt_precbc, rel_precbc); + if (!dist) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: cannot wrt across sections")); + return 1; + } + sym = value->wrt; + } + + if (vis & YASM_SYM_COMMON) { + /* In standard COFF, COMMON symbols have their length added in */ + if (!objfmt_coff->win32) { + /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; + /*@dependent@*/ /*@null@*/ yasm_intnum *common_size; + + csize_expr = yasm_symrec_get_common_size(sym); + assert(csize_expr != NULL); + common_size = yasm_expr_get_intnum(csize_expr, 1); + if (!common_size) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: common size too complex")); + return 1; + } + + if (yasm_intnum_sign(common_size) < 0) { + yasm_error_set(YASM_ERROR_VALUE, + N_("coff: common size is negative")); + return 1; + } + + intn_val += yasm_intnum_get_uint(common_size); + } + } else if (!(vis & YASM_SYM_EXTERN) && !objfmt_coff->win64) { + /*@dependent@*/ /*@null@*/ yasm_bytecode *sym_precbc; + + /* Local symbols need relocation to their section's start */ + if (yasm_symrec_get_label(sym, &sym_precbc)) { + yasm_section *sym_sect = yasm_bc_get_section(sym_precbc); + /*@null@*/ coff_section_data *sym_csd; + sym_csd = yasm_section_get_data(sym_sect, + &coff_section_data_cb); + assert(sym_csd != NULL); + sym = sym_csd->sym; + intn_val = yasm_bc_next_offset(sym_precbc); + if (COFF_SET_VMA) + intn_val += sym_csd->addr; + } + } + + if (value->curpos_rel) { + /* For standard COFF, need to adjust to start of section, e.g. + * subtract out the bytecode offset. + * For Win32 COFF, need to adjust based on value size and position. + * For Win64 COFF that's IP-relative, adjust to next bytecode; + * the difference between the offset+destsize and BC length is + * taken care of by special relocation types. + */ + if (objfmt_coff->win64 && value->ip_rel) + intn_val += bc->len*bc->mult_int; + else if (objfmt_coff->win32) + intn_val += offset+destsize; + else + intn_minus = bc->offset; + } + + if (value->seg_of) { + /* Segment generation; zero value. */ + intn_val = 0; + intn_minus = 0; + } + + /* Generate reloc */ + reloc = yasm_xmalloc(sizeof(coff_reloc)); + addr = bc->offset + offset; + if (COFF_SET_VMA) + addr += info->addr; + reloc->reloc.addr = yasm_intnum_create_uint(addr); + reloc->reloc.sym = sym; + + if (value->curpos_rel) { + if (objfmt_coff->machine == COFF_MACHINE_I386) { + if (valsize == 32) + reloc->type = COFF_RELOC_I386_REL32; + else { + yasm_error_set(YASM_ERROR_TYPE, + N_("coff: invalid relocation size")); + return 1; + } + } else if (objfmt_coff->machine == COFF_MACHINE_AMD64) { + if (valsize != 32) { + yasm_error_set(YASM_ERROR_TYPE, + N_("coff: invalid relocation size")); + return 1; + } + if (!value->ip_rel) + reloc->type = COFF_RELOC_AMD64_REL32; + else switch (bc->len*bc->mult_int - (offset+destsize)) { + case 0: + reloc->type = COFF_RELOC_AMD64_REL32; + break; + case 1: + reloc->type = COFF_RELOC_AMD64_REL32_1; + break; + case 2: + reloc->type = COFF_RELOC_AMD64_REL32_2; + break; + case 3: + reloc->type = COFF_RELOC_AMD64_REL32_3; + break; + case 4: + reloc->type = COFF_RELOC_AMD64_REL32_4; + break; + case 5: + reloc->type = COFF_RELOC_AMD64_REL32_5; + break; + default: + yasm_error_set(YASM_ERROR_TYPE, + N_("coff: invalid relocation size")); + return 1; + } + } else + yasm_internal_error(N_("coff objfmt: unrecognized machine")); + } else if (value->seg_of) { + if (objfmt_coff->machine == COFF_MACHINE_I386) + reloc->type = COFF_RELOC_I386_SECTION; + else if (objfmt_coff->machine == COFF_MACHINE_AMD64) + reloc->type = COFF_RELOC_AMD64_SECTION; + else + yasm_internal_error(N_("coff objfmt: unrecognized machine")); + } else if (value->section_rel) { + if (objfmt_coff->machine == COFF_MACHINE_I386) + reloc->type = COFF_RELOC_I386_SECREL; + else if (objfmt_coff->machine == COFF_MACHINE_AMD64) + reloc->type = COFF_RELOC_AMD64_SECREL; + else + yasm_internal_error(N_("coff objfmt: unrecognized machine")); + } else { + if (objfmt_coff->machine == COFF_MACHINE_I386) { + if (nobase) + reloc->type = COFF_RELOC_I386_ADDR32NB; + else + reloc->type = COFF_RELOC_I386_ADDR32; + } else if (objfmt_coff->machine == COFF_MACHINE_AMD64) { + if (valsize == 32) { + if (nobase) + reloc->type = COFF_RELOC_AMD64_ADDR32NB; + else + reloc->type = COFF_RELOC_AMD64_ADDR32; + } else if (valsize == 64) + reloc->type = COFF_RELOC_AMD64_ADDR64; + else { + yasm_error_set(YASM_ERROR_TYPE, + N_("coff: invalid relocation size")); + return 1; + } + } else + yasm_internal_error(N_("coff objfmt: unrecognized machine")); + } + info->csd->nreloc++; + yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); + } + + /* Build up final integer output from intn_val, intn_minus, value->abs, + * and dist. We do all this at the end to avoid creating temporary + * intnums above (except for dist). + */ + if (intn_minus <= intn_val) + intn = yasm_intnum_create_uint(intn_val-intn_minus); + else { + intn = yasm_intnum_create_uint(intn_minus-intn_val); + yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); + } + + if (value->abs) { + yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); + if (!intn2) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("coff: relocation too complex")); + yasm_intnum_destroy(intn); + if (dist) + yasm_intnum_destroy(dist); + return 1; + } + yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); + } + + if (dist) { + yasm_intnum_calc(intn, YASM_EXPR_ADD, dist); + yasm_intnum_destroy(dist); + } + + retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, + valsize, 0, bc, warn); + yasm_intnum_destroy(intn); + return retval; +} + +static int +coff_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + /*@null@*/ /*@only@*/ unsigned char *bigbuf; + unsigned long size = REGULAR_OUTBUF_SIZE; + int gap; + + assert(info != NULL); + + bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, + coff_objfmt_output_value, NULL); + + /* Don't bother doing anything else if size ended up being 0. */ + if (size == 0) { + if (bigbuf) + yasm_xfree(bigbuf); + return 0; + } + + info->csd->size += size; + + /* Warn that gaps are converted to 0 and write out the 0's. */ + if (gap) { + unsigned long left; + yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, + N_("uninitialized space declared in code/data section: zeroing")); + /* Write out in chunks */ + memset(info->buf, 0, REGULAR_OUTBUF_SIZE); + left = size; + while (left > REGULAR_OUTBUF_SIZE) { + fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); + left -= REGULAR_OUTBUF_SIZE; + } + fwrite(info->buf, left, 1, info->f); + } else { + /* Output buf (or bigbuf if non-NULL) to file */ + fwrite(bigbuf ? bigbuf : info->buf, (size_t)size, 1, info->f); + } + + /* If bigbuf was allocated, free it */ + if (bigbuf) + yasm_xfree(bigbuf); + + return 0; +} + +static int +coff_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ coff_section_data *csd; + long pos; + coff_reloc *reloc; + unsigned char *localbuf; + + assert(info != NULL); + csd = yasm_section_get_data(sect, &coff_section_data_cb); + assert(csd != NULL); + + /* Add to strtab if in win32 format and name > 8 chars */ + if (info->objfmt_coff->win32) { + size_t namelen = strlen(yasm_section_get_name(sect)); + if (namelen > 8) { + csd->strtab_name = info->strtab_offset; + info->strtab_offset += (unsigned long)(namelen + 1); + } + } + + if (!csd->isdebug) + csd->addr = info->addr; + + if ((csd->flags & COFF_STYP_STD_MASK) == COFF_STYP_BSS) { + /* Don't output BSS sections. + * TODO: Check for non-reserve bytecodes? + */ + pos = 0; /* position = 0 because it's not in the file */ + csd->size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); + } else { + pos = ftell(info->f); + if (pos == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return 1; + } + + info->sect = sect; + info->csd = csd; + yasm_section_bcs_traverse(sect, info->errwarns, info, + coff_objfmt_output_bytecode); + + /* Sanity check final section size */ + if (yasm_errwarns_num_errors(info->errwarns, 0) == 0 && + csd->size != yasm_bc_next_offset(yasm_section_bcs_last(sect))) + yasm_internal_error( + N_("coff: section computed size did not match actual size")); + } + + /* Empty? Go on to next section */ + if (csd->size == 0) + return 0; + + if (!csd->isdebug) + info->addr += csd->size; + csd->scnptr = (unsigned long)pos; + + /* No relocations to output? Go on to next section */ + if (csd->nreloc == 0) + return 0; + + pos = ftell(info->f); + if (pos == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return 1; + } + csd->relptr = (unsigned long)pos; + + /* If >=64K relocs (for Win32/64), we set a flag in the section header + * (NRELOC_OVFL) and the first relocation contains the number of relocs. + */ + if (csd->nreloc >= 64*1024 && info->objfmt_coff->win32) { + localbuf = info->buf; + YASM_WRITE_32_L(localbuf, csd->nreloc+1); /* address of relocation */ + YASM_WRITE_32_L(localbuf, 0); /* relocated symbol */ + YASM_WRITE_16_L(localbuf, 0); /* type of relocation */ + fwrite(info->buf, 10, 1, info->f); + } + + reloc = (coff_reloc *)yasm_section_relocs_first(sect); + while (reloc) { + /*@null@*/ coff_symrec_data *csymd; + localbuf = info->buf; + + csymd = yasm_symrec_get_data(reloc->reloc.sym, &coff_symrec_data_cb); + if (!csymd) + yasm_internal_error( + N_("coff: no symbol data for relocated symbol")); + + yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); + localbuf += 4; /* address of relocation */ + YASM_WRITE_32_L(localbuf, csymd->index); /* relocated symbol */ + YASM_WRITE_16_L(localbuf, reloc->type); /* type of relocation */ + fwrite(info->buf, 10, 1, info->f); + + reloc = (coff_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); + } + + return 0; +} + +static int +coff_objfmt_output_sectstr(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + const char *name; + size_t len; + + /* Add to strtab if in win32 format and name > 8 chars */ + if (!info->objfmt_coff->win32) + return 0; + + name = yasm_section_get_name(sect); + len = strlen(name); + if (len > 8) + fwrite(name, len+1, 1, info->f); + return 0; +} + +static int +coff_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_objfmt_coff *objfmt_coff; + /*@dependent@*/ /*@null@*/ coff_section_data *csd; + unsigned char *localbuf; + unsigned long align = yasm_section_get_align(sect); + + assert(info != NULL); + objfmt_coff = info->objfmt_coff; + csd = yasm_section_get_data(sect, &coff_section_data_cb); + assert(csd != NULL); + + /* Check to see if alignment is supported size */ + if (align > 8192) + align = 8192; + + /* Convert alignment into flags setting */ + csd->flags &= ~COFF_STYP_ALIGN_MASK; + while (align != 0) { + csd->flags += 1<<COFF_STYP_ALIGN_SHIFT; + align >>= 1; + } + + /* section name */ + localbuf = info->buf; + if (strlen(yasm_section_get_name(sect)) > 8) { + char namenum[30]; + sprintf(namenum, "/%ld", csd->strtab_name); + strncpy((char *)localbuf, namenum, 8); + } else + strncpy((char *)localbuf, yasm_section_get_name(sect), 8); + localbuf += 8; + if (csd->isdebug) { + YASM_WRITE_32_L(localbuf, 0); /* physical address */ + YASM_WRITE_32_L(localbuf, 0); /* virtual address */ + } else { + YASM_WRITE_32_L(localbuf, csd->addr); /* physical address */ + if (COFF_SET_VMA) + YASM_WRITE_32_L(localbuf, csd->addr);/* virtual address */ + else + YASM_WRITE_32_L(localbuf, 0); /* virtual address */ + } + YASM_WRITE_32_L(localbuf, csd->size); /* section size */ + YASM_WRITE_32_L(localbuf, csd->scnptr); /* file ptr to data */ + YASM_WRITE_32_L(localbuf, csd->relptr); /* file ptr to relocs */ + YASM_WRITE_32_L(localbuf, 0); /* file ptr to line nums */ + if (csd->nreloc >= 64*1024) { + /* Win32/64 has special handling for this case. */ + if (objfmt_coff->win32) + csd->flags |= COFF_STYP_NRELOC_OVFL; + else { + yasm_warn_set(YASM_WARN_GENERAL, + N_("too many relocations in section `%s'"), + yasm_section_get_name(sect)); + yasm_errwarn_propagate(info->errwarns, 0); + } + YASM_WRITE_16_L(localbuf, 0xFFFF); /* max out */ + } else + YASM_WRITE_16_L(localbuf, csd->nreloc); /* num of relocation entries */ + YASM_WRITE_16_L(localbuf, 0); /* num of line number entries */ + YASM_WRITE_32_L(localbuf, csd->flags); /* flags */ + fwrite(info->buf, 40, 1, info->f); + + return 0; +} + +static int +coff_objfmt_count_sym(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + coff_symrec_data *sym_data; + + assert(info != NULL); + + sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + + if (info->all_syms || vis != YASM_SYM_LOCAL || yasm_symrec_is_abs(sym) || + (sym_data && sym_data->forcevis)) { + /* Save index in symrec data */ + if (!sym_data) + sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, + COFF_SYMTAB_AUX_NONE); + /* Set storage class based on visibility if not already set */ + if (sym_data->sclass == COFF_SCL_NULL) { + if (vis & (YASM_SYM_EXTERN|YASM_SYM_GLOBAL|YASM_SYM_COMMON)) + sym_data->sclass = COFF_SCL_EXT; + else + sym_data->sclass = COFF_SCL_STAT; + } + + sym_data->index = info->indx; + + info->indx += sym_data->numaux + 1; + } + return 0; +} + +static int +coff_objfmt_output_sym(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + int is_abs = yasm_symrec_is_abs(sym); + /*@dependent@*/ /*@null@*/ coff_symrec_data *csymd; + yasm_valparamhead *objext_valparams = + yasm_symrec_get_objext_valparams(sym); + csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + + assert(info != NULL); + + /* Look for "function" flag on global syms */ + if (csymd && csymd->type == 0 && (vis & YASM_SYM_GLOBAL) != 0) { + if (objext_valparams) { + const char *id = yasm_vp_id(yasm_vps_first(objext_valparams)); + if (yasm__strcasecmp(id, "function") == 0) + csymd->type = 0x20; + } + } + + /* Don't output local syms unless outputting all syms */ + if (info->all_syms || vis != YASM_SYM_LOCAL || is_abs || + (csymd && csymd->forcevis)) { + /*@only*/ char *name; + const yasm_expr *equ_val; + const yasm_intnum *intn; + unsigned char *localbuf; + size_t len; + int aux; + unsigned long value = 0; + unsigned int scnum = 0xfffe; /* -2 = debugging symbol */ + /*@dependent@*/ /*@null@*/ yasm_section *sect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + unsigned long scnlen = 0; /* for sect auxent */ + unsigned long nreloc = 0; /* for sect auxent */ + yasm_objfmt_coff *objfmt_coff = info->objfmt_coff; + + if (is_abs) + name = yasm__xstrdup(".absolut"); + else + name = yasm_symrec_get_global_name(sym, info->object); + len = strlen(name); + + /* Get symrec's of_data (needed for storage class) */ + if (!csymd) + yasm_internal_error(N_("coff: expected sym data to be present")); + + /* Look at symrec for value/scnum/etc. */ + if (yasm_symrec_get_label(sym, &precbc)) { + if (precbc) + sect = yasm_bc_get_section(precbc); + else + sect = NULL; + /* it's a label: get value and offset. + * If there is not a section, leave as debugging symbol. + */ + if (sect) { + /*@dependent@*/ /*@null@*/ coff_section_data *csectd; + csectd = yasm_section_get_data(sect, &coff_section_data_cb); + if (csectd) { + scnum = csectd->scnum; + scnlen = csectd->size; + nreloc = csectd->nreloc; + if (COFF_SET_VMA) + value = csectd->addr; + } else + yasm_internal_error(N_("didn't understand section")); + if (precbc) + value += yasm_bc_next_offset(precbc); + } + } else if ((equ_val = yasm_symrec_get_equ(sym))) { + yasm_expr *equ_val_copy = yasm_expr_copy(equ_val); + intn = yasm_expr_get_intnum(&equ_val_copy, 1); + if (!intn) { + if (vis & YASM_SYM_GLOBAL) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("global EQU value not an integer expression")); + yasm_errwarn_propagate(info->errwarns, equ_val->line); + } + } else + value = yasm_intnum_get_uint(intn); + yasm_expr_destroy(equ_val_copy); + + scnum = 0xffff; /* -1 = absolute symbol */ + } else { + if (vis & YASM_SYM_COMMON) { + /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; + csize_expr = yasm_symrec_get_common_size(sym); + assert(csize_expr != NULL); + intn = yasm_expr_get_intnum(csize_expr, 1); + if (!intn) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("COMMON data size not an integer expression")); + yasm_errwarn_propagate(info->errwarns, + (*csize_expr)->line); + } else + value = yasm_intnum_get_uint(intn); + scnum = 0; + } + if (vis & YASM_SYM_EXTERN) + scnum = 0; + } + + localbuf = info->buf; + if (len > 8) { + YASM_WRITE_32_L(localbuf, 0); /* "zeros" field */ + YASM_WRITE_32_L(localbuf, info->strtab_offset); /* strtab offset */ + info->strtab_offset += (unsigned long)(len+1); + } else { + /* <8 chars, so no string table entry needed */ + strncpy((char *)localbuf, name, 8); + localbuf += 8; + } + YASM_WRITE_32_L(localbuf, value); /* value */ + YASM_WRITE_16_L(localbuf, scnum); /* section number */ + YASM_WRITE_16_L(localbuf, csymd->type); /* type */ + YASM_WRITE_8(localbuf, csymd->sclass); /* storage class */ + YASM_WRITE_8(localbuf, csymd->numaux); /* number of aux entries */ + fwrite(info->buf, 18, 1, info->f); + for (aux=0; aux<csymd->numaux; aux++) { + localbuf = info->buf; + memset(localbuf, 0, 18); + switch (csymd->auxtype) { + case COFF_SYMTAB_AUX_NONE: + break; + case COFF_SYMTAB_AUX_SECT: + YASM_WRITE_32_L(localbuf, scnlen); /* section length */ + YASM_WRITE_16_L(localbuf, nreloc); /* number relocs */ + YASM_WRITE_16_L(localbuf, 0); /* number line nums */ + break; + case COFF_SYMTAB_AUX_FILE: + len = strlen(csymd->aux[0].fname); + if (len > 14) { + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, info->strtab_offset); + info->strtab_offset += (unsigned long)(len+1); + } else + strncpy((char *)localbuf, csymd->aux[0].fname, 14); + break; + default: + yasm_internal_error( + N_("coff: unrecognized aux symtab type")); + } + fwrite(info->buf, 18, 1, info->f); + } + yasm_xfree(name); + } + return 0; +} + +static int +coff_objfmt_output_str(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ coff_objfmt_output_info *info = (coff_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + /*@dependent@*/ /*@null@*/ coff_symrec_data *csymd; + csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + + assert(info != NULL); + + /* Don't output local syms unless outputting all syms */ + if (info->all_syms || vis != YASM_SYM_LOCAL || + (csymd && csymd->forcevis)) { + /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); + size_t len = strlen(name); + int aux; + + if (!csymd) + yasm_internal_error(N_("coff: expected sym data to be present")); + + if (len > 8) + fwrite(name, len+1, 1, info->f); + for (aux=0; aux<csymd->numaux; aux++) { + switch (csymd->auxtype) { + case COFF_SYMTAB_AUX_FILE: + len = strlen(csymd->aux[0].fname); + if (len > 14) + fwrite(csymd->aux[0].fname, len+1, 1, info->f); + break; + default: + break; + } + } + yasm_xfree(name); + } + return 0; +} + +static void +coff_objfmt_output(yasm_object *object, FILE *f, int all_syms, + yasm_errwarns *errwarns) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + coff_objfmt_output_info info; + unsigned char *localbuf; + long pos; + unsigned long symtab_pos; + unsigned long symtab_count; + unsigned int flags; + unsigned long ts; + + if (objfmt_coff->proc_frame) { + yasm_error_set_xref(objfmt_coff->proc_frame, + N_("procedure started here")); + yasm_error_set(YASM_ERROR_GENERAL, + N_("end of file in procedure frame")); + yasm_errwarn_propagate(errwarns, 0); + return; + } + + if (objfmt_coff->filesym_data->aux[0].fname) + yasm_xfree(objfmt_coff->filesym_data->aux[0].fname); if (!object->deb_filename) { object->deb_filename = yasm_replace_path( objfmt_coff->objfmt.module->replace_map, objfmt_coff->objfmt.module->replace_map_size, object->src_filename, strlen(object->src_filename)); } - objfmt_coff->filesym_data->aux[0].fname = + objfmt_coff->filesym_data->aux[0].fname = yasm__xstrdup(object->deb_filename); - - /* Force all syms for win64 because they're needed for relocations. - * FIXME: Not *all* syms need to be output, only the ones needed for - * relocation. Find a way to do that someday. - */ - all_syms |= objfmt_coff->win64; - - info.strtab_offset = 4; - info.object = object; - info.objfmt_coff = objfmt_coff; - info.errwarns = errwarns; - info.f = f; - info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); - - /* Allocate space for headers by seeking forward */ - if (fseek(f, (long)(20+40*(objfmt_coff->parse_scnum-1)), SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - /* Finalize symbol table (assign index to each symbol) */ - info.indx = 0; - info.all_syms = all_syms; - yasm_symtab_traverse(object->symtab, &info, coff_objfmt_count_sym); - symtab_count = info.indx; - - /* Section data/relocs */ - if (COFF_SET_VMA) { - /* If we're setting the VMA, we need to do a first section pass to - * determine each section's addr value before actually outputting - * relocations, as a relocation's section address is added into the - * addends in the generated code. - */ - info.addr = 0; - if (yasm_object_sections_traverse(object, &info, - coff_objfmt_set_section_addr)) - return; - } - info.addr = 0; - if (yasm_object_sections_traverse(object, &info, - coff_objfmt_output_section)) - return; - - /* Symbol table */ - pos = ftell(f); - if (pos == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return; - } - symtab_pos = (unsigned long)pos; - yasm_symtab_traverse(object->symtab, &info, coff_objfmt_output_sym); - - /* String table */ - yasm_fwrite_32_l(info.strtab_offset, f); /* total length */ - yasm_object_sections_traverse(object, &info, coff_objfmt_output_sectstr); - yasm_symtab_traverse(object->symtab, &info, coff_objfmt_output_str); - - /* Write headers */ - if (fseek(f, 0, SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - localbuf = info.buf; - YASM_WRITE_16_L(localbuf, objfmt_coff->machine); /* magic number */ - YASM_WRITE_16_L(localbuf, objfmt_coff->parse_scnum-1);/* number of sects */ - if (getenv("YASM_TEST_SUITE")) - ts = 0; - else - ts = (unsigned long)time(NULL); - YASM_WRITE_32_L(localbuf, ts); /* time/date stamp */ - YASM_WRITE_32_L(localbuf, symtab_pos); /* file ptr to symtab */ - YASM_WRITE_32_L(localbuf, symtab_count); /* number of symtabs */ - YASM_WRITE_16_L(localbuf, 0); /* size of optional header (none) */ - /* flags */ - flags = 0; - if (strcmp(yasm_dbgfmt_keyword(object->dbgfmt), "null")==0) - flags = COFF_F_LNNO; - if (!all_syms) - flags |= COFF_F_LSYMS; - if (objfmt_coff->machine != COFF_MACHINE_AMD64) - flags |= COFF_F_AR32WR; - YASM_WRITE_16_L(localbuf, flags); - fwrite(info.buf, 20, 1, f); - - yasm_object_sections_traverse(object, &info, coff_objfmt_output_secthead); - - yasm_xfree(info.buf); -} - -static void -coff_objfmt_destroy(yasm_objfmt *objfmt) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)objfmt; - if (objfmt_coff->filesym_data->aux[0].fname) - yasm_xfree(objfmt_coff->filesym_data->aux[0].fname); - if (objfmt_coff->unwind) - yasm_win64__uwinfo_destroy(objfmt_coff->unwind); - yasm_xfree(objfmt); -} - -static yasm_section * -coff_objfmt_add_default_section(yasm_object *object) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_section *retval; - coff_section_data *csd; - int isnew; - - retval = yasm_object_get_general(object, ".text", 16, 1, 0, &isnew, 0); - if (isnew) { - csd = yasm_section_get_data(retval, &coff_section_data_cb); - csd->flags = COFF_STYP_TEXT; - if (objfmt_coff->win32) - csd->flags |= COFF_STYP_EXECUTE | COFF_STYP_READ; - yasm_section_set_default(retval, 1); - } - return retval; -} - -struct coff_section_switch_data { - int isdefault; - int gasflags; - unsigned long flags; - unsigned long flags2; - /*@only@*/ /*@null@*/ yasm_intnum *align_intn; -}; - -/* GAS-style flags */ -static int -coff_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d, - /*@unused@*/ uintptr_t arg) -{ - struct coff_section_switch_data *data = - (struct coff_section_switch_data *)d; - int alloc = 0, load = 0, readonly = 0, code = 0, datasect = 0; - int shared = 0; - const char *s = yasm_vp_string(vp); - size_t i; - - if (!s) { - yasm_error_set(YASM_ERROR_VALUE, N_("non-string section attribute")); - return -1; - } - - /* For GAS, default to read/write data */ - if (data->isdefault) - data->flags = COFF_STYP_TEXT | COFF_STYP_READ | COFF_STYP_WRITE; - - for (i=0; i<strlen(s); i++) { - switch (s[i]) { - case 'a': - break; - case 'b': - alloc = 1; - load = 0; - break; - case 'n': - load = 0; - break; - case 's': - shared = 1; - /*@fallthrough@*/ - case 'd': - datasect = 1; - load = 1; - readonly = 0; - break; - case 'x': - code = 1; - load = 1; - break; - case 'r': - datasect = 1; - load = 1; - readonly = 1; - break; - case 'w': - readonly = 0; - break; - default: - yasm_warn_set(YASM_WARN_GENERAL, - N_("unrecognized section attribute: `%c'"), - s[i]); - } - } - - if (code) - data->flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; - else if (datasect) - data->flags = COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE; - else if (readonly) - data->flags = COFF_STYP_DATA | COFF_STYP_READ; - else if (load) - data->flags = COFF_STYP_TEXT; - else if (alloc) - data->flags = COFF_STYP_BSS; - - if (shared) - data->flags |= COFF_STYP_SHARED; - - data->gasflags = 1; - return 0; -} - -static /*@observer@*/ /*@null@*/ yasm_section * -coff_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, - /*@unused@*/ /*@null@*/ - yasm_valparamhead *objext_valparams, - unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp; - yasm_section *retval; - int isnew; - int iscode = 0; - int flags_override; - const char *sectname; - char *realname; - int resonly = 0; - unsigned long align = 0; - coff_section_data *csd; - - struct coff_section_switch_data data; - - static const yasm_dir_help help[] = { - { "code", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ }, - { "text", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ }, - { "data", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE }, - { "rdata", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_DATA | COFF_STYP_READ }, - { "bss", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_BSS | COFF_STYP_READ | COFF_STYP_WRITE }, - { "info", 0, yasm_dir_helper_flag_set, - offsetof(struct coff_section_switch_data, flags), - COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ }, - { "gasflags", 1, coff_helper_gasflags, 0, 0 }, - /* Win32 only below this point */ - { "discard", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_DISCARD}, - { "nodiscard", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_DISCARD}, - { "cache", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOCACHE}, - { "nocache", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOCACHE}, - { "page", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOPAGE }, - { "nopage", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOPAGE }, - { "share", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_SHARED }, - { "noshare", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_SHARED }, - { "execute", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_EXECUTE}, - { "noexecute", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_EXECUTE}, - { "read", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_READ }, - { "noread", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_READ }, - { "write", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_WRITE }, - { "nowrite", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags), COFF_STYP_WRITE }, - { "base", 0, yasm_dir_helper_flag_and, - offsetof(struct coff_section_switch_data, flags2), COFF_FLAG_NOBASE}, - { "nobase", 0, yasm_dir_helper_flag_or, - offsetof(struct coff_section_switch_data, flags2), COFF_FLAG_NOBASE}, - { "align", 1, yasm_dir_helper_intn, - offsetof(struct coff_section_switch_data, align_intn), 0 } - }; - - vp = yasm_vps_first(valparams); - sectname = yasm_vp_string(vp); - if (!sectname) - return NULL; - vp = yasm_vps_next(vp); - - data.isdefault = 0; - data.gasflags = 0; - data.flags = 0; - data.flags2 = 0; - data.align_intn = NULL; - - if (strcmp(sectname, ".data") == 0) { - data.flags = COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE; - if (objfmt_coff->win32) { - if (objfmt_coff->machine == COFF_MACHINE_AMD64) - align = 16; - else - align = 4; - } - } else if (strcmp(sectname, ".bss") == 0) { - data.flags = COFF_STYP_BSS | COFF_STYP_READ | COFF_STYP_WRITE; - if (objfmt_coff->win32) { - if (objfmt_coff->machine == COFF_MACHINE_AMD64) - align = 16; - else - align = 4; - } - resonly = 1; - } else if (strcmp(sectname, ".text") == 0) { - data.flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; - if (objfmt_coff->win32) - align = 16; - } else if (strcmp(sectname, ".rdata") == 0 - || strncmp(sectname, ".rodata", 7) == 0 - || strncmp(sectname, ".rdata$", 7) == 0) { - data.flags = COFF_STYP_DATA | COFF_STYP_READ; - if (objfmt_coff->win32) - align = 8; - else - yasm_warn_set(YASM_WARN_GENERAL, - N_("Standard COFF does not support read-only data sections")); - } else if (strcmp(sectname, ".drectve") == 0) { - data.flags = COFF_STYP_INFO; - if (objfmt_coff->win32) - data.flags |= COFF_STYP_DISCARD | COFF_STYP_READ; - } else if (objfmt_coff->win64 && strcmp(sectname, ".pdata") == 0) { - data.flags = COFF_STYP_DATA | COFF_STYP_READ; - align = 4; - data.flags2 = COFF_FLAG_NOBASE; - } else if (objfmt_coff->win64 && strcmp(sectname, ".xdata") == 0) { - data.flags = COFF_STYP_DATA | COFF_STYP_READ; - align = 8; - data.flags2 = COFF_FLAG_NOBASE; - } else if (objfmt_coff->win32 && strcmp(sectname, ".sxdata") == 0) { - data.flags = COFF_STYP_INFO; - } else if (strcmp(sectname, ".comment") == 0) { - data.flags = COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ; - } else if (yasm__strncasecmp(sectname, ".debug", 6)==0) { - data.flags = COFF_STYP_DATA | COFF_STYP_DISCARD | COFF_STYP_READ; - align = 1; - } else { - /* Default to code, but set a flag so if we get gasflags we can - * change it (NASM and GAS have different defaults). - */ - data.isdefault = 1; - data.flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; - } - - flags_override = yasm_dir_helper(object, vp, line, help, - objfmt_coff->win32 ? NELEMS(help) : 7, - &data, yasm_dir_helper_valparam_warn); - if (flags_override < 0) - return NULL; /* error occurred */ - - if (data.flags & COFF_STYP_EXECUTE) - iscode = 1; - - if (!objfmt_coff->win32) - data.flags &= ~COFF_STYP_WIN32_MASK; - - if (data.align_intn) { - align = yasm_intnum_get_uint(data.align_intn); - yasm_intnum_destroy(data.align_intn); - - /* Alignments must be a power of two. */ - if (!is_exp2(align)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("argument to `%s' is not a power of two"), - "align"); - return NULL; - } - - /* Check to see if alignment is supported size */ - if (align > 8192) { - yasm_error_set(YASM_ERROR_VALUE, - N_("Win32 does not support alignments > 8192")); - return NULL; - } - } - - realname = yasm__xstrdup(sectname); - if (strlen(sectname) > 8 && !objfmt_coff->win32) { - /* win32 format supports >8 character section names in object - * files via "/nnnn" (where nnnn is decimal offset into string table), - * so only warn for regular COFF. - */ - yasm_warn_set(YASM_WARN_GENERAL, - N_("COFF section names limited to 8 characters: truncating")); - realname[8] = '\0'; - } - - retval = yasm_object_get_general(object, realname, align, iscode, - resonly, &isnew, line); - yasm_xfree(realname); - - csd = yasm_section_get_data(retval, &coff_section_data_cb); - - if (isnew || yasm_section_is_default(retval)) { - yasm_section_set_default(retval, 0); - csd->flags = data.flags; - csd->flags2 = data.flags2; - yasm_section_set_align(retval, align, line); - } else if (flags_override && !data.gasflags) - yasm_warn_set(YASM_WARN_GENERAL, - N_("section flags ignored on section redeclaration")); - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -coff_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - return NULL; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -win64_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - if (yasm__strcasecmp(name, "imagebase") == 0) { - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - return objfmt_coff->ssym_imagebase; - } - return NULL; -} - -static void -coff_section_data_destroy(void *data) -{ - yasm_xfree(data); -} - -static void -coff_section_data_print(void *data, FILE *f, int indent_level) -{ - coff_section_data *csd = (coff_section_data *)data; - - fprintf(f, "%*ssym=\n", indent_level, ""); - yasm_symrec_print(csd->sym, f, indent_level+1); - fprintf(f, "%*sscnum=%d\n", indent_level, "", csd->scnum); - fprintf(f, "%*sflags=", indent_level, ""); - switch (csd->flags & COFF_STYP_STD_MASK) { - case COFF_STYP_TEXT: - fprintf(f, "TEXT"); - break; - case COFF_STYP_DATA: - fprintf(f, "DATA"); - break; - case COFF_STYP_BSS: - fprintf(f, "BSS"); - break; - default: - fprintf(f, "UNKNOWN"); - break; - } - fprintf(f, "\n%*saddr=0x%lx\n", indent_level, "", csd->addr); - fprintf(f, "%*sscnptr=0x%lx\n", indent_level, "", csd->scnptr); - fprintf(f, "%*ssize=%ld\n", indent_level, "", csd->size); - fprintf(f, "%*srelptr=0x%lx\n", indent_level, "", csd->relptr); - fprintf(f, "%*snreloc=%ld\n", indent_level, "", csd->nreloc); - fprintf(f, "%*srelocs:\n", indent_level, ""); -} - -static void -coff_symrec_data_destroy(void *data) -{ - yasm_xfree(data); -} - -static void -coff_symrec_data_print(void *data, FILE *f, int indent_level) -{ - coff_symrec_data *csd = (coff_symrec_data *)data; - - fprintf(f, "%*ssymtab index=%lu\n", indent_level, "", csd->index); - fprintf(f, "%*ssclass=%d\n", indent_level, "", csd->sclass); -} - -static void -dir_export(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_valparam *vp; - /*@null@*/ const char *symname; - int isnew; - yasm_section *sect; - yasm_datavalhead dvs; - - /* Reference exported symbol (to generate error if not declared) */ - vp = yasm_vps_first(valparams); - symname = yasm_vp_id(vp); - if (symname) - yasm_symtab_use(object->symtab, symname, line); - else { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("argument to EXPORT must be symbol name")); - return; - } - - /* Add to end of linker directives */ - sect = yasm_object_get_general(object, ".drectve", 0, 0, 0, &isnew, line); - - /* Initialize directive section if needed */ - if (isnew) { - coff_section_data *csd; - csd = yasm_section_get_data(sect, &coff_section_data_cb); - csd->flags = COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ; - } - - /* Add text as data bytecode */ - yasm_dvs_initialize(&dvs); - yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup("-export:"), - strlen("-export:"))); - yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup(symname), - strlen(symname))); - yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup(" "), 1)); - yasm_section_bcs_append(sect, yasm_bc_create_data(&dvs, 1, 0, NULL, line)); -} - -static void -dir_safeseh(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_valparam *vp; - /*@null@*/ const char *symname; - yasm_symrec *sym; - int isnew; - yasm_section *sect; - - /* Reference symbol (to generate error if not declared). - * Also, symbol must be externally visible, so force it. - */ - vp = yasm_vps_first(valparams); - symname = yasm_vp_id(vp); - if (symname) { - coff_symrec_data *sym_data; - sym = yasm_symtab_use(object->symtab, symname, line); - sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - if (!sym_data) { - sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, - COFF_SYMTAB_AUX_NONE); - } - sym_data->forcevis = 1; - sym_data->type = 0x20; /* function */ - } else { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("argument to SAFESEH must be symbol name")); - return; - } - - /* - * Add symbol number to end of .sxdata section. - */ - - sect = yasm_object_get_general(object, ".sxdata", 0, 0, 0, &isnew, line); - - /* Initialize sxdata section if needed */ - if (isnew) { - coff_section_data *csd; - csd = yasm_section_get_data(sect, &coff_section_data_cb); - csd->flags = COFF_STYP_INFO; - } - - /* Add as sxdata bytecode */ - yasm_section_bcs_append(sect, - yasm_bc_create_common(&win32_sxdata_bc_callback, - sym, line)); -} - -static void -win32_sxdata_bc_destroy(void *contents) -{ - /* Contents is just the symbol pointer, so no need to delete */ -} - -static void -win32_sxdata_bc_print(const void *contents, FILE *f, int indent_level) -{ - /* TODO */ -} - -static int -win32_sxdata_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, - void *add_span_data) -{ - bc->len += 4; - return 0; -} - -static int -win32_sxdata_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, - unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - yasm_output_reloc_func output_reloc) -{ - yasm_symrec *sym = (yasm_symrec *)bc->contents; - unsigned char *buf = *bufp; - coff_symrec_data *csymd; - - csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - if (!csymd) - yasm_internal_error(N_("coff: no symbol data for SAFESEH symbol")); - - YASM_WRITE_32_L(buf, csymd->index); - - *bufp = buf; - return 0; -} - -static void -dir_ident(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparamhead sect_vps; - yasm_datavalhead dvs; - yasm_section *comment; - const char *sectname; - yasm_valparam *vp, *vp2; - - /* Accept, but do nothing with empty ident */ - if (!valparams) - return; - - vp = yasm_vps_first(valparams); - if (!vp) - return; - - if (objfmt_coff->win32) { - /* Put ident data into .comment section for COFF, or .rdata$zzz - * to be compatible with the GNU linker, which doesn't ignore - * .comment (see binutils/gas/config/obj-coff.c:476-502). - */ - sectname = ".rdata$zzz"; - } else { - sectname = ".comment"; - } - yasm_vps_initialize(§_vps); - vp2 = yasm_vp_create_id(NULL, yasm__xstrdup(sectname), '\0'); - yasm_vps_append(§_vps, vp2); - comment = coff_objfmt_section_switch(object, §_vps, NULL, line); - yasm_vps_delete(§_vps); - - /* To match GAS output, if the comment section is empty, put an - * initial 0 byte in the section. - */ - if (yasm_section_bcs_first(comment) == yasm_section_bcs_last(comment)) { - yasm_dvs_initialize(&dvs); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)), - line))); - yasm_section_bcs_append(comment, - yasm_bc_create_data(&dvs, 1, 0, object->arch, line)); - } - - yasm_dvs_initialize(&dvs); - do { - const char *s = yasm_vp_string(vp); - if (!s) { - yasm_error_set(YASM_ERROR_VALUE, - N_(".comment requires string parameters")); - yasm_dvs_delete(&dvs); - return; - } - yasm_dvs_append(&dvs, - yasm_dv_create_string(yasm__xstrdup(s), strlen(s))); - } while ((vp = yasm_vps_next(vp))); - - yasm_section_bcs_append(comment, - yasm_bc_create_data(&dvs, 1, 1, object->arch, line)); -} - -static void -dir_secrel32(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_datavalhead dvs; - yasm_valparam *vp; - - if (!object->cur_section) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_(".secrel32 can only be used inside of a section")); - return; - } - - vp = yasm_vps_first(valparams); - yasm_dvs_initialize(&dvs); - do { - yasm_expr *e = yasm_vp_expr(vp, object->symtab, line); - yasm_dataval *dv; - if (!e) { - yasm_error_set(YASM_ERROR_VALUE, - N_(".secrel32 requires expressions")); - yasm_dvs_delete(&dvs); - return; - } - dv = yasm_dv_create_expr(e); - yasm_dv_get_value(dv)->section_rel = 1; - yasm_dvs_append(&dvs, dv); - } while ((vp = yasm_vps_next(vp))); - - yasm_section_bcs_append(object->cur_section, - yasm_bc_create_data(&dvs, 4, 0, object->arch, line)); -} - -static void -dir_def(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp; - const char *symname; - yasm_symrec *sym; - coff_symrec_data *sym_data; - - if (objfmt_coff->def_sym) { - yasm_warn_set(YASM_WARN_GENERAL, - N_(".def pseudo-op used inside of .def/.endef; ignored")); - return; - } - - vp = yasm_vps_first(valparams); - symname = yasm_vp_id(vp); - if (!symname) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("argument to SAFESEH must be symbol name")); - return; - } - - sym = yasm_symtab_use(object->symtab, symname, line); - sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); - if (!sym_data) { - sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, - COFF_SYMTAB_AUX_NONE); - } - objfmt_coff->def_sym = sym_data; -} - -static void -dir_scl(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_intnum *intn = NULL; - - if (!objfmt_coff->def_sym) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("%s pseudo-op used outside of .def/.endef; ignored"), - ".scl"); - return; - } - - if (yasm_dir_helper_intn(object, yasm_vps_first(valparams), line, - &intn, 0) < 0) - return; - if (!intn) - return; - objfmt_coff->def_sym->sclass = yasm_intnum_get_uint(intn); - yasm_intnum_destroy(intn); -} - -static void -dir_type(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_intnum *intn = NULL; - - if (!objfmt_coff->def_sym) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("%s pseudo-op used outside of .def/.endef; ignored"), - ".type"); - return; - } - - if (yasm_dir_helper_intn(object, yasm_vps_first(valparams), line, - &intn, 0) < 0) - return; - if (!intn) - return; - objfmt_coff->def_sym->type = yasm_intnum_get_uint(intn); - yasm_intnum_destroy(intn); -} - -static void -dir_endef(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - if (!objfmt_coff->def_sym) { - yasm_warn_set(YASM_WARN_GENERAL, - N_(".endef pseudo-op used before .def; ignored")); - return; - } - objfmt_coff->def_sym = NULL; -} - -static void -dir_proc_frame(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - const char *name = yasm_vp_id(vp); - - if (objfmt_coff->proc_frame) { - yasm_error_set_xref(objfmt_coff->proc_frame, - N_("previous procedure started here")); - yasm_error_set(YASM_ERROR_SYNTAX, - N_("nested procedures not supported (didn't use [ENDPROC_FRAME]?)")); - return; - } - objfmt_coff->proc_frame = line; - objfmt_coff->done_prolog = 0; - objfmt_coff->unwind = yasm_win64__uwinfo_create(); - objfmt_coff->unwind->proc = yasm_symtab_use(object->symtab, name, line); - - /* Optional error handler */ - vp = yasm_vps_next(vp); - if (!vp || !(name = yasm_vp_id(vp))) - return; - objfmt_coff->unwind->ehandler = - yasm_symtab_use(object->symtab, name, line); -} - -static int -procframe_checkstate(yasm_objfmt_coff *objfmt_coff, const char *dirname) -{ - if (!objfmt_coff->proc_frame) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] without preceding [PROC_FRAME]"), dirname); - return 0; - } - if (objfmt_coff->done_prolog) { - yasm_error_set_xref(objfmt_coff->done_prolog, - N_("prologue ended here")); - yasm_error_set(YASM_ERROR_SYNTAX, N_("[%s] after end of prologue"), - dirname); - return 0; - } - if (!objfmt_coff->unwind) - yasm_internal_error(N_("unwind info not present")); - return 1; -} - -/* Get current assembly position. - * XXX: There should be a better way to do this. - */ -static yasm_symrec * -get_curpos(yasm_object *object, const char *dirname, unsigned long line) -{ - if (!object->cur_section) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] can only be used inside of a section"), - dirname); - return NULL; - } - return yasm_symtab_define_curpos(object->symtab, "$", - yasm_section_bcs_last(object->cur_section), line); -} - -static void -dir_pushreg(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - coff_unwind_code *code; - const uintptr_t *reg; - - if (!procframe_checkstate(objfmt_coff, "PUSHREG")) - return; - - if (vp->type != YASM_PARAM_EXPR || - !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] requires a register as the first parameter"), - "PUSHREG"); - return; - } - - /* Generate a PUSH_NONVOL unwind code. */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, "PUSHREG", line); - code->opcode = UWOP_PUSH_NONVOL; - code->info = (unsigned int)(*reg & 0xF); - yasm_value_initialize(&code->off, NULL, 0); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_setframe(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - coff_unwind_code *code; - const uintptr_t *reg; - yasm_expr *off = NULL; - - if (!procframe_checkstate(objfmt_coff, "SETFRAME")) - return; - - if (vp->type != YASM_PARAM_EXPR || - !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] requires a register as the first parameter"), - "SETFRAME"); - return; - } - - vp = yasm_vps_next(vp); - if (vp) - off = yasm_vp_expr(vp, object->symtab, line); - - /* Set the frame fields in the unwind info */ - objfmt_coff->unwind->framereg = (unsigned long)(*reg); - yasm_value_initialize(&objfmt_coff->unwind->frameoff, off, 8); - - /* Generate a SET_FPREG unwind code */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, "SETFRAME", line); - code->opcode = UWOP_SET_FPREG; - code->info = (unsigned int)(*reg & 0xF); - yasm_value_initialize(&code->off, off ? yasm_expr_copy(off) : NULL, 8); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_allocstack(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - /*@null@*/ /*@only@*/ yasm_expr *size; - coff_unwind_code *code; - - if (!procframe_checkstate(objfmt_coff, "ALLOCSTACK")) - return; - - size = yasm_vp_expr(vp, object->symtab, line); - if (!size) { - yasm_error_set(YASM_ERROR_SYNTAX, N_("[%s] requires a size"), - "ALLOCSTACK"); - return; - } - - /* Generate an ALLOC_SMALL unwind code; this will get enlarged to an - * ALLOC_LARGE if necessary. - */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, "ALLOCSTACK", line); - code->opcode = UWOP_ALLOC_SMALL; - code->info = 0; - yasm_value_initialize(&code->off, size, 7); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_save_common(yasm_object *object, yasm_valparamhead *valparams, - unsigned long line, const char *name, int op) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - coff_unwind_code *code; - const uintptr_t *reg; - /*@only@*/ /*@null@*/ yasm_expr *offset; - - if (!procframe_checkstate(objfmt_coff, name)) - return; - - if (vp->type != YASM_PARAM_EXPR || - !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] requires a register as the first parameter"), - name); - return; - } - - vp = yasm_vps_next(vp); - offset = yasm_vp_expr(vp, object->symtab, line); - if (!offset) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] requires an offset as the second parameter"), - name); - return; - } - - /* Generate a SAVE_XXX unwind code; this will get enlarged to a - * SAVE_XXX_FAR if necessary. - */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, name, line); - code->opcode = op; - code->info = (unsigned int)(*reg & 0xF); - yasm_value_initialize(&code->off, offset, 16); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_savereg(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - dir_save_common(object, valparams, line, "SAVEREG", UWOP_SAVE_NONVOL); -} - -static void -dir_savexmm128(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - dir_save_common(object, valparams, line, "SAVEXMM128", UWOP_SAVE_XMM128); -} - -static void -dir_pushframe(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - coff_unwind_code *code; - - if (!procframe_checkstate(objfmt_coff, "PUSHFRAME")) - return; - - /* Generate a PUSH_MACHFRAME unwind code. If there's any parameter, - * we set info to 1. Otherwise we set info to 0. - */ - code = yasm_xmalloc(sizeof(coff_unwind_code)); - code->proc = objfmt_coff->unwind->proc; - code->loc = get_curpos(object, "PUSHFRAME", line); - code->opcode = UWOP_PUSH_MACHFRAME; - code->info = vp != NULL; - yasm_value_initialize(&code->off, NULL, 0); - SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); -} - -static void -dir_endprolog(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - if (!procframe_checkstate(objfmt_coff, "ENDPROLOG")) - return; - objfmt_coff->done_prolog = line; - - objfmt_coff->unwind->prolog = get_curpos(object, "ENDPROLOG", line); -} - -static void -dir_endproc_frame(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; - yasm_section *sect; - coff_section_data *csd; - yasm_datavalhead dvs; - int isnew; - /*@dependent@*/ yasm_symrec *curpos, *unwindpos, *proc_sym, *xdata_sym; - - if (!objfmt_coff->proc_frame) { - yasm_error_set(YASM_ERROR_SYNTAX, - N_("[%s] without preceding [PROC_FRAME]"), - "ENDPROC_FRAME"); - return; - } - if (!objfmt_coff->done_prolog) { - yasm_error_set_xref(objfmt_coff->proc_frame, - N_("procedure started here")); - yasm_error_set(YASM_ERROR_SYNTAX, - N_("ended procedure without ending prologue"), - "ENDPROC_FRAME"); - objfmt_coff->proc_frame = 0; - yasm_win64__uwinfo_destroy(objfmt_coff->unwind); - objfmt_coff->unwind = NULL; - return; - } - if (!objfmt_coff->unwind) - yasm_internal_error(N_("unwind info not present")); - - proc_sym = objfmt_coff->unwind->proc; - - curpos = get_curpos(object, "ENDPROC_FRAME", line); - - /* - * Add unwind info to end of .xdata section. - */ - - sect = yasm_object_get_general(object, ".xdata", 0, 0, 0, &isnew, line); - - /* Initialize xdata section if needed */ - if (isnew) { - csd = yasm_section_get_data(sect, &coff_section_data_cb); - csd->flags = COFF_STYP_DATA | COFF_STYP_READ; - yasm_section_set_align(sect, 8, line); - } - - /* Get current position in .xdata section */ - unwindpos = yasm_symtab_define_curpos(object->symtab, "$", - yasm_section_bcs_last(sect), line); - /* Get symbol for .xdata as we'll want to reference it with WRT */ - csd = yasm_section_get_data(sect, &coff_section_data_cb); - xdata_sym = csd->sym; - - /* Add unwind info. Use line number of start of procedure. */ - yasm_win64__unwind_generate(sect, objfmt_coff->unwind, - objfmt_coff->proc_frame); - objfmt_coff->unwind = NULL; /* generate keeps the unwind pointer */ - - /* - * Add function lookup to end of .pdata section. - */ - - sect = yasm_object_get_general(object, ".pdata", 0, 0, 0, &isnew, line); - - /* Initialize pdata section if needed */ - if (isnew) { - csd = yasm_section_get_data(sect, &coff_section_data_cb); - csd->flags = COFF_STYP_DATA | COFF_STYP_READ; - csd->flags2 = COFF_FLAG_NOBASE; - yasm_section_set_align(sect, 4, line); - } - - /* Add function structure as data bytecode */ - yasm_dvs_initialize(&dvs); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create_ident(yasm_expr_sym(proc_sym), line))); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create(YASM_EXPR_WRT, yasm_expr_sym(curpos), - yasm_expr_sym(proc_sym), line))); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create(YASM_EXPR_WRT, yasm_expr_sym(unwindpos), - yasm_expr_sym(xdata_sym), line))); - yasm_section_bcs_append(sect, yasm_bc_create_data(&dvs, 4, 0, NULL, line)); - - objfmt_coff->proc_frame = 0; - objfmt_coff->done_prolog = 0; -} - -/* Define valid debug formats to use with this object format */ -static const char *coff_objfmt_dbgfmt_keywords[] = { - "null", - "dwarf2", - NULL -}; - -static const yasm_directive coff_objfmt_directives[] = { - { ".ident", "gas", dir_ident, YASM_DIR_ANY }, - { "ident", "nasm", dir_ident, YASM_DIR_ANY }, - { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, - { ".endef", "gas", dir_endef, YASM_DIR_ANY }, - { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, - { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, - { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, - { NULL, NULL, NULL, 0 } -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_coff_LTX_objfmt = { - "COFF (DJGPP)", - "coff", - "o", - 32, - 0, - coff_objfmt_dbgfmt_keywords, - "null", - coff_objfmt_directives, - NULL, /* no standard macros */ - coff_objfmt_create, - coff_objfmt_output, - coff_objfmt_destroy, - coff_objfmt_add_default_section, - coff_objfmt_init_new_section, - coff_objfmt_section_switch, - coff_objfmt_get_special_sym -}; - -/* Define valid debug formats to use with this object format */ -static const char *winXX_objfmt_dbgfmt_keywords[] = { - "null", - "dwarf2", - "cv8", - NULL -}; - -static const yasm_directive win32_objfmt_directives[] = { - { ".ident", "gas", dir_ident, YASM_DIR_ANY }, - { "ident", "nasm", dir_ident, YASM_DIR_ANY }, - { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, - { ".endef", "gas", dir_endef, YASM_DIR_ANY }, - { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, - { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, - { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, - { ".export", "gas", dir_export, YASM_DIR_ID_REQUIRED }, - { "export", "nasm", dir_export, YASM_DIR_ID_REQUIRED }, - { ".safeseh", "gas", dir_safeseh, YASM_DIR_ID_REQUIRED }, - { "safeseh", "nasm", dir_safeseh, YASM_DIR_ID_REQUIRED }, - { NULL, NULL, NULL, 0 } -}; - -static const char *win32_nasm_stdmac[] = { - "%imacro export 1+.nolist", - "[export %1]", - "%endmacro", - "%imacro safeseh 1+.nolist", - "[safeseh %1]", - "%endmacro", - NULL -}; - -static const yasm_stdmac win32_objfmt_stdmacs[] = { - { "nasm", "nasm", win32_nasm_stdmac }, - { NULL, NULL, NULL } -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_win32_LTX_objfmt = { - "Win32", - "win32", - "obj", - 32, - 1, - winXX_objfmt_dbgfmt_keywords, - "null", - win32_objfmt_directives, - win32_objfmt_stdmacs, - win32_objfmt_create, - coff_objfmt_output, - coff_objfmt_destroy, - coff_objfmt_add_default_section, - coff_objfmt_init_new_section, - coff_objfmt_section_switch, - coff_objfmt_get_special_sym -}; - -static const yasm_directive win64_objfmt_directives[] = { - { ".ident", "gas", dir_ident, YASM_DIR_ANY }, - { "ident", "nasm", dir_ident, YASM_DIR_ANY }, - { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, - { ".endef", "gas", dir_endef, YASM_DIR_ANY }, - { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, - { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, - { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, - { ".export", "gas", dir_export, YASM_DIR_ID_REQUIRED }, - { "export", "nasm", dir_export, YASM_DIR_ID_REQUIRED }, - { ".proc_frame", "gas", dir_proc_frame, YASM_DIR_ID_REQUIRED }, - { "proc_frame", "nasm", dir_proc_frame, YASM_DIR_ID_REQUIRED }, - { ".pushreg", "gas", dir_pushreg, YASM_DIR_ARG_REQUIRED }, - { "pushreg", "nasm", dir_pushreg, YASM_DIR_ARG_REQUIRED }, - { ".setframe", "gas", dir_setframe, YASM_DIR_ARG_REQUIRED }, - { "setframe", "nasm", dir_setframe, YASM_DIR_ARG_REQUIRED }, - { ".allocstack", "gas", dir_allocstack, YASM_DIR_ARG_REQUIRED }, - { "allocstack", "nasm", dir_allocstack, YASM_DIR_ARG_REQUIRED }, - { ".savereg", "gas", dir_savereg, YASM_DIR_ARG_REQUIRED }, - { "savereg", "nasm", dir_savereg, YASM_DIR_ARG_REQUIRED }, - { ".savexmm128", "gas", dir_savexmm128, YASM_DIR_ARG_REQUIRED }, - { "savexmm128", "nasm", dir_savexmm128, YASM_DIR_ARG_REQUIRED }, - { ".pushframe", "gas", dir_pushframe, YASM_DIR_ANY }, - { "pushframe", "nasm", dir_pushframe, YASM_DIR_ANY }, - { ".endprolog", "gas", dir_endprolog, YASM_DIR_ANY }, - { "endprolog", "nasm", dir_endprolog, YASM_DIR_ANY }, - { ".endproc_frame", "gas", dir_endproc_frame, YASM_DIR_ANY }, - { "endproc_frame", "nasm", dir_endproc_frame, YASM_DIR_ANY }, - { NULL, NULL, NULL, 0 } -}; - -#include "win64-nasm.c" -#include "win64-gas.c" - -static const yasm_stdmac win64_objfmt_stdmacs[] = { - { "nasm", "nasm", win64_nasm_stdmac }, - { "gas", "nasm", win64_gas_stdmac }, - { NULL, NULL, NULL } -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_win64_LTX_objfmt = { - "Win64", - "win64", - "obj", - 64, - 1, - winXX_objfmt_dbgfmt_keywords, - "null", - win64_objfmt_directives, - win64_objfmt_stdmacs, - win64_objfmt_create, - coff_objfmt_output, - coff_objfmt_destroy, - coff_objfmt_add_default_section, - coff_objfmt_init_new_section, - coff_objfmt_section_switch, - win64_objfmt_get_special_sym -}; -yasm_objfmt_module yasm_x64_LTX_objfmt = { - "Win64", - "x64", - "obj", - 64, - 1, - winXX_objfmt_dbgfmt_keywords, - "null", - win64_objfmt_directives, - win64_objfmt_stdmacs, - win64_objfmt_create, - coff_objfmt_output, - coff_objfmt_destroy, - coff_objfmt_add_default_section, - coff_objfmt_init_new_section, - coff_objfmt_section_switch, - win64_objfmt_get_special_sym -}; + + /* Force all syms for win64 because they're needed for relocations. + * FIXME: Not *all* syms need to be output, only the ones needed for + * relocation. Find a way to do that someday. + */ + all_syms |= objfmt_coff->win64; + + info.strtab_offset = 4; + info.object = object; + info.objfmt_coff = objfmt_coff; + info.errwarns = errwarns; + info.f = f; + info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); + + /* Allocate space for headers by seeking forward */ + if (fseek(f, (long)(20+40*(objfmt_coff->parse_scnum-1)), SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + /* Finalize symbol table (assign index to each symbol) */ + info.indx = 0; + info.all_syms = all_syms; + yasm_symtab_traverse(object->symtab, &info, coff_objfmt_count_sym); + symtab_count = info.indx; + + /* Section data/relocs */ + if (COFF_SET_VMA) { + /* If we're setting the VMA, we need to do a first section pass to + * determine each section's addr value before actually outputting + * relocations, as a relocation's section address is added into the + * addends in the generated code. + */ + info.addr = 0; + if (yasm_object_sections_traverse(object, &info, + coff_objfmt_set_section_addr)) + return; + } + info.addr = 0; + if (yasm_object_sections_traverse(object, &info, + coff_objfmt_output_section)) + return; + + /* Symbol table */ + pos = ftell(f); + if (pos == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return; + } + symtab_pos = (unsigned long)pos; + yasm_symtab_traverse(object->symtab, &info, coff_objfmt_output_sym); + + /* String table */ + yasm_fwrite_32_l(info.strtab_offset, f); /* total length */ + yasm_object_sections_traverse(object, &info, coff_objfmt_output_sectstr); + yasm_symtab_traverse(object->symtab, &info, coff_objfmt_output_str); + + /* Write headers */ + if (fseek(f, 0, SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + localbuf = info.buf; + YASM_WRITE_16_L(localbuf, objfmt_coff->machine); /* magic number */ + YASM_WRITE_16_L(localbuf, objfmt_coff->parse_scnum-1);/* number of sects */ + if (getenv("YASM_TEST_SUITE")) + ts = 0; + else + ts = (unsigned long)time(NULL); + YASM_WRITE_32_L(localbuf, ts); /* time/date stamp */ + YASM_WRITE_32_L(localbuf, symtab_pos); /* file ptr to symtab */ + YASM_WRITE_32_L(localbuf, symtab_count); /* number of symtabs */ + YASM_WRITE_16_L(localbuf, 0); /* size of optional header (none) */ + /* flags */ + flags = 0; + if (strcmp(yasm_dbgfmt_keyword(object->dbgfmt), "null")==0) + flags = COFF_F_LNNO; + if (!all_syms) + flags |= COFF_F_LSYMS; + if (objfmt_coff->machine != COFF_MACHINE_AMD64) + flags |= COFF_F_AR32WR; + YASM_WRITE_16_L(localbuf, flags); + fwrite(info.buf, 20, 1, f); + + yasm_object_sections_traverse(object, &info, coff_objfmt_output_secthead); + + yasm_xfree(info.buf); +} + +static void +coff_objfmt_destroy(yasm_objfmt *objfmt) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)objfmt; + if (objfmt_coff->filesym_data->aux[0].fname) + yasm_xfree(objfmt_coff->filesym_data->aux[0].fname); + if (objfmt_coff->unwind) + yasm_win64__uwinfo_destroy(objfmt_coff->unwind); + yasm_xfree(objfmt); +} + +static yasm_section * +coff_objfmt_add_default_section(yasm_object *object) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_section *retval; + coff_section_data *csd; + int isnew; + + retval = yasm_object_get_general(object, ".text", 16, 1, 0, &isnew, 0); + if (isnew) { + csd = yasm_section_get_data(retval, &coff_section_data_cb); + csd->flags = COFF_STYP_TEXT; + if (objfmt_coff->win32) + csd->flags |= COFF_STYP_EXECUTE | COFF_STYP_READ; + yasm_section_set_default(retval, 1); + } + return retval; +} + +struct coff_section_switch_data { + int isdefault; + int gasflags; + unsigned long flags; + unsigned long flags2; + /*@only@*/ /*@null@*/ yasm_intnum *align_intn; +}; + +/* GAS-style flags */ +static int +coff_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d, + /*@unused@*/ uintptr_t arg) +{ + struct coff_section_switch_data *data = + (struct coff_section_switch_data *)d; + int alloc = 0, load = 0, readonly = 0, code = 0, datasect = 0; + int shared = 0; + const char *s = yasm_vp_string(vp); + size_t i; + + if (!s) { + yasm_error_set(YASM_ERROR_VALUE, N_("non-string section attribute")); + return -1; + } + + /* For GAS, default to read/write data */ + if (data->isdefault) + data->flags = COFF_STYP_TEXT | COFF_STYP_READ | COFF_STYP_WRITE; + + for (i=0; i<strlen(s); i++) { + switch (s[i]) { + case 'a': + break; + case 'b': + alloc = 1; + load = 0; + break; + case 'n': + load = 0; + break; + case 's': + shared = 1; + /*@fallthrough@*/ + case 'd': + datasect = 1; + load = 1; + readonly = 0; + break; + case 'x': + code = 1; + load = 1; + break; + case 'r': + datasect = 1; + load = 1; + readonly = 1; + break; + case 'w': + readonly = 0; + break; + default: + yasm_warn_set(YASM_WARN_GENERAL, + N_("unrecognized section attribute: `%c'"), + s[i]); + } + } + + if (code) + data->flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; + else if (datasect) + data->flags = COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE; + else if (readonly) + data->flags = COFF_STYP_DATA | COFF_STYP_READ; + else if (load) + data->flags = COFF_STYP_TEXT; + else if (alloc) + data->flags = COFF_STYP_BSS; + + if (shared) + data->flags |= COFF_STYP_SHARED; + + data->gasflags = 1; + return 0; +} + +static /*@observer@*/ /*@null@*/ yasm_section * +coff_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, + /*@unused@*/ /*@null@*/ + yasm_valparamhead *objext_valparams, + unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp; + yasm_section *retval; + int isnew; + int iscode = 0; + int flags_override; + const char *sectname; + char *realname; + int resonly = 0; + unsigned long align = 0; + coff_section_data *csd; + + struct coff_section_switch_data data; + + static const yasm_dir_help help[] = { + { "code", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ }, + { "text", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ }, + { "data", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE }, + { "rdata", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_DATA | COFF_STYP_READ }, + { "bss", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_BSS | COFF_STYP_READ | COFF_STYP_WRITE }, + { "info", 0, yasm_dir_helper_flag_set, + offsetof(struct coff_section_switch_data, flags), + COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ }, + { "gasflags", 1, coff_helper_gasflags, 0, 0 }, + /* Win32 only below this point */ + { "discard", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_DISCARD}, + { "nodiscard", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_DISCARD}, + { "cache", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOCACHE}, + { "nocache", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOCACHE}, + { "page", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOPAGE }, + { "nopage", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_NOPAGE }, + { "share", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_SHARED }, + { "noshare", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_SHARED }, + { "execute", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_EXECUTE}, + { "noexecute", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_EXECUTE}, + { "read", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_READ }, + { "noread", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_READ }, + { "write", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_WRITE }, + { "nowrite", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags), COFF_STYP_WRITE }, + { "base", 0, yasm_dir_helper_flag_and, + offsetof(struct coff_section_switch_data, flags2), COFF_FLAG_NOBASE}, + { "nobase", 0, yasm_dir_helper_flag_or, + offsetof(struct coff_section_switch_data, flags2), COFF_FLAG_NOBASE}, + { "align", 1, yasm_dir_helper_intn, + offsetof(struct coff_section_switch_data, align_intn), 0 } + }; + + vp = yasm_vps_first(valparams); + sectname = yasm_vp_string(vp); + if (!sectname) + return NULL; + vp = yasm_vps_next(vp); + + data.isdefault = 0; + data.gasflags = 0; + data.flags = 0; + data.flags2 = 0; + data.align_intn = NULL; + + if (strcmp(sectname, ".data") == 0) { + data.flags = COFF_STYP_DATA | COFF_STYP_READ | COFF_STYP_WRITE; + if (objfmt_coff->win32) { + if (objfmt_coff->machine == COFF_MACHINE_AMD64) + align = 16; + else + align = 4; + } + } else if (strcmp(sectname, ".bss") == 0) { + data.flags = COFF_STYP_BSS | COFF_STYP_READ | COFF_STYP_WRITE; + if (objfmt_coff->win32) { + if (objfmt_coff->machine == COFF_MACHINE_AMD64) + align = 16; + else + align = 4; + } + resonly = 1; + } else if (strcmp(sectname, ".text") == 0) { + data.flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; + if (objfmt_coff->win32) + align = 16; + } else if (strcmp(sectname, ".rdata") == 0 + || strncmp(sectname, ".rodata", 7) == 0 + || strncmp(sectname, ".rdata$", 7) == 0) { + data.flags = COFF_STYP_DATA | COFF_STYP_READ; + if (objfmt_coff->win32) + align = 8; + else + yasm_warn_set(YASM_WARN_GENERAL, + N_("Standard COFF does not support read-only data sections")); + } else if (strcmp(sectname, ".drectve") == 0) { + data.flags = COFF_STYP_INFO; + if (objfmt_coff->win32) + data.flags |= COFF_STYP_DISCARD | COFF_STYP_READ; + } else if (objfmt_coff->win64 && strcmp(sectname, ".pdata") == 0) { + data.flags = COFF_STYP_DATA | COFF_STYP_READ; + align = 4; + data.flags2 = COFF_FLAG_NOBASE; + } else if (objfmt_coff->win64 && strcmp(sectname, ".xdata") == 0) { + data.flags = COFF_STYP_DATA | COFF_STYP_READ; + align = 8; + data.flags2 = COFF_FLAG_NOBASE; + } else if (objfmt_coff->win32 && strcmp(sectname, ".sxdata") == 0) { + data.flags = COFF_STYP_INFO; + } else if (strcmp(sectname, ".comment") == 0) { + data.flags = COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ; + } else if (yasm__strncasecmp(sectname, ".debug", 6)==0) { + data.flags = COFF_STYP_DATA | COFF_STYP_DISCARD | COFF_STYP_READ; + align = 1; + } else { + /* Default to code, but set a flag so if we get gasflags we can + * change it (NASM and GAS have different defaults). + */ + data.isdefault = 1; + data.flags = COFF_STYP_TEXT | COFF_STYP_EXECUTE | COFF_STYP_READ; + } + + flags_override = yasm_dir_helper(object, vp, line, help, + objfmt_coff->win32 ? NELEMS(help) : 7, + &data, yasm_dir_helper_valparam_warn); + if (flags_override < 0) + return NULL; /* error occurred */ + + if (data.flags & COFF_STYP_EXECUTE) + iscode = 1; + + if (!objfmt_coff->win32) + data.flags &= ~COFF_STYP_WIN32_MASK; + + if (data.align_intn) { + align = yasm_intnum_get_uint(data.align_intn); + yasm_intnum_destroy(data.align_intn); + + /* Alignments must be a power of two. */ + if (!is_exp2(align)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("argument to `%s' is not a power of two"), + "align"); + return NULL; + } + + /* Check to see if alignment is supported size */ + if (align > 8192) { + yasm_error_set(YASM_ERROR_VALUE, + N_("Win32 does not support alignments > 8192")); + return NULL; + } + } + + realname = yasm__xstrdup(sectname); + if (strlen(sectname) > 8 && !objfmt_coff->win32) { + /* win32 format supports >8 character section names in object + * files via "/nnnn" (where nnnn is decimal offset into string table), + * so only warn for regular COFF. + */ + yasm_warn_set(YASM_WARN_GENERAL, + N_("COFF section names limited to 8 characters: truncating")); + realname[8] = '\0'; + } + + retval = yasm_object_get_general(object, realname, align, iscode, + resonly, &isnew, line); + yasm_xfree(realname); + + csd = yasm_section_get_data(retval, &coff_section_data_cb); + + if (isnew || yasm_section_is_default(retval)) { + yasm_section_set_default(retval, 0); + csd->flags = data.flags; + csd->flags2 = data.flags2; + yasm_section_set_align(retval, align, line); + } else if (flags_override && !data.gasflags) + yasm_warn_set(YASM_WARN_GENERAL, + N_("section flags ignored on section redeclaration")); + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +coff_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + return NULL; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +win64_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + if (yasm__strcasecmp(name, "imagebase") == 0) { + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + return objfmt_coff->ssym_imagebase; + } + return NULL; +} + +static void +coff_section_data_destroy(void *data) +{ + yasm_xfree(data); +} + +static void +coff_section_data_print(void *data, FILE *f, int indent_level) +{ + coff_section_data *csd = (coff_section_data *)data; + + fprintf(f, "%*ssym=\n", indent_level, ""); + yasm_symrec_print(csd->sym, f, indent_level+1); + fprintf(f, "%*sscnum=%d\n", indent_level, "", csd->scnum); + fprintf(f, "%*sflags=", indent_level, ""); + switch (csd->flags & COFF_STYP_STD_MASK) { + case COFF_STYP_TEXT: + fprintf(f, "TEXT"); + break; + case COFF_STYP_DATA: + fprintf(f, "DATA"); + break; + case COFF_STYP_BSS: + fprintf(f, "BSS"); + break; + default: + fprintf(f, "UNKNOWN"); + break; + } + fprintf(f, "\n%*saddr=0x%lx\n", indent_level, "", csd->addr); + fprintf(f, "%*sscnptr=0x%lx\n", indent_level, "", csd->scnptr); + fprintf(f, "%*ssize=%ld\n", indent_level, "", csd->size); + fprintf(f, "%*srelptr=0x%lx\n", indent_level, "", csd->relptr); + fprintf(f, "%*snreloc=%ld\n", indent_level, "", csd->nreloc); + fprintf(f, "%*srelocs:\n", indent_level, ""); +} + +static void +coff_symrec_data_destroy(void *data) +{ + yasm_xfree(data); +} + +static void +coff_symrec_data_print(void *data, FILE *f, int indent_level) +{ + coff_symrec_data *csd = (coff_symrec_data *)data; + + fprintf(f, "%*ssymtab index=%lu\n", indent_level, "", csd->index); + fprintf(f, "%*ssclass=%d\n", indent_level, "", csd->sclass); +} + +static void +dir_export(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp; + /*@null@*/ const char *symname; + int isnew; + yasm_section *sect; + yasm_datavalhead dvs; + + /* Reference exported symbol (to generate error if not declared) */ + vp = yasm_vps_first(valparams); + symname = yasm_vp_id(vp); + if (symname) + yasm_symtab_use(object->symtab, symname, line); + else { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("argument to EXPORT must be symbol name")); + return; + } + + /* Add to end of linker directives */ + sect = yasm_object_get_general(object, ".drectve", 0, 0, 0, &isnew, line); + + /* Initialize directive section if needed */ + if (isnew) { + coff_section_data *csd; + csd = yasm_section_get_data(sect, &coff_section_data_cb); + csd->flags = COFF_STYP_INFO | COFF_STYP_DISCARD | COFF_STYP_READ; + } + + /* Add text as data bytecode */ + yasm_dvs_initialize(&dvs); + yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup("-export:"), + strlen("-export:"))); + yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup(symname), + strlen(symname))); + yasm_dvs_append(&dvs, yasm_dv_create_string(yasm__xstrdup(" "), 1)); + yasm_section_bcs_append(sect, yasm_bc_create_data(&dvs, 1, 0, NULL, line)); +} + +static void +dir_safeseh(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp; + /*@null@*/ const char *symname; + yasm_symrec *sym; + int isnew; + yasm_section *sect; + + /* Reference symbol (to generate error if not declared). + * Also, symbol must be externally visible, so force it. + */ + vp = yasm_vps_first(valparams); + symname = yasm_vp_id(vp); + if (symname) { + coff_symrec_data *sym_data; + sym = yasm_symtab_use(object->symtab, symname, line); + sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + if (!sym_data) { + sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, + COFF_SYMTAB_AUX_NONE); + } + sym_data->forcevis = 1; + sym_data->type = 0x20; /* function */ + } else { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("argument to SAFESEH must be symbol name")); + return; + } + + /* + * Add symbol number to end of .sxdata section. + */ + + sect = yasm_object_get_general(object, ".sxdata", 0, 0, 0, &isnew, line); + + /* Initialize sxdata section if needed */ + if (isnew) { + coff_section_data *csd; + csd = yasm_section_get_data(sect, &coff_section_data_cb); + csd->flags = COFF_STYP_INFO; + } + + /* Add as sxdata bytecode */ + yasm_section_bcs_append(sect, + yasm_bc_create_common(&win32_sxdata_bc_callback, + sym, line)); +} + +static void +win32_sxdata_bc_destroy(void *contents) +{ + /* Contents is just the symbol pointer, so no need to delete */ +} + +static void +win32_sxdata_bc_print(const void *contents, FILE *f, int indent_level) +{ + /* TODO */ +} + +static int +win32_sxdata_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + bc->len += 4; + return 0; +} + +static int +win32_sxdata_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + yasm_output_reloc_func output_reloc) +{ + yasm_symrec *sym = (yasm_symrec *)bc->contents; + unsigned char *buf = *bufp; + coff_symrec_data *csymd; + + csymd = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + if (!csymd) + yasm_internal_error(N_("coff: no symbol data for SAFESEH symbol")); + + YASM_WRITE_32_L(buf, csymd->index); + + *bufp = buf; + return 0; +} + +static void +dir_ident(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparamhead sect_vps; + yasm_datavalhead dvs; + yasm_section *comment; + const char *sectname; + yasm_valparam *vp, *vp2; + + /* Accept, but do nothing with empty ident */ + if (!valparams) + return; + + vp = yasm_vps_first(valparams); + if (!vp) + return; + + if (objfmt_coff->win32) { + /* Put ident data into .comment section for COFF, or .rdata$zzz + * to be compatible with the GNU linker, which doesn't ignore + * .comment (see binutils/gas/config/obj-coff.c:476-502). + */ + sectname = ".rdata$zzz"; + } else { + sectname = ".comment"; + } + yasm_vps_initialize(§_vps); + vp2 = yasm_vp_create_id(NULL, yasm__xstrdup(sectname), '\0'); + yasm_vps_append(§_vps, vp2); + comment = coff_objfmt_section_switch(object, §_vps, NULL, line); + yasm_vps_delete(§_vps); + + /* To match GAS output, if the comment section is empty, put an + * initial 0 byte in the section. + */ + if (yasm_section_bcs_first(comment) == yasm_section_bcs_last(comment)) { + yasm_dvs_initialize(&dvs); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)), + line))); + yasm_section_bcs_append(comment, + yasm_bc_create_data(&dvs, 1, 0, object->arch, line)); + } + + yasm_dvs_initialize(&dvs); + do { + const char *s = yasm_vp_string(vp); + if (!s) { + yasm_error_set(YASM_ERROR_VALUE, + N_(".comment requires string parameters")); + yasm_dvs_delete(&dvs); + return; + } + yasm_dvs_append(&dvs, + yasm_dv_create_string(yasm__xstrdup(s), strlen(s))); + } while ((vp = yasm_vps_next(vp))); + + yasm_section_bcs_append(comment, + yasm_bc_create_data(&dvs, 1, 1, object->arch, line)); +} + +static void +dir_secrel32(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_datavalhead dvs; + yasm_valparam *vp; + + if (!object->cur_section) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_(".secrel32 can only be used inside of a section")); + return; + } + + vp = yasm_vps_first(valparams); + yasm_dvs_initialize(&dvs); + do { + yasm_expr *e = yasm_vp_expr(vp, object->symtab, line); + yasm_dataval *dv; + if (!e) { + yasm_error_set(YASM_ERROR_VALUE, + N_(".secrel32 requires expressions")); + yasm_dvs_delete(&dvs); + return; + } + dv = yasm_dv_create_expr(e); + yasm_dv_get_value(dv)->section_rel = 1; + yasm_dvs_append(&dvs, dv); + } while ((vp = yasm_vps_next(vp))); + + yasm_section_bcs_append(object->cur_section, + yasm_bc_create_data(&dvs, 4, 0, object->arch, line)); +} + +static void +dir_def(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp; + const char *symname; + yasm_symrec *sym; + coff_symrec_data *sym_data; + + if (objfmt_coff->def_sym) { + yasm_warn_set(YASM_WARN_GENERAL, + N_(".def pseudo-op used inside of .def/.endef; ignored")); + return; + } + + vp = yasm_vps_first(valparams); + symname = yasm_vp_id(vp); + if (!symname) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("argument to SAFESEH must be symbol name")); + return; + } + + sym = yasm_symtab_use(object->symtab, symname, line); + sym_data = yasm_symrec_get_data(sym, &coff_symrec_data_cb); + if (!sym_data) { + sym_data = coff_objfmt_sym_set_data(sym, COFF_SCL_NULL, 0, + COFF_SYMTAB_AUX_NONE); + } + objfmt_coff->def_sym = sym_data; +} + +static void +dir_scl(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_intnum *intn = NULL; + + if (!objfmt_coff->def_sym) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("%s pseudo-op used outside of .def/.endef; ignored"), + ".scl"); + return; + } + + if (yasm_dir_helper_intn(object, yasm_vps_first(valparams), line, + &intn, 0) < 0) + return; + if (!intn) + return; + objfmt_coff->def_sym->sclass = yasm_intnum_get_uint(intn); + yasm_intnum_destroy(intn); +} + +static void +dir_type(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_intnum *intn = NULL; + + if (!objfmt_coff->def_sym) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("%s pseudo-op used outside of .def/.endef; ignored"), + ".type"); + return; + } + + if (yasm_dir_helper_intn(object, yasm_vps_first(valparams), line, + &intn, 0) < 0) + return; + if (!intn) + return; + objfmt_coff->def_sym->type = yasm_intnum_get_uint(intn); + yasm_intnum_destroy(intn); +} + +static void +dir_endef(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + if (!objfmt_coff->def_sym) { + yasm_warn_set(YASM_WARN_GENERAL, + N_(".endef pseudo-op used before .def; ignored")); + return; + } + objfmt_coff->def_sym = NULL; +} + +static void +dir_proc_frame(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + const char *name = yasm_vp_id(vp); + + if (objfmt_coff->proc_frame) { + yasm_error_set_xref(objfmt_coff->proc_frame, + N_("previous procedure started here")); + yasm_error_set(YASM_ERROR_SYNTAX, + N_("nested procedures not supported (didn't use [ENDPROC_FRAME]?)")); + return; + } + objfmt_coff->proc_frame = line; + objfmt_coff->done_prolog = 0; + objfmt_coff->unwind = yasm_win64__uwinfo_create(); + objfmt_coff->unwind->proc = yasm_symtab_use(object->symtab, name, line); + + /* Optional error handler */ + vp = yasm_vps_next(vp); + if (!vp || !(name = yasm_vp_id(vp))) + return; + objfmt_coff->unwind->ehandler = + yasm_symtab_use(object->symtab, name, line); +} + +static int +procframe_checkstate(yasm_objfmt_coff *objfmt_coff, const char *dirname) +{ + if (!objfmt_coff->proc_frame) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] without preceding [PROC_FRAME]"), dirname); + return 0; + } + if (objfmt_coff->done_prolog) { + yasm_error_set_xref(objfmt_coff->done_prolog, + N_("prologue ended here")); + yasm_error_set(YASM_ERROR_SYNTAX, N_("[%s] after end of prologue"), + dirname); + return 0; + } + if (!objfmt_coff->unwind) + yasm_internal_error(N_("unwind info not present")); + return 1; +} + +/* Get current assembly position. + * XXX: There should be a better way to do this. + */ +static yasm_symrec * +get_curpos(yasm_object *object, const char *dirname, unsigned long line) +{ + if (!object->cur_section) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] can only be used inside of a section"), + dirname); + return NULL; + } + return yasm_symtab_define_curpos(object->symtab, "$", + yasm_section_bcs_last(object->cur_section), line); +} + +static void +dir_pushreg(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + coff_unwind_code *code; + const uintptr_t *reg; + + if (!procframe_checkstate(objfmt_coff, "PUSHREG")) + return; + + if (vp->type != YASM_PARAM_EXPR || + !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] requires a register as the first parameter"), + "PUSHREG"); + return; + } + + /* Generate a PUSH_NONVOL unwind code. */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, "PUSHREG", line); + code->opcode = UWOP_PUSH_NONVOL; + code->info = (unsigned int)(*reg & 0xF); + yasm_value_initialize(&code->off, NULL, 0); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_setframe(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + coff_unwind_code *code; + const uintptr_t *reg; + yasm_expr *off = NULL; + + if (!procframe_checkstate(objfmt_coff, "SETFRAME")) + return; + + if (vp->type != YASM_PARAM_EXPR || + !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] requires a register as the first parameter"), + "SETFRAME"); + return; + } + + vp = yasm_vps_next(vp); + if (vp) + off = yasm_vp_expr(vp, object->symtab, line); + + /* Set the frame fields in the unwind info */ + objfmt_coff->unwind->framereg = (unsigned long)(*reg); + yasm_value_initialize(&objfmt_coff->unwind->frameoff, off, 8); + + /* Generate a SET_FPREG unwind code */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, "SETFRAME", line); + code->opcode = UWOP_SET_FPREG; + code->info = (unsigned int)(*reg & 0xF); + yasm_value_initialize(&code->off, off ? yasm_expr_copy(off) : NULL, 8); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_allocstack(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + /*@null@*/ /*@only@*/ yasm_expr *size; + coff_unwind_code *code; + + if (!procframe_checkstate(objfmt_coff, "ALLOCSTACK")) + return; + + size = yasm_vp_expr(vp, object->symtab, line); + if (!size) { + yasm_error_set(YASM_ERROR_SYNTAX, N_("[%s] requires a size"), + "ALLOCSTACK"); + return; + } + + /* Generate an ALLOC_SMALL unwind code; this will get enlarged to an + * ALLOC_LARGE if necessary. + */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, "ALLOCSTACK", line); + code->opcode = UWOP_ALLOC_SMALL; + code->info = 0; + yasm_value_initialize(&code->off, size, 7); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_save_common(yasm_object *object, yasm_valparamhead *valparams, + unsigned long line, const char *name, int op) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + coff_unwind_code *code; + const uintptr_t *reg; + /*@only@*/ /*@null@*/ yasm_expr *offset; + + if (!procframe_checkstate(objfmt_coff, name)) + return; + + if (vp->type != YASM_PARAM_EXPR || + !(reg = yasm_expr_get_reg(&vp->param.e, 0))) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] requires a register as the first parameter"), + name); + return; + } + + vp = yasm_vps_next(vp); + offset = yasm_vp_expr(vp, object->symtab, line); + if (!offset) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] requires an offset as the second parameter"), + name); + return; + } + + /* Generate a SAVE_XXX unwind code; this will get enlarged to a + * SAVE_XXX_FAR if necessary. + */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, name, line); + code->opcode = op; + code->info = (unsigned int)(*reg & 0xF); + yasm_value_initialize(&code->off, offset, 16); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_savereg(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + dir_save_common(object, valparams, line, "SAVEREG", UWOP_SAVE_NONVOL); +} + +static void +dir_savexmm128(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + dir_save_common(object, valparams, line, "SAVEXMM128", UWOP_SAVE_XMM128); +} + +static void +dir_pushframe(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + coff_unwind_code *code; + + if (!procframe_checkstate(objfmt_coff, "PUSHFRAME")) + return; + + /* Generate a PUSH_MACHFRAME unwind code. If there's any parameter, + * we set info to 1. Otherwise we set info to 0. + */ + code = yasm_xmalloc(sizeof(coff_unwind_code)); + code->proc = objfmt_coff->unwind->proc; + code->loc = get_curpos(object, "PUSHFRAME", line); + code->opcode = UWOP_PUSH_MACHFRAME; + code->info = vp != NULL; + yasm_value_initialize(&code->off, NULL, 0); + SLIST_INSERT_HEAD(&objfmt_coff->unwind->codes, code, link); +} + +static void +dir_endprolog(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + if (!procframe_checkstate(objfmt_coff, "ENDPROLOG")) + return; + objfmt_coff->done_prolog = line; + + objfmt_coff->unwind->prolog = get_curpos(object, "ENDPROLOG", line); +} + +static void +dir_endproc_frame(yasm_object *object, /*@null@*/ yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_coff *objfmt_coff = (yasm_objfmt_coff *)object->objfmt; + yasm_section *sect; + coff_section_data *csd; + yasm_datavalhead dvs; + int isnew; + /*@dependent@*/ yasm_symrec *curpos, *unwindpos, *proc_sym, *xdata_sym; + + if (!objfmt_coff->proc_frame) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("[%s] without preceding [PROC_FRAME]"), + "ENDPROC_FRAME"); + return; + } + if (!objfmt_coff->done_prolog) { + yasm_error_set_xref(objfmt_coff->proc_frame, + N_("procedure started here")); + yasm_error_set(YASM_ERROR_SYNTAX, + N_("ended procedure without ending prologue"), + "ENDPROC_FRAME"); + objfmt_coff->proc_frame = 0; + yasm_win64__uwinfo_destroy(objfmt_coff->unwind); + objfmt_coff->unwind = NULL; + return; + } + if (!objfmt_coff->unwind) + yasm_internal_error(N_("unwind info not present")); + + proc_sym = objfmt_coff->unwind->proc; + + curpos = get_curpos(object, "ENDPROC_FRAME", line); + + /* + * Add unwind info to end of .xdata section. + */ + + sect = yasm_object_get_general(object, ".xdata", 0, 0, 0, &isnew, line); + + /* Initialize xdata section if needed */ + if (isnew) { + csd = yasm_section_get_data(sect, &coff_section_data_cb); + csd->flags = COFF_STYP_DATA | COFF_STYP_READ; + yasm_section_set_align(sect, 8, line); + } + + /* Get current position in .xdata section */ + unwindpos = yasm_symtab_define_curpos(object->symtab, "$", + yasm_section_bcs_last(sect), line); + /* Get symbol for .xdata as we'll want to reference it with WRT */ + csd = yasm_section_get_data(sect, &coff_section_data_cb); + xdata_sym = csd->sym; + + /* Add unwind info. Use line number of start of procedure. */ + yasm_win64__unwind_generate(sect, objfmt_coff->unwind, + objfmt_coff->proc_frame); + objfmt_coff->unwind = NULL; /* generate keeps the unwind pointer */ + + /* + * Add function lookup to end of .pdata section. + */ + + sect = yasm_object_get_general(object, ".pdata", 0, 0, 0, &isnew, line); + + /* Initialize pdata section if needed */ + if (isnew) { + csd = yasm_section_get_data(sect, &coff_section_data_cb); + csd->flags = COFF_STYP_DATA | COFF_STYP_READ; + csd->flags2 = COFF_FLAG_NOBASE; + yasm_section_set_align(sect, 4, line); + } + + /* Add function structure as data bytecode */ + yasm_dvs_initialize(&dvs); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create_ident(yasm_expr_sym(proc_sym), line))); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create(YASM_EXPR_WRT, yasm_expr_sym(curpos), + yasm_expr_sym(proc_sym), line))); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create(YASM_EXPR_WRT, yasm_expr_sym(unwindpos), + yasm_expr_sym(xdata_sym), line))); + yasm_section_bcs_append(sect, yasm_bc_create_data(&dvs, 4, 0, NULL, line)); + + objfmt_coff->proc_frame = 0; + objfmt_coff->done_prolog = 0; +} + +/* Define valid debug formats to use with this object format */ +static const char *coff_objfmt_dbgfmt_keywords[] = { + "null", + "dwarf2", + NULL +}; + +static const yasm_directive coff_objfmt_directives[] = { + { ".ident", "gas", dir_ident, YASM_DIR_ANY }, + { "ident", "nasm", dir_ident, YASM_DIR_ANY }, + { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, + { ".endef", "gas", dir_endef, YASM_DIR_ANY }, + { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, + { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, + { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, + { NULL, NULL, NULL, 0 } +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_coff_LTX_objfmt = { + "COFF (DJGPP)", + "coff", + "o", + 32, + 0, + coff_objfmt_dbgfmt_keywords, + "null", + coff_objfmt_directives, + NULL, /* no standard macros */ + coff_objfmt_create, + coff_objfmt_output, + coff_objfmt_destroy, + coff_objfmt_add_default_section, + coff_objfmt_init_new_section, + coff_objfmt_section_switch, + coff_objfmt_get_special_sym +}; + +/* Define valid debug formats to use with this object format */ +static const char *winXX_objfmt_dbgfmt_keywords[] = { + "null", + "dwarf2", + "cv8", + NULL +}; + +static const yasm_directive win32_objfmt_directives[] = { + { ".ident", "gas", dir_ident, YASM_DIR_ANY }, + { "ident", "nasm", dir_ident, YASM_DIR_ANY }, + { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, + { ".endef", "gas", dir_endef, YASM_DIR_ANY }, + { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, + { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, + { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, + { ".export", "gas", dir_export, YASM_DIR_ID_REQUIRED }, + { "export", "nasm", dir_export, YASM_DIR_ID_REQUIRED }, + { ".safeseh", "gas", dir_safeseh, YASM_DIR_ID_REQUIRED }, + { "safeseh", "nasm", dir_safeseh, YASM_DIR_ID_REQUIRED }, + { NULL, NULL, NULL, 0 } +}; + +static const char *win32_nasm_stdmac[] = { + "%imacro export 1+.nolist", + "[export %1]", + "%endmacro", + "%imacro safeseh 1+.nolist", + "[safeseh %1]", + "%endmacro", + NULL +}; + +static const yasm_stdmac win32_objfmt_stdmacs[] = { + { "nasm", "nasm", win32_nasm_stdmac }, + { NULL, NULL, NULL } +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_win32_LTX_objfmt = { + "Win32", + "win32", + "obj", + 32, + 1, + winXX_objfmt_dbgfmt_keywords, + "null", + win32_objfmt_directives, + win32_objfmt_stdmacs, + win32_objfmt_create, + coff_objfmt_output, + coff_objfmt_destroy, + coff_objfmt_add_default_section, + coff_objfmt_init_new_section, + coff_objfmt_section_switch, + coff_objfmt_get_special_sym +}; + +static const yasm_directive win64_objfmt_directives[] = { + { ".ident", "gas", dir_ident, YASM_DIR_ANY }, + { "ident", "nasm", dir_ident, YASM_DIR_ANY }, + { ".def", "gas", dir_def, YASM_DIR_ID_REQUIRED }, + { ".endef", "gas", dir_endef, YASM_DIR_ANY }, + { ".scl", "gas", dir_scl, YASM_DIR_ARG_REQUIRED }, + { ".type", "gas", dir_type, YASM_DIR_ARG_REQUIRED }, + { ".secrel32", "gas", dir_secrel32, YASM_DIR_ARG_REQUIRED }, + { ".export", "gas", dir_export, YASM_DIR_ID_REQUIRED }, + { "export", "nasm", dir_export, YASM_DIR_ID_REQUIRED }, + { ".proc_frame", "gas", dir_proc_frame, YASM_DIR_ID_REQUIRED }, + { "proc_frame", "nasm", dir_proc_frame, YASM_DIR_ID_REQUIRED }, + { ".pushreg", "gas", dir_pushreg, YASM_DIR_ARG_REQUIRED }, + { "pushreg", "nasm", dir_pushreg, YASM_DIR_ARG_REQUIRED }, + { ".setframe", "gas", dir_setframe, YASM_DIR_ARG_REQUIRED }, + { "setframe", "nasm", dir_setframe, YASM_DIR_ARG_REQUIRED }, + { ".allocstack", "gas", dir_allocstack, YASM_DIR_ARG_REQUIRED }, + { "allocstack", "nasm", dir_allocstack, YASM_DIR_ARG_REQUIRED }, + { ".savereg", "gas", dir_savereg, YASM_DIR_ARG_REQUIRED }, + { "savereg", "nasm", dir_savereg, YASM_DIR_ARG_REQUIRED }, + { ".savexmm128", "gas", dir_savexmm128, YASM_DIR_ARG_REQUIRED }, + { "savexmm128", "nasm", dir_savexmm128, YASM_DIR_ARG_REQUIRED }, + { ".pushframe", "gas", dir_pushframe, YASM_DIR_ANY }, + { "pushframe", "nasm", dir_pushframe, YASM_DIR_ANY }, + { ".endprolog", "gas", dir_endprolog, YASM_DIR_ANY }, + { "endprolog", "nasm", dir_endprolog, YASM_DIR_ANY }, + { ".endproc_frame", "gas", dir_endproc_frame, YASM_DIR_ANY }, + { "endproc_frame", "nasm", dir_endproc_frame, YASM_DIR_ANY }, + { NULL, NULL, NULL, 0 } +}; + +#include "win64-nasm.c" +#include "win64-gas.c" + +static const yasm_stdmac win64_objfmt_stdmacs[] = { + { "nasm", "nasm", win64_nasm_stdmac }, + { "gas", "nasm", win64_gas_stdmac }, + { NULL, NULL, NULL } +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_win64_LTX_objfmt = { + "Win64", + "win64", + "obj", + 64, + 1, + winXX_objfmt_dbgfmt_keywords, + "null", + win64_objfmt_directives, + win64_objfmt_stdmacs, + win64_objfmt_create, + coff_objfmt_output, + coff_objfmt_destroy, + coff_objfmt_add_default_section, + coff_objfmt_init_new_section, + coff_objfmt_section_switch, + win64_objfmt_get_special_sym +}; +yasm_objfmt_module yasm_x64_LTX_objfmt = { + "Win64", + "x64", + "obj", + 64, + 1, + winXX_objfmt_dbgfmt_keywords, + "null", + win64_objfmt_directives, + win64_objfmt_stdmacs, + win64_objfmt_create, + coff_objfmt_output, + coff_objfmt_destroy, + coff_objfmt_add_default_section, + coff_objfmt_init_new_section, + coff_objfmt_section_switch, + win64_objfmt_get_special_sym +}; diff --git a/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h b/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h index 10d88a0982..cdd581acb2 100644 --- a/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h +++ b/contrib/tools/yasm/modules/objfmts/coff/coff-objfmt.h @@ -1,77 +1,77 @@ -/* - * COFF (DJGPP) object format - * - * Copyright (C) 2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef COFF_OBJFMT_H -#define COFF_OBJFMT_H - -typedef struct coff_unwind_code { - SLIST_ENTRY(coff_unwind_code) link; - - /*@dependent@*/ yasm_symrec *proc; /* Start of procedure */ - /*@dependent@*/ yasm_symrec *loc; /* Location of operation */ - /* Unwind operation code */ - enum { - UWOP_PUSH_NONVOL = 0, - UWOP_ALLOC_LARGE = 1, - UWOP_ALLOC_SMALL = 2, - UWOP_SET_FPREG = 3, - UWOP_SAVE_NONVOL = 4, - UWOP_SAVE_NONVOL_FAR = 5, - UWOP_SAVE_XMM128 = 8, - UWOP_SAVE_XMM128_FAR = 9, - UWOP_PUSH_MACHFRAME = 10 - } opcode; - unsigned int info; /* Operation info */ - yasm_value off; /* Offset expression (used for some codes) */ -} coff_unwind_code; - -typedef struct coff_unwind_info { - /*@dependent@*/ yasm_symrec *proc; /* Start of procedure */ - /*@dependent@*/ yasm_symrec *prolog; /* End of prologue */ - - /*@null@*/ /*@dependent@*/ yasm_symrec *ehandler; /* Error handler */ - - unsigned long framereg; /* Frame register */ - yasm_value frameoff; /* Frame offset */ - - /* Linked list of codes, in decreasing location offset order. - * Inserting at the head of this list during assembly naturally results - * in this sorting. - */ - SLIST_HEAD(coff_unwind_code_head, coff_unwind_code) codes; - - /* These aren't used until inside of generate. */ - yasm_value prolog_size; - yasm_value codes_count; -} coff_unwind_info; - -coff_unwind_info *yasm_win64__uwinfo_create(void); -void yasm_win64__uwinfo_destroy(coff_unwind_info *info); -void yasm_win64__unwind_generate(yasm_section *xdata, - /*@only@*/ coff_unwind_info *info, - unsigned long line); - -#endif +/* + * COFF (DJGPP) object format + * + * Copyright (C) 2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef COFF_OBJFMT_H +#define COFF_OBJFMT_H + +typedef struct coff_unwind_code { + SLIST_ENTRY(coff_unwind_code) link; + + /*@dependent@*/ yasm_symrec *proc; /* Start of procedure */ + /*@dependent@*/ yasm_symrec *loc; /* Location of operation */ + /* Unwind operation code */ + enum { + UWOP_PUSH_NONVOL = 0, + UWOP_ALLOC_LARGE = 1, + UWOP_ALLOC_SMALL = 2, + UWOP_SET_FPREG = 3, + UWOP_SAVE_NONVOL = 4, + UWOP_SAVE_NONVOL_FAR = 5, + UWOP_SAVE_XMM128 = 8, + UWOP_SAVE_XMM128_FAR = 9, + UWOP_PUSH_MACHFRAME = 10 + } opcode; + unsigned int info; /* Operation info */ + yasm_value off; /* Offset expression (used for some codes) */ +} coff_unwind_code; + +typedef struct coff_unwind_info { + /*@dependent@*/ yasm_symrec *proc; /* Start of procedure */ + /*@dependent@*/ yasm_symrec *prolog; /* End of prologue */ + + /*@null@*/ /*@dependent@*/ yasm_symrec *ehandler; /* Error handler */ + + unsigned long framereg; /* Frame register */ + yasm_value frameoff; /* Frame offset */ + + /* Linked list of codes, in decreasing location offset order. + * Inserting at the head of this list during assembly naturally results + * in this sorting. + */ + SLIST_HEAD(coff_unwind_code_head, coff_unwind_code) codes; + + /* These aren't used until inside of generate. */ + yasm_value prolog_size; + yasm_value codes_count; +} coff_unwind_info; + +coff_unwind_info *yasm_win64__uwinfo_create(void); +void yasm_win64__uwinfo_destroy(coff_unwind_info *info); +void yasm_win64__unwind_generate(yasm_section *xdata, + /*@only@*/ coff_unwind_info *info, + unsigned long line); + +#endif diff --git a/contrib/tools/yasm/modules/objfmts/coff/win64-except.c b/contrib/tools/yasm/modules/objfmts/coff/win64-except.c index 8c8ecbbc49..8fe585df8c 100644 --- a/contrib/tools/yasm/modules/objfmts/coff/win64-except.c +++ b/contrib/tools/yasm/modules/objfmts/coff/win64-except.c @@ -1,559 +1,559 @@ -/* - * Win64 structured exception handling support - * - * Copyright (C) 2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include <util.h> - -#include <libyasm.h> - -#include "coff-objfmt.h" - - -#define UNW_FLAG_EHANDLER 0x01 -#define UNW_FLAG_UHANDLER 0x02 -#define UNW_FLAG_CHAININFO 0x04 - -/* Bytecode callback function prototypes */ -static void win64_uwinfo_bc_destroy(void *contents); -static void win64_uwinfo_bc_print(const void *contents, FILE *f, - int indent_level); -static void win64_uwinfo_bc_finalize(yasm_bytecode *bc, - yasm_bytecode *prev_bc); -static int win64_uwinfo_bc_calc_len - (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); -static int win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, - long new_val, /*@out@*/ long *neg_thres, - /*@out@*/ long *pos_thres); -static int win64_uwinfo_bc_tobytes - (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - /*@null@*/ yasm_output_reloc_func output_reloc); - -static void win64_uwcode_bc_destroy(void *contents); -static void win64_uwcode_bc_print(const void *contents, FILE *f, - int indent_level); -static void win64_uwcode_bc_finalize(yasm_bytecode *bc, - yasm_bytecode *prev_bc); -static int win64_uwcode_bc_calc_len - (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); -static int win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, - long new_val, /*@out@*/ long *neg_thres, - /*@out@*/ long *pos_thres); -static int win64_uwcode_bc_tobytes - (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - /*@null@*/ yasm_output_reloc_func output_reloc); - -/* Bytecode callback structures */ -static const yasm_bytecode_callback win64_uwinfo_bc_callback = { - win64_uwinfo_bc_destroy, - win64_uwinfo_bc_print, - win64_uwinfo_bc_finalize, - NULL, - win64_uwinfo_bc_calc_len, - win64_uwinfo_bc_expand, - win64_uwinfo_bc_tobytes, - 0 -}; - -static const yasm_bytecode_callback win64_uwcode_bc_callback = { - win64_uwcode_bc_destroy, - win64_uwcode_bc_print, - win64_uwcode_bc_finalize, - NULL, - win64_uwcode_bc_calc_len, - win64_uwcode_bc_expand, - win64_uwcode_bc_tobytes, - 0 -}; - - -coff_unwind_info * -yasm_win64__uwinfo_create(void) -{ - coff_unwind_info *info = yasm_xmalloc(sizeof(coff_unwind_info)); - info->proc = NULL; - info->prolog = NULL; - info->ehandler = NULL; - info->framereg = 0; - /* Frameoff is really a 4-bit value, scaled by 16 */ - yasm_value_initialize(&info->frameoff, NULL, 8); - SLIST_INIT(&info->codes); - yasm_value_initialize(&info->prolog_size, NULL, 8); - yasm_value_initialize(&info->codes_count, NULL, 8); - return info; -} - -void -yasm_win64__uwinfo_destroy(coff_unwind_info *info) -{ - coff_unwind_code *code; - - yasm_value_delete(&info->frameoff); - yasm_value_delete(&info->prolog_size); - yasm_value_delete(&info->codes_count); - - while (!SLIST_EMPTY(&info->codes)) { - code = SLIST_FIRST(&info->codes); - SLIST_REMOVE_HEAD(&info->codes, link); - yasm_value_delete(&code->off); - yasm_xfree(code); - } - yasm_xfree(info); -} - -void -yasm_win64__unwind_generate(yasm_section *xdata, coff_unwind_info *info, - unsigned long line) -{ - yasm_bytecode *infobc, *codebc = NULL; - coff_unwind_code *code; - - /* 4-byte align the start of unwind info */ - yasm_section_bcs_append(xdata, yasm_bc_create_align( - yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)), - line), - NULL, NULL, NULL, line)); - - /* Prolog size = end of prolog - start of procedure */ - yasm_value_initialize(&info->prolog_size, - yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(info->prolog), - yasm_expr_sym(info->proc), line), - 8); - - /* Unwind info */ - infobc = yasm_bc_create_common(&win64_uwinfo_bc_callback, info, line); - yasm_section_bcs_append(xdata, infobc); - - /* Code array */ - SLIST_FOREACH(code, &info->codes, link) { - codebc = yasm_bc_create_common(&win64_uwcode_bc_callback, code, - yasm_symrec_get_def_line(code->loc)); - yasm_section_bcs_append(xdata, codebc); - } - - /* Avoid double-free (by code destroy and uwinfo destroy). */ - SLIST_INIT(&info->codes); - - /* Number of codes = (Last code - end of info) >> 1 */ - if (!codebc) { - yasm_value_initialize(&info->codes_count, - yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)), - line), - 8); - } else { - yasm_value_initialize(&info->codes_count, - yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr( - yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(codebc), - yasm_expr_precbc(infobc), line)), - yasm_expr_int(yasm_intnum_create_uint(1)), line), - 8); - } - - /* 4-byte align */ - yasm_section_bcs_append(xdata, yasm_bc_create_align( - yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)), - line), - NULL, NULL, NULL, line)); - - /* Exception handler, if present. Use data bytecode. */ - if (info->ehandler) { - yasm_datavalhead dvs; - - yasm_dvs_initialize(&dvs); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create_ident(yasm_expr_sym(info->ehandler), line))); - yasm_section_bcs_append(xdata, - yasm_bc_create_data(&dvs, 4, 0, NULL, line)); - } -} - -static void -win64_uwinfo_bc_destroy(void *contents) -{ - yasm_win64__uwinfo_destroy((coff_unwind_info *)contents); -} - -static void -win64_uwinfo_bc_print(const void *contents, FILE *f, int indent_level) -{ - /* TODO */ -} - -static void -win64_uwinfo_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) -{ - coff_unwind_info *info = (coff_unwind_info *)bc->contents; - - if (yasm_value_finalize(&info->prolog_size, prev_bc)) - yasm_internal_error(N_("prolog size expression too complex")); - - if (yasm_value_finalize(&info->codes_count, prev_bc)) - yasm_internal_error(N_("codes count expression too complex")); - - if (yasm_value_finalize(&info->frameoff, prev_bc)) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset expression too complex")); -} - -static int -win64_uwinfo_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, - void *add_span_data) -{ - coff_unwind_info *info = (coff_unwind_info *)bc->contents; - /*@only@*/ /*@null@*/ yasm_intnum *intn; - long intv; - - /* Want to make sure prolog size and codes count doesn't exceed - * byte-size, and scaled frame offset doesn't exceed 4 bits. - */ - add_span(add_span_data, bc, 1, &info->prolog_size, 0, 255); - add_span(add_span_data, bc, 2, &info->codes_count, 0, 255); - - intn = yasm_value_get_intnum(&info->frameoff, bc, 0); - if (intn) { - intv = yasm_intnum_get_int(intn); - if (intv < 0 || intv > 240) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld bytes, must be between 0 and 240"), - intv); - else if ((intv & 0xF) != 0) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld is not a multiple of 16"), intv); - yasm_intnum_destroy(intn); - } else - add_span(add_span_data, bc, 3, &info->frameoff, 0, 240); - - bc->len += 4; - return 0; -} - -static int -win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, - /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) -{ - coff_unwind_info *info = (coff_unwind_info *)bc->contents; - switch (span) { - case 1: - yasm_error_set_xref(yasm_symrec_get_def_line(info->prolog), - N_("prologue ended here")); - yasm_error_set(YASM_ERROR_VALUE, - N_("prologue %ld bytes, must be <256"), new_val); - return -1; - case 2: - yasm_error_set(YASM_ERROR_VALUE, - N_("%ld unwind codes, maximum of 255"), new_val); - return -1; - case 3: - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld bytes, must be between 0 and 240"), - new_val); - return -1; - default: - yasm_internal_error(N_("unrecognized span id")); - } - return 0; -} - -static int -win64_uwinfo_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, - unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - yasm_output_reloc_func output_reloc) -{ - coff_unwind_info *info = (coff_unwind_info *)bc->contents; - unsigned char *buf = *bufp; - /*@only@*/ /*@null@*/ yasm_intnum *frameoff; - long intv; - - /* Version and flags */ - if (info->ehandler) - YASM_WRITE_8(buf, 1 | (UNW_FLAG_EHANDLER << 3)); - else - YASM_WRITE_8(buf, 1); - - /* Size of prolog */ - output_value(&info->prolog_size, buf, 1, (unsigned long)(buf-bufstart), - bc, 1, d); - buf += 1; - - /* Count of codes */ - output_value(&info->codes_count, buf, 1, (unsigned long)(buf-bufstart), - bc, 1, d); - buf += 1; - - /* Frame register and offset */ - frameoff = yasm_value_get_intnum(&info->frameoff, bc, 1); - if (!frameoff) { - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset expression too complex")); - return 1; - } - intv = yasm_intnum_get_int(frameoff); - if (intv < 0 || intv > 240) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld bytes, must be between 0 and 240"), intv); - else if ((intv & 0xF) != 0) - yasm_error_set(YASM_ERROR_VALUE, - N_("frame offset of %ld is not a multiple of 16"), intv); - - YASM_WRITE_8(buf, ((unsigned long)intv & 0xF0) | (info->framereg & 0x0F)); - yasm_intnum_destroy(frameoff); - - *bufp = buf; - return 0; -} - -static void -win64_uwcode_bc_destroy(void *contents) -{ - coff_unwind_code *code = (coff_unwind_code *)contents; - yasm_value_delete(&code->off); - yasm_xfree(contents); -} - -static void -win64_uwcode_bc_print(const void *contents, FILE *f, int indent_level) -{ - /* TODO */ -} - -static void -win64_uwcode_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) -{ - coff_unwind_code *code = (coff_unwind_code *)bc->contents; - if (yasm_value_finalize(&code->off, prev_bc)) - yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex")); -} - -static int -win64_uwcode_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, - void *add_span_data) -{ - coff_unwind_code *code = (coff_unwind_code *)bc->contents; - int span = 0; - /*@only@*/ /*@null@*/ yasm_intnum *intn; - long intv; - long low, high, mask; - - bc->len += 2; /* Prolog offset, code, and info */ - - switch (code->opcode) { - case UWOP_PUSH_NONVOL: - case UWOP_SET_FPREG: - case UWOP_PUSH_MACHFRAME: - /* always 1 node */ - return 0; - case UWOP_ALLOC_SMALL: - case UWOP_ALLOC_LARGE: - /* Start with smallest, then work our way up as necessary */ - code->opcode = UWOP_ALLOC_SMALL; - code->info = 0; - span = 1; low = 8; high = 128; mask = 0x7; - break; - case UWOP_SAVE_NONVOL: - case UWOP_SAVE_NONVOL_FAR: - /* Start with smallest, then work our way up as necessary */ - code->opcode = UWOP_SAVE_NONVOL; - bc->len += 2; /* Scaled offset */ - span = 2; - low = 0; - high = 8*64*1024-8; /* 16-bit field, *8 scaling */ - mask = 0x7; - break; - case UWOP_SAVE_XMM128: - case UWOP_SAVE_XMM128_FAR: - /* Start with smallest, then work our way up as necessary */ - code->opcode = UWOP_SAVE_XMM128; - bc->len += 2; /* Scaled offset */ - span = 3; - low = 0; - high = 16*64*1024-16; /* 16-bit field, *16 scaling */ - mask = 0xF; - break; - default: - yasm_internal_error(N_("unrecognied unwind opcode")); - /*@unreached@*/ - return 0; - } - - intn = yasm_value_get_intnum(&code->off, bc, 0); - if (intn) { - intv = yasm_intnum_get_int(intn); - if (intv > high) { - /* Expand it ourselves here if we can and we're already larger */ - if (win64_uwcode_bc_expand(bc, span, intv, intv, &low, &high) > 0) - add_span(add_span_data, bc, span, &code->off, low, high); - } - if (intv < low) - yasm_error_set(YASM_ERROR_VALUE, - N_("negative offset not allowed")); - if ((intv & mask) != 0) - yasm_error_set(YASM_ERROR_VALUE, - N_("offset of %ld is not a multiple of %ld"), intv, mask+1); - yasm_intnum_destroy(intn); - } else - add_span(add_span_data, bc, span, &code->off, low, high); - return 0; -} - -static int -win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, - /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) -{ - coff_unwind_code *code = (coff_unwind_code *)bc->contents; - - if (new_val < 0) { - yasm_error_set(YASM_ERROR_VALUE, N_("negative offset not allowed")); - return -1; - } - - if (span == 1) { - /* 3 stages: SMALL, LARGE and info=0, LARGE and info=1 */ - if (code->opcode == UWOP_ALLOC_LARGE && code->info == 1) - yasm_internal_error(N_("expansion on already largest alloc")); - - if (code->opcode == UWOP_ALLOC_SMALL && new_val > 128) { - /* Overflowed small size */ - code->opcode = UWOP_ALLOC_LARGE; - bc->len += 2; - } - if (new_val <= 8*64*1024-8) { - /* Still can grow one more size */ - *pos_thres = 8*64*1024-8; - return 1; - } - /* We're into the largest size */ - code->info = 1; - bc->len += 2; - } else if (code->opcode == UWOP_SAVE_NONVOL && span == 2) { - code->opcode = UWOP_SAVE_NONVOL_FAR; - bc->len += 2; - } else if (code->opcode == UWOP_SAVE_XMM128 && span == 3) { - code->opcode = UWOP_SAVE_XMM128_FAR; - bc->len += 2; - } - return 0; -} - -static int -win64_uwcode_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, - unsigned char *bufstart, void *d, - yasm_output_value_func output_value, - yasm_output_reloc_func output_reloc) -{ - coff_unwind_code *code = (coff_unwind_code *)bc->contents; - unsigned char *buf = *bufp; - yasm_value val; - unsigned int size; - int shift; - long intv, low, high, mask; - yasm_intnum *intn; - - /* Offset in prolog */ - yasm_value_initialize(&val, - yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(code->loc), - yasm_expr_sym(code->proc), bc->line), - 8); - output_value(&val, buf, 1, (unsigned long)(buf-bufstart), bc, 1, d); - buf += 1; - yasm_value_delete(&val); - - /* Offset value */ - switch (code->opcode) { - case UWOP_PUSH_NONVOL: - case UWOP_SET_FPREG: - case UWOP_PUSH_MACHFRAME: - /* just 1 node, no offset; write opcode and info and we're done */ - YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF)); - *bufp = buf; - return 0; - case UWOP_ALLOC_SMALL: - /* 1 node, but offset stored in info */ - size = 0; low = 8; high = 128; shift = 3; mask = 0x7; - break; - case UWOP_ALLOC_LARGE: - if (code->info == 0) { - size = 2; low = 136; high = 8*64*1024-8; shift = 3; - } else { - size = 4; low = high = 0; shift = 0; - } - mask = 0x7; - break; - case UWOP_SAVE_NONVOL: - size = 2; low = 0; high = 8*64*1024-8; shift = 3; mask = 0x7; - break; - case UWOP_SAVE_XMM128: - size = 2; low = 0; high = 16*64*1024-16; shift = 4; mask = 0xF; - break; - case UWOP_SAVE_NONVOL_FAR: - size = 4; low = high = 0; shift = 0; mask = 0x7; - break; - case UWOP_SAVE_XMM128_FAR: - size = 4; low = high = 0; shift = 0; mask = 0xF; - break; - default: - yasm_internal_error(N_("unrecognied unwind opcode")); - /*@unreached@*/ - return 1; - } - - /* Check for overflow */ - intn = yasm_value_get_intnum(&code->off, bc, 1); - if (!intn) { - yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex")); - return 1; - } - intv = yasm_intnum_get_int(intn); - if (size != 4 && (intv < low || intv > high)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("offset of %ld bytes, must be between %ld and %ld"), - intv, low, high); - return 1; - } - if ((intv & mask) != 0) { - yasm_error_set(YASM_ERROR_VALUE, - N_("offset of %ld is not a multiple of %ld"), - intv, mask+1); - return 1; - } - - /* Stored value in info instead of extra code space */ - if (size == 0) - code->info = (yasm_intnum_get_uint(intn) >> shift)-1; - - /* Opcode and info */ - YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF)); - - if (size != 0) { - yasm_intnum_get_sized(intn, buf, size, size*8, -shift, 0, 1); - buf += size; - } - - yasm_intnum_destroy(intn); - - *bufp = buf; - return 0; -} +/* + * Win64 structured exception handling support + * + * Copyright (C) 2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <util.h> + +#include <libyasm.h> + +#include "coff-objfmt.h" + + +#define UNW_FLAG_EHANDLER 0x01 +#define UNW_FLAG_UHANDLER 0x02 +#define UNW_FLAG_CHAININFO 0x04 + +/* Bytecode callback function prototypes */ +static void win64_uwinfo_bc_destroy(void *contents); +static void win64_uwinfo_bc_print(const void *contents, FILE *f, + int indent_level); +static void win64_uwinfo_bc_finalize(yasm_bytecode *bc, + yasm_bytecode *prev_bc); +static int win64_uwinfo_bc_calc_len + (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); +static int win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, + long new_val, /*@out@*/ long *neg_thres, + /*@out@*/ long *pos_thres); +static int win64_uwinfo_bc_tobytes + (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +static void win64_uwcode_bc_destroy(void *contents); +static void win64_uwcode_bc_print(const void *contents, FILE *f, + int indent_level); +static void win64_uwcode_bc_finalize(yasm_bytecode *bc, + yasm_bytecode *prev_bc); +static int win64_uwcode_bc_calc_len + (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); +static int win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, + long new_val, /*@out@*/ long *neg_thres, + /*@out@*/ long *pos_thres); +static int win64_uwcode_bc_tobytes + (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +/* Bytecode callback structures */ +static const yasm_bytecode_callback win64_uwinfo_bc_callback = { + win64_uwinfo_bc_destroy, + win64_uwinfo_bc_print, + win64_uwinfo_bc_finalize, + NULL, + win64_uwinfo_bc_calc_len, + win64_uwinfo_bc_expand, + win64_uwinfo_bc_tobytes, + 0 +}; + +static const yasm_bytecode_callback win64_uwcode_bc_callback = { + win64_uwcode_bc_destroy, + win64_uwcode_bc_print, + win64_uwcode_bc_finalize, + NULL, + win64_uwcode_bc_calc_len, + win64_uwcode_bc_expand, + win64_uwcode_bc_tobytes, + 0 +}; + + +coff_unwind_info * +yasm_win64__uwinfo_create(void) +{ + coff_unwind_info *info = yasm_xmalloc(sizeof(coff_unwind_info)); + info->proc = NULL; + info->prolog = NULL; + info->ehandler = NULL; + info->framereg = 0; + /* Frameoff is really a 4-bit value, scaled by 16 */ + yasm_value_initialize(&info->frameoff, NULL, 8); + SLIST_INIT(&info->codes); + yasm_value_initialize(&info->prolog_size, NULL, 8); + yasm_value_initialize(&info->codes_count, NULL, 8); + return info; +} + +void +yasm_win64__uwinfo_destroy(coff_unwind_info *info) +{ + coff_unwind_code *code; + + yasm_value_delete(&info->frameoff); + yasm_value_delete(&info->prolog_size); + yasm_value_delete(&info->codes_count); + + while (!SLIST_EMPTY(&info->codes)) { + code = SLIST_FIRST(&info->codes); + SLIST_REMOVE_HEAD(&info->codes, link); + yasm_value_delete(&code->off); + yasm_xfree(code); + } + yasm_xfree(info); +} + +void +yasm_win64__unwind_generate(yasm_section *xdata, coff_unwind_info *info, + unsigned long line) +{ + yasm_bytecode *infobc, *codebc = NULL; + coff_unwind_code *code; + + /* 4-byte align the start of unwind info */ + yasm_section_bcs_append(xdata, yasm_bc_create_align( + yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)), + line), + NULL, NULL, NULL, line)); + + /* Prolog size = end of prolog - start of procedure */ + yasm_value_initialize(&info->prolog_size, + yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(info->prolog), + yasm_expr_sym(info->proc), line), + 8); + + /* Unwind info */ + infobc = yasm_bc_create_common(&win64_uwinfo_bc_callback, info, line); + yasm_section_bcs_append(xdata, infobc); + + /* Code array */ + SLIST_FOREACH(code, &info->codes, link) { + codebc = yasm_bc_create_common(&win64_uwcode_bc_callback, code, + yasm_symrec_get_def_line(code->loc)); + yasm_section_bcs_append(xdata, codebc); + } + + /* Avoid double-free (by code destroy and uwinfo destroy). */ + SLIST_INIT(&info->codes); + + /* Number of codes = (Last code - end of info) >> 1 */ + if (!codebc) { + yasm_value_initialize(&info->codes_count, + yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)), + line), + 8); + } else { + yasm_value_initialize(&info->codes_count, + yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr( + yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(codebc), + yasm_expr_precbc(infobc), line)), + yasm_expr_int(yasm_intnum_create_uint(1)), line), + 8); + } + + /* 4-byte align */ + yasm_section_bcs_append(xdata, yasm_bc_create_align( + yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)), + line), + NULL, NULL, NULL, line)); + + /* Exception handler, if present. Use data bytecode. */ + if (info->ehandler) { + yasm_datavalhead dvs; + + yasm_dvs_initialize(&dvs); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create_ident(yasm_expr_sym(info->ehandler), line))); + yasm_section_bcs_append(xdata, + yasm_bc_create_data(&dvs, 4, 0, NULL, line)); + } +} + +static void +win64_uwinfo_bc_destroy(void *contents) +{ + yasm_win64__uwinfo_destroy((coff_unwind_info *)contents); +} + +static void +win64_uwinfo_bc_print(const void *contents, FILE *f, int indent_level) +{ + /* TODO */ +} + +static void +win64_uwinfo_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + coff_unwind_info *info = (coff_unwind_info *)bc->contents; + + if (yasm_value_finalize(&info->prolog_size, prev_bc)) + yasm_internal_error(N_("prolog size expression too complex")); + + if (yasm_value_finalize(&info->codes_count, prev_bc)) + yasm_internal_error(N_("codes count expression too complex")); + + if (yasm_value_finalize(&info->frameoff, prev_bc)) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset expression too complex")); +} + +static int +win64_uwinfo_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + coff_unwind_info *info = (coff_unwind_info *)bc->contents; + /*@only@*/ /*@null@*/ yasm_intnum *intn; + long intv; + + /* Want to make sure prolog size and codes count doesn't exceed + * byte-size, and scaled frame offset doesn't exceed 4 bits. + */ + add_span(add_span_data, bc, 1, &info->prolog_size, 0, 255); + add_span(add_span_data, bc, 2, &info->codes_count, 0, 255); + + intn = yasm_value_get_intnum(&info->frameoff, bc, 0); + if (intn) { + intv = yasm_intnum_get_int(intn); + if (intv < 0 || intv > 240) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld bytes, must be between 0 and 240"), + intv); + else if ((intv & 0xF) != 0) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld is not a multiple of 16"), intv); + yasm_intnum_destroy(intn); + } else + add_span(add_span_data, bc, 3, &info->frameoff, 0, 240); + + bc->len += 4; + return 0; +} + +static int +win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) +{ + coff_unwind_info *info = (coff_unwind_info *)bc->contents; + switch (span) { + case 1: + yasm_error_set_xref(yasm_symrec_get_def_line(info->prolog), + N_("prologue ended here")); + yasm_error_set(YASM_ERROR_VALUE, + N_("prologue %ld bytes, must be <256"), new_val); + return -1; + case 2: + yasm_error_set(YASM_ERROR_VALUE, + N_("%ld unwind codes, maximum of 255"), new_val); + return -1; + case 3: + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld bytes, must be between 0 and 240"), + new_val); + return -1; + default: + yasm_internal_error(N_("unrecognized span id")); + } + return 0; +} + +static int +win64_uwinfo_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + yasm_output_reloc_func output_reloc) +{ + coff_unwind_info *info = (coff_unwind_info *)bc->contents; + unsigned char *buf = *bufp; + /*@only@*/ /*@null@*/ yasm_intnum *frameoff; + long intv; + + /* Version and flags */ + if (info->ehandler) + YASM_WRITE_8(buf, 1 | (UNW_FLAG_EHANDLER << 3)); + else + YASM_WRITE_8(buf, 1); + + /* Size of prolog */ + output_value(&info->prolog_size, buf, 1, (unsigned long)(buf-bufstart), + bc, 1, d); + buf += 1; + + /* Count of codes */ + output_value(&info->codes_count, buf, 1, (unsigned long)(buf-bufstart), + bc, 1, d); + buf += 1; + + /* Frame register and offset */ + frameoff = yasm_value_get_intnum(&info->frameoff, bc, 1); + if (!frameoff) { + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset expression too complex")); + return 1; + } + intv = yasm_intnum_get_int(frameoff); + if (intv < 0 || intv > 240) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld bytes, must be between 0 and 240"), intv); + else if ((intv & 0xF) != 0) + yasm_error_set(YASM_ERROR_VALUE, + N_("frame offset of %ld is not a multiple of 16"), intv); + + YASM_WRITE_8(buf, ((unsigned long)intv & 0xF0) | (info->framereg & 0x0F)); + yasm_intnum_destroy(frameoff); + + *bufp = buf; + return 0; +} + +static void +win64_uwcode_bc_destroy(void *contents) +{ + coff_unwind_code *code = (coff_unwind_code *)contents; + yasm_value_delete(&code->off); + yasm_xfree(contents); +} + +static void +win64_uwcode_bc_print(const void *contents, FILE *f, int indent_level) +{ + /* TODO */ +} + +static void +win64_uwcode_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + coff_unwind_code *code = (coff_unwind_code *)bc->contents; + if (yasm_value_finalize(&code->off, prev_bc)) + yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex")); +} + +static int +win64_uwcode_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + coff_unwind_code *code = (coff_unwind_code *)bc->contents; + int span = 0; + /*@only@*/ /*@null@*/ yasm_intnum *intn; + long intv; + long low, high, mask; + + bc->len += 2; /* Prolog offset, code, and info */ + + switch (code->opcode) { + case UWOP_PUSH_NONVOL: + case UWOP_SET_FPREG: + case UWOP_PUSH_MACHFRAME: + /* always 1 node */ + return 0; + case UWOP_ALLOC_SMALL: + case UWOP_ALLOC_LARGE: + /* Start with smallest, then work our way up as necessary */ + code->opcode = UWOP_ALLOC_SMALL; + code->info = 0; + span = 1; low = 8; high = 128; mask = 0x7; + break; + case UWOP_SAVE_NONVOL: + case UWOP_SAVE_NONVOL_FAR: + /* Start with smallest, then work our way up as necessary */ + code->opcode = UWOP_SAVE_NONVOL; + bc->len += 2; /* Scaled offset */ + span = 2; + low = 0; + high = 8*64*1024-8; /* 16-bit field, *8 scaling */ + mask = 0x7; + break; + case UWOP_SAVE_XMM128: + case UWOP_SAVE_XMM128_FAR: + /* Start with smallest, then work our way up as necessary */ + code->opcode = UWOP_SAVE_XMM128; + bc->len += 2; /* Scaled offset */ + span = 3; + low = 0; + high = 16*64*1024-16; /* 16-bit field, *16 scaling */ + mask = 0xF; + break; + default: + yasm_internal_error(N_("unrecognied unwind opcode")); + /*@unreached@*/ + return 0; + } + + intn = yasm_value_get_intnum(&code->off, bc, 0); + if (intn) { + intv = yasm_intnum_get_int(intn); + if (intv > high) { + /* Expand it ourselves here if we can and we're already larger */ + if (win64_uwcode_bc_expand(bc, span, intv, intv, &low, &high) > 0) + add_span(add_span_data, bc, span, &code->off, low, high); + } + if (intv < low) + yasm_error_set(YASM_ERROR_VALUE, + N_("negative offset not allowed")); + if ((intv & mask) != 0) + yasm_error_set(YASM_ERROR_VALUE, + N_("offset of %ld is not a multiple of %ld"), intv, mask+1); + yasm_intnum_destroy(intn); + } else + add_span(add_span_data, bc, span, &code->off, low, high); + return 0; +} + +static int +win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) +{ + coff_unwind_code *code = (coff_unwind_code *)bc->contents; + + if (new_val < 0) { + yasm_error_set(YASM_ERROR_VALUE, N_("negative offset not allowed")); + return -1; + } + + if (span == 1) { + /* 3 stages: SMALL, LARGE and info=0, LARGE and info=1 */ + if (code->opcode == UWOP_ALLOC_LARGE && code->info == 1) + yasm_internal_error(N_("expansion on already largest alloc")); + + if (code->opcode == UWOP_ALLOC_SMALL && new_val > 128) { + /* Overflowed small size */ + code->opcode = UWOP_ALLOC_LARGE; + bc->len += 2; + } + if (new_val <= 8*64*1024-8) { + /* Still can grow one more size */ + *pos_thres = 8*64*1024-8; + return 1; + } + /* We're into the largest size */ + code->info = 1; + bc->len += 2; + } else if (code->opcode == UWOP_SAVE_NONVOL && span == 2) { + code->opcode = UWOP_SAVE_NONVOL_FAR; + bc->len += 2; + } else if (code->opcode == UWOP_SAVE_XMM128 && span == 3) { + code->opcode = UWOP_SAVE_XMM128_FAR; + bc->len += 2; + } + return 0; +} + +static int +win64_uwcode_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + yasm_output_reloc_func output_reloc) +{ + coff_unwind_code *code = (coff_unwind_code *)bc->contents; + unsigned char *buf = *bufp; + yasm_value val; + unsigned int size; + int shift; + long intv, low, high, mask; + yasm_intnum *intn; + + /* Offset in prolog */ + yasm_value_initialize(&val, + yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(code->loc), + yasm_expr_sym(code->proc), bc->line), + 8); + output_value(&val, buf, 1, (unsigned long)(buf-bufstart), bc, 1, d); + buf += 1; + yasm_value_delete(&val); + + /* Offset value */ + switch (code->opcode) { + case UWOP_PUSH_NONVOL: + case UWOP_SET_FPREG: + case UWOP_PUSH_MACHFRAME: + /* just 1 node, no offset; write opcode and info and we're done */ + YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF)); + *bufp = buf; + return 0; + case UWOP_ALLOC_SMALL: + /* 1 node, but offset stored in info */ + size = 0; low = 8; high = 128; shift = 3; mask = 0x7; + break; + case UWOP_ALLOC_LARGE: + if (code->info == 0) { + size = 2; low = 136; high = 8*64*1024-8; shift = 3; + } else { + size = 4; low = high = 0; shift = 0; + } + mask = 0x7; + break; + case UWOP_SAVE_NONVOL: + size = 2; low = 0; high = 8*64*1024-8; shift = 3; mask = 0x7; + break; + case UWOP_SAVE_XMM128: + size = 2; low = 0; high = 16*64*1024-16; shift = 4; mask = 0xF; + break; + case UWOP_SAVE_NONVOL_FAR: + size = 4; low = high = 0; shift = 0; mask = 0x7; + break; + case UWOP_SAVE_XMM128_FAR: + size = 4; low = high = 0; shift = 0; mask = 0xF; + break; + default: + yasm_internal_error(N_("unrecognied unwind opcode")); + /*@unreached@*/ + return 1; + } + + /* Check for overflow */ + intn = yasm_value_get_intnum(&code->off, bc, 1); + if (!intn) { + yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex")); + return 1; + } + intv = yasm_intnum_get_int(intn); + if (size != 4 && (intv < low || intv > high)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("offset of %ld bytes, must be between %ld and %ld"), + intv, low, high); + return 1; + } + if ((intv & mask) != 0) { + yasm_error_set(YASM_ERROR_VALUE, + N_("offset of %ld is not a multiple of %ld"), + intv, mask+1); + return 1; + } + + /* Stored value in info instead of extra code space */ + if (size == 0) + code->info = (yasm_intnum_get_uint(intn) >> shift)-1; + + /* Opcode and info */ + YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF)); + + if (size != 0) { + yasm_intnum_get_sized(intn, buf, size, size*8, -shift, 0, 1); + buf += size; + } + + yasm_intnum_destroy(intn); + + *bufp = buf; + return 0; +} diff --git a/contrib/tools/yasm/modules/objfmts/dbg/dbg-objfmt.c b/contrib/tools/yasm/modules/objfmts/dbg/dbg-objfmt.c index bdf403c762..135edef012 100644 --- a/contrib/tools/yasm/modules/objfmts/dbg/dbg-objfmt.c +++ b/contrib/tools/yasm/modules/objfmts/dbg/dbg-objfmt.c @@ -1,183 +1,183 @@ -/* - * Debugging object format (used to debug object format module interface) - * - * Copyright (C) 2001-2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include <util.h> - -#include <libyasm.h> - - -typedef struct yasm_objfmt_dbg { - yasm_objfmt_base objfmt; /* base structure */ - - FILE *dbgfile; -} yasm_objfmt_dbg; - -yasm_objfmt_module yasm_dbg_LTX_objfmt; - - -static yasm_objfmt * -dbg_objfmt_create(yasm_object *object) -{ - yasm_objfmt_dbg *objfmt_dbg = yasm_xmalloc(sizeof(yasm_objfmt_dbg)); - - objfmt_dbg->objfmt.module = &yasm_dbg_LTX_objfmt; - - objfmt_dbg->dbgfile = tmpfile(); - if (!objfmt_dbg->dbgfile) { - fprintf(stderr, N_("could not open temporary file")); - return 0; - } - fprintf(objfmt_dbg->dbgfile, "create()\n"); - return (yasm_objfmt *)objfmt_dbg; -} - -static void -dbg_objfmt_output(yasm_object *object, FILE *f, int all_syms, - yasm_errwarns *errwarns) -{ - yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; - char buf[1024]; - size_t i; - - /* Copy temp file to real output file */ - rewind(objfmt_dbg->dbgfile); - while ((i = fread(buf, 1, 1024, objfmt_dbg->dbgfile))) { - if (fwrite(buf, 1, i, f) != i) - break; - } - - /* Reassign objfmt debug file to output file */ - fclose(objfmt_dbg->dbgfile); - objfmt_dbg->dbgfile = f; - - fprintf(objfmt_dbg->dbgfile, "output(f, object->\n"); - yasm_object_print(object, objfmt_dbg->dbgfile, 1); - fprintf(objfmt_dbg->dbgfile, "%d)\n", all_syms); - fprintf(objfmt_dbg->dbgfile, " Symbol Table:\n"); - yasm_symtab_print(object->symtab, objfmt_dbg->dbgfile, 1); -} - -static void -dbg_objfmt_destroy(yasm_objfmt *objfmt) -{ - yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)objfmt; - fprintf(objfmt_dbg->dbgfile, "destroy()\n"); - yasm_xfree(objfmt); -} - -static void -dbg_objfmt_init_new_section(yasm_section *sect, unsigned long line) -{ - yasm_object *object = yasm_section_get_object(sect); - yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; - fprintf(objfmt_dbg->dbgfile, "init_new_section(\"%s\", %lu)\n", - yasm_section_get_name(sect), line); - yasm_symtab_define_label(object->symtab, ".text", - yasm_section_bcs_first(sect), 1, 0); -} - -static yasm_section * -dbg_objfmt_add_default_section(yasm_object *object) -{ - yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; - yasm_section *retval; - int isnew; - - fprintf(objfmt_dbg->dbgfile, "add_default_section()\n"); - retval = yasm_object_get_general(object, ".text", 0, 0, 0, &isnew, 0); - if (isnew) { - yasm_section_set_default(retval, 1); - } - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_section * -dbg_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, - /*@unused@*/ /*@null@*/ - yasm_valparamhead *objext_valparams, - unsigned long line) -{ - yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; - yasm_valparam *vp; - yasm_section *retval; - int isnew; - - fprintf(objfmt_dbg->dbgfile, "section_switch(headp, "); - yasm_vps_print(valparams, objfmt_dbg->dbgfile); - fprintf(objfmt_dbg->dbgfile, ", "); - yasm_vps_print(objext_valparams, objfmt_dbg->dbgfile); - fprintf(objfmt_dbg->dbgfile, ", %lu), returning ", line); - - vp = yasm_vps_first(valparams); - if (!yasm_vp_string(vp)) { - fprintf(objfmt_dbg->dbgfile, "NULL\n"); - return NULL; - } - retval = yasm_object_get_general(object, yasm_vp_string(vp), 0, 0, 0, - &isnew, line); - if (isnew) { - fprintf(objfmt_dbg->dbgfile, "(new) "); - } - yasm_section_set_default(retval, 0); - fprintf(objfmt_dbg->dbgfile, "\"%s\" section\n", vp->val); - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -dbg_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; - fprintf(objfmt_dbg->dbgfile, "get_special_sym(object, \"%s\", \"%s\")\n", - name, parser); - return NULL; -} - -/* Define valid debug formats to use with this object format */ -static const char *dbg_objfmt_dbgfmt_keywords[] = { - "null", - NULL -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_dbg_LTX_objfmt = { - "Trace of all info passed to object format module", - "dbg", - "dbg", - 32, - 0, - dbg_objfmt_dbgfmt_keywords, - "null", - NULL, /* no directives */ - NULL, /* no standard macros */ - dbg_objfmt_create, - dbg_objfmt_output, - dbg_objfmt_destroy, - dbg_objfmt_add_default_section, - dbg_objfmt_init_new_section, - dbg_objfmt_section_switch, - dbg_objfmt_get_special_sym -}; +/* + * Debugging object format (used to debug object format module interface) + * + * Copyright (C) 2001-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <util.h> + +#include <libyasm.h> + + +typedef struct yasm_objfmt_dbg { + yasm_objfmt_base objfmt; /* base structure */ + + FILE *dbgfile; +} yasm_objfmt_dbg; + +yasm_objfmt_module yasm_dbg_LTX_objfmt; + + +static yasm_objfmt * +dbg_objfmt_create(yasm_object *object) +{ + yasm_objfmt_dbg *objfmt_dbg = yasm_xmalloc(sizeof(yasm_objfmt_dbg)); + + objfmt_dbg->objfmt.module = &yasm_dbg_LTX_objfmt; + + objfmt_dbg->dbgfile = tmpfile(); + if (!objfmt_dbg->dbgfile) { + fprintf(stderr, N_("could not open temporary file")); + return 0; + } + fprintf(objfmt_dbg->dbgfile, "create()\n"); + return (yasm_objfmt *)objfmt_dbg; +} + +static void +dbg_objfmt_output(yasm_object *object, FILE *f, int all_syms, + yasm_errwarns *errwarns) +{ + yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; + char buf[1024]; + size_t i; + + /* Copy temp file to real output file */ + rewind(objfmt_dbg->dbgfile); + while ((i = fread(buf, 1, 1024, objfmt_dbg->dbgfile))) { + if (fwrite(buf, 1, i, f) != i) + break; + } + + /* Reassign objfmt debug file to output file */ + fclose(objfmt_dbg->dbgfile); + objfmt_dbg->dbgfile = f; + + fprintf(objfmt_dbg->dbgfile, "output(f, object->\n"); + yasm_object_print(object, objfmt_dbg->dbgfile, 1); + fprintf(objfmt_dbg->dbgfile, "%d)\n", all_syms); + fprintf(objfmt_dbg->dbgfile, " Symbol Table:\n"); + yasm_symtab_print(object->symtab, objfmt_dbg->dbgfile, 1); +} + +static void +dbg_objfmt_destroy(yasm_objfmt *objfmt) +{ + yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)objfmt; + fprintf(objfmt_dbg->dbgfile, "destroy()\n"); + yasm_xfree(objfmt); +} + +static void +dbg_objfmt_init_new_section(yasm_section *sect, unsigned long line) +{ + yasm_object *object = yasm_section_get_object(sect); + yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; + fprintf(objfmt_dbg->dbgfile, "init_new_section(\"%s\", %lu)\n", + yasm_section_get_name(sect), line); + yasm_symtab_define_label(object->symtab, ".text", + yasm_section_bcs_first(sect), 1, 0); +} + +static yasm_section * +dbg_objfmt_add_default_section(yasm_object *object) +{ + yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; + yasm_section *retval; + int isnew; + + fprintf(objfmt_dbg->dbgfile, "add_default_section()\n"); + retval = yasm_object_get_general(object, ".text", 0, 0, 0, &isnew, 0); + if (isnew) { + yasm_section_set_default(retval, 1); + } + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_section * +dbg_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, + /*@unused@*/ /*@null@*/ + yasm_valparamhead *objext_valparams, + unsigned long line) +{ + yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; + yasm_valparam *vp; + yasm_section *retval; + int isnew; + + fprintf(objfmt_dbg->dbgfile, "section_switch(headp, "); + yasm_vps_print(valparams, objfmt_dbg->dbgfile); + fprintf(objfmt_dbg->dbgfile, ", "); + yasm_vps_print(objext_valparams, objfmt_dbg->dbgfile); + fprintf(objfmt_dbg->dbgfile, ", %lu), returning ", line); + + vp = yasm_vps_first(valparams); + if (!yasm_vp_string(vp)) { + fprintf(objfmt_dbg->dbgfile, "NULL\n"); + return NULL; + } + retval = yasm_object_get_general(object, yasm_vp_string(vp), 0, 0, 0, + &isnew, line); + if (isnew) { + fprintf(objfmt_dbg->dbgfile, "(new) "); + } + yasm_section_set_default(retval, 0); + fprintf(objfmt_dbg->dbgfile, "\"%s\" section\n", vp->val); + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +dbg_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + yasm_objfmt_dbg *objfmt_dbg = (yasm_objfmt_dbg *)object->objfmt; + fprintf(objfmt_dbg->dbgfile, "get_special_sym(object, \"%s\", \"%s\")\n", + name, parser); + return NULL; +} + +/* Define valid debug formats to use with this object format */ +static const char *dbg_objfmt_dbgfmt_keywords[] = { + "null", + NULL +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_dbg_LTX_objfmt = { + "Trace of all info passed to object format module", + "dbg", + "dbg", + 32, + 0, + dbg_objfmt_dbgfmt_keywords, + "null", + NULL, /* no directives */ + NULL, /* no standard macros */ + dbg_objfmt_create, + dbg_objfmt_output, + dbg_objfmt_destroy, + dbg_objfmt_add_default_section, + dbg_objfmt_init_new_section, + dbg_objfmt_section_switch, + dbg_objfmt_get_special_sym +}; diff --git a/contrib/tools/yasm/modules/objfmts/elf/elf-machine.h b/contrib/tools/yasm/modules/objfmts/elf/elf-machine.h index ea6cb4ec94..346b4f6f34 100644 --- a/contrib/tools/yasm/modules/objfmts/elf/elf-machine.h +++ b/contrib/tools/yasm/modules/objfmts/elf/elf-machine.h @@ -1,108 +1,108 @@ -/* - * ELF object machine specific format helpers - * - * Copyright (C) 2004-2007 Michael Urman - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ELF_MACHINE_H_INCLUDED -#define ELF_MACHINE_H_INCLUDED - -#define YASM_WRITE_32I_L(p, i) do {\ - assert(yasm_intnum_check_size(i, 32, 0, 2)); \ - yasm_intnum_get_sized(i, p, 4, 32, 0, 0, 0); \ - p += 4; } while (0) - -#define YASM_WRITE_64I_L(p, i) do {\ - assert(yasm_intnum_check_size(i, 64, 0, 2)); \ - yasm_intnum_get_sized(i, p, 8, 64, 0, 0, 0); \ - p += 8; } while (0) - -#define YASM_WRITE_64C_L(p, hi, lo) do {\ - YASM_WRITE_32_L(p, lo); \ - YASM_WRITE_32_L(p, hi); } while (0) - -#define YASM_WRITE_64Z_L(p, i) YASM_WRITE_64C_L(p, 0, i) - -typedef int(*func_accepts_reloc)(size_t val, yasm_symrec *wrt); -typedef void(*func_write_symtab_entry)(unsigned char *bufp, - elf_symtab_entry *entry, - yasm_intnum *value_intn, - yasm_intnum *size_intn); -typedef void(*func_write_secthead)(unsigned char *bufp, elf_secthead *shead); -typedef void(*func_write_secthead_rel)(unsigned char *bufp, - elf_secthead *shead, - elf_section_index symtab_idx, - elf_section_index sindex); - -typedef void(*func_handle_reloc_addend)(yasm_intnum *intn, - elf_reloc_entry *reloc, - unsigned long offset); -typedef unsigned int(*func_map_reloc_info_to_type)(elf_reloc_entry *reloc); -typedef void(*func_write_reloc)(unsigned char *bufp, - elf_reloc_entry *reloc, - unsigned int r_type, - unsigned int r_sym); -typedef void (*func_write_proghead)(unsigned char **bufpp, - elf_offset secthead_addr, - unsigned long secthead_count, - elf_section_index shstrtab_index); - -enum { - ELF_SSYM_SYM_RELATIVE = 1 << 0, - ELF_SSYM_CURPOS_ADJUST = 1 << 1, - ELF_SSYM_THREAD_LOCAL = 1 << 2 -}; - -typedef struct { - const char *name; /* should be something like ..name */ - const int sym_rel; /* symbol or section-relative? */ - const unsigned int reloc; /* relocation type */ - const unsigned int size; /* legal data size */ -} elf_machine_ssym; - -struct elf_machine_handler { - const char *arch; - const char *machine; - const char *reloc_section_prefix; - const unsigned long symtab_entry_size; - const unsigned long symtab_entry_align; - const unsigned long reloc_entry_size; - const unsigned long secthead_size; - const unsigned long proghead_size; - func_accepts_reloc accepts_reloc; - func_write_symtab_entry write_symtab_entry; - func_write_secthead write_secthead; - func_write_secthead_rel write_secthead_rel; - func_handle_reloc_addend handle_reloc_addend; - func_map_reloc_info_to_type map_reloc_info_to_type; - func_write_reloc write_reloc; - func_write_proghead write_proghead; - - elf_machine_ssym *ssyms; /* array of "special" syms */ - const size_t num_ssyms; /* size of array */ - - const int bits; /* usually 32 or 64 */ -}; - -#endif /* ELF_MACHINE_H_INCLUDED */ +/* + * ELF object machine specific format helpers + * + * Copyright (C) 2004-2007 Michael Urman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ELF_MACHINE_H_INCLUDED +#define ELF_MACHINE_H_INCLUDED + +#define YASM_WRITE_32I_L(p, i) do {\ + assert(yasm_intnum_check_size(i, 32, 0, 2)); \ + yasm_intnum_get_sized(i, p, 4, 32, 0, 0, 0); \ + p += 4; } while (0) + +#define YASM_WRITE_64I_L(p, i) do {\ + assert(yasm_intnum_check_size(i, 64, 0, 2)); \ + yasm_intnum_get_sized(i, p, 8, 64, 0, 0, 0); \ + p += 8; } while (0) + +#define YASM_WRITE_64C_L(p, hi, lo) do {\ + YASM_WRITE_32_L(p, lo); \ + YASM_WRITE_32_L(p, hi); } while (0) + +#define YASM_WRITE_64Z_L(p, i) YASM_WRITE_64C_L(p, 0, i) + +typedef int(*func_accepts_reloc)(size_t val, yasm_symrec *wrt); +typedef void(*func_write_symtab_entry)(unsigned char *bufp, + elf_symtab_entry *entry, + yasm_intnum *value_intn, + yasm_intnum *size_intn); +typedef void(*func_write_secthead)(unsigned char *bufp, elf_secthead *shead); +typedef void(*func_write_secthead_rel)(unsigned char *bufp, + elf_secthead *shead, + elf_section_index symtab_idx, + elf_section_index sindex); + +typedef void(*func_handle_reloc_addend)(yasm_intnum *intn, + elf_reloc_entry *reloc, + unsigned long offset); +typedef unsigned int(*func_map_reloc_info_to_type)(elf_reloc_entry *reloc); +typedef void(*func_write_reloc)(unsigned char *bufp, + elf_reloc_entry *reloc, + unsigned int r_type, + unsigned int r_sym); +typedef void (*func_write_proghead)(unsigned char **bufpp, + elf_offset secthead_addr, + unsigned long secthead_count, + elf_section_index shstrtab_index); + +enum { + ELF_SSYM_SYM_RELATIVE = 1 << 0, + ELF_SSYM_CURPOS_ADJUST = 1 << 1, + ELF_SSYM_THREAD_LOCAL = 1 << 2 +}; + +typedef struct { + const char *name; /* should be something like ..name */ + const int sym_rel; /* symbol or section-relative? */ + const unsigned int reloc; /* relocation type */ + const unsigned int size; /* legal data size */ +} elf_machine_ssym; + +struct elf_machine_handler { + const char *arch; + const char *machine; + const char *reloc_section_prefix; + const unsigned long symtab_entry_size; + const unsigned long symtab_entry_align; + const unsigned long reloc_entry_size; + const unsigned long secthead_size; + const unsigned long proghead_size; + func_accepts_reloc accepts_reloc; + func_write_symtab_entry write_symtab_entry; + func_write_secthead write_secthead; + func_write_secthead_rel write_secthead_rel; + func_handle_reloc_addend handle_reloc_addend; + func_map_reloc_info_to_type map_reloc_info_to_type; + func_write_reloc write_reloc; + func_write_proghead write_proghead; + + elf_machine_ssym *ssyms; /* array of "special" syms */ + const size_t num_ssyms; /* size of array */ + + const int bits; /* usually 32 or 64 */ +}; + +#endif /* ELF_MACHINE_H_INCLUDED */ diff --git a/contrib/tools/yasm/modules/objfmts/elf/elf-objfmt.c b/contrib/tools/yasm/modules/objfmts/elf/elf-objfmt.c index 6874a1689f..e447218ad5 100644 --- a/contrib/tools/yasm/modules/objfmts/elf/elf-objfmt.c +++ b/contrib/tools/yasm/modules/objfmts/elf/elf-objfmt.c @@ -1,1403 +1,1403 @@ -/* - * ELF object format - * - * Copyright (C) 2003-2007 Michael Urman - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include <util.h> - -/* Notes - * - * elf-objfmt uses the "linking" view of an ELF file: - * ELF header, an optional program header table, several sections, - * and a section header table - * - * The ELF header tells us some overall program information, - * where to find the PHT (if it exists) with phnum and phentsize, - * and where to find the SHT with shnum and shentsize - * - * The PHT doesn't seem to be generated by NASM for elftest.asm - * - * The SHT - * - * Each Section is spatially disjoint, and has exactly one SHT entry. - */ - -#include <libyasm.h> - -#include "elf.h" -#include "elf-machine.h" - -typedef struct yasm_objfmt_elf { - yasm_objfmt_base objfmt; /* base structure */ - - elf_symtab_head* elf_symtab; /* symbol table of indexed syms */ - elf_strtab_head* shstrtab; /* section name strtab */ - elf_strtab_head* strtab; /* strtab entries */ - - elf_strtab_entry *file_strtab_entry;/* .file symbol associated string */ - yasm_symrec *dotdotsym; /* ..sym symbol */ -} yasm_objfmt_elf; - -typedef struct { - yasm_objfmt_elf *objfmt_elf; - yasm_errwarns *errwarns; - FILE *f; - elf_secthead *shead; - yasm_section *sect; - yasm_object *object; - unsigned long sindex; - yasm_symrec *GOT_sym; -} elf_objfmt_output_info; - -typedef struct { - yasm_object *object; - yasm_objfmt_elf *objfmt_elf; - yasm_errwarns *errwarns; - int local_names; -} build_symtab_info; - -yasm_objfmt_module yasm_elf_LTX_objfmt; -yasm_objfmt_module yasm_elf32_LTX_objfmt; -yasm_objfmt_module yasm_elf64_LTX_objfmt; -yasm_objfmt_module yasm_elfx32_LTX_objfmt; - - -static elf_symtab_entry * -elf_objfmt_symtab_append(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym, - elf_section_index sectidx, elf_symbol_binding bind, - elf_symbol_type type, elf_symbol_vis vis, - yasm_expr *size, elf_address *value, - yasm_object *object) -{ - elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data); - - if (!entry) { - /*@only@*/ char *symname = yasm_symrec_get_global_name(sym, object); - elf_strtab_entry *name = - elf_strtab_append_str(objfmt_elf->strtab, symname); - yasm_xfree(symname); - entry = elf_symtab_entry_create(name, sym); - yasm_symrec_add_data(sym, &elf_symrec_data, entry); - } - - /* Only append to table if not already appended */ - if (!elf_sym_in_table(entry)) - elf_symtab_append_entry(objfmt_elf->elf_symtab, entry); - - elf_symtab_set_nonzero(entry, NULL, sectidx, bind, type, size, value); - elf_sym_set_visibility(entry, vis); - - return entry; -} - -static elf_symtab_entry * -build_extern(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym, yasm_object *object) -{ - yasm_valparamhead *objext_valparams = - yasm_symrec_get_objext_valparams(sym); - - if (objext_valparams) { - yasm_valparam *vp = yasm_vps_first(objext_valparams); - for (; vp; vp = yasm_vps_next(vp)) { - if (yasm_vp_string(vp)) - yasm_error_set(YASM_ERROR_TYPE, - N_("unrecognized symbol type `%s'"), - yasm_vp_string(vp)); - } - } - - return elf_objfmt_symtab_append(objfmt_elf, sym, SHN_UNDEF, STB_GLOBAL, 0, - STV_DEFAULT, NULL, NULL, object); -} - -struct elf_build_global_data { - yasm_expr *size; - unsigned long type; /* elf_symbol_type */ - elf_symbol_vis vis; - unsigned int vis_overrides; -}; - -static int -elf_global_helper_valparam(void *obj, yasm_valparam *vp, unsigned long line, - void *d) - -{ - struct elf_build_global_data *data = (struct elf_build_global_data *)d; - const char *s; - - if (!vp->val && (s = yasm_vp_id(vp))) { - yasm_error_set(YASM_ERROR_TYPE, N_("unrecognized symbol type `%s'"), - s); - return -1; - } else if (!vp->val && vp->type == YASM_PARAM_EXPR && !data->size) { - data->size = yasm_expr_copy(vp->param.e); - return 0; - } else - return yasm_dir_helper_valparam_warn(obj, vp, line, d); -} - -static int -elf_global_helper_vis(void *obj, yasm_valparam *vp, unsigned long line, - void *d, uintptr_t vis) -{ - struct elf_build_global_data *data = (struct elf_build_global_data *)d; - data->vis = vis; - data->vis_overrides++; - return 0; -} - - -static elf_symtab_entry * -build_global(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym, yasm_object *object) -{ - yasm_valparamhead *objext_valparams = - yasm_symrec_get_objext_valparams(sym); - - struct elf_build_global_data data; - - static const yasm_dir_help help[] = { - { "function", 0, yasm_dir_helper_flag_set, - offsetof(struct elf_build_global_data, type), STT_FUNC }, - { "data", 0, yasm_dir_helper_flag_set, - offsetof(struct elf_build_global_data, type), STT_OBJECT }, - { "object", 0, yasm_dir_helper_flag_set, - offsetof(struct elf_build_global_data, type), STT_OBJECT }, - { "internal", 0, elf_global_helper_vis, 0, STV_INTERNAL }, - { "hidden", 0, elf_global_helper_vis, 0, STV_HIDDEN }, - { "protected", 0, elf_global_helper_vis, 0, STV_PROTECTED }, - }; - - data.size = NULL; - data.type = 0; - data.vis = STV_DEFAULT; - data.vis_overrides = 0; - - if (objext_valparams) - yasm_dir_helper(sym, yasm_vps_first(objext_valparams), - yasm_symrec_get_decl_line(sym), help, NELEMS(help), - &data, elf_global_helper_valparam); - - if (data.vis_overrides > 1) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("More than one symbol visibility provided; using last")); - } - - return elf_objfmt_symtab_append(objfmt_elf, sym, SHN_UNDEF, STB_GLOBAL, - data.type, data.vis, data.size, NULL, - object); -} - -static /*@null@*/ elf_symtab_entry * -build_common(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym, yasm_object *object) -{ - yasm_expr **size = yasm_symrec_get_common_size(sym); - yasm_valparamhead *objext_valparams = - yasm_symrec_get_objext_valparams(sym); - unsigned long addralign = 0; - - if (objext_valparams) { - yasm_valparam *vp = yasm_vps_first(objext_valparams); - for (; vp; vp = yasm_vps_next(vp)) { - if (!vp->val) { - /*@only@*/ /*@null@*/ yasm_expr *align_expr; - /*@dependent@*/ /*@null@*/ const yasm_intnum *align_intn; - - if (!(align_expr = yasm_vp_expr(vp, object->symtab, - yasm_symrec_get_def_line(sym))) - || !(align_intn = yasm_expr_get_intnum(&align_expr, 0))) { - yasm_error_set(YASM_ERROR_VALUE, - N_("alignment constraint is not an integer")); - if (align_expr) - yasm_expr_destroy(align_expr); - return NULL; - } - addralign = yasm_intnum_get_uint(align_intn); - yasm_expr_destroy(align_expr); - - /* Alignments must be a power of two. */ - if (!is_exp2(addralign)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("alignment constraint is not a power of two")); - return NULL; - } - } else - yasm_warn_set(YASM_WARN_GENERAL, - N_("Unrecognized qualifier `%s'"), vp->val); - } - } - - return elf_objfmt_symtab_append(objfmt_elf, sym, SHN_COMMON, STB_GLOBAL, - 0, STV_DEFAULT, *size, &addralign, object); -} - -static int -elf_objfmt_build_symtab(yasm_symrec *sym, /*@null@*/ void *d) -{ - build_symtab_info *info = (build_symtab_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - yasm_sym_status status = yasm_symrec_get_status(sym); - elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data); - elf_address value=0; - yasm_section *sect=NULL; - yasm_bytecode *precbc=NULL; - - assert(info != NULL); - - if (vis & YASM_SYM_EXTERN) { - entry = build_extern(info->objfmt_elf, sym, info->object); - yasm_errwarn_propagate(info->errwarns, - yasm_symrec_get_decl_line(sym)); - return 0; - } - - if (vis & YASM_SYM_COMMON) { - entry = build_common(info->objfmt_elf, sym, info->object); - yasm_errwarn_propagate(info->errwarns, - yasm_symrec_get_decl_line(sym)); - /* If the COMMON variable was actually defined, fall through. */ - if (!(status & YASM_SYM_DEFINED)) - return 0; - } - - /* Ignore any undefined at this point. */ - if (!(status & YASM_SYM_DEFINED)) - return 0; - - if (!yasm_symrec_get_label(sym, &precbc)) { - if (!yasm_symrec_get_equ(sym) && !yasm_symrec_is_abs(sym)) - return 0; - precbc = NULL; - } - - if (precbc) - sect = yasm_bc_get_section(precbc); - - if (entry && elf_sym_in_table(entry)) - ; - else if (vis & YASM_SYM_GLOBAL) { - entry = build_global(info->objfmt_elf, sym, info->object); - yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); - } else { - int is_sect = 0; - - /* Locals (except when debugging) do not need to be - * in the symbol table, unless they're a section. - */ - if (sect && - strcmp(yasm_symrec_get_name(sym), yasm_section_get_name(sect))==0) - is_sect = 1; -#if 0 - /* FIXME: to enable this we must have handling in place for special - * symbols. - */ - if (!info->local_names && !is_sect) - return 0; -#else - if (yasm_symrec_get_equ(sym) && !yasm_symrec_is_abs(sym)) - return 0; -#endif - entry = yasm_symrec_get_data(sym, &elf_symrec_data); - if (!entry) { - /*@only@*/ char *symname = - yasm_symrec_get_global_name(sym, info->object); - elf_strtab_entry *name = !info->local_names || is_sect ? NULL : - elf_strtab_append_str(info->objfmt_elf->strtab, symname); - yasm_xfree(symname); - entry = elf_symtab_entry_create(name, sym); - yasm_symrec_add_data(sym, &elf_symrec_data, entry); - } - - if (!elf_sym_in_table(entry)) - elf_symtab_insert_local_sym(info->objfmt_elf->elf_symtab, entry); - - elf_symtab_set_nonzero(entry, sect, 0, STB_LOCAL, - is_sect ? STT_SECTION : 0, NULL, 0); - - if (is_sect) - return 0; - } - - if (precbc) - value = yasm_bc_next_offset(precbc); - elf_symtab_set_nonzero(entry, sect, 0, 0, 0, NULL, &value); - - return 0; -} - -static yasm_objfmt * -elf_objfmt_create_common(yasm_object *object, yasm_objfmt_module *module, - int bits_pref, - const elf_machine_handler **elf_march_out) -{ - yasm_objfmt_elf *objfmt_elf = yasm_xmalloc(sizeof(yasm_objfmt_elf)); - yasm_symrec *filesym; - elf_symtab_entry *entry; - const elf_machine_handler *elf_march; - - objfmt_elf->objfmt.module = module; - elf_march = elf_set_arch(object->arch, object->symtab, bits_pref); - if (!elf_march) { - yasm_xfree(objfmt_elf); - return NULL; - } - if (elf_march_out) - *elf_march_out = elf_march; - - objfmt_elf->shstrtab = elf_strtab_create(); - objfmt_elf->strtab = elf_strtab_create(); - objfmt_elf->elf_symtab = elf_symtab_create(); - - /* FIXME: misuse of NULL bytecode here; it works, but only barely. */ - filesym = yasm_symtab_define_label(object->symtab, ".file", NULL, 0, 0); +/* + * ELF object format + * + * Copyright (C) 2003-2007 Michael Urman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <util.h> + +/* Notes + * + * elf-objfmt uses the "linking" view of an ELF file: + * ELF header, an optional program header table, several sections, + * and a section header table + * + * The ELF header tells us some overall program information, + * where to find the PHT (if it exists) with phnum and phentsize, + * and where to find the SHT with shnum and shentsize + * + * The PHT doesn't seem to be generated by NASM for elftest.asm + * + * The SHT + * + * Each Section is spatially disjoint, and has exactly one SHT entry. + */ + +#include <libyasm.h> + +#include "elf.h" +#include "elf-machine.h" + +typedef struct yasm_objfmt_elf { + yasm_objfmt_base objfmt; /* base structure */ + + elf_symtab_head* elf_symtab; /* symbol table of indexed syms */ + elf_strtab_head* shstrtab; /* section name strtab */ + elf_strtab_head* strtab; /* strtab entries */ + + elf_strtab_entry *file_strtab_entry;/* .file symbol associated string */ + yasm_symrec *dotdotsym; /* ..sym symbol */ +} yasm_objfmt_elf; + +typedef struct { + yasm_objfmt_elf *objfmt_elf; + yasm_errwarns *errwarns; + FILE *f; + elf_secthead *shead; + yasm_section *sect; + yasm_object *object; + unsigned long sindex; + yasm_symrec *GOT_sym; +} elf_objfmt_output_info; + +typedef struct { + yasm_object *object; + yasm_objfmt_elf *objfmt_elf; + yasm_errwarns *errwarns; + int local_names; +} build_symtab_info; + +yasm_objfmt_module yasm_elf_LTX_objfmt; +yasm_objfmt_module yasm_elf32_LTX_objfmt; +yasm_objfmt_module yasm_elf64_LTX_objfmt; +yasm_objfmt_module yasm_elfx32_LTX_objfmt; + + +static elf_symtab_entry * +elf_objfmt_symtab_append(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym, + elf_section_index sectidx, elf_symbol_binding bind, + elf_symbol_type type, elf_symbol_vis vis, + yasm_expr *size, elf_address *value, + yasm_object *object) +{ + elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data); + + if (!entry) { + /*@only@*/ char *symname = yasm_symrec_get_global_name(sym, object); + elf_strtab_entry *name = + elf_strtab_append_str(objfmt_elf->strtab, symname); + yasm_xfree(symname); + entry = elf_symtab_entry_create(name, sym); + yasm_symrec_add_data(sym, &elf_symrec_data, entry); + } + + /* Only append to table if not already appended */ + if (!elf_sym_in_table(entry)) + elf_symtab_append_entry(objfmt_elf->elf_symtab, entry); + + elf_symtab_set_nonzero(entry, NULL, sectidx, bind, type, size, value); + elf_sym_set_visibility(entry, vis); + + return entry; +} + +static elf_symtab_entry * +build_extern(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym, yasm_object *object) +{ + yasm_valparamhead *objext_valparams = + yasm_symrec_get_objext_valparams(sym); + + if (objext_valparams) { + yasm_valparam *vp = yasm_vps_first(objext_valparams); + for (; vp; vp = yasm_vps_next(vp)) { + if (yasm_vp_string(vp)) + yasm_error_set(YASM_ERROR_TYPE, + N_("unrecognized symbol type `%s'"), + yasm_vp_string(vp)); + } + } + + return elf_objfmt_symtab_append(objfmt_elf, sym, SHN_UNDEF, STB_GLOBAL, 0, + STV_DEFAULT, NULL, NULL, object); +} + +struct elf_build_global_data { + yasm_expr *size; + unsigned long type; /* elf_symbol_type */ + elf_symbol_vis vis; + unsigned int vis_overrides; +}; + +static int +elf_global_helper_valparam(void *obj, yasm_valparam *vp, unsigned long line, + void *d) + +{ + struct elf_build_global_data *data = (struct elf_build_global_data *)d; + const char *s; + + if (!vp->val && (s = yasm_vp_id(vp))) { + yasm_error_set(YASM_ERROR_TYPE, N_("unrecognized symbol type `%s'"), + s); + return -1; + } else if (!vp->val && vp->type == YASM_PARAM_EXPR && !data->size) { + data->size = yasm_expr_copy(vp->param.e); + return 0; + } else + return yasm_dir_helper_valparam_warn(obj, vp, line, d); +} + +static int +elf_global_helper_vis(void *obj, yasm_valparam *vp, unsigned long line, + void *d, uintptr_t vis) +{ + struct elf_build_global_data *data = (struct elf_build_global_data *)d; + data->vis = vis; + data->vis_overrides++; + return 0; +} + + +static elf_symtab_entry * +build_global(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym, yasm_object *object) +{ + yasm_valparamhead *objext_valparams = + yasm_symrec_get_objext_valparams(sym); + + struct elf_build_global_data data; + + static const yasm_dir_help help[] = { + { "function", 0, yasm_dir_helper_flag_set, + offsetof(struct elf_build_global_data, type), STT_FUNC }, + { "data", 0, yasm_dir_helper_flag_set, + offsetof(struct elf_build_global_data, type), STT_OBJECT }, + { "object", 0, yasm_dir_helper_flag_set, + offsetof(struct elf_build_global_data, type), STT_OBJECT }, + { "internal", 0, elf_global_helper_vis, 0, STV_INTERNAL }, + { "hidden", 0, elf_global_helper_vis, 0, STV_HIDDEN }, + { "protected", 0, elf_global_helper_vis, 0, STV_PROTECTED }, + }; + + data.size = NULL; + data.type = 0; + data.vis = STV_DEFAULT; + data.vis_overrides = 0; + + if (objext_valparams) + yasm_dir_helper(sym, yasm_vps_first(objext_valparams), + yasm_symrec_get_decl_line(sym), help, NELEMS(help), + &data, elf_global_helper_valparam); + + if (data.vis_overrides > 1) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("More than one symbol visibility provided; using last")); + } + + return elf_objfmt_symtab_append(objfmt_elf, sym, SHN_UNDEF, STB_GLOBAL, + data.type, data.vis, data.size, NULL, + object); +} + +static /*@null@*/ elf_symtab_entry * +build_common(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym, yasm_object *object) +{ + yasm_expr **size = yasm_symrec_get_common_size(sym); + yasm_valparamhead *objext_valparams = + yasm_symrec_get_objext_valparams(sym); + unsigned long addralign = 0; + + if (objext_valparams) { + yasm_valparam *vp = yasm_vps_first(objext_valparams); + for (; vp; vp = yasm_vps_next(vp)) { + if (!vp->val) { + /*@only@*/ /*@null@*/ yasm_expr *align_expr; + /*@dependent@*/ /*@null@*/ const yasm_intnum *align_intn; + + if (!(align_expr = yasm_vp_expr(vp, object->symtab, + yasm_symrec_get_def_line(sym))) + || !(align_intn = yasm_expr_get_intnum(&align_expr, 0))) { + yasm_error_set(YASM_ERROR_VALUE, + N_("alignment constraint is not an integer")); + if (align_expr) + yasm_expr_destroy(align_expr); + return NULL; + } + addralign = yasm_intnum_get_uint(align_intn); + yasm_expr_destroy(align_expr); + + /* Alignments must be a power of two. */ + if (!is_exp2(addralign)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("alignment constraint is not a power of two")); + return NULL; + } + } else + yasm_warn_set(YASM_WARN_GENERAL, + N_("Unrecognized qualifier `%s'"), vp->val); + } + } + + return elf_objfmt_symtab_append(objfmt_elf, sym, SHN_COMMON, STB_GLOBAL, + 0, STV_DEFAULT, *size, &addralign, object); +} + +static int +elf_objfmt_build_symtab(yasm_symrec *sym, /*@null@*/ void *d) +{ + build_symtab_info *info = (build_symtab_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + yasm_sym_status status = yasm_symrec_get_status(sym); + elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data); + elf_address value=0; + yasm_section *sect=NULL; + yasm_bytecode *precbc=NULL; + + assert(info != NULL); + + if (vis & YASM_SYM_EXTERN) { + entry = build_extern(info->objfmt_elf, sym, info->object); + yasm_errwarn_propagate(info->errwarns, + yasm_symrec_get_decl_line(sym)); + return 0; + } + + if (vis & YASM_SYM_COMMON) { + entry = build_common(info->objfmt_elf, sym, info->object); + yasm_errwarn_propagate(info->errwarns, + yasm_symrec_get_decl_line(sym)); + /* If the COMMON variable was actually defined, fall through. */ + if (!(status & YASM_SYM_DEFINED)) + return 0; + } + + /* Ignore any undefined at this point. */ + if (!(status & YASM_SYM_DEFINED)) + return 0; + + if (!yasm_symrec_get_label(sym, &precbc)) { + if (!yasm_symrec_get_equ(sym) && !yasm_symrec_is_abs(sym)) + return 0; + precbc = NULL; + } + + if (precbc) + sect = yasm_bc_get_section(precbc); + + if (entry && elf_sym_in_table(entry)) + ; + else if (vis & YASM_SYM_GLOBAL) { + entry = build_global(info->objfmt_elf, sym, info->object); + yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); + } else { + int is_sect = 0; + + /* Locals (except when debugging) do not need to be + * in the symbol table, unless they're a section. + */ + if (sect && + strcmp(yasm_symrec_get_name(sym), yasm_section_get_name(sect))==0) + is_sect = 1; +#if 0 + /* FIXME: to enable this we must have handling in place for special + * symbols. + */ + if (!info->local_names && !is_sect) + return 0; +#else + if (yasm_symrec_get_equ(sym) && !yasm_symrec_is_abs(sym)) + return 0; +#endif + entry = yasm_symrec_get_data(sym, &elf_symrec_data); + if (!entry) { + /*@only@*/ char *symname = + yasm_symrec_get_global_name(sym, info->object); + elf_strtab_entry *name = !info->local_names || is_sect ? NULL : + elf_strtab_append_str(info->objfmt_elf->strtab, symname); + yasm_xfree(symname); + entry = elf_symtab_entry_create(name, sym); + yasm_symrec_add_data(sym, &elf_symrec_data, entry); + } + + if (!elf_sym_in_table(entry)) + elf_symtab_insert_local_sym(info->objfmt_elf->elf_symtab, entry); + + elf_symtab_set_nonzero(entry, sect, 0, STB_LOCAL, + is_sect ? STT_SECTION : 0, NULL, 0); + + if (is_sect) + return 0; + } + + if (precbc) + value = yasm_bc_next_offset(precbc); + elf_symtab_set_nonzero(entry, sect, 0, 0, 0, NULL, &value); + + return 0; +} + +static yasm_objfmt * +elf_objfmt_create_common(yasm_object *object, yasm_objfmt_module *module, + int bits_pref, + const elf_machine_handler **elf_march_out) +{ + yasm_objfmt_elf *objfmt_elf = yasm_xmalloc(sizeof(yasm_objfmt_elf)); + yasm_symrec *filesym; + elf_symtab_entry *entry; + const elf_machine_handler *elf_march; + + objfmt_elf->objfmt.module = module; + elf_march = elf_set_arch(object->arch, object->symtab, bits_pref); + if (!elf_march) { + yasm_xfree(objfmt_elf); + return NULL; + } + if (elf_march_out) + *elf_march_out = elf_march; + + objfmt_elf->shstrtab = elf_strtab_create(); + objfmt_elf->strtab = elf_strtab_create(); + objfmt_elf->elf_symtab = elf_symtab_create(); + + /* FIXME: misuse of NULL bytecode here; it works, but only barely. */ + filesym = yasm_symtab_define_label(object->symtab, ".file", NULL, 0, 0); if (!object->deb_filename) { object->deb_filename = yasm_replace_path( module->replace_map, module->replace_map_size, object->src_filename, strlen(object->src_filename)); } - /* Put in current input filename; we'll replace it in output() */ - objfmt_elf->file_strtab_entry = + /* Put in current input filename; we'll replace it in output() */ + objfmt_elf->file_strtab_entry = elf_strtab_append_str(objfmt_elf->strtab, object->deb_filename); - entry = elf_symtab_entry_create(objfmt_elf->file_strtab_entry, filesym); - yasm_symrec_add_data(filesym, &elf_symrec_data, entry); - elf_symtab_set_nonzero(entry, NULL, SHN_ABS, STB_LOCAL, STT_FILE, NULL, - NULL); - elf_symtab_append_entry(objfmt_elf->elf_symtab, entry); - - /* FIXME: misuse of NULL bytecode */ - objfmt_elf->dotdotsym = - yasm_symtab_define_label(object->symtab, "..sym", NULL, 0, 0); - - return (yasm_objfmt *)objfmt_elf; -} - -static yasm_objfmt * -elf_objfmt_create(yasm_object *object) -{ - const elf_machine_handler *elf_march; - yasm_objfmt *objfmt; - yasm_objfmt_elf *objfmt_elf; - - objfmt = elf_objfmt_create_common(object, &yasm_elf_LTX_objfmt, 0, - &elf_march); - if (objfmt) { - objfmt_elf = (yasm_objfmt_elf *)objfmt; - /* Figure out which bitness of object format to use */ - if (strcmp (elf_march->machine, "x32") == 0) - objfmt_elf->objfmt.module = &yasm_elfx32_LTX_objfmt; - else if (elf_march->bits == 32) - objfmt_elf->objfmt.module = &yasm_elf32_LTX_objfmt; - else if (elf_march->bits == 64) - objfmt_elf->objfmt.module = &yasm_elf64_LTX_objfmt; - } - return objfmt; -} - -static yasm_objfmt * -elf32_objfmt_create(yasm_object *object) -{ - return elf_objfmt_create_common(object, &yasm_elf32_LTX_objfmt, 32, NULL); -} - -static yasm_objfmt * -elf64_objfmt_create(yasm_object *object) -{ - return elf_objfmt_create_common(object, &yasm_elf64_LTX_objfmt, 64, NULL); -} - -static yasm_objfmt * -elfx32_objfmt_create(yasm_object *object) -{ - return elf_objfmt_create_common(object, &yasm_elfx32_LTX_objfmt, 32, NULL); -} - -static long -elf_objfmt_output_align(FILE *f, unsigned int align) -{ - long pos; - unsigned long delta; - if (!is_exp2(align)) - yasm_internal_error("requested alignment not a power of two"); - - pos = ftell(f); - if (pos == -1) { - yasm_error_set(YASM_ERROR_IO, - N_("could not get file position on output file")); - return -1; - } - delta = align - (pos & (align-1)); - if (delta != align) { - pos += delta; - if (fseek(f, pos, SEEK_SET) < 0) { - yasm_error_set(YASM_ERROR_IO, - N_("could not set file position on output file")); - return -1; - } - } - return pos; -} - -static int -elf_objfmt_output_reloc(yasm_symrec *sym, yasm_bytecode *bc, - unsigned char *buf, unsigned int destsize, - unsigned int valsize, int warn, void *d) -{ - elf_reloc_entry *reloc; - elf_objfmt_output_info *info = d; - yasm_intnum *zero; - int retval; - - reloc = elf_reloc_entry_create(sym, NULL, - yasm_intnum_create_uint(bc->offset), 0, valsize, 0); - if (reloc == NULL) { - yasm_error_set(YASM_ERROR_TYPE, N_("elf: invalid relocation size")); - return 1; - } - /* allocate .rel[a] sections on a need-basis */ - elf_secthead_append_reloc(info->sect, info->shead, reloc); - - zero = yasm_intnum_create_uint(0); - elf_handle_reloc_addend(zero, reloc, 0); - retval = yasm_arch_intnum_tobytes(info->object->arch, zero, buf, destsize, - valsize, 0, bc, warn); - yasm_intnum_destroy(zero); - return retval; -} - -static int -elf_objfmt_output_value(yasm_value *value, unsigned char *buf, - unsigned int destsize, unsigned long offset, - yasm_bytecode *bc, int warn, /*@null@*/ void *d) -{ - /*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ yasm_intnum *intn; - unsigned long intn_val; - /*@null@*/ elf_reloc_entry *reloc = NULL; - int retval; - unsigned int valsize = value->size; - - if (info == NULL) - yasm_internal_error("null info struct"); - - if (value->abs) - value->abs = yasm_expr_simplify(value->abs, 1); - - /* Try to output constant and PC-relative section-local first. - * Note this does NOT output any value with a SEG, WRT, external, - * cross-section, or non-PC-relative reference (those are handled below). - */ - switch (yasm_value_output_basic(value, buf, destsize, bc, warn, - info->object->arch)) { - case -1: - return 1; - case 0: - break; - default: - return 0; - } - - /* Handle other expressions, with relocation if necessary */ - if (value->seg_of || value->section_rel || value->rshift > 0) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("elf: relocation too complex")); - return 1; - } - - intn_val = 0; - if (value->rel) { - yasm_sym_vis vis = yasm_symrec_get_visibility(value->rel); - /*@dependent@*/ /*@null@*/ yasm_symrec *sym = value->rel; - /*@dependent@*/ /*@null@*/ yasm_symrec *wrt = value->wrt; - - if (wrt == info->objfmt_elf->dotdotsym) - wrt = NULL; - else if (wrt && elf_is_wrt_sym_relative(wrt)) - ; - else if (wrt && elf_is_wrt_pos_adjusted(wrt)) - intn_val = offset + bc->offset; - else if (vis == YASM_SYM_LOCAL) { - yasm_bytecode *sym_precbc; - /* Local symbols need relocation to their section's start, and - * add in the offset of the bytecode (within the target section) - * into the abs portion. - * - * This is only done if the symbol is relocated against the - * section instead of the symbol itself. - */ - if (yasm_symrec_get_label(sym, &sym_precbc)) { - /* Relocate to section start */ - yasm_section *sym_sect = yasm_bc_get_section(sym_precbc); - /*@null@*/ elf_secthead *sym_shead; - sym_shead = yasm_section_get_data(sym_sect, &elf_section_data); - assert(sym_shead != NULL); - sym = elf_secthead_get_sym(sym_shead); - - intn_val = yasm_bc_next_offset(sym_precbc); - } - } - - /* For PC-relative, need to add offset of expression within bc. */ - if (value->curpos_rel) - intn_val += offset; - - /* Check for _GLOBAL_OFFSET_TABLE_ symbol reference */ - reloc = elf_reloc_entry_create(sym, wrt, - yasm_intnum_create_uint(bc->offset + offset), value->curpos_rel, - valsize, sym == info->GOT_sym); - if (reloc == NULL) { - yasm_error_set(YASM_ERROR_TYPE, - N_("elf: invalid relocation (WRT or size)")); - return 1; - } - /* allocate .rel[a] sections on a need-basis */ - elf_secthead_append_reloc(info->sect, info->shead, reloc); - } - - intn = yasm_intnum_create_uint(intn_val); - - if (value->abs) { - yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); - if (!intn2) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("elf: relocation too complex")); - yasm_intnum_destroy(intn); - return 1; - } - yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); - } - - if (reloc) - elf_handle_reloc_addend(intn, reloc, offset); - retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, - valsize, 0, bc, warn); - yasm_intnum_destroy(intn); - return retval; -} - -static int -elf_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) -{ - /*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d; - unsigned char buf[256]; - /*@null@*/ /*@only@*/ unsigned char *bigbuf; - unsigned long size = 256; - int gap; - - if (info == NULL) - yasm_internal_error("null info struct"); - - bigbuf = yasm_bc_tobytes(bc, buf, &size, &gap, info, - elf_objfmt_output_value, elf_objfmt_output_reloc); - - /* Don't bother doing anything else if size ended up being 0. */ - if (size == 0) { - if (bigbuf) - yasm_xfree(bigbuf); - return 0; - } - else { - yasm_intnum *bcsize = yasm_intnum_create_uint(size); - elf_secthead_add_size(info->shead, bcsize); - yasm_intnum_destroy(bcsize); - } - - /* Warn that gaps are converted to 0 and write out the 0's. */ - if (gap) { - unsigned long left; - yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, - N_("uninitialized space declared in code/data section: zeroing")); - /* Write out in chunks */ - memset(buf, 0, 256); - left = size; - while (left > 256) { - fwrite(buf, 256, 1, info->f); - left -= 256; - } - fwrite(buf, left, 1, info->f); - } else { - /* Output buf (or bigbuf if non-NULL) to file */ - fwrite(bigbuf ? bigbuf : buf, (size_t)size, 1, info->f); - } - - /* If bigbuf was allocated, free it */ - if (bigbuf) - yasm_xfree(bigbuf); - - return 0; -} - -static int -elf_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ elf_secthead *shead; - long pos; - char *relname; - const char *sectname; - - if (info == NULL) - yasm_internal_error("null info struct"); - shead = yasm_section_get_data(sect, &elf_section_data); - if (shead == NULL) - yasm_internal_error("no associated data"); - - if (elf_secthead_get_align(shead) == 0) - elf_secthead_set_align(shead, yasm_section_get_align(sect)); - - /* don't output header-only sections */ - if ((elf_secthead_get_type(shead) & SHT_NOBITS) == SHT_NOBITS) - { - yasm_bytecode *last = yasm_section_bcs_last(sect); - if (last) { - yasm_intnum *sectsize; - sectsize = yasm_intnum_create_uint(yasm_bc_next_offset(last)); - elf_secthead_add_size(shead, sectsize); - yasm_intnum_destroy(sectsize); - } - elf_secthead_set_index(shead, ++info->sindex); - return 0; - } - - if ((pos = ftell(info->f)) == -1) { - yasm_error_set(YASM_ERROR_IO, - N_("couldn't read position on output stream")); - yasm_errwarn_propagate(info->errwarns, 0); - } - pos = elf_secthead_set_file_offset(shead, pos); - if (fseek(info->f, pos, SEEK_SET) < 0) { - yasm_error_set(YASM_ERROR_IO, N_("couldn't seek on output stream")); - yasm_errwarn_propagate(info->errwarns, 0); - } - - info->sect = sect; - info->shead = shead; - yasm_section_bcs_traverse(sect, info->errwarns, info, - elf_objfmt_output_bytecode); - - elf_secthead_set_index(shead, ++info->sindex); - - /* No relocations to output? Go on to next section */ - if (elf_secthead_write_relocs_to_file(info->f, sect, shead, - info->errwarns) == 0) - return 0; - elf_secthead_set_rel_index(shead, ++info->sindex); - - /* name the relocation section .rel[a].foo */ - sectname = yasm_section_get_name(sect); - relname = elf_secthead_name_reloc_section(sectname); - elf_secthead_set_rel_name(shead, - elf_strtab_append_str(info->objfmt_elf->shstrtab, relname)); - yasm_xfree(relname); - - return 0; -} - -static int -elf_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ elf_secthead *shead; - - if (info == NULL) - yasm_internal_error("null info struct"); - shead = yasm_section_get_data(sect, &elf_section_data); - if (shead == NULL) - yasm_internal_error("no section header attached to section"); - - if(elf_secthead_write_to_file(info->f, shead, info->sindex+1)) - info->sindex++; - - /* output strtab headers here? */ - - /* relocation entries for .foo are stored in section .rel[a].foo */ - if(elf_secthead_write_rel_to_file(info->f, 3, sect, shead, - info->sindex+1)) - info->sindex++; - - return 0; -} - -static void -elf_objfmt_output(yasm_object *object, FILE *f, int all_syms, - yasm_errwarns *errwarns) -{ - yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; - elf_objfmt_output_info info; - build_symtab_info buildsym_info; - long pos; - unsigned long elf_shead_addr; - elf_secthead *esdn; - unsigned long elf_strtab_offset, elf_shstrtab_offset, elf_symtab_offset; - unsigned long elf_strtab_size, elf_shstrtab_size, elf_symtab_size; - elf_strtab_entry *elf_strtab_name, *elf_shstrtab_name, *elf_symtab_name; - unsigned long elf_symtab_nlocal; - - info.object = object; - info.objfmt_elf = objfmt_elf; - info.errwarns = errwarns; - info.f = f; - info.GOT_sym = yasm_symtab_get(object->symtab, "_GLOBAL_OFFSET_TABLE_"); - + entry = elf_symtab_entry_create(objfmt_elf->file_strtab_entry, filesym); + yasm_symrec_add_data(filesym, &elf_symrec_data, entry); + elf_symtab_set_nonzero(entry, NULL, SHN_ABS, STB_LOCAL, STT_FILE, NULL, + NULL); + elf_symtab_append_entry(objfmt_elf->elf_symtab, entry); + + /* FIXME: misuse of NULL bytecode */ + objfmt_elf->dotdotsym = + yasm_symtab_define_label(object->symtab, "..sym", NULL, 0, 0); + + return (yasm_objfmt *)objfmt_elf; +} + +static yasm_objfmt * +elf_objfmt_create(yasm_object *object) +{ + const elf_machine_handler *elf_march; + yasm_objfmt *objfmt; + yasm_objfmt_elf *objfmt_elf; + + objfmt = elf_objfmt_create_common(object, &yasm_elf_LTX_objfmt, 0, + &elf_march); + if (objfmt) { + objfmt_elf = (yasm_objfmt_elf *)objfmt; + /* Figure out which bitness of object format to use */ + if (strcmp (elf_march->machine, "x32") == 0) + objfmt_elf->objfmt.module = &yasm_elfx32_LTX_objfmt; + else if (elf_march->bits == 32) + objfmt_elf->objfmt.module = &yasm_elf32_LTX_objfmt; + else if (elf_march->bits == 64) + objfmt_elf->objfmt.module = &yasm_elf64_LTX_objfmt; + } + return objfmt; +} + +static yasm_objfmt * +elf32_objfmt_create(yasm_object *object) +{ + return elf_objfmt_create_common(object, &yasm_elf32_LTX_objfmt, 32, NULL); +} + +static yasm_objfmt * +elf64_objfmt_create(yasm_object *object) +{ + return elf_objfmt_create_common(object, &yasm_elf64_LTX_objfmt, 64, NULL); +} + +static yasm_objfmt * +elfx32_objfmt_create(yasm_object *object) +{ + return elf_objfmt_create_common(object, &yasm_elfx32_LTX_objfmt, 32, NULL); +} + +static long +elf_objfmt_output_align(FILE *f, unsigned int align) +{ + long pos; + unsigned long delta; + if (!is_exp2(align)) + yasm_internal_error("requested alignment not a power of two"); + + pos = ftell(f); + if (pos == -1) { + yasm_error_set(YASM_ERROR_IO, + N_("could not get file position on output file")); + return -1; + } + delta = align - (pos & (align-1)); + if (delta != align) { + pos += delta; + if (fseek(f, pos, SEEK_SET) < 0) { + yasm_error_set(YASM_ERROR_IO, + N_("could not set file position on output file")); + return -1; + } + } + return pos; +} + +static int +elf_objfmt_output_reloc(yasm_symrec *sym, yasm_bytecode *bc, + unsigned char *buf, unsigned int destsize, + unsigned int valsize, int warn, void *d) +{ + elf_reloc_entry *reloc; + elf_objfmt_output_info *info = d; + yasm_intnum *zero; + int retval; + + reloc = elf_reloc_entry_create(sym, NULL, + yasm_intnum_create_uint(bc->offset), 0, valsize, 0); + if (reloc == NULL) { + yasm_error_set(YASM_ERROR_TYPE, N_("elf: invalid relocation size")); + return 1; + } + /* allocate .rel[a] sections on a need-basis */ + elf_secthead_append_reloc(info->sect, info->shead, reloc); + + zero = yasm_intnum_create_uint(0); + elf_handle_reloc_addend(zero, reloc, 0); + retval = yasm_arch_intnum_tobytes(info->object->arch, zero, buf, destsize, + valsize, 0, bc, warn); + yasm_intnum_destroy(zero); + return retval; +} + +static int +elf_objfmt_output_value(yasm_value *value, unsigned char *buf, + unsigned int destsize, unsigned long offset, + yasm_bytecode *bc, int warn, /*@null@*/ void *d) +{ + /*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ yasm_intnum *intn; + unsigned long intn_val; + /*@null@*/ elf_reloc_entry *reloc = NULL; + int retval; + unsigned int valsize = value->size; + + if (info == NULL) + yasm_internal_error("null info struct"); + + if (value->abs) + value->abs = yasm_expr_simplify(value->abs, 1); + + /* Try to output constant and PC-relative section-local first. + * Note this does NOT output any value with a SEG, WRT, external, + * cross-section, or non-PC-relative reference (those are handled below). + */ + switch (yasm_value_output_basic(value, buf, destsize, bc, warn, + info->object->arch)) { + case -1: + return 1; + case 0: + break; + default: + return 0; + } + + /* Handle other expressions, with relocation if necessary */ + if (value->seg_of || value->section_rel || value->rshift > 0) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("elf: relocation too complex")); + return 1; + } + + intn_val = 0; + if (value->rel) { + yasm_sym_vis vis = yasm_symrec_get_visibility(value->rel); + /*@dependent@*/ /*@null@*/ yasm_symrec *sym = value->rel; + /*@dependent@*/ /*@null@*/ yasm_symrec *wrt = value->wrt; + + if (wrt == info->objfmt_elf->dotdotsym) + wrt = NULL; + else if (wrt && elf_is_wrt_sym_relative(wrt)) + ; + else if (wrt && elf_is_wrt_pos_adjusted(wrt)) + intn_val = offset + bc->offset; + else if (vis == YASM_SYM_LOCAL) { + yasm_bytecode *sym_precbc; + /* Local symbols need relocation to their section's start, and + * add in the offset of the bytecode (within the target section) + * into the abs portion. + * + * This is only done if the symbol is relocated against the + * section instead of the symbol itself. + */ + if (yasm_symrec_get_label(sym, &sym_precbc)) { + /* Relocate to section start */ + yasm_section *sym_sect = yasm_bc_get_section(sym_precbc); + /*@null@*/ elf_secthead *sym_shead; + sym_shead = yasm_section_get_data(sym_sect, &elf_section_data); + assert(sym_shead != NULL); + sym = elf_secthead_get_sym(sym_shead); + + intn_val = yasm_bc_next_offset(sym_precbc); + } + } + + /* For PC-relative, need to add offset of expression within bc. */ + if (value->curpos_rel) + intn_val += offset; + + /* Check for _GLOBAL_OFFSET_TABLE_ symbol reference */ + reloc = elf_reloc_entry_create(sym, wrt, + yasm_intnum_create_uint(bc->offset + offset), value->curpos_rel, + valsize, sym == info->GOT_sym); + if (reloc == NULL) { + yasm_error_set(YASM_ERROR_TYPE, + N_("elf: invalid relocation (WRT or size)")); + return 1; + } + /* allocate .rel[a] sections on a need-basis */ + elf_secthead_append_reloc(info->sect, info->shead, reloc); + } + + intn = yasm_intnum_create_uint(intn_val); + + if (value->abs) { + yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); + if (!intn2) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("elf: relocation too complex")); + yasm_intnum_destroy(intn); + return 1; + } + yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); + } + + if (reloc) + elf_handle_reloc_addend(intn, reloc, offset); + retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, + valsize, 0, bc, warn); + yasm_intnum_destroy(intn); + return retval; +} + +static int +elf_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) +{ + /*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d; + unsigned char buf[256]; + /*@null@*/ /*@only@*/ unsigned char *bigbuf; + unsigned long size = 256; + int gap; + + if (info == NULL) + yasm_internal_error("null info struct"); + + bigbuf = yasm_bc_tobytes(bc, buf, &size, &gap, info, + elf_objfmt_output_value, elf_objfmt_output_reloc); + + /* Don't bother doing anything else if size ended up being 0. */ + if (size == 0) { + if (bigbuf) + yasm_xfree(bigbuf); + return 0; + } + else { + yasm_intnum *bcsize = yasm_intnum_create_uint(size); + elf_secthead_add_size(info->shead, bcsize); + yasm_intnum_destroy(bcsize); + } + + /* Warn that gaps are converted to 0 and write out the 0's. */ + if (gap) { + unsigned long left; + yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, + N_("uninitialized space declared in code/data section: zeroing")); + /* Write out in chunks */ + memset(buf, 0, 256); + left = size; + while (left > 256) { + fwrite(buf, 256, 1, info->f); + left -= 256; + } + fwrite(buf, left, 1, info->f); + } else { + /* Output buf (or bigbuf if non-NULL) to file */ + fwrite(bigbuf ? bigbuf : buf, (size_t)size, 1, info->f); + } + + /* If bigbuf was allocated, free it */ + if (bigbuf) + yasm_xfree(bigbuf); + + return 0; +} + +static int +elf_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ elf_secthead *shead; + long pos; + char *relname; + const char *sectname; + + if (info == NULL) + yasm_internal_error("null info struct"); + shead = yasm_section_get_data(sect, &elf_section_data); + if (shead == NULL) + yasm_internal_error("no associated data"); + + if (elf_secthead_get_align(shead) == 0) + elf_secthead_set_align(shead, yasm_section_get_align(sect)); + + /* don't output header-only sections */ + if ((elf_secthead_get_type(shead) & SHT_NOBITS) == SHT_NOBITS) + { + yasm_bytecode *last = yasm_section_bcs_last(sect); + if (last) { + yasm_intnum *sectsize; + sectsize = yasm_intnum_create_uint(yasm_bc_next_offset(last)); + elf_secthead_add_size(shead, sectsize); + yasm_intnum_destroy(sectsize); + } + elf_secthead_set_index(shead, ++info->sindex); + return 0; + } + + if ((pos = ftell(info->f)) == -1) { + yasm_error_set(YASM_ERROR_IO, + N_("couldn't read position on output stream")); + yasm_errwarn_propagate(info->errwarns, 0); + } + pos = elf_secthead_set_file_offset(shead, pos); + if (fseek(info->f, pos, SEEK_SET) < 0) { + yasm_error_set(YASM_ERROR_IO, N_("couldn't seek on output stream")); + yasm_errwarn_propagate(info->errwarns, 0); + } + + info->sect = sect; + info->shead = shead; + yasm_section_bcs_traverse(sect, info->errwarns, info, + elf_objfmt_output_bytecode); + + elf_secthead_set_index(shead, ++info->sindex); + + /* No relocations to output? Go on to next section */ + if (elf_secthead_write_relocs_to_file(info->f, sect, shead, + info->errwarns) == 0) + return 0; + elf_secthead_set_rel_index(shead, ++info->sindex); + + /* name the relocation section .rel[a].foo */ + sectname = yasm_section_get_name(sect); + relname = elf_secthead_name_reloc_section(sectname); + elf_secthead_set_rel_name(shead, + elf_strtab_append_str(info->objfmt_elf->shstrtab, relname)); + yasm_xfree(relname); + + return 0; +} + +static int +elf_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ elf_secthead *shead; + + if (info == NULL) + yasm_internal_error("null info struct"); + shead = yasm_section_get_data(sect, &elf_section_data); + if (shead == NULL) + yasm_internal_error("no section header attached to section"); + + if(elf_secthead_write_to_file(info->f, shead, info->sindex+1)) + info->sindex++; + + /* output strtab headers here? */ + + /* relocation entries for .foo are stored in section .rel[a].foo */ + if(elf_secthead_write_rel_to_file(info->f, 3, sect, shead, + info->sindex+1)) + info->sindex++; + + return 0; +} + +static void +elf_objfmt_output(yasm_object *object, FILE *f, int all_syms, + yasm_errwarns *errwarns) +{ + yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; + elf_objfmt_output_info info; + build_symtab_info buildsym_info; + long pos; + unsigned long elf_shead_addr; + elf_secthead *esdn; + unsigned long elf_strtab_offset, elf_shstrtab_offset, elf_symtab_offset; + unsigned long elf_strtab_size, elf_shstrtab_size, elf_symtab_size; + elf_strtab_entry *elf_strtab_name, *elf_shstrtab_name, *elf_symtab_name; + unsigned long elf_symtab_nlocal; + + info.object = object; + info.objfmt_elf = objfmt_elf; + info.errwarns = errwarns; + info.f = f; + info.GOT_sym = yasm_symtab_get(object->symtab, "_GLOBAL_OFFSET_TABLE_"); + if (!object->deb_filename) { object->deb_filename = yasm_replace_path( objfmt_elf->objfmt.module->replace_map, objfmt_elf->objfmt.module->replace_map_size, object->src_filename, strlen(object->src_filename)); } - /* Update filename strtab */ - elf_strtab_entry_set_str(objfmt_elf->file_strtab_entry, + /* Update filename strtab */ + elf_strtab_entry_set_str(objfmt_elf->file_strtab_entry, object->deb_filename); - - /* Allocate space for Ehdr by seeking forward */ - if (fseek(f, (long)(elf_proghead_get_size()), SEEK_SET) < 0) { - yasm_error_set(YASM_ERROR_IO, N_("could not seek on output file")); - yasm_errwarn_propagate(errwarns, 0); - return; - } - - /* add all (local) syms to symtab because relocation needs a symtab index - * if all_syms, register them by name. if not, use strtab entry 0 */ - buildsym_info.object = object; - buildsym_info.objfmt_elf = objfmt_elf; - buildsym_info.errwarns = errwarns; - buildsym_info.local_names = all_syms; - yasm_symtab_traverse(object->symtab, &buildsym_info, - elf_objfmt_build_symtab); - elf_symtab_nlocal = elf_symtab_assign_indices(objfmt_elf->elf_symtab); - - /* output known sections - includes reloc sections which aren't in yasm's - * list. Assign indices as we go. */ - info.sindex = 3; - if (yasm_object_sections_traverse(object, &info, - elf_objfmt_output_section)) - return; - - /* add final sections to the shstrtab */ - elf_strtab_name = elf_strtab_append_str(objfmt_elf->shstrtab, ".strtab"); - elf_symtab_name = elf_strtab_append_str(objfmt_elf->shstrtab, ".symtab"); - elf_shstrtab_name = elf_strtab_append_str(objfmt_elf->shstrtab, - ".shstrtab"); - - /* output .shstrtab */ - if ((pos = elf_objfmt_output_align(f, 4)) == -1) { - yasm_errwarn_propagate(errwarns, 0); - return; - } - elf_shstrtab_offset = (unsigned long) pos; - elf_shstrtab_size = elf_strtab_output_to_file(f, objfmt_elf->shstrtab); - - /* output .strtab */ - if ((pos = elf_objfmt_output_align(f, 4)) == -1) { - yasm_errwarn_propagate(errwarns, 0); - return; - } - elf_strtab_offset = (unsigned long) pos; - elf_strtab_size = elf_strtab_output_to_file(f, objfmt_elf->strtab); - - /* output .symtab - last section so all others have indexes */ - if ((pos = elf_objfmt_output_align(f, 4)) == -1) { - yasm_errwarn_propagate(errwarns, 0); - return; - } - elf_symtab_offset = (unsigned long) pos; - elf_symtab_size = elf_symtab_write_to_file(f, objfmt_elf->elf_symtab, - errwarns); - - /* output section header table */ - if ((pos = elf_objfmt_output_align(f, 16)) == -1) { - yasm_errwarn_propagate(errwarns, 0); - return; - } - elf_shead_addr = (unsigned long) pos; - - /* stabs debugging support */ - if (strcmp(yasm_dbgfmt_keyword(object->dbgfmt), "stabs")==0) { - yasm_section *stabsect = yasm_object_find_general(object, ".stab"); - yasm_section *stabstrsect = - yasm_object_find_general(object, ".stabstr"); - if (stabsect && stabstrsect) { - elf_secthead *stab = - yasm_section_get_data(stabsect, &elf_section_data); - elf_secthead *stabstr = - yasm_section_get_data(stabstrsect, &elf_section_data); - if (stab && stabstr) { - elf_secthead_set_link(stab, elf_secthead_get_index(stabstr)); - } - else - yasm_internal_error(N_("missing .stab or .stabstr section/data")); - } - } - - /* output dummy section header - 0 */ - info.sindex = 0; - - esdn = elf_secthead_create(NULL, SHT_NULL, 0, 0, 0); - elf_secthead_set_index(esdn, 0); - elf_secthead_write_to_file(f, esdn, 0); - elf_secthead_destroy(esdn); - - esdn = elf_secthead_create(elf_shstrtab_name, SHT_STRTAB, 0, - elf_shstrtab_offset, elf_shstrtab_size); - elf_secthead_set_index(esdn, 1); - elf_secthead_write_to_file(f, esdn, 1); - elf_secthead_destroy(esdn); - - esdn = elf_secthead_create(elf_strtab_name, SHT_STRTAB, 0, - elf_strtab_offset, elf_strtab_size); - elf_secthead_set_index(esdn, 2); - elf_secthead_write_to_file(f, esdn, 2); - elf_secthead_destroy(esdn); - - esdn = elf_secthead_create(elf_symtab_name, SHT_SYMTAB, 0, - elf_symtab_offset, elf_symtab_size); - elf_secthead_set_index(esdn, 3); - elf_secthead_set_info(esdn, elf_symtab_nlocal); - elf_secthead_set_link(esdn, 2); /* for .strtab, which is index 2 */ - elf_secthead_write_to_file(f, esdn, 3); - elf_secthead_destroy(esdn); - - info.sindex = 3; - /* output remaining section headers */ - yasm_object_sections_traverse(object, &info, elf_objfmt_output_secthead); - - /* output Ehdr */ - if (fseek(f, 0, SEEK_SET) < 0) { - yasm_error_set(YASM_ERROR_IO, N_("could not seek on output file")); - yasm_errwarn_propagate(errwarns, 0); - return; - } - - elf_proghead_write_to_file(f, elf_shead_addr, info.sindex+1, 1); -} - -static void -elf_objfmt_destroy(yasm_objfmt *objfmt) -{ - yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)objfmt; - elf_symtab_destroy(objfmt_elf->elf_symtab); - elf_strtab_destroy(objfmt_elf->shstrtab); - elf_strtab_destroy(objfmt_elf->strtab); - yasm_xfree(objfmt); -} - -static void -elf_objfmt_init_new_section(yasm_section *sect, unsigned long line) -{ - yasm_object *object = yasm_section_get_object(sect); - const char *sectname = yasm_section_get_name(sect); - yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; - elf_secthead *esd; - yasm_symrec *sym; - elf_strtab_entry *name = elf_strtab_append_str(objfmt_elf->shstrtab, - sectname); - - elf_section_type type=SHT_PROGBITS; - elf_size entsize=0; - - if (yasm__strcasecmp(sectname, ".stab")==0) { - entsize = 12; - } else if (yasm__strcasecmp(sectname, ".stabstr")==0) { - type = SHT_STRTAB; - } - - esd = elf_secthead_create(name, type, 0, 0, 0); - elf_secthead_set_entsize(esd, entsize); - yasm_section_add_data(sect, &elf_section_data, esd); - sym = yasm_symtab_define_label(object->symtab, sectname, - yasm_section_bcs_first(sect), 1, line); - - elf_secthead_set_sym(esd, sym); -} - -static yasm_section * -elf_objfmt_add_default_section(yasm_object *object) -{ - yasm_section *retval; - int isnew; - - retval = yasm_object_get_general(object, ".text", 16, 1, 0, &isnew, 0); - if (isnew) - { - elf_secthead *esd = yasm_section_get_data(retval, &elf_section_data); - elf_secthead_set_typeflags(esd, SHT_PROGBITS, - SHF_ALLOC + SHF_EXECINSTR); - yasm_section_set_default(retval, 1); - } - return retval; -} - -struct elf_section_switch_data { - /*@only@*/ /*@null@*/ yasm_intnum *align_intn; - unsigned long flags; - unsigned long type; - int gasflags; - int stdsect; -}; - -/* GAS-style flags */ -static int -elf_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d, - /*@unused@*/ uintptr_t arg) -{ - struct elf_section_switch_data *data = (struct elf_section_switch_data *)d; - const char *s = yasm_vp_string(vp); - size_t i; - - if (!s) { - yasm_error_set(YASM_ERROR_VALUE, - N_("non-string section attribute")); - return -1; - } - - if (data->stdsect && strlen(s) == 0) { - data->gasflags = 1; - return 0; - } - - data->flags = 0; - for (i=0; i<strlen(s); i++) { - switch (s[i]) { - case 'a': - data->flags |= SHF_ALLOC; - break; - case 'w': - data->flags |= SHF_WRITE; - break; - case 'x': - data->flags |= SHF_EXECINSTR; - break; - case 'M': - data->flags |= SHF_MERGE; - break; - case 'S': - data->flags |= SHF_STRINGS; - break; - case 'G': - data->flags |= SHF_GROUP; - break; - case 'T': - data->flags |= SHF_TLS; - break; - default: - yasm_warn_set(YASM_WARN_GENERAL, - N_("unrecognized section attribute: `%c'"), - s[i]); - } - } - - data->gasflags = 1; - return 0; -} - -static /*@observer@*/ /*@null@*/ yasm_section * -elf_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, - /*@null@*/ yasm_valparamhead *objext_valparams, - unsigned long line) -{ - yasm_valparam *vp; - yasm_section *retval; - int isnew; - unsigned long align = 4; - int flags_override = 0; - const char *sectname; - int resonly = 0; - - struct elf_section_switch_data data; - - static const yasm_dir_help help[] = { - { "alloc", 0, yasm_dir_helper_flag_or, - offsetof(struct elf_section_switch_data, flags), SHF_ALLOC }, - { "exec", 0, yasm_dir_helper_flag_or, - offsetof(struct elf_section_switch_data, flags), SHF_EXECINSTR }, - { "write", 0, yasm_dir_helper_flag_or, - offsetof(struct elf_section_switch_data, flags), SHF_WRITE }, - { "tls", 0, yasm_dir_helper_flag_or, - offsetof(struct elf_section_switch_data, flags), SHF_TLS }, - { "progbits", 0, yasm_dir_helper_flag_set, - offsetof(struct elf_section_switch_data, type), SHT_PROGBITS }, - { "noalloc", 0, yasm_dir_helper_flag_and, - offsetof(struct elf_section_switch_data, flags), SHF_ALLOC }, - { "noexec", 0, yasm_dir_helper_flag_and, - offsetof(struct elf_section_switch_data, flags), SHF_EXECINSTR }, - { "nowrite", 0, yasm_dir_helper_flag_and, - offsetof(struct elf_section_switch_data, flags), SHF_WRITE }, - { "notls", 0, yasm_dir_helper_flag_and, - offsetof(struct elf_section_switch_data, flags), SHF_TLS }, - { "noprogbits", 0, yasm_dir_helper_flag_set, - offsetof(struct elf_section_switch_data, type), SHT_NOBITS }, - { "nobits", 0, yasm_dir_helper_flag_set, - offsetof(struct elf_section_switch_data, type), SHT_NOBITS }, - { "gasflags", 1, elf_helper_gasflags, 0, 0 }, - { "align", 1, yasm_dir_helper_intn, - offsetof(struct elf_section_switch_data, align_intn), 0 } - }; - /*@only@*/ /*@null@*/ yasm_expr *merge_expr = NULL; - /*@dependent@*/ /*@null@*/ const yasm_intnum *merge_intn = NULL; - elf_secthead *esd; - - data.align_intn = NULL; - data.flags = SHF_ALLOC; - data.type = SHT_PROGBITS; - data.gasflags = 0; - data.stdsect = 1; - - vp = yasm_vps_first(valparams); - sectname = yasm_vp_string(vp); - if (!sectname) - return NULL; - vp = yasm_vps_next(vp); - - if (strcmp(sectname, ".bss") == 0) { - data.type = SHT_NOBITS; - data.flags = SHF_ALLOC + SHF_WRITE; - resonly = 1; - } else if (strcmp(sectname, ".data") == 0) { - data.type = SHT_PROGBITS; - data.flags = SHF_ALLOC + SHF_WRITE; - } else if (strcmp(sectname, ".tdata") == 0) { - data.type = SHT_PROGBITS; - data.flags = SHF_ALLOC + SHF_WRITE + SHF_TLS; - } else if (strcmp(sectname, ".rodata") == 0) { - data.type = SHT_PROGBITS; - data.flags = SHF_ALLOC; - } else if (strcmp(sectname, ".text") == 0) { - align = 16; - data.type = SHT_PROGBITS; - data.flags = SHF_ALLOC + SHF_EXECINSTR; - } else if (strcmp(sectname, ".comment") == 0) { - align = 0; - data.type = SHT_PROGBITS; - data.flags = 0; - } else { - /* Default to code */ - align = 1; - data.stdsect = 0; - } - - flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), - &data, yasm_dir_helper_valparam_warn); - if (flags_override < 0) - return NULL; /* error occurred */ - - if (data.align_intn) { - align = yasm_intnum_get_uint(data.align_intn); - yasm_intnum_destroy(data.align_intn); - - /* Alignments must be a power of two. */ - if (!is_exp2(align)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("argument to `%s' is not a power of two"), - "align"); - return NULL; - } - } - - /* Handle merge entity size */ - if (data.flags & SHF_MERGE) { - if (objext_valparams && (vp = yasm_vps_first(objext_valparams)) - && !vp->val) { - if (!(merge_expr = yasm_vp_expr(vp, object->symtab, line)) || - !(merge_intn = yasm_expr_get_intnum(&merge_expr, 0))) - yasm_warn_set(YASM_WARN_GENERAL, - N_("invalid merge entity size")); - } else { - yasm_warn_set(YASM_WARN_GENERAL, - N_("entity size for SHF_MERGE not specified")); - data.flags &= ~SHF_MERGE; - } - } - - retval = yasm_object_get_general(object, sectname, align, - (data.flags & SHF_EXECINSTR) != 0, - resonly, &isnew, line); - - esd = yasm_section_get_data(retval, &elf_section_data); - - if (isnew || yasm_section_is_default(retval)) { - yasm_section_set_default(retval, 0); - elf_secthead_set_typeflags(esd, data.type, data.flags); - if (merge_intn) - elf_secthead_set_entsize(esd, yasm_intnum_get_uint(merge_intn)); - yasm_section_set_align(retval, align, line); - } else if (flags_override && !data.gasflags) - yasm_warn_set(YASM_WARN_GENERAL, - N_("section flags ignored on section redeclaration")); - if (merge_expr) - yasm_expr_destroy(merge_expr); - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -elf_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - if (yasm__strcasecmp(name, "sym") == 0) { - yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; - return objfmt_elf->dotdotsym; - } - return elf_get_special_sym(name, parser); -} - -static void -dir_type(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - const char *symname = yasm_vp_id(vp); - /* Get symbol elf data */ - yasm_symrec *sym = yasm_symtab_use(object->symtab, symname, line); - elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data); - /*@null@*/ const char *type; - - /* Create entry if necessary */ - if (!entry) { - entry = elf_symtab_entry_create( - elf_strtab_append_str(objfmt_elf->strtab, symname), sym); - yasm_symrec_add_data(sym, &elf_symrec_data, entry); - } - - /* Pull new type from param */ - vp = yasm_vps_next(vp); - if (vp && !vp->val && (type = yasm_vp_id(vp))) { - if (yasm__strcasecmp(type, "function") == 0) - elf_sym_set_type(entry, STT_FUNC); - else if (yasm__strcasecmp(type, "object") == 0) - elf_sym_set_type(entry, STT_OBJECT); - else if (yasm__strcasecmp(type, "tls_object") == 0) - elf_sym_set_type(entry, STT_TLS); - else if (yasm__strcasecmp(type, "notype") == 0) - elf_sym_set_type(entry, STT_NOTYPE); - else - yasm_warn_set(YASM_WARN_GENERAL, - N_("unrecognized symbol type `%s'"), type); - } else - yasm_error_set(YASM_ERROR_SYNTAX, N_("no type specified")); -} - -static void -dir_size(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - const char *symname = yasm_vp_id(vp); - /* Get symbol elf data */ - yasm_symrec *sym = yasm_symtab_use(object->symtab, symname, line); - elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data); - /*@only@*/ /*@null@*/ yasm_expr *size; - - /* Create entry if necessary */ - if (!entry) { - entry = elf_symtab_entry_create( - elf_strtab_append_str(objfmt_elf->strtab, symname), sym); - yasm_symrec_add_data(sym, &elf_symrec_data, entry); - } - - /* Pull new size from param */ - vp = yasm_vps_next(vp); - if (vp && !vp->val && (size = yasm_vp_expr(vp, object->symtab, line))) - elf_sym_set_size(entry, size); - else - yasm_error_set(YASM_ERROR_SYNTAX, N_("no size specified")); -} - -static void -dir_weak(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; - yasm_valparam *vp = yasm_vps_first(valparams); - const char *symname = yasm_vp_id(vp); - yasm_symrec *sym = yasm_symtab_declare(object->symtab, symname, - YASM_SYM_GLOBAL, line); - elf_objfmt_symtab_append(objfmt_elf, sym, SHN_UNDEF, STB_WEAK, 0, - STV_DEFAULT, NULL, NULL, object); -} - -static void -dir_ident(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_valparamhead sect_vps; - yasm_datavalhead dvs; - yasm_section *comment; - yasm_valparam *vp; - yasm_valparam *vp2; - - /* Accept, but do nothing with empty ident */ - if (!valparams) - return; - vp = yasm_vps_first(valparams); - if (!vp) - return; - - /* Put ident data into .comment section */ - yasm_vps_initialize(§_vps); - vp2 = yasm_vp_create_string(NULL, yasm__xstrdup(".comment")); - yasm_vps_append(§_vps, vp2); - comment = elf_objfmt_section_switch(object, §_vps, NULL, line); - yasm_vps_delete(§_vps); - - /* To match GAS output, if the comment section is empty, put an - * initial 0 byte in the section. - */ - if (yasm_section_bcs_first(comment) == yasm_section_bcs_last(comment)) { - yasm_dvs_initialize(&dvs); - yasm_dvs_append(&dvs, yasm_dv_create_expr( - yasm_expr_create_ident( - yasm_expr_int(yasm_intnum_create_uint(0)), line))); - yasm_section_bcs_append(comment, - yasm_bc_create_data(&dvs, 1, 0, object->arch, line)); - } - - yasm_dvs_initialize(&dvs); - do { - const char *s = yasm_vp_string(vp); - if (!s) { - yasm_error_set(YASM_ERROR_VALUE, - N_(".comment requires string parameters")); - yasm_dvs_delete(&dvs); - return; - } - yasm_dvs_append(&dvs, - yasm_dv_create_string(yasm__xstrdup(s), strlen(s))); - } while ((vp = yasm_vps_next(vp))); - - yasm_section_bcs_append(comment, - yasm_bc_create_data(&dvs, 1, 1, object->arch, line)); -} - -/* Define valid debug formats to use with this object format */ -static const char *elf_objfmt_dbgfmt_keywords[] = { - "null", - "stabs", - "dwarf2", - NULL -}; - -static const yasm_directive elf_objfmt_directives[] = { - { ".type", "gas", dir_type, YASM_DIR_ID_REQUIRED }, - { ".size", "gas", dir_size, YASM_DIR_ID_REQUIRED }, - { ".weak", "gas", dir_weak, YASM_DIR_ID_REQUIRED }, - { ".ident", "gas", dir_ident, YASM_DIR_ANY }, - { "type", "nasm", dir_type, YASM_DIR_ID_REQUIRED }, - { "size", "nasm", dir_size, YASM_DIR_ID_REQUIRED }, - { "weak", "nasm", dir_weak, YASM_DIR_ID_REQUIRED }, - { "ident", "nasm", dir_ident, YASM_DIR_ANY }, - { NULL, NULL, NULL, 0 } -}; - -static const char *elf_nasm_stdmac[] = { - "%imacro type 1+.nolist", - "[type %1]", - "%endmacro", - "%imacro size 1+.nolist", - "[size %1]", - "%endmacro", - "%imacro weak 1+.nolist", - "[weak %1]", - "%endmacro", - NULL -}; - -static const yasm_stdmac elf_objfmt_stdmacs[] = { - { "nasm", "nasm", elf_nasm_stdmac }, - { NULL, NULL, NULL } -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_elf_LTX_objfmt = { - "ELF", - "elf", - "o", - 32, - 0, - elf_objfmt_dbgfmt_keywords, - "null", - elf_objfmt_directives, - elf_objfmt_stdmacs, - elf_objfmt_create, - elf_objfmt_output, - elf_objfmt_destroy, - elf_objfmt_add_default_section, - elf_objfmt_init_new_section, - elf_objfmt_section_switch, - elf_objfmt_get_special_sym -}; - -yasm_objfmt_module yasm_elf32_LTX_objfmt = { - "ELF (32-bit)", - "elf32", - "o", - 32, - 0, - elf_objfmt_dbgfmt_keywords, - "null", - elf_objfmt_directives, - elf_objfmt_stdmacs, - elf32_objfmt_create, - elf_objfmt_output, - elf_objfmt_destroy, - elf_objfmt_add_default_section, - elf_objfmt_init_new_section, - elf_objfmt_section_switch, - elf_objfmt_get_special_sym -}; - -yasm_objfmt_module yasm_elf64_LTX_objfmt = { - "ELF (64-bit)", - "elf64", - "o", - 64, - 0, - elf_objfmt_dbgfmt_keywords, - "null", - elf_objfmt_directives, - elf_objfmt_stdmacs, - elf64_objfmt_create, - elf_objfmt_output, - elf_objfmt_destroy, - elf_objfmt_add_default_section, - elf_objfmt_init_new_section, - elf_objfmt_section_switch, - elf_objfmt_get_special_sym -}; - -yasm_objfmt_module yasm_elfx32_LTX_objfmt = { - "ELF (x32)", - "elfx32", - "o", - 64, - 0, - elf_objfmt_dbgfmt_keywords, - "null", - elf_objfmt_directives, - elf_objfmt_stdmacs, - elfx32_objfmt_create, - elf_objfmt_output, - elf_objfmt_destroy, - elf_objfmt_add_default_section, - elf_objfmt_init_new_section, - elf_objfmt_section_switch, - elf_objfmt_get_special_sym -}; + + /* Allocate space for Ehdr by seeking forward */ + if (fseek(f, (long)(elf_proghead_get_size()), SEEK_SET) < 0) { + yasm_error_set(YASM_ERROR_IO, N_("could not seek on output file")); + yasm_errwarn_propagate(errwarns, 0); + return; + } + + /* add all (local) syms to symtab because relocation needs a symtab index + * if all_syms, register them by name. if not, use strtab entry 0 */ + buildsym_info.object = object; + buildsym_info.objfmt_elf = objfmt_elf; + buildsym_info.errwarns = errwarns; + buildsym_info.local_names = all_syms; + yasm_symtab_traverse(object->symtab, &buildsym_info, + elf_objfmt_build_symtab); + elf_symtab_nlocal = elf_symtab_assign_indices(objfmt_elf->elf_symtab); + + /* output known sections - includes reloc sections which aren't in yasm's + * list. Assign indices as we go. */ + info.sindex = 3; + if (yasm_object_sections_traverse(object, &info, + elf_objfmt_output_section)) + return; + + /* add final sections to the shstrtab */ + elf_strtab_name = elf_strtab_append_str(objfmt_elf->shstrtab, ".strtab"); + elf_symtab_name = elf_strtab_append_str(objfmt_elf->shstrtab, ".symtab"); + elf_shstrtab_name = elf_strtab_append_str(objfmt_elf->shstrtab, + ".shstrtab"); + + /* output .shstrtab */ + if ((pos = elf_objfmt_output_align(f, 4)) == -1) { + yasm_errwarn_propagate(errwarns, 0); + return; + } + elf_shstrtab_offset = (unsigned long) pos; + elf_shstrtab_size = elf_strtab_output_to_file(f, objfmt_elf->shstrtab); + + /* output .strtab */ + if ((pos = elf_objfmt_output_align(f, 4)) == -1) { + yasm_errwarn_propagate(errwarns, 0); + return; + } + elf_strtab_offset = (unsigned long) pos; + elf_strtab_size = elf_strtab_output_to_file(f, objfmt_elf->strtab); + + /* output .symtab - last section so all others have indexes */ + if ((pos = elf_objfmt_output_align(f, 4)) == -1) { + yasm_errwarn_propagate(errwarns, 0); + return; + } + elf_symtab_offset = (unsigned long) pos; + elf_symtab_size = elf_symtab_write_to_file(f, objfmt_elf->elf_symtab, + errwarns); + + /* output section header table */ + if ((pos = elf_objfmt_output_align(f, 16)) == -1) { + yasm_errwarn_propagate(errwarns, 0); + return; + } + elf_shead_addr = (unsigned long) pos; + + /* stabs debugging support */ + if (strcmp(yasm_dbgfmt_keyword(object->dbgfmt), "stabs")==0) { + yasm_section *stabsect = yasm_object_find_general(object, ".stab"); + yasm_section *stabstrsect = + yasm_object_find_general(object, ".stabstr"); + if (stabsect && stabstrsect) { + elf_secthead *stab = + yasm_section_get_data(stabsect, &elf_section_data); + elf_secthead *stabstr = + yasm_section_get_data(stabstrsect, &elf_section_data); + if (stab && stabstr) { + elf_secthead_set_link(stab, elf_secthead_get_index(stabstr)); + } + else + yasm_internal_error(N_("missing .stab or .stabstr section/data")); + } + } + + /* output dummy section header - 0 */ + info.sindex = 0; + + esdn = elf_secthead_create(NULL, SHT_NULL, 0, 0, 0); + elf_secthead_set_index(esdn, 0); + elf_secthead_write_to_file(f, esdn, 0); + elf_secthead_destroy(esdn); + + esdn = elf_secthead_create(elf_shstrtab_name, SHT_STRTAB, 0, + elf_shstrtab_offset, elf_shstrtab_size); + elf_secthead_set_index(esdn, 1); + elf_secthead_write_to_file(f, esdn, 1); + elf_secthead_destroy(esdn); + + esdn = elf_secthead_create(elf_strtab_name, SHT_STRTAB, 0, + elf_strtab_offset, elf_strtab_size); + elf_secthead_set_index(esdn, 2); + elf_secthead_write_to_file(f, esdn, 2); + elf_secthead_destroy(esdn); + + esdn = elf_secthead_create(elf_symtab_name, SHT_SYMTAB, 0, + elf_symtab_offset, elf_symtab_size); + elf_secthead_set_index(esdn, 3); + elf_secthead_set_info(esdn, elf_symtab_nlocal); + elf_secthead_set_link(esdn, 2); /* for .strtab, which is index 2 */ + elf_secthead_write_to_file(f, esdn, 3); + elf_secthead_destroy(esdn); + + info.sindex = 3; + /* output remaining section headers */ + yasm_object_sections_traverse(object, &info, elf_objfmt_output_secthead); + + /* output Ehdr */ + if (fseek(f, 0, SEEK_SET) < 0) { + yasm_error_set(YASM_ERROR_IO, N_("could not seek on output file")); + yasm_errwarn_propagate(errwarns, 0); + return; + } + + elf_proghead_write_to_file(f, elf_shead_addr, info.sindex+1, 1); +} + +static void +elf_objfmt_destroy(yasm_objfmt *objfmt) +{ + yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)objfmt; + elf_symtab_destroy(objfmt_elf->elf_symtab); + elf_strtab_destroy(objfmt_elf->shstrtab); + elf_strtab_destroy(objfmt_elf->strtab); + yasm_xfree(objfmt); +} + +static void +elf_objfmt_init_new_section(yasm_section *sect, unsigned long line) +{ + yasm_object *object = yasm_section_get_object(sect); + const char *sectname = yasm_section_get_name(sect); + yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; + elf_secthead *esd; + yasm_symrec *sym; + elf_strtab_entry *name = elf_strtab_append_str(objfmt_elf->shstrtab, + sectname); + + elf_section_type type=SHT_PROGBITS; + elf_size entsize=0; + + if (yasm__strcasecmp(sectname, ".stab")==0) { + entsize = 12; + } else if (yasm__strcasecmp(sectname, ".stabstr")==0) { + type = SHT_STRTAB; + } + + esd = elf_secthead_create(name, type, 0, 0, 0); + elf_secthead_set_entsize(esd, entsize); + yasm_section_add_data(sect, &elf_section_data, esd); + sym = yasm_symtab_define_label(object->symtab, sectname, + yasm_section_bcs_first(sect), 1, line); + + elf_secthead_set_sym(esd, sym); +} + +static yasm_section * +elf_objfmt_add_default_section(yasm_object *object) +{ + yasm_section *retval; + int isnew; + + retval = yasm_object_get_general(object, ".text", 16, 1, 0, &isnew, 0); + if (isnew) + { + elf_secthead *esd = yasm_section_get_data(retval, &elf_section_data); + elf_secthead_set_typeflags(esd, SHT_PROGBITS, + SHF_ALLOC + SHF_EXECINSTR); + yasm_section_set_default(retval, 1); + } + return retval; +} + +struct elf_section_switch_data { + /*@only@*/ /*@null@*/ yasm_intnum *align_intn; + unsigned long flags; + unsigned long type; + int gasflags; + int stdsect; +}; + +/* GAS-style flags */ +static int +elf_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d, + /*@unused@*/ uintptr_t arg) +{ + struct elf_section_switch_data *data = (struct elf_section_switch_data *)d; + const char *s = yasm_vp_string(vp); + size_t i; + + if (!s) { + yasm_error_set(YASM_ERROR_VALUE, + N_("non-string section attribute")); + return -1; + } + + if (data->stdsect && strlen(s) == 0) { + data->gasflags = 1; + return 0; + } + + data->flags = 0; + for (i=0; i<strlen(s); i++) { + switch (s[i]) { + case 'a': + data->flags |= SHF_ALLOC; + break; + case 'w': + data->flags |= SHF_WRITE; + break; + case 'x': + data->flags |= SHF_EXECINSTR; + break; + case 'M': + data->flags |= SHF_MERGE; + break; + case 'S': + data->flags |= SHF_STRINGS; + break; + case 'G': + data->flags |= SHF_GROUP; + break; + case 'T': + data->flags |= SHF_TLS; + break; + default: + yasm_warn_set(YASM_WARN_GENERAL, + N_("unrecognized section attribute: `%c'"), + s[i]); + } + } + + data->gasflags = 1; + return 0; +} + +static /*@observer@*/ /*@null@*/ yasm_section * +elf_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, + /*@null@*/ yasm_valparamhead *objext_valparams, + unsigned long line) +{ + yasm_valparam *vp; + yasm_section *retval; + int isnew; + unsigned long align = 4; + int flags_override = 0; + const char *sectname; + int resonly = 0; + + struct elf_section_switch_data data; + + static const yasm_dir_help help[] = { + { "alloc", 0, yasm_dir_helper_flag_or, + offsetof(struct elf_section_switch_data, flags), SHF_ALLOC }, + { "exec", 0, yasm_dir_helper_flag_or, + offsetof(struct elf_section_switch_data, flags), SHF_EXECINSTR }, + { "write", 0, yasm_dir_helper_flag_or, + offsetof(struct elf_section_switch_data, flags), SHF_WRITE }, + { "tls", 0, yasm_dir_helper_flag_or, + offsetof(struct elf_section_switch_data, flags), SHF_TLS }, + { "progbits", 0, yasm_dir_helper_flag_set, + offsetof(struct elf_section_switch_data, type), SHT_PROGBITS }, + { "noalloc", 0, yasm_dir_helper_flag_and, + offsetof(struct elf_section_switch_data, flags), SHF_ALLOC }, + { "noexec", 0, yasm_dir_helper_flag_and, + offsetof(struct elf_section_switch_data, flags), SHF_EXECINSTR }, + { "nowrite", 0, yasm_dir_helper_flag_and, + offsetof(struct elf_section_switch_data, flags), SHF_WRITE }, + { "notls", 0, yasm_dir_helper_flag_and, + offsetof(struct elf_section_switch_data, flags), SHF_TLS }, + { "noprogbits", 0, yasm_dir_helper_flag_set, + offsetof(struct elf_section_switch_data, type), SHT_NOBITS }, + { "nobits", 0, yasm_dir_helper_flag_set, + offsetof(struct elf_section_switch_data, type), SHT_NOBITS }, + { "gasflags", 1, elf_helper_gasflags, 0, 0 }, + { "align", 1, yasm_dir_helper_intn, + offsetof(struct elf_section_switch_data, align_intn), 0 } + }; + /*@only@*/ /*@null@*/ yasm_expr *merge_expr = NULL; + /*@dependent@*/ /*@null@*/ const yasm_intnum *merge_intn = NULL; + elf_secthead *esd; + + data.align_intn = NULL; + data.flags = SHF_ALLOC; + data.type = SHT_PROGBITS; + data.gasflags = 0; + data.stdsect = 1; + + vp = yasm_vps_first(valparams); + sectname = yasm_vp_string(vp); + if (!sectname) + return NULL; + vp = yasm_vps_next(vp); + + if (strcmp(sectname, ".bss") == 0) { + data.type = SHT_NOBITS; + data.flags = SHF_ALLOC + SHF_WRITE; + resonly = 1; + } else if (strcmp(sectname, ".data") == 0) { + data.type = SHT_PROGBITS; + data.flags = SHF_ALLOC + SHF_WRITE; + } else if (strcmp(sectname, ".tdata") == 0) { + data.type = SHT_PROGBITS; + data.flags = SHF_ALLOC + SHF_WRITE + SHF_TLS; + } else if (strcmp(sectname, ".rodata") == 0) { + data.type = SHT_PROGBITS; + data.flags = SHF_ALLOC; + } else if (strcmp(sectname, ".text") == 0) { + align = 16; + data.type = SHT_PROGBITS; + data.flags = SHF_ALLOC + SHF_EXECINSTR; + } else if (strcmp(sectname, ".comment") == 0) { + align = 0; + data.type = SHT_PROGBITS; + data.flags = 0; + } else { + /* Default to code */ + align = 1; + data.stdsect = 0; + } + + flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), + &data, yasm_dir_helper_valparam_warn); + if (flags_override < 0) + return NULL; /* error occurred */ + + if (data.align_intn) { + align = yasm_intnum_get_uint(data.align_intn); + yasm_intnum_destroy(data.align_intn); + + /* Alignments must be a power of two. */ + if (!is_exp2(align)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("argument to `%s' is not a power of two"), + "align"); + return NULL; + } + } + + /* Handle merge entity size */ + if (data.flags & SHF_MERGE) { + if (objext_valparams && (vp = yasm_vps_first(objext_valparams)) + && !vp->val) { + if (!(merge_expr = yasm_vp_expr(vp, object->symtab, line)) || + !(merge_intn = yasm_expr_get_intnum(&merge_expr, 0))) + yasm_warn_set(YASM_WARN_GENERAL, + N_("invalid merge entity size")); + } else { + yasm_warn_set(YASM_WARN_GENERAL, + N_("entity size for SHF_MERGE not specified")); + data.flags &= ~SHF_MERGE; + } + } + + retval = yasm_object_get_general(object, sectname, align, + (data.flags & SHF_EXECINSTR) != 0, + resonly, &isnew, line); + + esd = yasm_section_get_data(retval, &elf_section_data); + + if (isnew || yasm_section_is_default(retval)) { + yasm_section_set_default(retval, 0); + elf_secthead_set_typeflags(esd, data.type, data.flags); + if (merge_intn) + elf_secthead_set_entsize(esd, yasm_intnum_get_uint(merge_intn)); + yasm_section_set_align(retval, align, line); + } else if (flags_override && !data.gasflags) + yasm_warn_set(YASM_WARN_GENERAL, + N_("section flags ignored on section redeclaration")); + if (merge_expr) + yasm_expr_destroy(merge_expr); + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +elf_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + if (yasm__strcasecmp(name, "sym") == 0) { + yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; + return objfmt_elf->dotdotsym; + } + return elf_get_special_sym(name, parser); +} + +static void +dir_type(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + const char *symname = yasm_vp_id(vp); + /* Get symbol elf data */ + yasm_symrec *sym = yasm_symtab_use(object->symtab, symname, line); + elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data); + /*@null@*/ const char *type; + + /* Create entry if necessary */ + if (!entry) { + entry = elf_symtab_entry_create( + elf_strtab_append_str(objfmt_elf->strtab, symname), sym); + yasm_symrec_add_data(sym, &elf_symrec_data, entry); + } + + /* Pull new type from param */ + vp = yasm_vps_next(vp); + if (vp && !vp->val && (type = yasm_vp_id(vp))) { + if (yasm__strcasecmp(type, "function") == 0) + elf_sym_set_type(entry, STT_FUNC); + else if (yasm__strcasecmp(type, "object") == 0) + elf_sym_set_type(entry, STT_OBJECT); + else if (yasm__strcasecmp(type, "tls_object") == 0) + elf_sym_set_type(entry, STT_TLS); + else if (yasm__strcasecmp(type, "notype") == 0) + elf_sym_set_type(entry, STT_NOTYPE); + else + yasm_warn_set(YASM_WARN_GENERAL, + N_("unrecognized symbol type `%s'"), type); + } else + yasm_error_set(YASM_ERROR_SYNTAX, N_("no type specified")); +} + +static void +dir_size(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + const char *symname = yasm_vp_id(vp); + /* Get symbol elf data */ + yasm_symrec *sym = yasm_symtab_use(object->symtab, symname, line); + elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data); + /*@only@*/ /*@null@*/ yasm_expr *size; + + /* Create entry if necessary */ + if (!entry) { + entry = elf_symtab_entry_create( + elf_strtab_append_str(objfmt_elf->strtab, symname), sym); + yasm_symrec_add_data(sym, &elf_symrec_data, entry); + } + + /* Pull new size from param */ + vp = yasm_vps_next(vp); + if (vp && !vp->val && (size = yasm_vp_expr(vp, object->symtab, line))) + elf_sym_set_size(entry, size); + else + yasm_error_set(YASM_ERROR_SYNTAX, N_("no size specified")); +} + +static void +dir_weak(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt; + yasm_valparam *vp = yasm_vps_first(valparams); + const char *symname = yasm_vp_id(vp); + yasm_symrec *sym = yasm_symtab_declare(object->symtab, symname, + YASM_SYM_GLOBAL, line); + elf_objfmt_symtab_append(objfmt_elf, sym, SHN_UNDEF, STB_WEAK, 0, + STV_DEFAULT, NULL, NULL, object); +} + +static void +dir_ident(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparamhead sect_vps; + yasm_datavalhead dvs; + yasm_section *comment; + yasm_valparam *vp; + yasm_valparam *vp2; + + /* Accept, but do nothing with empty ident */ + if (!valparams) + return; + vp = yasm_vps_first(valparams); + if (!vp) + return; + + /* Put ident data into .comment section */ + yasm_vps_initialize(§_vps); + vp2 = yasm_vp_create_string(NULL, yasm__xstrdup(".comment")); + yasm_vps_append(§_vps, vp2); + comment = elf_objfmt_section_switch(object, §_vps, NULL, line); + yasm_vps_delete(§_vps); + + /* To match GAS output, if the comment section is empty, put an + * initial 0 byte in the section. + */ + if (yasm_section_bcs_first(comment) == yasm_section_bcs_last(comment)) { + yasm_dvs_initialize(&dvs); + yasm_dvs_append(&dvs, yasm_dv_create_expr( + yasm_expr_create_ident( + yasm_expr_int(yasm_intnum_create_uint(0)), line))); + yasm_section_bcs_append(comment, + yasm_bc_create_data(&dvs, 1, 0, object->arch, line)); + } + + yasm_dvs_initialize(&dvs); + do { + const char *s = yasm_vp_string(vp); + if (!s) { + yasm_error_set(YASM_ERROR_VALUE, + N_(".comment requires string parameters")); + yasm_dvs_delete(&dvs); + return; + } + yasm_dvs_append(&dvs, + yasm_dv_create_string(yasm__xstrdup(s), strlen(s))); + } while ((vp = yasm_vps_next(vp))); + + yasm_section_bcs_append(comment, + yasm_bc_create_data(&dvs, 1, 1, object->arch, line)); +} + +/* Define valid debug formats to use with this object format */ +static const char *elf_objfmt_dbgfmt_keywords[] = { + "null", + "stabs", + "dwarf2", + NULL +}; + +static const yasm_directive elf_objfmt_directives[] = { + { ".type", "gas", dir_type, YASM_DIR_ID_REQUIRED }, + { ".size", "gas", dir_size, YASM_DIR_ID_REQUIRED }, + { ".weak", "gas", dir_weak, YASM_DIR_ID_REQUIRED }, + { ".ident", "gas", dir_ident, YASM_DIR_ANY }, + { "type", "nasm", dir_type, YASM_DIR_ID_REQUIRED }, + { "size", "nasm", dir_size, YASM_DIR_ID_REQUIRED }, + { "weak", "nasm", dir_weak, YASM_DIR_ID_REQUIRED }, + { "ident", "nasm", dir_ident, YASM_DIR_ANY }, + { NULL, NULL, NULL, 0 } +}; + +static const char *elf_nasm_stdmac[] = { + "%imacro type 1+.nolist", + "[type %1]", + "%endmacro", + "%imacro size 1+.nolist", + "[size %1]", + "%endmacro", + "%imacro weak 1+.nolist", + "[weak %1]", + "%endmacro", + NULL +}; + +static const yasm_stdmac elf_objfmt_stdmacs[] = { + { "nasm", "nasm", elf_nasm_stdmac }, + { NULL, NULL, NULL } +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_elf_LTX_objfmt = { + "ELF", + "elf", + "o", + 32, + 0, + elf_objfmt_dbgfmt_keywords, + "null", + elf_objfmt_directives, + elf_objfmt_stdmacs, + elf_objfmt_create, + elf_objfmt_output, + elf_objfmt_destroy, + elf_objfmt_add_default_section, + elf_objfmt_init_new_section, + elf_objfmt_section_switch, + elf_objfmt_get_special_sym +}; + +yasm_objfmt_module yasm_elf32_LTX_objfmt = { + "ELF (32-bit)", + "elf32", + "o", + 32, + 0, + elf_objfmt_dbgfmt_keywords, + "null", + elf_objfmt_directives, + elf_objfmt_stdmacs, + elf32_objfmt_create, + elf_objfmt_output, + elf_objfmt_destroy, + elf_objfmt_add_default_section, + elf_objfmt_init_new_section, + elf_objfmt_section_switch, + elf_objfmt_get_special_sym +}; + +yasm_objfmt_module yasm_elf64_LTX_objfmt = { + "ELF (64-bit)", + "elf64", + "o", + 64, + 0, + elf_objfmt_dbgfmt_keywords, + "null", + elf_objfmt_directives, + elf_objfmt_stdmacs, + elf64_objfmt_create, + elf_objfmt_output, + elf_objfmt_destroy, + elf_objfmt_add_default_section, + elf_objfmt_init_new_section, + elf_objfmt_section_switch, + elf_objfmt_get_special_sym +}; + +yasm_objfmt_module yasm_elfx32_LTX_objfmt = { + "ELF (x32)", + "elfx32", + "o", + 64, + 0, + elf_objfmt_dbgfmt_keywords, + "null", + elf_objfmt_directives, + elf_objfmt_stdmacs, + elfx32_objfmt_create, + elf_objfmt_output, + elf_objfmt_destroy, + elf_objfmt_add_default_section, + elf_objfmt_init_new_section, + elf_objfmt_section_switch, + elf_objfmt_get_special_sym +}; diff --git a/contrib/tools/yasm/modules/objfmts/elf/elf-x86-amd64.c b/contrib/tools/yasm/modules/objfmts/elf/elf-x86-amd64.c index 3ba376af03..5384b768ed 100644 --- a/contrib/tools/yasm/modules/objfmts/elf/elf-x86-amd64.c +++ b/contrib/tools/yasm/modules/objfmts/elf/elf-x86-amd64.c @@ -1,256 +1,256 @@ -/* - * ELF object format helpers - x86:amd64 - * - * Copyright (C) 2004-2007 Michael Urman - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <util.h> - -#include <libyasm.h> -#define YASM_OBJFMT_ELF_INTERNAL -#include "elf.h" -#include "elf-machine.h" - -static elf_machine_ssym elf_x86_amd64_ssyms[] = { - {"pltoff", ELF_SSYM_SYM_RELATIVE, R_X86_64_PLTOFF64, 64}, - {"plt", ELF_SSYM_SYM_RELATIVE, R_X86_64_PLT32, 32}, - {"gotplt", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOTPLT64, 64}, - {"gotoff", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOTOFF64, 64}, - {"gotpcrel", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOTPCREL, 32}, - {"tlsgd", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_TLSGD, 32}, - {"tlsld", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_TLSLD, 32}, - {"gottpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_GOTTPOFF, 32}, - {"tpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_TPOFF32, 32}, - {"dtpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_DTPOFF32, 32}, - {"got", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOT32, 32}, - {"tlsdesc", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_GOTPC32_TLSDESC, 32}, - {"tlscall", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_TLSDESC_CALL, 32} -}; - -static int -elf_x86_amd64_accepts_reloc(size_t val, yasm_symrec *wrt) -{ - if (wrt) { - const elf_machine_ssym *ssym = (elf_machine_ssym *) - yasm_symrec_get_data(wrt, &elf_ssym_symrec_data); - if (!ssym || val != ssym->size) - return 0; - return 1; - } - return (val&(val-1)) ? 0 : ((val & (8|16|32|64)) != 0); -} - -static void -elf_x86_amd64_write_symtab_entry(unsigned char *bufp, - elf_symtab_entry *entry, - yasm_intnum *value_intn, - yasm_intnum *size_intn) -{ - YASM_WRITE_32_L(bufp, entry->name ? entry->name->index : 0); - YASM_WRITE_8(bufp, ELF64_ST_INFO(entry->bind, entry->type)); - YASM_WRITE_8(bufp, ELF64_ST_OTHER(entry->vis)); - if (entry->sect) { - elf_secthead *shead = - yasm_section_get_data(entry->sect, &elf_section_data); - if (!shead) - yasm_internal_error(N_("symbol references section without data")); - YASM_WRITE_16_L(bufp, shead->index); - } else { - YASM_WRITE_16_L(bufp, entry->index); - } - YASM_WRITE_64I_L(bufp, value_intn); - YASM_WRITE_64I_L(bufp, size_intn); -} - -static void -elf_x86_amd64_write_secthead(unsigned char *bufp, elf_secthead *shead) -{ - YASM_WRITE_32_L(bufp, shead->name ? shead->name->index : 0); - YASM_WRITE_32_L(bufp, shead->type); - YASM_WRITE_64Z_L(bufp, shead->flags); - YASM_WRITE_64Z_L(bufp, 0); /* vmem address */ - YASM_WRITE_64Z_L(bufp, shead->offset); - YASM_WRITE_64I_L(bufp, shead->size); - - YASM_WRITE_32_L(bufp, shead->link); - YASM_WRITE_32_L(bufp, shead->info); - - YASM_WRITE_64Z_L(bufp, shead->align); - YASM_WRITE_64Z_L(bufp, shead->entsize); -} - -static void -elf_x86_amd64_write_secthead_rel(unsigned char *bufp, - elf_secthead *shead, - elf_section_index symtab_idx, - elf_section_index sindex) -{ - yasm_intnum *nreloc; - yasm_intnum *relocsize; - - YASM_WRITE_32_L(bufp, shead->rel_name ? shead->rel_name->index : 0); - YASM_WRITE_32_L(bufp, SHT_RELA); - YASM_WRITE_64Z_L(bufp, 0); - YASM_WRITE_64Z_L(bufp, 0); - YASM_WRITE_64Z_L(bufp, shead->rel_offset); - - nreloc = yasm_intnum_create_uint(shead->nreloc); - relocsize = yasm_intnum_create_uint(RELOC64A_SIZE); - yasm_intnum_calc(relocsize, YASM_EXPR_MUL, nreloc); - YASM_WRITE_64I_L(bufp, relocsize); /* size */ - yasm_intnum_destroy(nreloc); - yasm_intnum_destroy(relocsize); - - YASM_WRITE_32_L(bufp, symtab_idx); /* link: symtab index */ - YASM_WRITE_32_L(bufp, shead->index); /* info: relocated's index */ - YASM_WRITE_64Z_L(bufp, RELOC64_ALIGN); /* align */ - YASM_WRITE_64Z_L(bufp, RELOC64A_SIZE); /* entity size */ -} - -static void -elf_x86_amd64_handle_reloc_addend(yasm_intnum *intn, - elf_reloc_entry *reloc, - unsigned long offset) -{ - /* .rela: copy value out as addend, replace original with 0 */ - reloc->addend = yasm_intnum_copy(intn); - yasm_intnum_zero(intn); -} - -static unsigned int -elf_x86_amd64_map_reloc_info_to_type(elf_reloc_entry *reloc) -{ - if (reloc->wrt) { - const elf_machine_ssym *ssym = (elf_machine_ssym *) - yasm_symrec_get_data(reloc->wrt, &elf_ssym_symrec_data); - if (!ssym || reloc->valsize != ssym->size) - yasm_internal_error(N_("Unsupported WRT")); - - /* Force TLS type; this is required by the linker. */ - if (ssym->sym_rel & ELF_SSYM_THREAD_LOCAL) { - elf_symtab_entry *esym; - - esym = yasm_symrec_get_data(reloc->reloc.sym, &elf_symrec_data); - if (esym) - esym->type = STT_TLS; - } - /* Map PC-relative GOT to appropriate relocation */ - if (reloc->rtype_rel && ssym->reloc == R_X86_64_GOT32) - return (unsigned char) R_X86_64_GOTPCREL; - return (unsigned char) ssym->reloc; - } else if (reloc->is_GOT_sym && reloc->valsize == 32) { - return (unsigned char) R_X86_64_GOTPC32; - } else if (reloc->is_GOT_sym && reloc->valsize == 64) { - return (unsigned char) R_X86_64_GOTPC64; - } else if (reloc->rtype_rel) { - switch (reloc->valsize) { - case 8: return (unsigned char) R_X86_64_PC8; - case 16: return (unsigned char) R_X86_64_PC16; - case 32: return (unsigned char) R_X86_64_PC32; - case 64: return (unsigned char) R_X86_64_PC64; - default: yasm_internal_error(N_("Unsupported relocation size")); - } - } else { - switch (reloc->valsize) { - case 8: return (unsigned char) R_X86_64_8; - case 16: return (unsigned char) R_X86_64_16; - case 32: return (unsigned char) R_X86_64_32; - case 64: return (unsigned char) R_X86_64_64; - default: yasm_internal_error(N_("Unsupported relocation size")); - } - } - return 0; -} - -static void -elf_x86_amd64_write_reloc(unsigned char *bufp, elf_reloc_entry *reloc, - unsigned int r_type, unsigned int r_sym) -{ - YASM_WRITE_64I_L(bufp, reloc->reloc.addr); - /*YASM_WRITE_64_L(bufp, ELF64_R_INFO(r_sym, r_type));*/ - YASM_WRITE_64C_L(bufp, r_sym, r_type); - if (reloc->addend) - YASM_WRITE_64I_L(bufp, reloc->addend); - else { - YASM_WRITE_32_L(bufp, 0); - YASM_WRITE_32_L(bufp, 0); - } -} - -static void -elf_x86_amd64_write_proghead(unsigned char **bufpp, - elf_offset secthead_addr, - unsigned long secthead_count, - elf_section_index shstrtab_index) -{ - unsigned char *bufp = *bufpp; - unsigned char *buf = bufp-4; - YASM_WRITE_8(bufp, ELFCLASS64); /* elf class */ - YASM_WRITE_8(bufp, ELFDATA2LSB); /* data encoding :: MSB? */ - YASM_WRITE_8(bufp, EV_CURRENT); /* elf version */ - YASM_WRITE_8(bufp, ELFOSABI_SYSV); /* os/abi */ - YASM_WRITE_8(bufp, 0); /* SYSV v3 ABI=0 */ - while (bufp-buf < EI_NIDENT) /* e_ident padding */ - YASM_WRITE_8(bufp, 0); - - YASM_WRITE_16_L(bufp, ET_REL); /* e_type - object file */ - YASM_WRITE_16_L(bufp, EM_X86_64); /* e_machine - or others */ - YASM_WRITE_32_L(bufp, EV_CURRENT); /* elf version */ - YASM_WRITE_64Z_L(bufp, 0); /* e_entry */ - YASM_WRITE_64Z_L(bufp, 0); /* e_phoff */ - YASM_WRITE_64Z_L(bufp, secthead_addr); /* e_shoff secthead off */ - - YASM_WRITE_32_L(bufp, 0); /* e_flags */ - YASM_WRITE_16_L(bufp, EHDR64_SIZE); /* e_ehsize */ - YASM_WRITE_16_L(bufp, 0); /* e_phentsize */ - YASM_WRITE_16_L(bufp, 0); /* e_phnum */ - YASM_WRITE_16_L(bufp, SHDR64_SIZE); /* e_shentsize */ - YASM_WRITE_16_L(bufp, secthead_count); /* e_shnum */ - YASM_WRITE_16_L(bufp, shstrtab_index); /* e_shstrndx */ - *bufpp = bufp; -} - -const elf_machine_handler -elf_machine_handler_x86_amd64 = { - "x86", "amd64", ".rela", - SYMTAB64_SIZE, SYMTAB64_ALIGN, RELOC64A_SIZE, SHDR64_SIZE, EHDR64_SIZE, - elf_x86_amd64_accepts_reloc, - elf_x86_amd64_write_symtab_entry, - elf_x86_amd64_write_secthead, - elf_x86_amd64_write_secthead_rel, - elf_x86_amd64_handle_reloc_addend, - elf_x86_amd64_map_reloc_info_to_type, - elf_x86_amd64_write_reloc, - elf_x86_amd64_write_proghead, - elf_x86_amd64_ssyms, - sizeof(elf_x86_amd64_ssyms)/sizeof(elf_x86_amd64_ssyms[0]), - 64 -}; +/* + * ELF object format helpers - x86:amd64 + * + * Copyright (C) 2004-2007 Michael Urman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <util.h> + +#include <libyasm.h> +#define YASM_OBJFMT_ELF_INTERNAL +#include "elf.h" +#include "elf-machine.h" + +static elf_machine_ssym elf_x86_amd64_ssyms[] = { + {"pltoff", ELF_SSYM_SYM_RELATIVE, R_X86_64_PLTOFF64, 64}, + {"plt", ELF_SSYM_SYM_RELATIVE, R_X86_64_PLT32, 32}, + {"gotplt", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOTPLT64, 64}, + {"gotoff", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOTOFF64, 64}, + {"gotpcrel", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOTPCREL, 32}, + {"tlsgd", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_TLSGD, 32}, + {"tlsld", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_TLSLD, 32}, + {"gottpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_GOTTPOFF, 32}, + {"tpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_TPOFF32, 32}, + {"dtpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_DTPOFF32, 32}, + {"got", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOT32, 32}, + {"tlsdesc", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_GOTPC32_TLSDESC, 32}, + {"tlscall", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_TLSDESC_CALL, 32} +}; + +static int +elf_x86_amd64_accepts_reloc(size_t val, yasm_symrec *wrt) +{ + if (wrt) { + const elf_machine_ssym *ssym = (elf_machine_ssym *) + yasm_symrec_get_data(wrt, &elf_ssym_symrec_data); + if (!ssym || val != ssym->size) + return 0; + return 1; + } + return (val&(val-1)) ? 0 : ((val & (8|16|32|64)) != 0); +} + +static void +elf_x86_amd64_write_symtab_entry(unsigned char *bufp, + elf_symtab_entry *entry, + yasm_intnum *value_intn, + yasm_intnum *size_intn) +{ + YASM_WRITE_32_L(bufp, entry->name ? entry->name->index : 0); + YASM_WRITE_8(bufp, ELF64_ST_INFO(entry->bind, entry->type)); + YASM_WRITE_8(bufp, ELF64_ST_OTHER(entry->vis)); + if (entry->sect) { + elf_secthead *shead = + yasm_section_get_data(entry->sect, &elf_section_data); + if (!shead) + yasm_internal_error(N_("symbol references section without data")); + YASM_WRITE_16_L(bufp, shead->index); + } else { + YASM_WRITE_16_L(bufp, entry->index); + } + YASM_WRITE_64I_L(bufp, value_intn); + YASM_WRITE_64I_L(bufp, size_intn); +} + +static void +elf_x86_amd64_write_secthead(unsigned char *bufp, elf_secthead *shead) +{ + YASM_WRITE_32_L(bufp, shead->name ? shead->name->index : 0); + YASM_WRITE_32_L(bufp, shead->type); + YASM_WRITE_64Z_L(bufp, shead->flags); + YASM_WRITE_64Z_L(bufp, 0); /* vmem address */ + YASM_WRITE_64Z_L(bufp, shead->offset); + YASM_WRITE_64I_L(bufp, shead->size); + + YASM_WRITE_32_L(bufp, shead->link); + YASM_WRITE_32_L(bufp, shead->info); + + YASM_WRITE_64Z_L(bufp, shead->align); + YASM_WRITE_64Z_L(bufp, shead->entsize); +} + +static void +elf_x86_amd64_write_secthead_rel(unsigned char *bufp, + elf_secthead *shead, + elf_section_index symtab_idx, + elf_section_index sindex) +{ + yasm_intnum *nreloc; + yasm_intnum *relocsize; + + YASM_WRITE_32_L(bufp, shead->rel_name ? shead->rel_name->index : 0); + YASM_WRITE_32_L(bufp, SHT_RELA); + YASM_WRITE_64Z_L(bufp, 0); + YASM_WRITE_64Z_L(bufp, 0); + YASM_WRITE_64Z_L(bufp, shead->rel_offset); + + nreloc = yasm_intnum_create_uint(shead->nreloc); + relocsize = yasm_intnum_create_uint(RELOC64A_SIZE); + yasm_intnum_calc(relocsize, YASM_EXPR_MUL, nreloc); + YASM_WRITE_64I_L(bufp, relocsize); /* size */ + yasm_intnum_destroy(nreloc); + yasm_intnum_destroy(relocsize); + + YASM_WRITE_32_L(bufp, symtab_idx); /* link: symtab index */ + YASM_WRITE_32_L(bufp, shead->index); /* info: relocated's index */ + YASM_WRITE_64Z_L(bufp, RELOC64_ALIGN); /* align */ + YASM_WRITE_64Z_L(bufp, RELOC64A_SIZE); /* entity size */ +} + +static void +elf_x86_amd64_handle_reloc_addend(yasm_intnum *intn, + elf_reloc_entry *reloc, + unsigned long offset) +{ + /* .rela: copy value out as addend, replace original with 0 */ + reloc->addend = yasm_intnum_copy(intn); + yasm_intnum_zero(intn); +} + +static unsigned int +elf_x86_amd64_map_reloc_info_to_type(elf_reloc_entry *reloc) +{ + if (reloc->wrt) { + const elf_machine_ssym *ssym = (elf_machine_ssym *) + yasm_symrec_get_data(reloc->wrt, &elf_ssym_symrec_data); + if (!ssym || reloc->valsize != ssym->size) + yasm_internal_error(N_("Unsupported WRT")); + + /* Force TLS type; this is required by the linker. */ + if (ssym->sym_rel & ELF_SSYM_THREAD_LOCAL) { + elf_symtab_entry *esym; + + esym = yasm_symrec_get_data(reloc->reloc.sym, &elf_symrec_data); + if (esym) + esym->type = STT_TLS; + } + /* Map PC-relative GOT to appropriate relocation */ + if (reloc->rtype_rel && ssym->reloc == R_X86_64_GOT32) + return (unsigned char) R_X86_64_GOTPCREL; + return (unsigned char) ssym->reloc; + } else if (reloc->is_GOT_sym && reloc->valsize == 32) { + return (unsigned char) R_X86_64_GOTPC32; + } else if (reloc->is_GOT_sym && reloc->valsize == 64) { + return (unsigned char) R_X86_64_GOTPC64; + } else if (reloc->rtype_rel) { + switch (reloc->valsize) { + case 8: return (unsigned char) R_X86_64_PC8; + case 16: return (unsigned char) R_X86_64_PC16; + case 32: return (unsigned char) R_X86_64_PC32; + case 64: return (unsigned char) R_X86_64_PC64; + default: yasm_internal_error(N_("Unsupported relocation size")); + } + } else { + switch (reloc->valsize) { + case 8: return (unsigned char) R_X86_64_8; + case 16: return (unsigned char) R_X86_64_16; + case 32: return (unsigned char) R_X86_64_32; + case 64: return (unsigned char) R_X86_64_64; + default: yasm_internal_error(N_("Unsupported relocation size")); + } + } + return 0; +} + +static void +elf_x86_amd64_write_reloc(unsigned char *bufp, elf_reloc_entry *reloc, + unsigned int r_type, unsigned int r_sym) +{ + YASM_WRITE_64I_L(bufp, reloc->reloc.addr); + /*YASM_WRITE_64_L(bufp, ELF64_R_INFO(r_sym, r_type));*/ + YASM_WRITE_64C_L(bufp, r_sym, r_type); + if (reloc->addend) + YASM_WRITE_64I_L(bufp, reloc->addend); + else { + YASM_WRITE_32_L(bufp, 0); + YASM_WRITE_32_L(bufp, 0); + } +} + +static void +elf_x86_amd64_write_proghead(unsigned char **bufpp, + elf_offset secthead_addr, + unsigned long secthead_count, + elf_section_index shstrtab_index) +{ + unsigned char *bufp = *bufpp; + unsigned char *buf = bufp-4; + YASM_WRITE_8(bufp, ELFCLASS64); /* elf class */ + YASM_WRITE_8(bufp, ELFDATA2LSB); /* data encoding :: MSB? */ + YASM_WRITE_8(bufp, EV_CURRENT); /* elf version */ + YASM_WRITE_8(bufp, ELFOSABI_SYSV); /* os/abi */ + YASM_WRITE_8(bufp, 0); /* SYSV v3 ABI=0 */ + while (bufp-buf < EI_NIDENT) /* e_ident padding */ + YASM_WRITE_8(bufp, 0); + + YASM_WRITE_16_L(bufp, ET_REL); /* e_type - object file */ + YASM_WRITE_16_L(bufp, EM_X86_64); /* e_machine - or others */ + YASM_WRITE_32_L(bufp, EV_CURRENT); /* elf version */ + YASM_WRITE_64Z_L(bufp, 0); /* e_entry */ + YASM_WRITE_64Z_L(bufp, 0); /* e_phoff */ + YASM_WRITE_64Z_L(bufp, secthead_addr); /* e_shoff secthead off */ + + YASM_WRITE_32_L(bufp, 0); /* e_flags */ + YASM_WRITE_16_L(bufp, EHDR64_SIZE); /* e_ehsize */ + YASM_WRITE_16_L(bufp, 0); /* e_phentsize */ + YASM_WRITE_16_L(bufp, 0); /* e_phnum */ + YASM_WRITE_16_L(bufp, SHDR64_SIZE); /* e_shentsize */ + YASM_WRITE_16_L(bufp, secthead_count); /* e_shnum */ + YASM_WRITE_16_L(bufp, shstrtab_index); /* e_shstrndx */ + *bufpp = bufp; +} + +const elf_machine_handler +elf_machine_handler_x86_amd64 = { + "x86", "amd64", ".rela", + SYMTAB64_SIZE, SYMTAB64_ALIGN, RELOC64A_SIZE, SHDR64_SIZE, EHDR64_SIZE, + elf_x86_amd64_accepts_reloc, + elf_x86_amd64_write_symtab_entry, + elf_x86_amd64_write_secthead, + elf_x86_amd64_write_secthead_rel, + elf_x86_amd64_handle_reloc_addend, + elf_x86_amd64_map_reloc_info_to_type, + elf_x86_amd64_write_reloc, + elf_x86_amd64_write_proghead, + elf_x86_amd64_ssyms, + sizeof(elf_x86_amd64_ssyms)/sizeof(elf_x86_amd64_ssyms[0]), + 64 +}; diff --git a/contrib/tools/yasm/modules/objfmts/elf/elf-x86-x32.c b/contrib/tools/yasm/modules/objfmts/elf/elf-x86-x32.c index 40a25a12a7..aad2d10dac 100644 --- a/contrib/tools/yasm/modules/objfmts/elf/elf-x86-x32.c +++ b/contrib/tools/yasm/modules/objfmts/elf/elf-x86-x32.c @@ -1,251 +1,251 @@ -/* - * ELF object format helpers - x86:x32 - * - * Copyright (C) 2012 Michael Urman and H.J. Lu - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <util.h> - -#include <libyasm.h> -#define YASM_OBJFMT_ELF_INTERNAL -#include "elf.h" -#include "elf-machine.h" - -static elf_machine_ssym elf_x86_x32_ssyms[] = { - {"plt", ELF_SSYM_SYM_RELATIVE, R_X86_64_PLT32, 32}, - {"gotpcrel", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOTPCREL, 32}, - {"tlsgd", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_TLSGD, 32}, - {"tlsld", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_TLSLD, 32}, - {"gottpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_GOTTPOFF, 32}, - {"tpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_TPOFF32, 32}, - {"dtpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_DTPOFF32, 32}, - {"got", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOT32, 32}, - {"tlsdesc", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_GOTPC32_TLSDESC, 32}, - {"tlscall", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_X86_64_TLSDESC_CALL, 32} -}; - -static int -elf_x86_x32_accepts_reloc(size_t val, yasm_symrec *wrt) -{ - if (wrt) { - const elf_machine_ssym *ssym = (elf_machine_ssym *) - yasm_symrec_get_data(wrt, &elf_ssym_symrec_data); - if (!ssym || val != ssym->size) - return 0; - return 1; - } - return (val&(val-1)) ? 0 : ((val & (8|16|32)) != 0); -} - -static void -elf_x86_x32_write_symtab_entry(unsigned char *bufp, - elf_symtab_entry *entry, - yasm_intnum *value_intn, - yasm_intnum *size_intn) -{ - YASM_WRITE_32_L(bufp, entry->name ? entry->name->index : 0); - YASM_WRITE_32I_L(bufp, value_intn); - YASM_WRITE_32I_L(bufp, size_intn); - - YASM_WRITE_8(bufp, ELF32_ST_INFO(entry->bind, entry->type)); - YASM_WRITE_8(bufp, ELF32_ST_OTHER(entry->vis)); - if (entry->sect) { - elf_secthead *shead = - yasm_section_get_data(entry->sect, &elf_section_data); - if (!shead) - yasm_internal_error(N_("symbol references section without data")); - YASM_WRITE_16_L(bufp, shead->index); - } else { - YASM_WRITE_16_L(bufp, entry->index); - } -} - -static void -elf_x86_x32_write_secthead(unsigned char *bufp, elf_secthead *shead) -{ - YASM_WRITE_32_L(bufp, shead->name ? shead->name->index : 0); - YASM_WRITE_32_L(bufp, shead->type); - YASM_WRITE_32_L(bufp, shead->flags); - YASM_WRITE_32_L(bufp, 0); /* vmem address */ - YASM_WRITE_32_L(bufp, shead->offset); - YASM_WRITE_32I_L(bufp, shead->size); - - YASM_WRITE_32_L(bufp, shead->link); - YASM_WRITE_32_L(bufp, shead->info); - - YASM_WRITE_32_L(bufp, shead->align); - YASM_WRITE_32_L(bufp, shead->entsize); -} - -static void -elf_x86_x32_write_secthead_rel(unsigned char *bufp, - elf_secthead *shead, - elf_section_index symtab_idx, - elf_section_index sindex) -{ - yasm_intnum *nreloc; - yasm_intnum *relocsize; - - YASM_WRITE_32_L(bufp, shead->rel_name ? shead->rel_name->index : 0); - YASM_WRITE_32_L(bufp, SHT_RELA); - YASM_WRITE_32_L(bufp, 0); - YASM_WRITE_32_L(bufp, 0); - YASM_WRITE_32_L(bufp, shead->rel_offset); - - nreloc = yasm_intnum_create_uint(shead->nreloc); - relocsize = yasm_intnum_create_uint(RELOC32A_SIZE); - yasm_intnum_calc(relocsize, YASM_EXPR_MUL, nreloc); - YASM_WRITE_32I_L(bufp, relocsize); /* size */ - yasm_intnum_destroy(nreloc); - yasm_intnum_destroy(relocsize); - - YASM_WRITE_32_L(bufp, symtab_idx); /* link: symtab index */ - YASM_WRITE_32_L(bufp, shead->index); /* info: relocated's index */ - YASM_WRITE_32_L(bufp, RELOC32_ALIGN); /* align */ - YASM_WRITE_32_L(bufp, RELOC32A_SIZE); /* entity size */ -} - -static void -elf_x86_x32_handle_reloc_addend(yasm_intnum *intn, - elf_reloc_entry *reloc, - unsigned long offset) -{ - /* .rela: copy value out as addend, replace original with 0 */ - reloc->addend = yasm_intnum_copy(intn); - yasm_intnum_zero(intn); -} - -static unsigned int -elf_x86_x32_map_reloc_info_to_type(elf_reloc_entry *reloc) -{ - if (reloc->wrt) { - const elf_machine_ssym *ssym = (elf_machine_ssym *) - yasm_symrec_get_data(reloc->wrt, &elf_ssym_symrec_data); - if (!ssym || reloc->valsize != ssym->size) - yasm_internal_error(N_("Unsupported WRT")); - - /* Force TLS type; this is required by the linker. */ - if (ssym->sym_rel & ELF_SSYM_THREAD_LOCAL) { - elf_symtab_entry *esym; - - esym = yasm_symrec_get_data(reloc->reloc.sym, &elf_symrec_data); - if (esym) - esym->type = STT_TLS; - } - /* Map PC-relative GOT to appropriate relocation */ - if (reloc->rtype_rel && ssym->reloc == R_X86_64_GOT32) - return (unsigned char) R_X86_64_GOTPCREL; - return (unsigned char) ssym->reloc; - } else if (reloc->is_GOT_sym && reloc->valsize == 32) { - return (unsigned char) R_X86_64_GOTPC32; - } else if (reloc->is_GOT_sym && reloc->valsize == 64) { - yasm_internal_error(N_("Unsupported relocation size")); - } else if (reloc->rtype_rel) { - switch (reloc->valsize) { - case 8: return (unsigned char) R_X86_64_PC8; - case 16: return (unsigned char) R_X86_64_PC16; - case 32: return (unsigned char) R_X86_64_PC32; - default: yasm_internal_error(N_("Unsupported relocation size")); - } - } else { - switch (reloc->valsize) { - case 8: return (unsigned char) R_X86_64_8; - case 16: return (unsigned char) R_X86_64_16; - case 32: return (unsigned char) R_X86_64_32; - case 64: return (unsigned char) R_X86_64_64; - default: yasm_internal_error(N_("Unsupported relocation size")); - } - } - return 0; -} - -static void -elf_x86_x32_write_reloc(unsigned char *bufp, elf_reloc_entry *reloc, - unsigned int r_type, unsigned int r_sym) -{ - YASM_WRITE_32I_L(bufp, reloc->reloc.addr); - YASM_WRITE_32_L(bufp, ELF32_R_INFO((unsigned long)r_sym, (unsigned char)r_type)); - if (reloc->addend) - YASM_WRITE_32I_L(bufp, reloc->addend); - else { - YASM_WRITE_32_L(bufp, 0); - } -} - -static void -elf_x86_x32_write_proghead(unsigned char **bufpp, - elf_offset secthead_addr, - unsigned long secthead_count, - elf_section_index shstrtab_index) -{ - unsigned char *bufp = *bufpp; - unsigned char *buf = bufp-4; - YASM_WRITE_8(bufp, ELFCLASS32); /* elf class */ - YASM_WRITE_8(bufp, ELFDATA2LSB); /* data encoding :: MSB? */ - YASM_WRITE_8(bufp, EV_CURRENT); /* elf version */ - YASM_WRITE_8(bufp, ELFOSABI_SYSV); /* os/abi */ - YASM_WRITE_8(bufp, 0); /* SYSV v3 ABI=0 */ - while (bufp-buf < EI_NIDENT) /* e_ident padding */ - YASM_WRITE_8(bufp, 0); - - YASM_WRITE_16_L(bufp, ET_REL); /* e_type - object file */ - YASM_WRITE_16_L(bufp, EM_X86_64); /* e_machine - or others */ - YASM_WRITE_32_L(bufp, EV_CURRENT); /* elf version */ - YASM_WRITE_32_L(bufp, 0); /* e_entry */ - YASM_WRITE_32_L(bufp, 0); /* e_phoff */ - YASM_WRITE_32_L(bufp, secthead_addr); /* e_shoff secthead off */ - - YASM_WRITE_32_L(bufp, 0); /* e_flags */ - YASM_WRITE_16_L(bufp, EHDR32_SIZE); /* e_ehsize */ - YASM_WRITE_16_L(bufp, 0); /* e_phentsize */ - YASM_WRITE_16_L(bufp, 0); /* e_phnum */ - YASM_WRITE_16_L(bufp, SHDR32_SIZE); /* e_shentsize */ - YASM_WRITE_16_L(bufp, secthead_count); /* e_shnum */ - YASM_WRITE_16_L(bufp, shstrtab_index); /* e_shstrndx */ - *bufpp = bufp; -} - -const elf_machine_handler -elf_machine_handler_x86_x32 = { - "x86", "x32", ".rela", - SYMTAB32_SIZE, SYMTAB32_ALIGN, RELOC32A_SIZE, SHDR32_SIZE, EHDR32_SIZE, - elf_x86_x32_accepts_reloc, - elf_x86_x32_write_symtab_entry, - elf_x86_x32_write_secthead, - elf_x86_x32_write_secthead_rel, - elf_x86_x32_handle_reloc_addend, - elf_x86_x32_map_reloc_info_to_type, - elf_x86_x32_write_reloc, - elf_x86_x32_write_proghead, - elf_x86_x32_ssyms, - sizeof(elf_x86_x32_ssyms)/sizeof(elf_x86_x32_ssyms[0]), - 32 -}; +/* + * ELF object format helpers - x86:x32 + * + * Copyright (C) 2012 Michael Urman and H.J. Lu + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <util.h> + +#include <libyasm.h> +#define YASM_OBJFMT_ELF_INTERNAL +#include "elf.h" +#include "elf-machine.h" + +static elf_machine_ssym elf_x86_x32_ssyms[] = { + {"plt", ELF_SSYM_SYM_RELATIVE, R_X86_64_PLT32, 32}, + {"gotpcrel", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOTPCREL, 32}, + {"tlsgd", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_TLSGD, 32}, + {"tlsld", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_TLSLD, 32}, + {"gottpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_GOTTPOFF, 32}, + {"tpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_TPOFF32, 32}, + {"dtpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_DTPOFF32, 32}, + {"got", ELF_SSYM_SYM_RELATIVE, R_X86_64_GOT32, 32}, + {"tlsdesc", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_GOTPC32_TLSDESC, 32}, + {"tlscall", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_X86_64_TLSDESC_CALL, 32} +}; + +static int +elf_x86_x32_accepts_reloc(size_t val, yasm_symrec *wrt) +{ + if (wrt) { + const elf_machine_ssym *ssym = (elf_machine_ssym *) + yasm_symrec_get_data(wrt, &elf_ssym_symrec_data); + if (!ssym || val != ssym->size) + return 0; + return 1; + } + return (val&(val-1)) ? 0 : ((val & (8|16|32)) != 0); +} + +static void +elf_x86_x32_write_symtab_entry(unsigned char *bufp, + elf_symtab_entry *entry, + yasm_intnum *value_intn, + yasm_intnum *size_intn) +{ + YASM_WRITE_32_L(bufp, entry->name ? entry->name->index : 0); + YASM_WRITE_32I_L(bufp, value_intn); + YASM_WRITE_32I_L(bufp, size_intn); + + YASM_WRITE_8(bufp, ELF32_ST_INFO(entry->bind, entry->type)); + YASM_WRITE_8(bufp, ELF32_ST_OTHER(entry->vis)); + if (entry->sect) { + elf_secthead *shead = + yasm_section_get_data(entry->sect, &elf_section_data); + if (!shead) + yasm_internal_error(N_("symbol references section without data")); + YASM_WRITE_16_L(bufp, shead->index); + } else { + YASM_WRITE_16_L(bufp, entry->index); + } +} + +static void +elf_x86_x32_write_secthead(unsigned char *bufp, elf_secthead *shead) +{ + YASM_WRITE_32_L(bufp, shead->name ? shead->name->index : 0); + YASM_WRITE_32_L(bufp, shead->type); + YASM_WRITE_32_L(bufp, shead->flags); + YASM_WRITE_32_L(bufp, 0); /* vmem address */ + YASM_WRITE_32_L(bufp, shead->offset); + YASM_WRITE_32I_L(bufp, shead->size); + + YASM_WRITE_32_L(bufp, shead->link); + YASM_WRITE_32_L(bufp, shead->info); + + YASM_WRITE_32_L(bufp, shead->align); + YASM_WRITE_32_L(bufp, shead->entsize); +} + +static void +elf_x86_x32_write_secthead_rel(unsigned char *bufp, + elf_secthead *shead, + elf_section_index symtab_idx, + elf_section_index sindex) +{ + yasm_intnum *nreloc; + yasm_intnum *relocsize; + + YASM_WRITE_32_L(bufp, shead->rel_name ? shead->rel_name->index : 0); + YASM_WRITE_32_L(bufp, SHT_RELA); + YASM_WRITE_32_L(bufp, 0); + YASM_WRITE_32_L(bufp, 0); + YASM_WRITE_32_L(bufp, shead->rel_offset); + + nreloc = yasm_intnum_create_uint(shead->nreloc); + relocsize = yasm_intnum_create_uint(RELOC32A_SIZE); + yasm_intnum_calc(relocsize, YASM_EXPR_MUL, nreloc); + YASM_WRITE_32I_L(bufp, relocsize); /* size */ + yasm_intnum_destroy(nreloc); + yasm_intnum_destroy(relocsize); + + YASM_WRITE_32_L(bufp, symtab_idx); /* link: symtab index */ + YASM_WRITE_32_L(bufp, shead->index); /* info: relocated's index */ + YASM_WRITE_32_L(bufp, RELOC32_ALIGN); /* align */ + YASM_WRITE_32_L(bufp, RELOC32A_SIZE); /* entity size */ +} + +static void +elf_x86_x32_handle_reloc_addend(yasm_intnum *intn, + elf_reloc_entry *reloc, + unsigned long offset) +{ + /* .rela: copy value out as addend, replace original with 0 */ + reloc->addend = yasm_intnum_copy(intn); + yasm_intnum_zero(intn); +} + +static unsigned int +elf_x86_x32_map_reloc_info_to_type(elf_reloc_entry *reloc) +{ + if (reloc->wrt) { + const elf_machine_ssym *ssym = (elf_machine_ssym *) + yasm_symrec_get_data(reloc->wrt, &elf_ssym_symrec_data); + if (!ssym || reloc->valsize != ssym->size) + yasm_internal_error(N_("Unsupported WRT")); + + /* Force TLS type; this is required by the linker. */ + if (ssym->sym_rel & ELF_SSYM_THREAD_LOCAL) { + elf_symtab_entry *esym; + + esym = yasm_symrec_get_data(reloc->reloc.sym, &elf_symrec_data); + if (esym) + esym->type = STT_TLS; + } + /* Map PC-relative GOT to appropriate relocation */ + if (reloc->rtype_rel && ssym->reloc == R_X86_64_GOT32) + return (unsigned char) R_X86_64_GOTPCREL; + return (unsigned char) ssym->reloc; + } else if (reloc->is_GOT_sym && reloc->valsize == 32) { + return (unsigned char) R_X86_64_GOTPC32; + } else if (reloc->is_GOT_sym && reloc->valsize == 64) { + yasm_internal_error(N_("Unsupported relocation size")); + } else if (reloc->rtype_rel) { + switch (reloc->valsize) { + case 8: return (unsigned char) R_X86_64_PC8; + case 16: return (unsigned char) R_X86_64_PC16; + case 32: return (unsigned char) R_X86_64_PC32; + default: yasm_internal_error(N_("Unsupported relocation size")); + } + } else { + switch (reloc->valsize) { + case 8: return (unsigned char) R_X86_64_8; + case 16: return (unsigned char) R_X86_64_16; + case 32: return (unsigned char) R_X86_64_32; + case 64: return (unsigned char) R_X86_64_64; + default: yasm_internal_error(N_("Unsupported relocation size")); + } + } + return 0; +} + +static void +elf_x86_x32_write_reloc(unsigned char *bufp, elf_reloc_entry *reloc, + unsigned int r_type, unsigned int r_sym) +{ + YASM_WRITE_32I_L(bufp, reloc->reloc.addr); + YASM_WRITE_32_L(bufp, ELF32_R_INFO((unsigned long)r_sym, (unsigned char)r_type)); + if (reloc->addend) + YASM_WRITE_32I_L(bufp, reloc->addend); + else { + YASM_WRITE_32_L(bufp, 0); + } +} + +static void +elf_x86_x32_write_proghead(unsigned char **bufpp, + elf_offset secthead_addr, + unsigned long secthead_count, + elf_section_index shstrtab_index) +{ + unsigned char *bufp = *bufpp; + unsigned char *buf = bufp-4; + YASM_WRITE_8(bufp, ELFCLASS32); /* elf class */ + YASM_WRITE_8(bufp, ELFDATA2LSB); /* data encoding :: MSB? */ + YASM_WRITE_8(bufp, EV_CURRENT); /* elf version */ + YASM_WRITE_8(bufp, ELFOSABI_SYSV); /* os/abi */ + YASM_WRITE_8(bufp, 0); /* SYSV v3 ABI=0 */ + while (bufp-buf < EI_NIDENT) /* e_ident padding */ + YASM_WRITE_8(bufp, 0); + + YASM_WRITE_16_L(bufp, ET_REL); /* e_type - object file */ + YASM_WRITE_16_L(bufp, EM_X86_64); /* e_machine - or others */ + YASM_WRITE_32_L(bufp, EV_CURRENT); /* elf version */ + YASM_WRITE_32_L(bufp, 0); /* e_entry */ + YASM_WRITE_32_L(bufp, 0); /* e_phoff */ + YASM_WRITE_32_L(bufp, secthead_addr); /* e_shoff secthead off */ + + YASM_WRITE_32_L(bufp, 0); /* e_flags */ + YASM_WRITE_16_L(bufp, EHDR32_SIZE); /* e_ehsize */ + YASM_WRITE_16_L(bufp, 0); /* e_phentsize */ + YASM_WRITE_16_L(bufp, 0); /* e_phnum */ + YASM_WRITE_16_L(bufp, SHDR32_SIZE); /* e_shentsize */ + YASM_WRITE_16_L(bufp, secthead_count); /* e_shnum */ + YASM_WRITE_16_L(bufp, shstrtab_index); /* e_shstrndx */ + *bufpp = bufp; +} + +const elf_machine_handler +elf_machine_handler_x86_x32 = { + "x86", "x32", ".rela", + SYMTAB32_SIZE, SYMTAB32_ALIGN, RELOC32A_SIZE, SHDR32_SIZE, EHDR32_SIZE, + elf_x86_x32_accepts_reloc, + elf_x86_x32_write_symtab_entry, + elf_x86_x32_write_secthead, + elf_x86_x32_write_secthead_rel, + elf_x86_x32_handle_reloc_addend, + elf_x86_x32_map_reloc_info_to_type, + elf_x86_x32_write_reloc, + elf_x86_x32_write_proghead, + elf_x86_x32_ssyms, + sizeof(elf_x86_x32_ssyms)/sizeof(elf_x86_x32_ssyms[0]), + 32 +}; diff --git a/contrib/tools/yasm/modules/objfmts/elf/elf-x86-x86.c b/contrib/tools/yasm/modules/objfmts/elf/elf-x86-x86.c index d79ec6eabd..8ebaa87381 100644 --- a/contrib/tools/yasm/modules/objfmts/elf/elf-x86-x86.c +++ b/contrib/tools/yasm/modules/objfmts/elf/elf-x86-x86.c @@ -1,242 +1,242 @@ -/* - * ELF object format helpers - x86:x86 - * - * Copyright (C) 2004-2007 Michael Urman - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <util.h> - -#include <libyasm.h> -#define YASM_OBJFMT_ELF_INTERNAL -#include "elf.h" -#include "elf-machine.h" - -static elf_machine_ssym elf_x86_x86_ssyms[] = { - {"plt", ELF_SSYM_SYM_RELATIVE, R_386_PLT32, 32}, - {"gotoff", 0, R_386_GOTOFF, 32}, - /* special one for NASM */ - {"gotpc", ELF_SSYM_CURPOS_ADJUST, R_386_GOTPC, 32}, - {"tlsgd", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_GD, 32}, - {"tlsldm", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_LDM, 32}, - {"gottpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_IE_32, 32}, - {"tpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_LE_32, 32}, - {"ntpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_LE, 32}, - {"dtpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_LDO_32, 32}, - {"gotntpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_GOTIE, 32}, - {"indntpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_IE, 32}, - {"got", ELF_SSYM_SYM_RELATIVE, R_386_GOT32, 32}, - {"tlsdesc", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_GOTDESC, 32}, - {"tlscall", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, - R_386_TLS_DESC_CALL, 32} -}; - -static int -elf_x86_x86_accepts_reloc(size_t val, yasm_symrec *wrt) -{ - if (wrt) { - const elf_machine_ssym *ssym = (elf_machine_ssym *) - yasm_symrec_get_data(wrt, &elf_ssym_symrec_data); - if (!ssym || val != ssym->size) - return 0; - return 1; - } - return (val&(val-1)) ? 0 : ((val & (8|16|32)) != 0); -} - -static void -elf_x86_x86_write_symtab_entry(unsigned char *bufp, - elf_symtab_entry *entry, - yasm_intnum *value_intn, - yasm_intnum *size_intn) -{ - YASM_WRITE_32_L(bufp, entry->name ? entry->name->index : 0); - YASM_WRITE_32I_L(bufp, value_intn); - YASM_WRITE_32I_L(bufp, size_intn); - - YASM_WRITE_8(bufp, ELF32_ST_INFO(entry->bind, entry->type)); - YASM_WRITE_8(bufp, ELF32_ST_OTHER(entry->vis)); - if (entry->sect) { - elf_secthead *shead = - yasm_section_get_data(entry->sect, &elf_section_data); - if (!shead) - yasm_internal_error(N_("symbol references section without data")); - YASM_WRITE_16_L(bufp, shead->index); - } else { - YASM_WRITE_16_L(bufp, entry->index); - } -} - -static void -elf_x86_x86_write_secthead(unsigned char *bufp, elf_secthead *shead) -{ - YASM_WRITE_32_L(bufp, shead->name ? shead->name->index : 0); - YASM_WRITE_32_L(bufp, shead->type); - YASM_WRITE_32_L(bufp, shead->flags); - YASM_WRITE_32_L(bufp, 0); /* vmem address */ - - YASM_WRITE_32_L(bufp, shead->offset); - YASM_WRITE_32I_L(bufp, shead->size); - YASM_WRITE_32_L(bufp, shead->link); - YASM_WRITE_32_L(bufp, shead->info); - - YASM_WRITE_32_L(bufp, shead->align); - YASM_WRITE_32_L(bufp, shead->entsize); - -} - -static void -elf_x86_x86_write_secthead_rel(unsigned char *bufp, - elf_secthead *shead, - elf_section_index symtab_idx, - elf_section_index sindex) -{ - YASM_WRITE_32_L(bufp, shead->rel_name ? shead->rel_name->index : 0); - YASM_WRITE_32_L(bufp, SHT_REL); - YASM_WRITE_32_L(bufp, 0); - YASM_WRITE_32_L(bufp, 0); - - YASM_WRITE_32_L(bufp, shead->rel_offset); - YASM_WRITE_32_L(bufp, RELOC32_SIZE * shead->nreloc);/* size */ - YASM_WRITE_32_L(bufp, symtab_idx); /* link: symtab index */ - YASM_WRITE_32_L(bufp, shead->index); /* info: relocated's index */ - - YASM_WRITE_32_L(bufp, RELOC32_ALIGN); /* align */ - YASM_WRITE_32_L(bufp, RELOC32_SIZE); /* entity size */ -} - -static void -elf_x86_x86_handle_reloc_addend(yasm_intnum *intn, - elf_reloc_entry *reloc, - unsigned long offset) -{ - if (!reloc->wrt && reloc->is_GOT_sym && reloc->valsize == 32 && offset != 0) - { - yasm_intnum *off_intn = yasm_intnum_create_uint(offset); - yasm_intnum_calc(intn, YASM_EXPR_ADD, off_intn); - yasm_intnum_destroy(off_intn); - } - return; /* .rel: Leave addend in intn */ -} - -static unsigned int -elf_x86_x86_map_reloc_info_to_type(elf_reloc_entry *reloc) -{ - if (reloc->wrt) { - const elf_machine_ssym *ssym = (elf_machine_ssym *) - yasm_symrec_get_data(reloc->wrt, &elf_ssym_symrec_data); - if (!ssym || reloc->valsize != ssym->size) - yasm_internal_error(N_("Unsupported WRT")); - - /* Force TLS type; this is required by the linker. */ - if (ssym->sym_rel & ELF_SSYM_THREAD_LOCAL) { - elf_symtab_entry *esym; - - esym = yasm_symrec_get_data(reloc->reloc.sym, &elf_symrec_data); - if (esym) - esym->type = STT_TLS; - } - return (unsigned char) ssym->reloc; - } else if (reloc->is_GOT_sym && reloc->valsize == 32) { - return (unsigned char) R_386_GOTPC; - } else if (reloc->rtype_rel) { - switch (reloc->valsize) { - case 8: return (unsigned char) R_386_PC8; - case 16: return (unsigned char) R_386_PC16; - case 32: return (unsigned char) R_386_PC32; - default: yasm_internal_error(N_("Unsupported relocation size")); - } - } else { - switch (reloc->valsize) { - case 8: return (unsigned char) R_386_8; - case 16: return (unsigned char) R_386_16; - case 32: return (unsigned char) R_386_32; - default: yasm_internal_error(N_("Unsupported relocation size")); - } - } - return 0; -} - -static void -elf_x86_x86_write_reloc(unsigned char *bufp, elf_reloc_entry *reloc, - unsigned int r_type, unsigned int r_sym) -{ - YASM_WRITE_32I_L(bufp, reloc->reloc.addr); - YASM_WRITE_32_L(bufp, ELF32_R_INFO((unsigned long)r_sym, (unsigned char)r_type)); -} - -static void -elf_x86_x86_write_proghead(unsigned char **bufpp, - elf_offset secthead_addr, - unsigned long secthead_count, - elf_section_index shstrtab_index) -{ - unsigned char *bufp = *bufpp; - unsigned char *buf = bufp-4; - YASM_WRITE_8(bufp, ELFCLASS32); /* elf class */ - YASM_WRITE_8(bufp, ELFDATA2LSB); /* data encoding :: MSB? */ - YASM_WRITE_8(bufp, EV_CURRENT); /* elf version */ - while (bufp-buf < EI_NIDENT) /* e_ident padding */ - YASM_WRITE_8(bufp, 0); - - YASM_WRITE_16_L(bufp, ET_REL); /* e_type - object file */ - YASM_WRITE_16_L(bufp, EM_386); /* e_machine - or others */ - YASM_WRITE_32_L(bufp, EV_CURRENT); /* elf version */ - YASM_WRITE_32_L(bufp, 0); /* e_entry exection startaddr */ - YASM_WRITE_32_L(bufp, 0); /* e_phoff program header off */ - YASM_WRITE_32_L(bufp, secthead_addr); /* e_shoff section header off */ - YASM_WRITE_32_L(bufp, 0); /* e_flags also by arch */ - YASM_WRITE_16_L(bufp, EHDR32_SIZE); /* e_ehsize */ - YASM_WRITE_16_L(bufp, 0); /* e_phentsize */ - YASM_WRITE_16_L(bufp, 0); /* e_phnum */ - YASM_WRITE_16_L(bufp, SHDR32_SIZE); /* e_shentsize */ - YASM_WRITE_16_L(bufp, secthead_count); /* e_shnum */ - YASM_WRITE_16_L(bufp, shstrtab_index); /* e_shstrndx */ - *bufpp = bufp; -} - -const elf_machine_handler -elf_machine_handler_x86_x86 = { - "x86", "x86", ".rel", - SYMTAB32_SIZE, SYMTAB32_ALIGN, RELOC32_SIZE, SHDR32_SIZE, EHDR32_SIZE, - elf_x86_x86_accepts_reloc, - elf_x86_x86_write_symtab_entry, - elf_x86_x86_write_secthead, - elf_x86_x86_write_secthead_rel, - elf_x86_x86_handle_reloc_addend, - elf_x86_x86_map_reloc_info_to_type, - elf_x86_x86_write_reloc, - elf_x86_x86_write_proghead, - elf_x86_x86_ssyms, - sizeof(elf_x86_x86_ssyms)/sizeof(elf_x86_x86_ssyms[0]), - 32 -}; +/* + * ELF object format helpers - x86:x86 + * + * Copyright (C) 2004-2007 Michael Urman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <util.h> + +#include <libyasm.h> +#define YASM_OBJFMT_ELF_INTERNAL +#include "elf.h" +#include "elf-machine.h" + +static elf_machine_ssym elf_x86_x86_ssyms[] = { + {"plt", ELF_SSYM_SYM_RELATIVE, R_386_PLT32, 32}, + {"gotoff", 0, R_386_GOTOFF, 32}, + /* special one for NASM */ + {"gotpc", ELF_SSYM_CURPOS_ADJUST, R_386_GOTPC, 32}, + {"tlsgd", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_GD, 32}, + {"tlsldm", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_LDM, 32}, + {"gottpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_IE_32, 32}, + {"tpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_LE_32, 32}, + {"ntpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_LE, 32}, + {"dtpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_LDO_32, 32}, + {"gotntpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_GOTIE, 32}, + {"indntpoff", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_IE, 32}, + {"got", ELF_SSYM_SYM_RELATIVE, R_386_GOT32, 32}, + {"tlsdesc", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_GOTDESC, 32}, + {"tlscall", ELF_SSYM_SYM_RELATIVE|ELF_SSYM_THREAD_LOCAL, + R_386_TLS_DESC_CALL, 32} +}; + +static int +elf_x86_x86_accepts_reloc(size_t val, yasm_symrec *wrt) +{ + if (wrt) { + const elf_machine_ssym *ssym = (elf_machine_ssym *) + yasm_symrec_get_data(wrt, &elf_ssym_symrec_data); + if (!ssym || val != ssym->size) + return 0; + return 1; + } + return (val&(val-1)) ? 0 : ((val & (8|16|32)) != 0); +} + +static void +elf_x86_x86_write_symtab_entry(unsigned char *bufp, + elf_symtab_entry *entry, + yasm_intnum *value_intn, + yasm_intnum *size_intn) +{ + YASM_WRITE_32_L(bufp, entry->name ? entry->name->index : 0); + YASM_WRITE_32I_L(bufp, value_intn); + YASM_WRITE_32I_L(bufp, size_intn); + + YASM_WRITE_8(bufp, ELF32_ST_INFO(entry->bind, entry->type)); + YASM_WRITE_8(bufp, ELF32_ST_OTHER(entry->vis)); + if (entry->sect) { + elf_secthead *shead = + yasm_section_get_data(entry->sect, &elf_section_data); + if (!shead) + yasm_internal_error(N_("symbol references section without data")); + YASM_WRITE_16_L(bufp, shead->index); + } else { + YASM_WRITE_16_L(bufp, entry->index); + } +} + +static void +elf_x86_x86_write_secthead(unsigned char *bufp, elf_secthead *shead) +{ + YASM_WRITE_32_L(bufp, shead->name ? shead->name->index : 0); + YASM_WRITE_32_L(bufp, shead->type); + YASM_WRITE_32_L(bufp, shead->flags); + YASM_WRITE_32_L(bufp, 0); /* vmem address */ + + YASM_WRITE_32_L(bufp, shead->offset); + YASM_WRITE_32I_L(bufp, shead->size); + YASM_WRITE_32_L(bufp, shead->link); + YASM_WRITE_32_L(bufp, shead->info); + + YASM_WRITE_32_L(bufp, shead->align); + YASM_WRITE_32_L(bufp, shead->entsize); + +} + +static void +elf_x86_x86_write_secthead_rel(unsigned char *bufp, + elf_secthead *shead, + elf_section_index symtab_idx, + elf_section_index sindex) +{ + YASM_WRITE_32_L(bufp, shead->rel_name ? shead->rel_name->index : 0); + YASM_WRITE_32_L(bufp, SHT_REL); + YASM_WRITE_32_L(bufp, 0); + YASM_WRITE_32_L(bufp, 0); + + YASM_WRITE_32_L(bufp, shead->rel_offset); + YASM_WRITE_32_L(bufp, RELOC32_SIZE * shead->nreloc);/* size */ + YASM_WRITE_32_L(bufp, symtab_idx); /* link: symtab index */ + YASM_WRITE_32_L(bufp, shead->index); /* info: relocated's index */ + + YASM_WRITE_32_L(bufp, RELOC32_ALIGN); /* align */ + YASM_WRITE_32_L(bufp, RELOC32_SIZE); /* entity size */ +} + +static void +elf_x86_x86_handle_reloc_addend(yasm_intnum *intn, + elf_reloc_entry *reloc, + unsigned long offset) +{ + if (!reloc->wrt && reloc->is_GOT_sym && reloc->valsize == 32 && offset != 0) + { + yasm_intnum *off_intn = yasm_intnum_create_uint(offset); + yasm_intnum_calc(intn, YASM_EXPR_ADD, off_intn); + yasm_intnum_destroy(off_intn); + } + return; /* .rel: Leave addend in intn */ +} + +static unsigned int +elf_x86_x86_map_reloc_info_to_type(elf_reloc_entry *reloc) +{ + if (reloc->wrt) { + const elf_machine_ssym *ssym = (elf_machine_ssym *) + yasm_symrec_get_data(reloc->wrt, &elf_ssym_symrec_data); + if (!ssym || reloc->valsize != ssym->size) + yasm_internal_error(N_("Unsupported WRT")); + + /* Force TLS type; this is required by the linker. */ + if (ssym->sym_rel & ELF_SSYM_THREAD_LOCAL) { + elf_symtab_entry *esym; + + esym = yasm_symrec_get_data(reloc->reloc.sym, &elf_symrec_data); + if (esym) + esym->type = STT_TLS; + } + return (unsigned char) ssym->reloc; + } else if (reloc->is_GOT_sym && reloc->valsize == 32) { + return (unsigned char) R_386_GOTPC; + } else if (reloc->rtype_rel) { + switch (reloc->valsize) { + case 8: return (unsigned char) R_386_PC8; + case 16: return (unsigned char) R_386_PC16; + case 32: return (unsigned char) R_386_PC32; + default: yasm_internal_error(N_("Unsupported relocation size")); + } + } else { + switch (reloc->valsize) { + case 8: return (unsigned char) R_386_8; + case 16: return (unsigned char) R_386_16; + case 32: return (unsigned char) R_386_32; + default: yasm_internal_error(N_("Unsupported relocation size")); + } + } + return 0; +} + +static void +elf_x86_x86_write_reloc(unsigned char *bufp, elf_reloc_entry *reloc, + unsigned int r_type, unsigned int r_sym) +{ + YASM_WRITE_32I_L(bufp, reloc->reloc.addr); + YASM_WRITE_32_L(bufp, ELF32_R_INFO((unsigned long)r_sym, (unsigned char)r_type)); +} + +static void +elf_x86_x86_write_proghead(unsigned char **bufpp, + elf_offset secthead_addr, + unsigned long secthead_count, + elf_section_index shstrtab_index) +{ + unsigned char *bufp = *bufpp; + unsigned char *buf = bufp-4; + YASM_WRITE_8(bufp, ELFCLASS32); /* elf class */ + YASM_WRITE_8(bufp, ELFDATA2LSB); /* data encoding :: MSB? */ + YASM_WRITE_8(bufp, EV_CURRENT); /* elf version */ + while (bufp-buf < EI_NIDENT) /* e_ident padding */ + YASM_WRITE_8(bufp, 0); + + YASM_WRITE_16_L(bufp, ET_REL); /* e_type - object file */ + YASM_WRITE_16_L(bufp, EM_386); /* e_machine - or others */ + YASM_WRITE_32_L(bufp, EV_CURRENT); /* elf version */ + YASM_WRITE_32_L(bufp, 0); /* e_entry exection startaddr */ + YASM_WRITE_32_L(bufp, 0); /* e_phoff program header off */ + YASM_WRITE_32_L(bufp, secthead_addr); /* e_shoff section header off */ + YASM_WRITE_32_L(bufp, 0); /* e_flags also by arch */ + YASM_WRITE_16_L(bufp, EHDR32_SIZE); /* e_ehsize */ + YASM_WRITE_16_L(bufp, 0); /* e_phentsize */ + YASM_WRITE_16_L(bufp, 0); /* e_phnum */ + YASM_WRITE_16_L(bufp, SHDR32_SIZE); /* e_shentsize */ + YASM_WRITE_16_L(bufp, secthead_count); /* e_shnum */ + YASM_WRITE_16_L(bufp, shstrtab_index); /* e_shstrndx */ + *bufpp = bufp; +} + +const elf_machine_handler +elf_machine_handler_x86_x86 = { + "x86", "x86", ".rel", + SYMTAB32_SIZE, SYMTAB32_ALIGN, RELOC32_SIZE, SHDR32_SIZE, EHDR32_SIZE, + elf_x86_x86_accepts_reloc, + elf_x86_x86_write_symtab_entry, + elf_x86_x86_write_secthead, + elf_x86_x86_write_secthead_rel, + elf_x86_x86_handle_reloc_addend, + elf_x86_x86_map_reloc_info_to_type, + elf_x86_x86_write_reloc, + elf_x86_x86_write_proghead, + elf_x86_x86_ssyms, + sizeof(elf_x86_x86_ssyms)/sizeof(elf_x86_x86_ssyms[0]), + 32 +}; diff --git a/contrib/tools/yasm/modules/objfmts/elf/elf.c b/contrib/tools/yasm/modules/objfmts/elf/elf.c index 2486bba8df..04f3fe661a 100644 --- a/contrib/tools/yasm/modules/objfmts/elf/elf.c +++ b/contrib/tools/yasm/modules/objfmts/elf/elf.c @@ -1,960 +1,960 @@ -/* - * ELF object format helpers - * - * Copyright (C) 2003-2007 Michael Urman - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <util.h> - -#include <libyasm.h> -#define YASM_OBJFMT_ELF_INTERNAL -#include "elf.h" -#include "elf-machine.h" - -static void elf_section_data_destroy(void *data); -static void elf_secthead_print(void *data, FILE *f, int indent_level); - -const yasm_assoc_data_callback elf_section_data = { - elf_section_data_destroy, - elf_secthead_print -}; - -static void elf_symrec_data_destroy(/*@only@*/ void *d); -static void elf_symtab_entry_print(void *data, FILE *f, int indent_level); -static void elf_ssym_symtab_entry_print(void *data, FILE *f, int indent_level); - -const yasm_assoc_data_callback elf_symrec_data = { - elf_symrec_data_destroy, - elf_symtab_entry_print -}; - -const yasm_assoc_data_callback elf_ssym_symrec_data = { - elf_symrec_data_destroy, - elf_ssym_symtab_entry_print -}; - -extern elf_machine_handler - elf_machine_handler_x86_x86, - elf_machine_handler_x86_amd64, - elf_machine_handler_x86_x32; - -static const elf_machine_handler *elf_machine_handlers[] = -{ - &elf_machine_handler_x86_x86, - &elf_machine_handler_x86_amd64, - &elf_machine_handler_x86_x32, - NULL -}; -static const elf_machine_handler elf_null_machine = {0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0}; -static elf_machine_handler const *elf_march = &elf_null_machine; -static yasm_symrec **elf_ssyms; - -const elf_machine_handler * -elf_set_arch(yasm_arch *arch, yasm_symtab *symtab, int bits_pref) -{ - const char *machine = yasm_arch_get_machine(arch); - int i; - - for (i=0, elf_march = elf_machine_handlers[0]; - elf_march != NULL; - elf_march = elf_machine_handlers[++i]) - { - if (yasm__strcasecmp(yasm_arch_keyword(arch), elf_march->arch)==0) { - if (yasm__strcasecmp(machine, elf_march->machine)==0) { - if (bits_pref == 0 || bits_pref == elf_march->bits) - break; - } else if (bits_pref == elf_march->bits - && yasm__strcasecmp(machine, "amd64") == 0 - && yasm__strcasecmp(elf_march->machine, "x32") == 0) - break; - } - } - - if (elf_march && elf_march->num_ssyms > 0) - { - /* Allocate "special" syms */ - elf_ssyms = - yasm_xmalloc(elf_march->num_ssyms * sizeof(yasm_symrec *)); - for (i=0; (unsigned int)i<elf_march->num_ssyms; i++) - { - /* FIXME: misuse of NULL bytecode */ - elf_ssyms[i] = yasm_symtab_define_label(symtab, - elf_march->ssyms[i].name, - NULL, 0, 0); - yasm_symrec_add_data(elf_ssyms[i], &elf_ssym_symrec_data, - (void*)&elf_march->ssyms[i]); - } - } - - return elf_march; -} - -yasm_symrec * -elf_get_special_sym(const char *name, const char *parser) -{ - int i; - for (i=0; (unsigned int)i<elf_march->num_ssyms; i++) { - if (yasm__strcasecmp(name, elf_march->ssyms[i].name) == 0) - return elf_ssyms[i]; - } - return NULL; -} - -/* reloc functions */ -int elf_ssym_has_flag(yasm_symrec *wrt, int flag); - -int -elf_is_wrt_sym_relative(yasm_symrec *wrt) -{ - return elf_ssym_has_flag(wrt, ELF_SSYM_SYM_RELATIVE); -} - -int -elf_is_wrt_pos_adjusted(yasm_symrec *wrt) -{ - return elf_ssym_has_flag(wrt, ELF_SSYM_CURPOS_ADJUST); -} - -int -elf_ssym_has_flag(yasm_symrec *wrt, int flag) -{ - int i; - for (i=0; (unsigned int)i<elf_march->num_ssyms; i++) { - if (elf_ssyms[i] == wrt) - return (elf_march->ssyms[i].sym_rel & flag) != 0; - } - return 0; -} - -/* takes ownership of addr */ -elf_reloc_entry * -elf_reloc_entry_create(yasm_symrec *sym, - yasm_symrec *wrt, - yasm_intnum *addr, - int rel, - size_t valsize, - int is_GOT_sym) -{ - elf_reloc_entry *entry; - - if (!elf_march->accepts_reloc) - yasm_internal_error(N_("Unsupported machine for ELF output")); - - if (!elf_march->accepts_reloc(valsize, wrt)) - { - if (addr) - yasm_intnum_destroy(addr); - return NULL; - } - - if (sym == NULL) - yasm_internal_error("sym is null"); - - entry = yasm_xmalloc(sizeof(elf_reloc_entry)); - entry->reloc.sym = sym; - entry->reloc.addr = addr; - entry->rtype_rel = rel; - entry->valsize = valsize; - entry->addend = NULL; - entry->wrt = wrt; - entry->is_GOT_sym = is_GOT_sym; - - return entry; -} - -void -elf_reloc_entry_destroy(void *entry) -{ - if (((elf_reloc_entry*)entry)->addend) - yasm_intnum_destroy(((elf_reloc_entry*)entry)->addend); - yasm_xfree(entry); -} - -/* strtab functions */ -elf_strtab_entry * -elf_strtab_entry_create(const char *str) -{ - elf_strtab_entry *entry = yasm_xmalloc(sizeof(elf_strtab_entry)); - entry->str = yasm__xstrdup(str); - entry->index = 0; - return entry; -} - -void -elf_strtab_entry_set_str(elf_strtab_entry *entry, const char *str) -{ - elf_strtab_entry *last; - if (entry->str) - yasm_xfree(entry->str); - entry->str = yasm__xstrdup(str); - - /* Update all following indices since string length probably changes */ - last = entry; - entry = STAILQ_NEXT(last, qlink); - while (entry) { - entry->index = last->index + (unsigned long)strlen(last->str) + 1; - last = entry; - entry = STAILQ_NEXT(last, qlink); - } -} - -elf_strtab_head * -elf_strtab_create() -{ - elf_strtab_head *strtab = yasm_xmalloc(sizeof(elf_strtab_head)); - elf_strtab_entry *entry = yasm_xmalloc(sizeof(elf_strtab_entry)); - - STAILQ_INIT(strtab); - entry->index = 0; - entry->str = yasm__xstrdup(""); - - STAILQ_INSERT_TAIL(strtab, entry, qlink); - return strtab; -} - -elf_strtab_entry * -elf_strtab_append_str(elf_strtab_head *strtab, const char *str) -{ - elf_strtab_entry *last, *entry; - - if (strtab == NULL) - yasm_internal_error("strtab is null"); - if (STAILQ_EMPTY(strtab)) - yasm_internal_error("strtab is missing initial dummy entry"); - - last = STAILQ_LAST(strtab, elf_strtab_entry, qlink); - - entry = elf_strtab_entry_create(str); - entry->index = last->index + (unsigned long)strlen(last->str) + 1; - - STAILQ_INSERT_TAIL(strtab, entry, qlink); - return entry; -} - -void -elf_strtab_destroy(elf_strtab_head *strtab) -{ - elf_strtab_entry *s1, *s2; - - if (strtab == NULL) - yasm_internal_error("strtab is null"); - if (STAILQ_EMPTY(strtab)) - yasm_internal_error("strtab is missing initial dummy entry"); - - s1 = STAILQ_FIRST(strtab); - while (s1 != NULL) { - s2 = STAILQ_NEXT(s1, qlink); - yasm_xfree(s1->str); - yasm_xfree(s1); - s1 = s2; - } - yasm_xfree(strtab); -} - -unsigned long -elf_strtab_output_to_file(FILE *f, elf_strtab_head *strtab) -{ - unsigned long size = 0; - elf_strtab_entry *entry; - - if (strtab == NULL) - yasm_internal_error("strtab is null"); - - /* consider optimizing tables here */ - STAILQ_FOREACH(entry, strtab, qlink) { - size_t len = 1 + strlen(entry->str); - fwrite(entry->str, len, 1, f); - size += (unsigned long)len; - } - return size; -} - - - -/* symtab functions */ -elf_symtab_entry * -elf_symtab_entry_create(elf_strtab_entry *name, - yasm_symrec *sym) -{ - elf_symtab_entry *entry = yasm_xmalloc(sizeof(elf_symtab_entry)); - entry->in_table = 0; - entry->sym = sym; - entry->sect = NULL; - entry->name = name; - entry->value = 0; - - entry->xsize = NULL; - entry->size = 0; - entry->index = 0; - entry->bind = 0; - entry->type = STT_NOTYPE; - entry->vis = STV_DEFAULT; - - return entry; -} - -static void -elf_symtab_entry_destroy(elf_symtab_entry *entry) -{ - if (entry == NULL) - yasm_internal_error("symtab entry is null"); - - yasm_xfree(entry); -} - -static void -elf_symrec_data_destroy(void *data) -{ - /* do nothing, as this stuff is in the symtab anyway... this speaks of bad - * design/use or this stuff, i fear */ - - /* watch for double-free here ... */ - /*elf_symtab_entry_destroy((elf_symtab_entry *)data);*/ -} - -static void -elf_symtab_entry_print(void *data, FILE *f, int indent_level) -{ - elf_symtab_entry *entry = data; - if (entry == NULL) - yasm_internal_error("symtab entry is null"); - - fprintf(f, "%*sbind=", indent_level, ""); - switch (entry->bind) { - case STB_LOCAL: fprintf(f, "local\n"); break; - case STB_GLOBAL: fprintf(f, "global\n"); break; - case STB_WEAK: fprintf(f, "weak\n"); break; - default: fprintf(f, "undef\n"); break; - } - fprintf(f, "%*stype=", indent_level, ""); - switch (entry->type) { - case STT_NOTYPE: fprintf(f, "notype\n"); break; - case STT_OBJECT: fprintf(f, "object\n"); break; - case STT_FUNC: fprintf(f, "func\n"); break; - case STT_SECTION: fprintf(f, "section\n");break; - case STT_FILE: fprintf(f, "file\n"); break; - default: fprintf(f, "undef\n"); break; - } - fprintf(f, "%*ssize=", indent_level, ""); - if (entry->xsize) - yasm_expr_print(entry->xsize, f); - else - fprintf(f, "%ld", entry->size); - fprintf(f, "\n"); -} - -static void -elf_ssym_symtab_entry_print(void *data, FILE *f, int indent_level) -{ - /* TODO */ -} - -elf_symtab_head * -elf_symtab_create() -{ - elf_symtab_head *symtab = yasm_xmalloc(sizeof(elf_symtab_head)); - elf_symtab_entry *entry = yasm_xmalloc(sizeof(elf_symtab_entry)); - - STAILQ_INIT(symtab); - entry->in_table = 1; - entry->sym = NULL; - entry->sect = NULL; - entry->name = NULL; - entry->value = 0; - entry->xsize = NULL; - entry->size = 0; - entry->index = SHN_UNDEF; - entry->bind = STB_LOCAL; - entry->type = STT_NOTYPE; - entry->vis = STV_DEFAULT; - entry->symindex = 0; - STAILQ_INSERT_TAIL(symtab, entry, qlink); - return symtab; -} - -void -elf_symtab_append_entry(elf_symtab_head *symtab, elf_symtab_entry *entry) -{ - if (symtab == NULL) - yasm_internal_error("symtab is null"); - if (entry == NULL) - yasm_internal_error("symtab entry is null"); - if (STAILQ_EMPTY(symtab)) - yasm_internal_error(N_("symtab is missing initial dummy entry")); - - STAILQ_INSERT_TAIL(symtab, entry, qlink); - entry->in_table = 1; -} - -void -elf_symtab_insert_local_sym(elf_symtab_head *symtab, elf_symtab_entry *entry) -{ - elf_symtab_entry *after = STAILQ_FIRST(symtab); - elf_symtab_entry *before = NULL; - - while (after && (after->bind == STB_LOCAL)) { - before = after; - if (before->type == STT_FILE) break; - after = STAILQ_NEXT(after, qlink); - } - STAILQ_INSERT_AFTER(symtab, before, entry, qlink); - entry->in_table = 1; -} - -void -elf_symtab_destroy(elf_symtab_head *symtab) -{ - elf_symtab_entry *s1, *s2; - - if (symtab == NULL) - yasm_internal_error("symtab is null"); - if (STAILQ_EMPTY(symtab)) - yasm_internal_error(N_("symtab is missing initial dummy entry")); - - s1 = STAILQ_FIRST(symtab); - while (s1 != NULL) { - s2 = STAILQ_NEXT(s1, qlink); - elf_symtab_entry_destroy(s1); - s1 = s2; - } - yasm_xfree(symtab); -} - -unsigned long -elf_symtab_assign_indices(elf_symtab_head *symtab) -{ - elf_symtab_entry *entry, *prev=NULL; - unsigned long last_local=0; - - if (symtab == NULL) - yasm_internal_error("symtab is null"); - if (STAILQ_EMPTY(symtab)) - yasm_internal_error(N_("symtab is missing initial dummy entry")); - - STAILQ_FOREACH(entry, symtab, qlink) { - if (prev) - entry->symindex = prev->symindex + 1; - if (entry->bind == STB_LOCAL) - last_local = entry->symindex; - prev = entry; - } - return last_local + 1; -} - -unsigned long -elf_symtab_write_to_file(FILE *f, elf_symtab_head *symtab, - yasm_errwarns *errwarns) -{ - unsigned char buf[SYMTAB_MAXSIZE], *bufp; - elf_symtab_entry *entry; - unsigned long size = 0; - - if (!symtab) - yasm_internal_error(N_("symtab is null")); - - STAILQ_FOREACH(entry, symtab, qlink) { - - yasm_intnum *size_intn=NULL, *value_intn=NULL; - bufp = buf; - - /* get size (if specified); expr overrides stored integer */ - if (entry->xsize) { - size_intn = yasm_intnum_copy( - yasm_expr_get_intnum(&entry->xsize, 1)); - if (!size_intn) { - yasm_error_set(YASM_ERROR_VALUE, - N_("size specifier not an integer expression")); - yasm_errwarn_propagate(errwarns, entry->xsize->line); - } - } - else - size_intn = yasm_intnum_create_uint(entry->size); - - /* get EQU value for constants */ - if (entry->sym) { - const yasm_expr *equ_expr_c; - equ_expr_c = yasm_symrec_get_equ(entry->sym); - - if (equ_expr_c != NULL) { - const yasm_intnum *equ_intn; - yasm_expr *equ_expr = yasm_expr_copy(equ_expr_c); - equ_intn = yasm_expr_get_intnum(&equ_expr, 1); - - if (equ_intn == NULL) { - yasm_error_set(YASM_ERROR_VALUE, - N_("EQU value not an integer expression")); - yasm_errwarn_propagate(errwarns, equ_expr->line); - } else - value_intn = yasm_intnum_copy(equ_intn); - entry->index = SHN_ABS; - yasm_expr_destroy(equ_expr); - } - } - if (value_intn == NULL) - value_intn = yasm_intnum_create_uint(entry->value); - - /* If symbol is in a TLS section, force its type to TLS. */ - if (entry->sym) { - yasm_bytecode *precbc; - yasm_section *sect; - elf_secthead *shead; - if (yasm_symrec_get_label(entry->sym, &precbc) && - (sect = yasm_bc_get_section(precbc)) && - (shead = yasm_section_get_data(sect, &elf_section_data)) && - shead->flags & SHF_TLS) { - entry->type = STT_TLS; - } - } - - if (!elf_march->write_symtab_entry || !elf_march->symtab_entry_size) - yasm_internal_error(N_("Unsupported machine for ELF output")); - elf_march->write_symtab_entry(bufp, entry, value_intn, size_intn); - fwrite(buf, elf_march->symtab_entry_size, 1, f); - size += elf_march->symtab_entry_size; - - yasm_intnum_destroy(size_intn); - yasm_intnum_destroy(value_intn); - } - return size; -} - -void elf_symtab_set_nonzero(elf_symtab_entry *entry, - yasm_section *sect, - elf_section_index sectidx, - elf_symbol_binding bind, - elf_symbol_type type, - yasm_expr *xsize, - elf_address *value) -{ - if (!entry) - yasm_internal_error("NULL entry"); - if (sect) entry->sect = sect; - if (sectidx) entry->index = sectidx; - if (bind) entry->bind = bind; - if (type) entry->type = type; - if (xsize) entry->xsize = xsize; - if (value) entry->value = *value; -} - -void -elf_sym_set_visibility(elf_symtab_entry *entry, - elf_symbol_vis vis) -{ - entry->vis = ELF_ST_VISIBILITY(vis); -} - -void -elf_sym_set_type(elf_symtab_entry *entry, - elf_symbol_type type) -{ - entry->type = type; -} - -void -elf_sym_set_size(elf_symtab_entry *entry, - struct yasm_expr *size) -{ - if (entry->xsize) - yasm_expr_destroy(entry->xsize); - entry->xsize = size; -} - -int -elf_sym_in_table(elf_symtab_entry *entry) -{ - return entry->in_table; -} - -elf_secthead * -elf_secthead_create(elf_strtab_entry *name, - elf_section_type type, - elf_section_flags flags, - elf_address offset, - elf_size size) -{ - elf_secthead *esd = yasm_xmalloc(sizeof(elf_secthead)); - - esd->type = type; - esd->flags = flags; - esd->offset = offset; - esd->size = yasm_intnum_create_uint(size); - esd->link = 0; - esd->info = 0; - esd->align = 0; - esd->entsize = 0; - esd->index = 0; - - esd->sym = NULL; - esd->name = name; - esd->index = 0; - esd->rel_name = NULL; - esd->rel_index = 0; - esd->rel_offset = 0; - esd->nreloc = 0; - - if (name && (strcmp(name->str, ".symtab") == 0)) { - if (!elf_march->symtab_entry_size || !elf_march->symtab_entry_align) - yasm_internal_error(N_("unsupported ELF format")); - esd->entsize = elf_march->symtab_entry_size; - esd->align = elf_march->symtab_entry_align; - } - - return esd; -} - -void -elf_secthead_destroy(elf_secthead *shead) -{ - if (shead == NULL) - yasm_internal_error(N_("shead is null")); - - yasm_intnum_destroy(shead->size); - - yasm_xfree(shead); -} - -static void -elf_section_data_destroy(void *data) -{ - elf_secthead_destroy((elf_secthead *)data); -} - -static void -elf_secthead_print(void *data, FILE *f, int indent_level) -{ - elf_secthead *sect = data; - fprintf(f, "%*sname=%s\n", indent_level, "", - sect->name ? sect->name->str : "<undef>"); - fprintf(f, "%*ssym=\n", indent_level, ""); - yasm_symrec_print(sect->sym, f, indent_level+1); - fprintf(f, "%*sindex=0x%x\n", indent_level, "", sect->index); - fprintf(f, "%*sflags=", indent_level, ""); - if (sect->flags & SHF_WRITE) - fprintf(f, "WRITE "); - if (sect->flags & SHF_ALLOC) - fprintf(f, "ALLOC "); - if (sect->flags & SHF_EXECINSTR) - fprintf(f, "EXEC "); - /*if (sect->flags & SHF_MASKPROC) - fprintf(f, "PROC-SPECIFIC"); */ - fprintf(f, "%*soffset=0x%lx\n", indent_level, "", sect->offset); - fprintf(f, "%*ssize=0x%lx\n", indent_level, "", - yasm_intnum_get_uint(sect->size)); - fprintf(f, "%*slink=0x%x\n", indent_level, "", sect->link); - fprintf(f, "%*salign=%lu\n", indent_level, "", sect->align); - fprintf(f, "%*snreloc=%ld\n", indent_level, "", sect->nreloc); -} - -unsigned long -elf_secthead_write_to_file(FILE *f, elf_secthead *shead, - elf_section_index sindex) -{ - unsigned char buf[SHDR_MAXSIZE], *bufp = buf; - shead->index = sindex; - - if (shead == NULL) - yasm_internal_error("shead is null"); - - if (!elf_march->write_secthead || !elf_march->secthead_size) - yasm_internal_error(N_("Unsupported machine for ELF output")); - elf_march->write_secthead(bufp, shead); - if (fwrite(buf, elf_march->secthead_size, 1, f)) - return elf_march->secthead_size; - yasm_internal_error(N_("Failed to write an elf section header")); - return 0; -} - -void -elf_secthead_append_reloc(yasm_section *sect, elf_secthead *shead, - elf_reloc_entry *reloc) -{ - if (sect == NULL) - yasm_internal_error("sect is null"); - if (shead == NULL) - yasm_internal_error("shead is null"); - if (reloc == NULL) - yasm_internal_error("reloc is null"); - - shead->nreloc++; - yasm_section_add_reloc(sect, (yasm_reloc *)reloc, elf_reloc_entry_destroy); -} - -char * -elf_secthead_name_reloc_section(const char *basesect) -{ - if (!elf_march->reloc_section_prefix) - { - yasm_internal_error(N_("Unsupported machine for ELF output")); - return NULL; - } - else - { - size_t prepend_length = strlen(elf_march->reloc_section_prefix); - char *sectname = yasm_xmalloc(prepend_length + strlen(basesect) + 1); - strcpy(sectname, elf_march->reloc_section_prefix); - strcat(sectname, basesect); - return sectname; - } -} - -void -elf_handle_reloc_addend(yasm_intnum *intn, - elf_reloc_entry *reloc, - unsigned long offset) -{ - if (!elf_march->handle_reloc_addend) - yasm_internal_error(N_("Unsupported machine for ELF output")); - elf_march->handle_reloc_addend(intn, reloc, offset); -} - -unsigned long -elf_secthead_write_rel_to_file(FILE *f, elf_section_index symtab_idx, - yasm_section *sect, elf_secthead *shead, - elf_section_index sindex) -{ - unsigned char buf[SHDR_MAXSIZE], *bufp = buf; - - if (shead == NULL) - yasm_internal_error("shead is null"); - - if (!yasm_section_relocs_first(sect)) - return 0; /* no relocations, no .rel.* section header */ - - shead->rel_index = sindex; - - if (!elf_march->write_secthead_rel || !elf_march->secthead_size) - yasm_internal_error(N_("Unsupported machine for ELF output")); - elf_march->write_secthead_rel(bufp, shead, symtab_idx, sindex); - if (fwrite(buf, elf_march->secthead_size, 1, f)) - return elf_march->secthead_size; - yasm_internal_error(N_("Failed to write an elf section header")); - return 0; -} - -unsigned long -elf_secthead_write_relocs_to_file(FILE *f, yasm_section *sect, - elf_secthead *shead, yasm_errwarns *errwarns) -{ - elf_reloc_entry *reloc; - unsigned char buf[RELOC_MAXSIZE], *bufp; - unsigned long size = 0; - long pos; - - if (shead == NULL) - yasm_internal_error("shead is null"); - - reloc = (elf_reloc_entry *)yasm_section_relocs_first(sect); - if (!reloc) - return 0; - - /* first align section to multiple of 4 */ - pos = ftell(f); - if (pos == -1) { - yasm_error_set(YASM_ERROR_IO, - N_("couldn't read position on output stream")); - yasm_errwarn_propagate(errwarns, 0); - } - pos = (pos + 3) & ~3; - if (fseek(f, pos, SEEK_SET) < 0) { - yasm_error_set(YASM_ERROR_IO, N_("couldn't seek on output stream")); - yasm_errwarn_propagate(errwarns, 0); - } - shead->rel_offset = (unsigned long)pos; - - - while (reloc) { - unsigned int r_type=0, r_sym; - elf_symtab_entry *esym; - - esym = yasm_symrec_get_data(reloc->reloc.sym, &elf_symrec_data); - if (esym) - r_sym = esym->symindex; - else - r_sym = STN_UNDEF; - - if (!elf_march->map_reloc_info_to_type) - yasm_internal_error(N_("Unsupported arch/machine for elf output")); - r_type = elf_march->map_reloc_info_to_type(reloc); - - bufp = buf; - if (!elf_march->write_reloc || !elf_march->reloc_entry_size) - yasm_internal_error(N_("Unsupported arch/machine for elf output")); - elf_march->write_reloc(bufp, reloc, r_type, r_sym); - fwrite(buf, elf_march->reloc_entry_size, 1, f); - size += elf_march->reloc_entry_size; - - reloc = (elf_reloc_entry *) - yasm_section_reloc_next((yasm_reloc *)reloc); - } - return size; -} - -elf_section_type -elf_secthead_get_type(elf_secthead *shead) -{ - return shead->type; -} - -void -elf_secthead_set_typeflags(elf_secthead *shead, elf_section_type type, - elf_section_flags flags) -{ - shead->type = type; - shead->flags = flags; -} - -int -elf_secthead_is_empty(elf_secthead *shead) -{ - return yasm_intnum_is_zero(shead->size); -} - -yasm_symrec * -elf_secthead_get_sym(elf_secthead *shead) -{ - return shead->sym; -} - -elf_section_index -elf_secthead_get_index(elf_secthead *shead) -{ - return shead->index; -} - -unsigned long -elf_secthead_get_align(const elf_secthead *shead) -{ - return shead->align; -} - -unsigned long -elf_secthead_set_align(elf_secthead *shead, unsigned long align) -{ - return shead->align = align; -} - -elf_section_info -elf_secthead_set_info(elf_secthead *shead, elf_section_info info) -{ - return shead->info = info; -} - -elf_section_index -elf_secthead_set_index(elf_secthead *shead, elf_section_index sectidx) -{ - return shead->index = sectidx; -} - -elf_section_index -elf_secthead_set_link(elf_secthead *shead, elf_section_index link) -{ - return shead->link = link; -} - -elf_section_index -elf_secthead_set_rel_index(elf_secthead *shead, elf_section_index sectidx) -{ - return shead->rel_index = sectidx; -} - -elf_strtab_entry * -elf_secthead_set_rel_name(elf_secthead *shead, elf_strtab_entry *entry) -{ - return shead->rel_name = entry; -} - -elf_size -elf_secthead_set_entsize(elf_secthead *shead, elf_size size) -{ - return shead->entsize = size; -} - -yasm_symrec * -elf_secthead_set_sym(elf_secthead *shead, yasm_symrec *sym) -{ - return shead->sym = sym; -} - -void -elf_secthead_add_size(elf_secthead *shead, yasm_intnum *size) -{ - if (size) { - yasm_intnum_calc(shead->size, YASM_EXPR_ADD, size); - } -} - -long -elf_secthead_set_file_offset(elf_secthead *shead, long pos) -{ - unsigned long align = shead->align; - - if (align == 0 || align == 1) { - shead->offset = (unsigned long)pos; - return pos; - } - else if (align & (align - 1)) - yasm_internal_error( - N_("alignment %d for section `%s' is not a power of 2")); - /*, align, sect->name->str);*/ - - shead->offset = (unsigned long)((pos + align - 1) & ~(align - 1)); - return (long)shead->offset; -} - -unsigned long -elf_proghead_get_size(void) -{ - if (!elf_march->proghead_size) - yasm_internal_error(N_("Unsupported ELF format for output")); - return elf_march->proghead_size; -} - -unsigned long -elf_proghead_write_to_file(FILE *f, - elf_offset secthead_addr, - unsigned long secthead_count, - elf_section_index shstrtab_index) -{ - unsigned char buf[EHDR_MAXSIZE], *bufp = buf; - - YASM_WRITE_8(bufp, ELFMAG0); /* ELF magic number */ - YASM_WRITE_8(bufp, ELFMAG1); - YASM_WRITE_8(bufp, ELFMAG2); - YASM_WRITE_8(bufp, ELFMAG3); - - if (!elf_march->write_proghead || !elf_march->proghead_size) - yasm_internal_error(N_("Unsupported ELF format for output")); - elf_march->write_proghead(&bufp, secthead_addr, secthead_count, shstrtab_index); - - if (((unsigned)(bufp - buf)) != elf_march->proghead_size) - yasm_internal_error(N_("ELF program header is not proper length")); - - if (fwrite(buf, elf_march->proghead_size, 1, f)) - return elf_march->proghead_size; - - yasm_internal_error(N_("Failed to write ELF program header")); - return 0; -} +/* + * ELF object format helpers + * + * Copyright (C) 2003-2007 Michael Urman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <util.h> + +#include <libyasm.h> +#define YASM_OBJFMT_ELF_INTERNAL +#include "elf.h" +#include "elf-machine.h" + +static void elf_section_data_destroy(void *data); +static void elf_secthead_print(void *data, FILE *f, int indent_level); + +const yasm_assoc_data_callback elf_section_data = { + elf_section_data_destroy, + elf_secthead_print +}; + +static void elf_symrec_data_destroy(/*@only@*/ void *d); +static void elf_symtab_entry_print(void *data, FILE *f, int indent_level); +static void elf_ssym_symtab_entry_print(void *data, FILE *f, int indent_level); + +const yasm_assoc_data_callback elf_symrec_data = { + elf_symrec_data_destroy, + elf_symtab_entry_print +}; + +const yasm_assoc_data_callback elf_ssym_symrec_data = { + elf_symrec_data_destroy, + elf_ssym_symtab_entry_print +}; + +extern elf_machine_handler + elf_machine_handler_x86_x86, + elf_machine_handler_x86_amd64, + elf_machine_handler_x86_x32; + +static const elf_machine_handler *elf_machine_handlers[] = +{ + &elf_machine_handler_x86_x86, + &elf_machine_handler_x86_amd64, + &elf_machine_handler_x86_x32, + NULL +}; +static const elf_machine_handler elf_null_machine = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0}; +static elf_machine_handler const *elf_march = &elf_null_machine; +static yasm_symrec **elf_ssyms; + +const elf_machine_handler * +elf_set_arch(yasm_arch *arch, yasm_symtab *symtab, int bits_pref) +{ + const char *machine = yasm_arch_get_machine(arch); + int i; + + for (i=0, elf_march = elf_machine_handlers[0]; + elf_march != NULL; + elf_march = elf_machine_handlers[++i]) + { + if (yasm__strcasecmp(yasm_arch_keyword(arch), elf_march->arch)==0) { + if (yasm__strcasecmp(machine, elf_march->machine)==0) { + if (bits_pref == 0 || bits_pref == elf_march->bits) + break; + } else if (bits_pref == elf_march->bits + && yasm__strcasecmp(machine, "amd64") == 0 + && yasm__strcasecmp(elf_march->machine, "x32") == 0) + break; + } + } + + if (elf_march && elf_march->num_ssyms > 0) + { + /* Allocate "special" syms */ + elf_ssyms = + yasm_xmalloc(elf_march->num_ssyms * sizeof(yasm_symrec *)); + for (i=0; (unsigned int)i<elf_march->num_ssyms; i++) + { + /* FIXME: misuse of NULL bytecode */ + elf_ssyms[i] = yasm_symtab_define_label(symtab, + elf_march->ssyms[i].name, + NULL, 0, 0); + yasm_symrec_add_data(elf_ssyms[i], &elf_ssym_symrec_data, + (void*)&elf_march->ssyms[i]); + } + } + + return elf_march; +} + +yasm_symrec * +elf_get_special_sym(const char *name, const char *parser) +{ + int i; + for (i=0; (unsigned int)i<elf_march->num_ssyms; i++) { + if (yasm__strcasecmp(name, elf_march->ssyms[i].name) == 0) + return elf_ssyms[i]; + } + return NULL; +} + +/* reloc functions */ +int elf_ssym_has_flag(yasm_symrec *wrt, int flag); + +int +elf_is_wrt_sym_relative(yasm_symrec *wrt) +{ + return elf_ssym_has_flag(wrt, ELF_SSYM_SYM_RELATIVE); +} + +int +elf_is_wrt_pos_adjusted(yasm_symrec *wrt) +{ + return elf_ssym_has_flag(wrt, ELF_SSYM_CURPOS_ADJUST); +} + +int +elf_ssym_has_flag(yasm_symrec *wrt, int flag) +{ + int i; + for (i=0; (unsigned int)i<elf_march->num_ssyms; i++) { + if (elf_ssyms[i] == wrt) + return (elf_march->ssyms[i].sym_rel & flag) != 0; + } + return 0; +} + +/* takes ownership of addr */ +elf_reloc_entry * +elf_reloc_entry_create(yasm_symrec *sym, + yasm_symrec *wrt, + yasm_intnum *addr, + int rel, + size_t valsize, + int is_GOT_sym) +{ + elf_reloc_entry *entry; + + if (!elf_march->accepts_reloc) + yasm_internal_error(N_("Unsupported machine for ELF output")); + + if (!elf_march->accepts_reloc(valsize, wrt)) + { + if (addr) + yasm_intnum_destroy(addr); + return NULL; + } + + if (sym == NULL) + yasm_internal_error("sym is null"); + + entry = yasm_xmalloc(sizeof(elf_reloc_entry)); + entry->reloc.sym = sym; + entry->reloc.addr = addr; + entry->rtype_rel = rel; + entry->valsize = valsize; + entry->addend = NULL; + entry->wrt = wrt; + entry->is_GOT_sym = is_GOT_sym; + + return entry; +} + +void +elf_reloc_entry_destroy(void *entry) +{ + if (((elf_reloc_entry*)entry)->addend) + yasm_intnum_destroy(((elf_reloc_entry*)entry)->addend); + yasm_xfree(entry); +} + +/* strtab functions */ +elf_strtab_entry * +elf_strtab_entry_create(const char *str) +{ + elf_strtab_entry *entry = yasm_xmalloc(sizeof(elf_strtab_entry)); + entry->str = yasm__xstrdup(str); + entry->index = 0; + return entry; +} + +void +elf_strtab_entry_set_str(elf_strtab_entry *entry, const char *str) +{ + elf_strtab_entry *last; + if (entry->str) + yasm_xfree(entry->str); + entry->str = yasm__xstrdup(str); + + /* Update all following indices since string length probably changes */ + last = entry; + entry = STAILQ_NEXT(last, qlink); + while (entry) { + entry->index = last->index + (unsigned long)strlen(last->str) + 1; + last = entry; + entry = STAILQ_NEXT(last, qlink); + } +} + +elf_strtab_head * +elf_strtab_create() +{ + elf_strtab_head *strtab = yasm_xmalloc(sizeof(elf_strtab_head)); + elf_strtab_entry *entry = yasm_xmalloc(sizeof(elf_strtab_entry)); + + STAILQ_INIT(strtab); + entry->index = 0; + entry->str = yasm__xstrdup(""); + + STAILQ_INSERT_TAIL(strtab, entry, qlink); + return strtab; +} + +elf_strtab_entry * +elf_strtab_append_str(elf_strtab_head *strtab, const char *str) +{ + elf_strtab_entry *last, *entry; + + if (strtab == NULL) + yasm_internal_error("strtab is null"); + if (STAILQ_EMPTY(strtab)) + yasm_internal_error("strtab is missing initial dummy entry"); + + last = STAILQ_LAST(strtab, elf_strtab_entry, qlink); + + entry = elf_strtab_entry_create(str); + entry->index = last->index + (unsigned long)strlen(last->str) + 1; + + STAILQ_INSERT_TAIL(strtab, entry, qlink); + return entry; +} + +void +elf_strtab_destroy(elf_strtab_head *strtab) +{ + elf_strtab_entry *s1, *s2; + + if (strtab == NULL) + yasm_internal_error("strtab is null"); + if (STAILQ_EMPTY(strtab)) + yasm_internal_error("strtab is missing initial dummy entry"); + + s1 = STAILQ_FIRST(strtab); + while (s1 != NULL) { + s2 = STAILQ_NEXT(s1, qlink); + yasm_xfree(s1->str); + yasm_xfree(s1); + s1 = s2; + } + yasm_xfree(strtab); +} + +unsigned long +elf_strtab_output_to_file(FILE *f, elf_strtab_head *strtab) +{ + unsigned long size = 0; + elf_strtab_entry *entry; + + if (strtab == NULL) + yasm_internal_error("strtab is null"); + + /* consider optimizing tables here */ + STAILQ_FOREACH(entry, strtab, qlink) { + size_t len = 1 + strlen(entry->str); + fwrite(entry->str, len, 1, f); + size += (unsigned long)len; + } + return size; +} + + + +/* symtab functions */ +elf_symtab_entry * +elf_symtab_entry_create(elf_strtab_entry *name, + yasm_symrec *sym) +{ + elf_symtab_entry *entry = yasm_xmalloc(sizeof(elf_symtab_entry)); + entry->in_table = 0; + entry->sym = sym; + entry->sect = NULL; + entry->name = name; + entry->value = 0; + + entry->xsize = NULL; + entry->size = 0; + entry->index = 0; + entry->bind = 0; + entry->type = STT_NOTYPE; + entry->vis = STV_DEFAULT; + + return entry; +} + +static void +elf_symtab_entry_destroy(elf_symtab_entry *entry) +{ + if (entry == NULL) + yasm_internal_error("symtab entry is null"); + + yasm_xfree(entry); +} + +static void +elf_symrec_data_destroy(void *data) +{ + /* do nothing, as this stuff is in the symtab anyway... this speaks of bad + * design/use or this stuff, i fear */ + + /* watch for double-free here ... */ + /*elf_symtab_entry_destroy((elf_symtab_entry *)data);*/ +} + +static void +elf_symtab_entry_print(void *data, FILE *f, int indent_level) +{ + elf_symtab_entry *entry = data; + if (entry == NULL) + yasm_internal_error("symtab entry is null"); + + fprintf(f, "%*sbind=", indent_level, ""); + switch (entry->bind) { + case STB_LOCAL: fprintf(f, "local\n"); break; + case STB_GLOBAL: fprintf(f, "global\n"); break; + case STB_WEAK: fprintf(f, "weak\n"); break; + default: fprintf(f, "undef\n"); break; + } + fprintf(f, "%*stype=", indent_level, ""); + switch (entry->type) { + case STT_NOTYPE: fprintf(f, "notype\n"); break; + case STT_OBJECT: fprintf(f, "object\n"); break; + case STT_FUNC: fprintf(f, "func\n"); break; + case STT_SECTION: fprintf(f, "section\n");break; + case STT_FILE: fprintf(f, "file\n"); break; + default: fprintf(f, "undef\n"); break; + } + fprintf(f, "%*ssize=", indent_level, ""); + if (entry->xsize) + yasm_expr_print(entry->xsize, f); + else + fprintf(f, "%ld", entry->size); + fprintf(f, "\n"); +} + +static void +elf_ssym_symtab_entry_print(void *data, FILE *f, int indent_level) +{ + /* TODO */ +} + +elf_symtab_head * +elf_symtab_create() +{ + elf_symtab_head *symtab = yasm_xmalloc(sizeof(elf_symtab_head)); + elf_symtab_entry *entry = yasm_xmalloc(sizeof(elf_symtab_entry)); + + STAILQ_INIT(symtab); + entry->in_table = 1; + entry->sym = NULL; + entry->sect = NULL; + entry->name = NULL; + entry->value = 0; + entry->xsize = NULL; + entry->size = 0; + entry->index = SHN_UNDEF; + entry->bind = STB_LOCAL; + entry->type = STT_NOTYPE; + entry->vis = STV_DEFAULT; + entry->symindex = 0; + STAILQ_INSERT_TAIL(symtab, entry, qlink); + return symtab; +} + +void +elf_symtab_append_entry(elf_symtab_head *symtab, elf_symtab_entry *entry) +{ + if (symtab == NULL) + yasm_internal_error("symtab is null"); + if (entry == NULL) + yasm_internal_error("symtab entry is null"); + if (STAILQ_EMPTY(symtab)) + yasm_internal_error(N_("symtab is missing initial dummy entry")); + + STAILQ_INSERT_TAIL(symtab, entry, qlink); + entry->in_table = 1; +} + +void +elf_symtab_insert_local_sym(elf_symtab_head *symtab, elf_symtab_entry *entry) +{ + elf_symtab_entry *after = STAILQ_FIRST(symtab); + elf_symtab_entry *before = NULL; + + while (after && (after->bind == STB_LOCAL)) { + before = after; + if (before->type == STT_FILE) break; + after = STAILQ_NEXT(after, qlink); + } + STAILQ_INSERT_AFTER(symtab, before, entry, qlink); + entry->in_table = 1; +} + +void +elf_symtab_destroy(elf_symtab_head *symtab) +{ + elf_symtab_entry *s1, *s2; + + if (symtab == NULL) + yasm_internal_error("symtab is null"); + if (STAILQ_EMPTY(symtab)) + yasm_internal_error(N_("symtab is missing initial dummy entry")); + + s1 = STAILQ_FIRST(symtab); + while (s1 != NULL) { + s2 = STAILQ_NEXT(s1, qlink); + elf_symtab_entry_destroy(s1); + s1 = s2; + } + yasm_xfree(symtab); +} + +unsigned long +elf_symtab_assign_indices(elf_symtab_head *symtab) +{ + elf_symtab_entry *entry, *prev=NULL; + unsigned long last_local=0; + + if (symtab == NULL) + yasm_internal_error("symtab is null"); + if (STAILQ_EMPTY(symtab)) + yasm_internal_error(N_("symtab is missing initial dummy entry")); + + STAILQ_FOREACH(entry, symtab, qlink) { + if (prev) + entry->symindex = prev->symindex + 1; + if (entry->bind == STB_LOCAL) + last_local = entry->symindex; + prev = entry; + } + return last_local + 1; +} + +unsigned long +elf_symtab_write_to_file(FILE *f, elf_symtab_head *symtab, + yasm_errwarns *errwarns) +{ + unsigned char buf[SYMTAB_MAXSIZE], *bufp; + elf_symtab_entry *entry; + unsigned long size = 0; + + if (!symtab) + yasm_internal_error(N_("symtab is null")); + + STAILQ_FOREACH(entry, symtab, qlink) { + + yasm_intnum *size_intn=NULL, *value_intn=NULL; + bufp = buf; + + /* get size (if specified); expr overrides stored integer */ + if (entry->xsize) { + size_intn = yasm_intnum_copy( + yasm_expr_get_intnum(&entry->xsize, 1)); + if (!size_intn) { + yasm_error_set(YASM_ERROR_VALUE, + N_("size specifier not an integer expression")); + yasm_errwarn_propagate(errwarns, entry->xsize->line); + } + } + else + size_intn = yasm_intnum_create_uint(entry->size); + + /* get EQU value for constants */ + if (entry->sym) { + const yasm_expr *equ_expr_c; + equ_expr_c = yasm_symrec_get_equ(entry->sym); + + if (equ_expr_c != NULL) { + const yasm_intnum *equ_intn; + yasm_expr *equ_expr = yasm_expr_copy(equ_expr_c); + equ_intn = yasm_expr_get_intnum(&equ_expr, 1); + + if (equ_intn == NULL) { + yasm_error_set(YASM_ERROR_VALUE, + N_("EQU value not an integer expression")); + yasm_errwarn_propagate(errwarns, equ_expr->line); + } else + value_intn = yasm_intnum_copy(equ_intn); + entry->index = SHN_ABS; + yasm_expr_destroy(equ_expr); + } + } + if (value_intn == NULL) + value_intn = yasm_intnum_create_uint(entry->value); + + /* If symbol is in a TLS section, force its type to TLS. */ + if (entry->sym) { + yasm_bytecode *precbc; + yasm_section *sect; + elf_secthead *shead; + if (yasm_symrec_get_label(entry->sym, &precbc) && + (sect = yasm_bc_get_section(precbc)) && + (shead = yasm_section_get_data(sect, &elf_section_data)) && + shead->flags & SHF_TLS) { + entry->type = STT_TLS; + } + } + + if (!elf_march->write_symtab_entry || !elf_march->symtab_entry_size) + yasm_internal_error(N_("Unsupported machine for ELF output")); + elf_march->write_symtab_entry(bufp, entry, value_intn, size_intn); + fwrite(buf, elf_march->symtab_entry_size, 1, f); + size += elf_march->symtab_entry_size; + + yasm_intnum_destroy(size_intn); + yasm_intnum_destroy(value_intn); + } + return size; +} + +void elf_symtab_set_nonzero(elf_symtab_entry *entry, + yasm_section *sect, + elf_section_index sectidx, + elf_symbol_binding bind, + elf_symbol_type type, + yasm_expr *xsize, + elf_address *value) +{ + if (!entry) + yasm_internal_error("NULL entry"); + if (sect) entry->sect = sect; + if (sectidx) entry->index = sectidx; + if (bind) entry->bind = bind; + if (type) entry->type = type; + if (xsize) entry->xsize = xsize; + if (value) entry->value = *value; +} + +void +elf_sym_set_visibility(elf_symtab_entry *entry, + elf_symbol_vis vis) +{ + entry->vis = ELF_ST_VISIBILITY(vis); +} + +void +elf_sym_set_type(elf_symtab_entry *entry, + elf_symbol_type type) +{ + entry->type = type; +} + +void +elf_sym_set_size(elf_symtab_entry *entry, + struct yasm_expr *size) +{ + if (entry->xsize) + yasm_expr_destroy(entry->xsize); + entry->xsize = size; +} + +int +elf_sym_in_table(elf_symtab_entry *entry) +{ + return entry->in_table; +} + +elf_secthead * +elf_secthead_create(elf_strtab_entry *name, + elf_section_type type, + elf_section_flags flags, + elf_address offset, + elf_size size) +{ + elf_secthead *esd = yasm_xmalloc(sizeof(elf_secthead)); + + esd->type = type; + esd->flags = flags; + esd->offset = offset; + esd->size = yasm_intnum_create_uint(size); + esd->link = 0; + esd->info = 0; + esd->align = 0; + esd->entsize = 0; + esd->index = 0; + + esd->sym = NULL; + esd->name = name; + esd->index = 0; + esd->rel_name = NULL; + esd->rel_index = 0; + esd->rel_offset = 0; + esd->nreloc = 0; + + if (name && (strcmp(name->str, ".symtab") == 0)) { + if (!elf_march->symtab_entry_size || !elf_march->symtab_entry_align) + yasm_internal_error(N_("unsupported ELF format")); + esd->entsize = elf_march->symtab_entry_size; + esd->align = elf_march->symtab_entry_align; + } + + return esd; +} + +void +elf_secthead_destroy(elf_secthead *shead) +{ + if (shead == NULL) + yasm_internal_error(N_("shead is null")); + + yasm_intnum_destroy(shead->size); + + yasm_xfree(shead); +} + +static void +elf_section_data_destroy(void *data) +{ + elf_secthead_destroy((elf_secthead *)data); +} + +static void +elf_secthead_print(void *data, FILE *f, int indent_level) +{ + elf_secthead *sect = data; + fprintf(f, "%*sname=%s\n", indent_level, "", + sect->name ? sect->name->str : "<undef>"); + fprintf(f, "%*ssym=\n", indent_level, ""); + yasm_symrec_print(sect->sym, f, indent_level+1); + fprintf(f, "%*sindex=0x%x\n", indent_level, "", sect->index); + fprintf(f, "%*sflags=", indent_level, ""); + if (sect->flags & SHF_WRITE) + fprintf(f, "WRITE "); + if (sect->flags & SHF_ALLOC) + fprintf(f, "ALLOC "); + if (sect->flags & SHF_EXECINSTR) + fprintf(f, "EXEC "); + /*if (sect->flags & SHF_MASKPROC) + fprintf(f, "PROC-SPECIFIC"); */ + fprintf(f, "%*soffset=0x%lx\n", indent_level, "", sect->offset); + fprintf(f, "%*ssize=0x%lx\n", indent_level, "", + yasm_intnum_get_uint(sect->size)); + fprintf(f, "%*slink=0x%x\n", indent_level, "", sect->link); + fprintf(f, "%*salign=%lu\n", indent_level, "", sect->align); + fprintf(f, "%*snreloc=%ld\n", indent_level, "", sect->nreloc); +} + +unsigned long +elf_secthead_write_to_file(FILE *f, elf_secthead *shead, + elf_section_index sindex) +{ + unsigned char buf[SHDR_MAXSIZE], *bufp = buf; + shead->index = sindex; + + if (shead == NULL) + yasm_internal_error("shead is null"); + + if (!elf_march->write_secthead || !elf_march->secthead_size) + yasm_internal_error(N_("Unsupported machine for ELF output")); + elf_march->write_secthead(bufp, shead); + if (fwrite(buf, elf_march->secthead_size, 1, f)) + return elf_march->secthead_size; + yasm_internal_error(N_("Failed to write an elf section header")); + return 0; +} + +void +elf_secthead_append_reloc(yasm_section *sect, elf_secthead *shead, + elf_reloc_entry *reloc) +{ + if (sect == NULL) + yasm_internal_error("sect is null"); + if (shead == NULL) + yasm_internal_error("shead is null"); + if (reloc == NULL) + yasm_internal_error("reloc is null"); + + shead->nreloc++; + yasm_section_add_reloc(sect, (yasm_reloc *)reloc, elf_reloc_entry_destroy); +} + +char * +elf_secthead_name_reloc_section(const char *basesect) +{ + if (!elf_march->reloc_section_prefix) + { + yasm_internal_error(N_("Unsupported machine for ELF output")); + return NULL; + } + else + { + size_t prepend_length = strlen(elf_march->reloc_section_prefix); + char *sectname = yasm_xmalloc(prepend_length + strlen(basesect) + 1); + strcpy(sectname, elf_march->reloc_section_prefix); + strcat(sectname, basesect); + return sectname; + } +} + +void +elf_handle_reloc_addend(yasm_intnum *intn, + elf_reloc_entry *reloc, + unsigned long offset) +{ + if (!elf_march->handle_reloc_addend) + yasm_internal_error(N_("Unsupported machine for ELF output")); + elf_march->handle_reloc_addend(intn, reloc, offset); +} + +unsigned long +elf_secthead_write_rel_to_file(FILE *f, elf_section_index symtab_idx, + yasm_section *sect, elf_secthead *shead, + elf_section_index sindex) +{ + unsigned char buf[SHDR_MAXSIZE], *bufp = buf; + + if (shead == NULL) + yasm_internal_error("shead is null"); + + if (!yasm_section_relocs_first(sect)) + return 0; /* no relocations, no .rel.* section header */ + + shead->rel_index = sindex; + + if (!elf_march->write_secthead_rel || !elf_march->secthead_size) + yasm_internal_error(N_("Unsupported machine for ELF output")); + elf_march->write_secthead_rel(bufp, shead, symtab_idx, sindex); + if (fwrite(buf, elf_march->secthead_size, 1, f)) + return elf_march->secthead_size; + yasm_internal_error(N_("Failed to write an elf section header")); + return 0; +} + +unsigned long +elf_secthead_write_relocs_to_file(FILE *f, yasm_section *sect, + elf_secthead *shead, yasm_errwarns *errwarns) +{ + elf_reloc_entry *reloc; + unsigned char buf[RELOC_MAXSIZE], *bufp; + unsigned long size = 0; + long pos; + + if (shead == NULL) + yasm_internal_error("shead is null"); + + reloc = (elf_reloc_entry *)yasm_section_relocs_first(sect); + if (!reloc) + return 0; + + /* first align section to multiple of 4 */ + pos = ftell(f); + if (pos == -1) { + yasm_error_set(YASM_ERROR_IO, + N_("couldn't read position on output stream")); + yasm_errwarn_propagate(errwarns, 0); + } + pos = (pos + 3) & ~3; + if (fseek(f, pos, SEEK_SET) < 0) { + yasm_error_set(YASM_ERROR_IO, N_("couldn't seek on output stream")); + yasm_errwarn_propagate(errwarns, 0); + } + shead->rel_offset = (unsigned long)pos; + + + while (reloc) { + unsigned int r_type=0, r_sym; + elf_symtab_entry *esym; + + esym = yasm_symrec_get_data(reloc->reloc.sym, &elf_symrec_data); + if (esym) + r_sym = esym->symindex; + else + r_sym = STN_UNDEF; + + if (!elf_march->map_reloc_info_to_type) + yasm_internal_error(N_("Unsupported arch/machine for elf output")); + r_type = elf_march->map_reloc_info_to_type(reloc); + + bufp = buf; + if (!elf_march->write_reloc || !elf_march->reloc_entry_size) + yasm_internal_error(N_("Unsupported arch/machine for elf output")); + elf_march->write_reloc(bufp, reloc, r_type, r_sym); + fwrite(buf, elf_march->reloc_entry_size, 1, f); + size += elf_march->reloc_entry_size; + + reloc = (elf_reloc_entry *) + yasm_section_reloc_next((yasm_reloc *)reloc); + } + return size; +} + +elf_section_type +elf_secthead_get_type(elf_secthead *shead) +{ + return shead->type; +} + +void +elf_secthead_set_typeflags(elf_secthead *shead, elf_section_type type, + elf_section_flags flags) +{ + shead->type = type; + shead->flags = flags; +} + +int +elf_secthead_is_empty(elf_secthead *shead) +{ + return yasm_intnum_is_zero(shead->size); +} + +yasm_symrec * +elf_secthead_get_sym(elf_secthead *shead) +{ + return shead->sym; +} + +elf_section_index +elf_secthead_get_index(elf_secthead *shead) +{ + return shead->index; +} + +unsigned long +elf_secthead_get_align(const elf_secthead *shead) +{ + return shead->align; +} + +unsigned long +elf_secthead_set_align(elf_secthead *shead, unsigned long align) +{ + return shead->align = align; +} + +elf_section_info +elf_secthead_set_info(elf_secthead *shead, elf_section_info info) +{ + return shead->info = info; +} + +elf_section_index +elf_secthead_set_index(elf_secthead *shead, elf_section_index sectidx) +{ + return shead->index = sectidx; +} + +elf_section_index +elf_secthead_set_link(elf_secthead *shead, elf_section_index link) +{ + return shead->link = link; +} + +elf_section_index +elf_secthead_set_rel_index(elf_secthead *shead, elf_section_index sectidx) +{ + return shead->rel_index = sectidx; +} + +elf_strtab_entry * +elf_secthead_set_rel_name(elf_secthead *shead, elf_strtab_entry *entry) +{ + return shead->rel_name = entry; +} + +elf_size +elf_secthead_set_entsize(elf_secthead *shead, elf_size size) +{ + return shead->entsize = size; +} + +yasm_symrec * +elf_secthead_set_sym(elf_secthead *shead, yasm_symrec *sym) +{ + return shead->sym = sym; +} + +void +elf_secthead_add_size(elf_secthead *shead, yasm_intnum *size) +{ + if (size) { + yasm_intnum_calc(shead->size, YASM_EXPR_ADD, size); + } +} + +long +elf_secthead_set_file_offset(elf_secthead *shead, long pos) +{ + unsigned long align = shead->align; + + if (align == 0 || align == 1) { + shead->offset = (unsigned long)pos; + return pos; + } + else if (align & (align - 1)) + yasm_internal_error( + N_("alignment %d for section `%s' is not a power of 2")); + /*, align, sect->name->str);*/ + + shead->offset = (unsigned long)((pos + align - 1) & ~(align - 1)); + return (long)shead->offset; +} + +unsigned long +elf_proghead_get_size(void) +{ + if (!elf_march->proghead_size) + yasm_internal_error(N_("Unsupported ELF format for output")); + return elf_march->proghead_size; +} + +unsigned long +elf_proghead_write_to_file(FILE *f, + elf_offset secthead_addr, + unsigned long secthead_count, + elf_section_index shstrtab_index) +{ + unsigned char buf[EHDR_MAXSIZE], *bufp = buf; + + YASM_WRITE_8(bufp, ELFMAG0); /* ELF magic number */ + YASM_WRITE_8(bufp, ELFMAG1); + YASM_WRITE_8(bufp, ELFMAG2); + YASM_WRITE_8(bufp, ELFMAG3); + + if (!elf_march->write_proghead || !elf_march->proghead_size) + yasm_internal_error(N_("Unsupported ELF format for output")); + elf_march->write_proghead(&bufp, secthead_addr, secthead_count, shstrtab_index); + + if (((unsigned)(bufp - buf)) != elf_march->proghead_size) + yasm_internal_error(N_("ELF program header is not proper length")); + + if (fwrite(buf, elf_march->proghead_size, 1, f)) + return elf_march->proghead_size; + + yasm_internal_error(N_("Failed to write ELF program header")); + return 0; +} diff --git a/contrib/tools/yasm/modules/objfmts/elf/elf.h b/contrib/tools/yasm/modules/objfmts/elf/elf.h index fce629ae45..73498477e8 100644 --- a/contrib/tools/yasm/modules/objfmts/elf/elf.h +++ b/contrib/tools/yasm/modules/objfmts/elf/elf.h @@ -1,532 +1,532 @@ -/* - * ELF object format helpers - * - * Copyright (C) 2003-2007 Michael Urman - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ELF_H_INCLUDED -#define ELF_H_INCLUDED - -typedef struct elf_reloc_entry elf_reloc_entry; -typedef struct elf_reloc_head elf_reloc_head; -typedef struct elf_secthead elf_secthead; -typedef struct elf_strtab_entry elf_strtab_entry; -typedef struct elf_strtab_head elf_strtab_head; -typedef struct elf_symtab_entry elf_symtab_entry; -typedef struct elf_symtab_head elf_symtab_head; - -typedef struct elf_machine_handler elf_machine_handler; - -typedef unsigned long elf_address; -typedef unsigned long elf_offset; -typedef unsigned long elf_size; -typedef unsigned long elf_section_info; - -typedef enum { - ET_NONE = 0, - ET_REL = 1, /* Relocatable */ - ET_EXEC = 2, /* Executable */ - ET_DYN = 3, /* Shared object */ - ET_CORE = 4, /* Core */ - ET_LOOS = 0xfe00, /* Environment specific */ - ET_HIOS = 0xfeff, - ET_LOPROC = 0xff00, /* Processor specific */ - ET_HIPROC = 0xffff -} elf_file_type; - -typedef enum { - EM_NONE = 0, - EM_M32 = 1, /* AT&T WE 32100 */ - EM_SPARC = 2, /* SPARC */ - EM_386 = 3, /* Intel 80386 */ - EM_68K = 4, /* Motorola 68000 */ - EM_88K = 5, /* Motorola 88000 */ - EM_860 = 7, /* Intel 80860 */ - EM_MIPS = 8, /* MIPS RS3000 */ - - EM_S370 = 9, /* IBM System/370 */ - EM_MIPS_RS4_BE = 10, /* MIPS R4000 Big-Endian (dep)*/ - EM_PARISC = 15, /* HPPA */ - EM_SPARC32PLUS = 18, /* SPARC v8plus */ - EM_PPC = 20, /* PowerPC 32-bit */ - EM_PPC64 = 21, /* PowerPC 64-bit */ - EM_ARM = 40, /* ARM */ - EM_SPARCV9 = 43, /* SPARC v9 64-bit */ - EM_IA_64 = 50, /* Intel IA-64 */ - EM_X86_64 = 62, /* AMD x86-64 */ - EM_ALPHA = 0x9026 /* Alpha (no ABI) */ -} elf_machine; - -typedef enum { - ELFMAG0 = 0x7f, - ELFMAG1 = 0x45, - ELFMAG2 = 0x4c, - ELFMAG3 = 0x46 -} elf_magic; - -typedef enum { - EV_NONE = 0, /* invalid */ - EV_CURRENT = 1 /* current */ -} elf_version; - -typedef enum { - EI_MAG0 = 0, /* File id */ - EI_MAG1 = 1, - EI_MAG2 = 2, - EI_MAG3 = 3, - EI_CLASS = 4, /* File class */ - EI_DATA = 5, /* Data encoding */ - EI_VERSION = 6, /* File version */ - EI_OSABI = 7, /* OS and ABI */ - EI_ABIVERSION = 8, /* version of ABI */ - - EI_PAD = 9, /* Pad to end; start here */ - EI_NIDENT = 16 /* Sizeof e_ident[] */ -} elf_identification_index; - -typedef enum { - ELFOSABI_SYSV = 0, /* System V ABI */ - ELFOSABI_HPUX = 1, /* HP-UX os */ - ELFOSABI_STANDALONE = 255 /* Standalone / embedded app */ -} elf_osabi_index; - -typedef enum { - ELFCLASSNONE = 0, /* invalid */ - ELFCLASS32 = 1, /* 32-bit */ - ELFCLASS64 = 2 /* 64-bit */ -} elf_class; - -typedef enum { - ELFDATANONE = 0, - ELFDATA2LSB = 1, - ELFDATA2MSB = 2 -} elf_data_encoding; - -/* elf section types - index of semantics */ -typedef enum { - SHT_NULL = 0, /* inactive section - no associated data */ - SHT_PROGBITS = 1, /* defined by program for its own meaning */ - SHT_SYMTAB = 2, /* symbol table (primarily) for linking */ - SHT_STRTAB = 3, /* string table - symbols need names */ - SHT_RELA = 4, /* relocation entries w/ explicit addends */ - SHT_HASH = 5, /* symbol hash table - for dynamic linking */ - SHT_DYNAMIC = 6, /* information for dynamic linking */ - SHT_NOTE = 7, /* extra data marking the file somehow */ - SHT_NOBITS = 8, /* no stored data, but occupies runtime space */ - SHT_REL = 9, /* relocations entries w/o explicit addends */ - SHT_SHLIB = 10, /* reserved; unspecified semantics */ - SHT_DYNSYM = 11, /* like symtab, but more for dynamic linking */ - - SHT_LOOS = 0x60000000, /* reserved for environment specific use */ - SHT_HIOS = 0x6fffffff, - SHT_LOPROC = 0x70000000, /* reserved for processor specific semantics */ - SHT_HIPROC = 0x7fffffff/*, - SHT_LOUSER = 0x80000000,*/ /* reserved for applications; safe */ - /*SHT_HIUSER = 0xffffffff*/ -} elf_section_type; - -/* elf section flags - bitfield of attributes */ -typedef enum { - SHF_WRITE = 0x1, /* data should be writable at runtime */ - SHF_ALLOC = 0x2, /* occupies memory at runtime */ - SHF_EXECINSTR = 0x4, /* contains machine instructions */ - SHF_MERGE = 0x10, /* data can be merged */ - SHF_STRINGS = 0x20, /* contains 0-terminated strings */ - SHF_GROUP = 0x200, /* member of a section group */ - SHF_TLS = 0x400, /* thread local storage */ - SHF_MASKOS = 0x0f000000/*,*//* environment specific use */ - /*SHF_MASKPROC = 0xf0000000*/ /* bits reserved for processor specific needs */ -} elf_section_flags; - -/* elf section index - just the special ones */ -typedef enum { - SHN_UNDEF = 0, /* undefined symbol; requires other global */ - SHN_LORESERVE = 0xff00, /* reserved for various semantics */ - SHN_LOPROC = 0xff00, /* reserved for processor specific semantics */ - SHN_HIPROC = 0xff1f, - SHN_LOOS = 0xff20, /* reserved for environment specific use */ - SHN_HIOS = 0xff3f, - SHN_ABS = 0xfff1, /* associated symbols don't change on reloc */ - SHN_COMMON = 0xfff2, /* associated symbols refer to unallocated */ - SHN_HIRESERVE = 0xffff -} elf_section_index; - -/* elf symbol binding - index of visibility/behavior */ -typedef enum { - STB_LOCAL = 0, /* invisible outside defining file */ - STB_GLOBAL = 1, /* visible to all combined object files */ - STB_WEAK = 2, /* global but lower precedence */ - - STB_LOOS = 10, /* Environment specific use */ - STB_HIOS = 12, - STB_LOPROC = 13, /* reserved for processor specific semantics */ - STB_HIPROC = 15 -} elf_symbol_binding; - -/* elf symbol type - index of classifications */ -typedef enum { - STT_NOTYPE = 0, /* type not specified */ - STT_OBJECT = 1, /* data object such as a variable, array, etc */ - STT_FUNC = 2, /* a function or executable code */ - STT_SECTION = 3, /* a section: often for relocation, STB_LOCAL */ - STT_FILE = 4, /* often source filename: STB_LOCAL, SHN_ABS */ - STT_COMMON = 5, /* Uninitialized common block. */ - STT_TLS = 6, /* TLS object. */ - STT_NUM = 7, - - STT_LOOS = 10, /* Environment specific use */ - STT_HIOS = 12, - STT_LOPROC = 13, /* reserved for processor specific semantics */ - STT_HIPROC = 15 -} elf_symbol_type; - -typedef enum { - STN_UNDEF = 0 -} elf_symbol_index; - -/* elf symbol visibility - lower two bits of OTHER field */ -typedef enum { - STV_DEFAULT = 0, /* Default symbol visibility rules */ - STV_INTERNAL = 1, /* Processor specific hidden class */ - STV_HIDDEN = 2, /* Sym unavailable in other modules */ - STV_PROTECTED = 3 /* Not preemptable, not exported */ -} elf_symbol_vis; - - -/* internal only object definitions */ -#ifdef YASM_OBJFMT_ELF_INTERNAL - -#define ELF_VISIBILITY_MASK 0x03 -#define ELF_ST_VISIBILITY(v) ((v) & ELF_VISIBILITY_MASK) - -#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t)) -#define ELF32_ST_OTHER(vis) ELF_ST_VISIBILITY(vis) - -#define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -#define ELF64_R_INFO(s,t) (((s)<<32) + ((t) & 0xffffffffL)) -#define ELF64_ST_OTHER(vis) ELF_ST_VISIBILITY(vis) - -#define EHDR32_SIZE 52 -#define EHDR64_SIZE 64 -#define EHDR_MAXSIZE 64 - -#define SHDR32_SIZE 40 -#define SHDR64_SIZE 64 -#define SHDR_MAXSIZE 64 - -#define SYMTAB32_SIZE 16 -#define SYMTAB64_SIZE 24 -#define SYMTAB_MAXSIZE 24 - -#define SYMTAB32_ALIGN 4 -#define SYMTAB64_ALIGN 8 - -#define RELOC32_SIZE 8 -#define RELOC32A_SIZE 12 -#define RELOC64_SIZE 16 -#define RELOC64A_SIZE 24 -#define RELOC_MAXSIZE 24 - -#define RELOC32_ALIGN 4 -#define RELOC64_ALIGN 8 - - -/* elf relocation type - index of semantics - * - * A = Addend (r_addend for RELA, value at location for REL) - * B = Base address - * G = Offset into global offset table (GOT) - * GOT = Address of the global offset table (GOT) - * L = Location of procedure linkage table (PLT) - * P = Location of location being relocated (r_offset) - * S = Value of symbol - */ -typedef enum { - R_386_NONE = 0, /* none */ - R_386_32 = 1, /* word32, S + A */ - R_386_PC32 = 2, /* word32, S + A - P */ - R_386_GOT32 = 3, /* word32, G + A - P */ - R_386_PLT32 = 4, /* word32, L + A - P */ - R_386_COPY = 5, /* none */ - R_386_GLOB_DAT = 6, /* word32, S */ - R_386_JMP_SLOT = 7, /* word32, S */ - R_386_RELATIVE = 8, /* word32, B + A */ - R_386_GOTOFF = 9, /* word32, S + A - GOT */ - R_386_GOTPC = 10, /* word32, GOT + A - P */ - R_386_TLS_TPOFF = 14, /* Negative offset in static TLS block (GNU - version) */ - R_386_TLS_IE = 15, /* Absolute address of GOT entry for negative - static TLS block offset */ - R_386_TLS_GOTIE = 16, /* GOT entry for negative static TLS block - offset */ - R_386_TLS_LE = 17, /* Negative offset relative to static TLS - (GNU version) */ - R_386_TLS_GD = 18, /* Direct 32 bit for GNU version of GD TLS */ - R_386_TLS_LDM = 19, /* Direct 32 bit for GNU version of LD TLS - in LE code */ - R_386_16 = 20, /* word16, S + A (GNU extension) */ - R_386_PC16 = 21, /* word16, S + A - P (GNU extension) */ - R_386_8 = 22, /* word8, S + A (GNU extension) */ - R_386_PC8 = 23, /* word8, S + A - P (GNU extension) */ - R_386_TLS_GD_32 = 24, /* Direct 32 bit for GD TLS */ - R_386_TLS_GD_PUSH = 25, /* Tag for pushl in GD TLS code */ - R_386_TLS_GD_CALL = 26, /* Relocation for call to */ - R_386_TLS_GD_POP = 27, /* Tag for popl in GD TLS code */ - R_386_TLS_LDM_32 = 28, /* Direct 32 bit for local dynamic code */ - R_386_TLS_LDM_PUSH = 29, /* Tag for pushl in LDM TLS code */ - R_386_TLS_LDM_CALL = 30, /* Relocation for call to */ - R_386_TLS_LDM_POP = 31, /* Tag for popl in LDM TLS code */ - R_386_TLS_LDO_32 = 32, /* Offset relative to TLS block */ - R_386_TLS_IE_32 = 33, /* GOT entry for static TLS block */ - R_386_TLS_LE_32 = 34, /* Offset relative to static TLS block */ - R_386_TLS_DTPMOD32 = 35, /* ID of module containing symbol */ - R_386_TLS_DTPOFF32 = 36, /* Offset in TLS block */ - R_386_TLS_TPOFF32 = 37, /* Offset in static TLS block */ - R_386_TLS_GOTDESC = 39, - R_386_TLS_DESC_CALL = 40, - R_386_TLS_DESC = 41 -} elf_386_relocation_type; - -typedef enum { - R_X86_64_NONE = 0, /* none */ - R_X86_64_64 = 1, /* word64, S + A */ - R_X86_64_PC32 = 2, /* word32, S + A - P */ - R_X86_64_GOT32 = 3, /* word32, G + A */ - R_X86_64_PLT32 = 4, /* word32, L + A - P */ - R_X86_64_COPY = 5, /* none */ - R_X86_64_GLOB_DAT = 6, /* word64, S, set GOT entry to data address */ - R_X86_64_JMP_SLOT = 7, /* word64, S, set GOT entry to code address */ - R_X86_64_RELATIVE = 8, /* word64, B + A */ - R_X86_64_GOTPCREL = 9, /* word32, G + GOT + A - P */ - R_X86_64_32 = 10, /* word32 (zero extend), S + A */ - R_X86_64_32S = 11, /* word32 (sign extend), S + A */ - R_X86_64_16 = 12, /* word16, S + A */ - R_X86_64_PC16 = 13, /* word16, S + A - P */ - R_X86_64_8 = 14, /* word8, S + A */ - R_X86_64_PC8 = 15, /* word8, S + A - P */ - R_X86_64_DPTMOD64 = 16, /* word64, ID of module containing symbol */ - R_X86_64_DTPOFF64 = 17, /* word64, offset in TLS block */ - R_X86_64_TPOFF64 = 18, /* word64, offset in initial TLS block */ - R_X86_64_TLSGD = 19, /* word32, PC-rel offset to GD GOT block */ - R_X86_64_TLSLD = 20, /* word32, PC-rel offset to LD GOT block */ - R_X86_64_DTPOFF32 = 21, /* word32, offset to TLS block */ - R_X86_64_GOTTPOFF = 22, /* word32, PC-rel offset to IE GOT entry */ - R_X86_64_TPOFF32 = 23, /* word32, offset in initial TLS block */ - R_X86_64_PC64 = 24, /* word64, PC relative */ - R_X86_64_GOTOFF64 = 25, /* word64, offset to GOT */ - R_X86_64_GOTPC32 = 26, /* word32, signed pc relative to GOT */ - R_X86_64_GOT64 = 27, /* word64, GOT entry offset */ - R_X86_64_GOTPCREL64 = 28, /* word64, signed pc relative to GOT entry */ - R_X86_64_GOTPC64 = 29, /* word64, signed pc relative to GOT */ - R_X86_64_GOTPLT64 = 30, /* like GOT64, but indicates PLT entry needed */ - R_X86_64_PLTOFF64 = 31, /* word64, GOT relative offset to PLT entry */ - R_X86_64_GOTPC32_TLSDESC = 34, /* GOT offset for TLS descriptor */ - R_X86_64_TLSDESC_CALL = 35, /* Marker for call through TLS descriptor */ - R_X86_64_TLSDESC = 36 /* TLS descriptor */ -} elf_x86_64_relocation_type; - -struct elf_secthead { - elf_section_type type; - elf_section_flags flags; - elf_address offset; - yasm_intnum *size; - elf_section_index link; - elf_section_info info; /* see note ESD1 */ - unsigned long align; - elf_size entsize; - - yasm_symrec *sym; - elf_strtab_entry *name; - elf_section_index index; - - elf_strtab_entry *rel_name; - elf_section_index rel_index; - elf_address rel_offset; - unsigned long nreloc; -}; - -/* Note ESD1: - * for section types SHT_REL, SHT_RELA: - * link -> index of associated symbol table - * info -> index of relocated section - * for section types SHT_SYMTAB, SHT_DYNSYM: - * link -> index of associated string table - * info -> 1+index of last "local symbol" (bind == STB_LOCAL) - * (for section type SHT_DNAMIC: - * link -> index of string table - * info -> 0 ) - * (for section type SHT_HASH: - * link -> index of symbol table to which hash applies - * info -> 0 ) - * for all others: - * link -> SHN_UNDEF - * info -> 0 - */ - -struct elf_reloc_entry { - yasm_reloc reloc; - int rtype_rel; - size_t valsize; - yasm_intnum *addend; - /*@null@*/ yasm_symrec *wrt; - int is_GOT_sym; -}; - -STAILQ_HEAD(elf_strtab_head, elf_strtab_entry); -struct elf_strtab_entry { - STAILQ_ENTRY(elf_strtab_entry) qlink; - unsigned long index; - char *str; -}; - -STAILQ_HEAD(elf_symtab_head, elf_symtab_entry); -struct elf_symtab_entry { - STAILQ_ENTRY(elf_symtab_entry) qlink; - int in_table; - yasm_symrec *sym; - yasm_section *sect; - elf_strtab_entry *name; - elf_address value; - /*@dependent@*/ yasm_expr *xsize; - elf_size size; - elf_section_index index; - elf_symbol_binding bind; - elf_symbol_type type; - elf_symbol_vis vis; - elf_symbol_index symindex; -}; - -#endif /* defined(YASM_OBJFMT_ELF_INTERNAL) */ - -extern const yasm_assoc_data_callback elf_section_data; -extern const yasm_assoc_data_callback elf_symrec_data; -extern const yasm_assoc_data_callback elf_ssym_symrec_data; - - -const elf_machine_handler *elf_set_arch(struct yasm_arch *arch, - yasm_symtab *symtab, - int bits_pref); - -yasm_symrec *elf_get_special_sym(const char *name, const char *parser); - -/* reloc functions */ -int elf_is_wrt_sym_relative(yasm_symrec *wrt); -int elf_is_wrt_pos_adjusted(yasm_symrec *wrt); -elf_reloc_entry *elf_reloc_entry_create(yasm_symrec *sym, - /*@null@*/ yasm_symrec *wrt, - yasm_intnum *addr, - int rel, - size_t valsize, - int is_GOT_sym); -void elf_reloc_entry_destroy(void *entry); - -/* strtab functions */ -elf_strtab_entry *elf_strtab_entry_create(const char *str); -void elf_strtab_entry_set_str(elf_strtab_entry *entry, const char *str); -elf_strtab_head *elf_strtab_create(void); -elf_strtab_entry *elf_strtab_append_str(elf_strtab_head *head, const char *str); -void elf_strtab_destroy(elf_strtab_head *head); -unsigned long elf_strtab_output_to_file(FILE *f, elf_strtab_head *head); - -/* symtab functions */ -elf_symtab_entry *elf_symtab_entry_create(elf_strtab_entry *name, - struct yasm_symrec *sym); -elf_symtab_head *elf_symtab_create(void); -void elf_symtab_append_entry(elf_symtab_head *symtab, elf_symtab_entry *entry); -void elf_symtab_insert_local_sym(elf_symtab_head *symtab, - elf_symtab_entry *entry); -void elf_symtab_destroy(elf_symtab_head *head); -unsigned long elf_symtab_assign_indices(elf_symtab_head *symtab); -unsigned long elf_symtab_write_to_file(FILE *f, elf_symtab_head *symtab, - yasm_errwarns *errwarns); -void elf_symtab_set_nonzero(elf_symtab_entry *entry, - struct yasm_section *sect, - elf_section_index sectidx, - elf_symbol_binding bind, - elf_symbol_type type, - struct yasm_expr *size, - elf_address *value); -void elf_sym_set_visibility(elf_symtab_entry *entry, - elf_symbol_vis vis); -void elf_sym_set_type(elf_symtab_entry *entry, elf_symbol_type type); -void elf_sym_set_size(elf_symtab_entry *entry, struct yasm_expr *size); -int elf_sym_in_table(elf_symtab_entry *entry); - -/* section header functions */ -elf_secthead *elf_secthead_create(elf_strtab_entry *name, - elf_section_type type, - elf_section_flags flags, - elf_address offset, - elf_size size); -void elf_secthead_destroy(elf_secthead *esd); -unsigned long elf_secthead_write_to_file(FILE *f, elf_secthead *esd, - elf_section_index sindex); -void elf_secthead_append_reloc(yasm_section *sect, elf_secthead *shead, - elf_reloc_entry *reloc); -elf_section_type elf_secthead_get_type(elf_secthead *shead); -void elf_secthead_set_typeflags(elf_secthead *shead, elf_section_type type, - elf_section_flags flags); -int elf_secthead_is_empty(elf_secthead *shead); -struct yasm_symrec *elf_secthead_get_sym(elf_secthead *shead); -unsigned long elf_secthead_get_align(const elf_secthead *shead); -unsigned long elf_secthead_set_align(elf_secthead *shead, unsigned long align); -elf_section_index elf_secthead_get_index(elf_secthead *shead); -elf_section_info elf_secthead_set_info(elf_secthead *shead, - elf_section_info info); -elf_section_index elf_secthead_set_index(elf_secthead *shead, - elf_section_index sectidx); -elf_section_index elf_secthead_set_link(elf_secthead *shead, - elf_section_index link); -elf_section_index elf_secthead_set_rel_index(elf_secthead *shead, - elf_section_index sectidx); -elf_strtab_entry *elf_secthead_set_rel_name(elf_secthead *shead, - elf_strtab_entry *entry); -elf_size elf_secthead_set_entsize(elf_secthead *shead, elf_size size); -struct yasm_symrec *elf_secthead_set_sym(elf_secthead *shead, - struct yasm_symrec *sym); -void elf_secthead_add_size(elf_secthead *shead, yasm_intnum *size); -char *elf_secthead_name_reloc_section(const char *basesect); -void elf_handle_reloc_addend(yasm_intnum *intn, - elf_reloc_entry *reloc, - unsigned long offset); -unsigned long elf_secthead_write_rel_to_file(FILE *f, elf_section_index symtab, - yasm_section *sect, - elf_secthead *esd, - elf_section_index sindex); -unsigned long elf_secthead_write_relocs_to_file(FILE *f, yasm_section *sect, - elf_secthead *shead, - yasm_errwarns *errwarns); -long elf_secthead_set_file_offset(elf_secthead *shead, long pos); - -/* program header function */ -unsigned long -elf_proghead_get_size(void); -unsigned long -elf_proghead_write_to_file(FILE *f, - elf_offset secthead_addr, - unsigned long secthead_count, - elf_section_index shstrtab_index); - -#endif /* ELF_H_INCLUDED */ +/* + * ELF object format helpers + * + * Copyright (C) 2003-2007 Michael Urman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ELF_H_INCLUDED +#define ELF_H_INCLUDED + +typedef struct elf_reloc_entry elf_reloc_entry; +typedef struct elf_reloc_head elf_reloc_head; +typedef struct elf_secthead elf_secthead; +typedef struct elf_strtab_entry elf_strtab_entry; +typedef struct elf_strtab_head elf_strtab_head; +typedef struct elf_symtab_entry elf_symtab_entry; +typedef struct elf_symtab_head elf_symtab_head; + +typedef struct elf_machine_handler elf_machine_handler; + +typedef unsigned long elf_address; +typedef unsigned long elf_offset; +typedef unsigned long elf_size; +typedef unsigned long elf_section_info; + +typedef enum { + ET_NONE = 0, + ET_REL = 1, /* Relocatable */ + ET_EXEC = 2, /* Executable */ + ET_DYN = 3, /* Shared object */ + ET_CORE = 4, /* Core */ + ET_LOOS = 0xfe00, /* Environment specific */ + ET_HIOS = 0xfeff, + ET_LOPROC = 0xff00, /* Processor specific */ + ET_HIPROC = 0xffff +} elf_file_type; + +typedef enum { + EM_NONE = 0, + EM_M32 = 1, /* AT&T WE 32100 */ + EM_SPARC = 2, /* SPARC */ + EM_386 = 3, /* Intel 80386 */ + EM_68K = 4, /* Motorola 68000 */ + EM_88K = 5, /* Motorola 88000 */ + EM_860 = 7, /* Intel 80860 */ + EM_MIPS = 8, /* MIPS RS3000 */ + + EM_S370 = 9, /* IBM System/370 */ + EM_MIPS_RS4_BE = 10, /* MIPS R4000 Big-Endian (dep)*/ + EM_PARISC = 15, /* HPPA */ + EM_SPARC32PLUS = 18, /* SPARC v8plus */ + EM_PPC = 20, /* PowerPC 32-bit */ + EM_PPC64 = 21, /* PowerPC 64-bit */ + EM_ARM = 40, /* ARM */ + EM_SPARCV9 = 43, /* SPARC v9 64-bit */ + EM_IA_64 = 50, /* Intel IA-64 */ + EM_X86_64 = 62, /* AMD x86-64 */ + EM_ALPHA = 0x9026 /* Alpha (no ABI) */ +} elf_machine; + +typedef enum { + ELFMAG0 = 0x7f, + ELFMAG1 = 0x45, + ELFMAG2 = 0x4c, + ELFMAG3 = 0x46 +} elf_magic; + +typedef enum { + EV_NONE = 0, /* invalid */ + EV_CURRENT = 1 /* current */ +} elf_version; + +typedef enum { + EI_MAG0 = 0, /* File id */ + EI_MAG1 = 1, + EI_MAG2 = 2, + EI_MAG3 = 3, + EI_CLASS = 4, /* File class */ + EI_DATA = 5, /* Data encoding */ + EI_VERSION = 6, /* File version */ + EI_OSABI = 7, /* OS and ABI */ + EI_ABIVERSION = 8, /* version of ABI */ + + EI_PAD = 9, /* Pad to end; start here */ + EI_NIDENT = 16 /* Sizeof e_ident[] */ +} elf_identification_index; + +typedef enum { + ELFOSABI_SYSV = 0, /* System V ABI */ + ELFOSABI_HPUX = 1, /* HP-UX os */ + ELFOSABI_STANDALONE = 255 /* Standalone / embedded app */ +} elf_osabi_index; + +typedef enum { + ELFCLASSNONE = 0, /* invalid */ + ELFCLASS32 = 1, /* 32-bit */ + ELFCLASS64 = 2 /* 64-bit */ +} elf_class; + +typedef enum { + ELFDATANONE = 0, + ELFDATA2LSB = 1, + ELFDATA2MSB = 2 +} elf_data_encoding; + +/* elf section types - index of semantics */ +typedef enum { + SHT_NULL = 0, /* inactive section - no associated data */ + SHT_PROGBITS = 1, /* defined by program for its own meaning */ + SHT_SYMTAB = 2, /* symbol table (primarily) for linking */ + SHT_STRTAB = 3, /* string table - symbols need names */ + SHT_RELA = 4, /* relocation entries w/ explicit addends */ + SHT_HASH = 5, /* symbol hash table - for dynamic linking */ + SHT_DYNAMIC = 6, /* information for dynamic linking */ + SHT_NOTE = 7, /* extra data marking the file somehow */ + SHT_NOBITS = 8, /* no stored data, but occupies runtime space */ + SHT_REL = 9, /* relocations entries w/o explicit addends */ + SHT_SHLIB = 10, /* reserved; unspecified semantics */ + SHT_DYNSYM = 11, /* like symtab, but more for dynamic linking */ + + SHT_LOOS = 0x60000000, /* reserved for environment specific use */ + SHT_HIOS = 0x6fffffff, + SHT_LOPROC = 0x70000000, /* reserved for processor specific semantics */ + SHT_HIPROC = 0x7fffffff/*, + SHT_LOUSER = 0x80000000,*/ /* reserved for applications; safe */ + /*SHT_HIUSER = 0xffffffff*/ +} elf_section_type; + +/* elf section flags - bitfield of attributes */ +typedef enum { + SHF_WRITE = 0x1, /* data should be writable at runtime */ + SHF_ALLOC = 0x2, /* occupies memory at runtime */ + SHF_EXECINSTR = 0x4, /* contains machine instructions */ + SHF_MERGE = 0x10, /* data can be merged */ + SHF_STRINGS = 0x20, /* contains 0-terminated strings */ + SHF_GROUP = 0x200, /* member of a section group */ + SHF_TLS = 0x400, /* thread local storage */ + SHF_MASKOS = 0x0f000000/*,*//* environment specific use */ + /*SHF_MASKPROC = 0xf0000000*/ /* bits reserved for processor specific needs */ +} elf_section_flags; + +/* elf section index - just the special ones */ +typedef enum { + SHN_UNDEF = 0, /* undefined symbol; requires other global */ + SHN_LORESERVE = 0xff00, /* reserved for various semantics */ + SHN_LOPROC = 0xff00, /* reserved for processor specific semantics */ + SHN_HIPROC = 0xff1f, + SHN_LOOS = 0xff20, /* reserved for environment specific use */ + SHN_HIOS = 0xff3f, + SHN_ABS = 0xfff1, /* associated symbols don't change on reloc */ + SHN_COMMON = 0xfff2, /* associated symbols refer to unallocated */ + SHN_HIRESERVE = 0xffff +} elf_section_index; + +/* elf symbol binding - index of visibility/behavior */ +typedef enum { + STB_LOCAL = 0, /* invisible outside defining file */ + STB_GLOBAL = 1, /* visible to all combined object files */ + STB_WEAK = 2, /* global but lower precedence */ + + STB_LOOS = 10, /* Environment specific use */ + STB_HIOS = 12, + STB_LOPROC = 13, /* reserved for processor specific semantics */ + STB_HIPROC = 15 +} elf_symbol_binding; + +/* elf symbol type - index of classifications */ +typedef enum { + STT_NOTYPE = 0, /* type not specified */ + STT_OBJECT = 1, /* data object such as a variable, array, etc */ + STT_FUNC = 2, /* a function or executable code */ + STT_SECTION = 3, /* a section: often for relocation, STB_LOCAL */ + STT_FILE = 4, /* often source filename: STB_LOCAL, SHN_ABS */ + STT_COMMON = 5, /* Uninitialized common block. */ + STT_TLS = 6, /* TLS object. */ + STT_NUM = 7, + + STT_LOOS = 10, /* Environment specific use */ + STT_HIOS = 12, + STT_LOPROC = 13, /* reserved for processor specific semantics */ + STT_HIPROC = 15 +} elf_symbol_type; + +typedef enum { + STN_UNDEF = 0 +} elf_symbol_index; + +/* elf symbol visibility - lower two bits of OTHER field */ +typedef enum { + STV_DEFAULT = 0, /* Default symbol visibility rules */ + STV_INTERNAL = 1, /* Processor specific hidden class */ + STV_HIDDEN = 2, /* Sym unavailable in other modules */ + STV_PROTECTED = 3 /* Not preemptable, not exported */ +} elf_symbol_vis; + + +/* internal only object definitions */ +#ifdef YASM_OBJFMT_ELF_INTERNAL + +#define ELF_VISIBILITY_MASK 0x03 +#define ELF_ST_VISIBILITY(v) ((v) & ELF_VISIBILITY_MASK) + +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) +#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t)) +#define ELF32_ST_OTHER(vis) ELF_ST_VISIBILITY(vis) + +#define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) +#define ELF64_R_INFO(s,t) (((s)<<32) + ((t) & 0xffffffffL)) +#define ELF64_ST_OTHER(vis) ELF_ST_VISIBILITY(vis) + +#define EHDR32_SIZE 52 +#define EHDR64_SIZE 64 +#define EHDR_MAXSIZE 64 + +#define SHDR32_SIZE 40 +#define SHDR64_SIZE 64 +#define SHDR_MAXSIZE 64 + +#define SYMTAB32_SIZE 16 +#define SYMTAB64_SIZE 24 +#define SYMTAB_MAXSIZE 24 + +#define SYMTAB32_ALIGN 4 +#define SYMTAB64_ALIGN 8 + +#define RELOC32_SIZE 8 +#define RELOC32A_SIZE 12 +#define RELOC64_SIZE 16 +#define RELOC64A_SIZE 24 +#define RELOC_MAXSIZE 24 + +#define RELOC32_ALIGN 4 +#define RELOC64_ALIGN 8 + + +/* elf relocation type - index of semantics + * + * A = Addend (r_addend for RELA, value at location for REL) + * B = Base address + * G = Offset into global offset table (GOT) + * GOT = Address of the global offset table (GOT) + * L = Location of procedure linkage table (PLT) + * P = Location of location being relocated (r_offset) + * S = Value of symbol + */ +typedef enum { + R_386_NONE = 0, /* none */ + R_386_32 = 1, /* word32, S + A */ + R_386_PC32 = 2, /* word32, S + A - P */ + R_386_GOT32 = 3, /* word32, G + A - P */ + R_386_PLT32 = 4, /* word32, L + A - P */ + R_386_COPY = 5, /* none */ + R_386_GLOB_DAT = 6, /* word32, S */ + R_386_JMP_SLOT = 7, /* word32, S */ + R_386_RELATIVE = 8, /* word32, B + A */ + R_386_GOTOFF = 9, /* word32, S + A - GOT */ + R_386_GOTPC = 10, /* word32, GOT + A - P */ + R_386_TLS_TPOFF = 14, /* Negative offset in static TLS block (GNU + version) */ + R_386_TLS_IE = 15, /* Absolute address of GOT entry for negative + static TLS block offset */ + R_386_TLS_GOTIE = 16, /* GOT entry for negative static TLS block + offset */ + R_386_TLS_LE = 17, /* Negative offset relative to static TLS + (GNU version) */ + R_386_TLS_GD = 18, /* Direct 32 bit for GNU version of GD TLS */ + R_386_TLS_LDM = 19, /* Direct 32 bit for GNU version of LD TLS + in LE code */ + R_386_16 = 20, /* word16, S + A (GNU extension) */ + R_386_PC16 = 21, /* word16, S + A - P (GNU extension) */ + R_386_8 = 22, /* word8, S + A (GNU extension) */ + R_386_PC8 = 23, /* word8, S + A - P (GNU extension) */ + R_386_TLS_GD_32 = 24, /* Direct 32 bit for GD TLS */ + R_386_TLS_GD_PUSH = 25, /* Tag for pushl in GD TLS code */ + R_386_TLS_GD_CALL = 26, /* Relocation for call to */ + R_386_TLS_GD_POP = 27, /* Tag for popl in GD TLS code */ + R_386_TLS_LDM_32 = 28, /* Direct 32 bit for local dynamic code */ + R_386_TLS_LDM_PUSH = 29, /* Tag for pushl in LDM TLS code */ + R_386_TLS_LDM_CALL = 30, /* Relocation for call to */ + R_386_TLS_LDM_POP = 31, /* Tag for popl in LDM TLS code */ + R_386_TLS_LDO_32 = 32, /* Offset relative to TLS block */ + R_386_TLS_IE_32 = 33, /* GOT entry for static TLS block */ + R_386_TLS_LE_32 = 34, /* Offset relative to static TLS block */ + R_386_TLS_DTPMOD32 = 35, /* ID of module containing symbol */ + R_386_TLS_DTPOFF32 = 36, /* Offset in TLS block */ + R_386_TLS_TPOFF32 = 37, /* Offset in static TLS block */ + R_386_TLS_GOTDESC = 39, + R_386_TLS_DESC_CALL = 40, + R_386_TLS_DESC = 41 +} elf_386_relocation_type; + +typedef enum { + R_X86_64_NONE = 0, /* none */ + R_X86_64_64 = 1, /* word64, S + A */ + R_X86_64_PC32 = 2, /* word32, S + A - P */ + R_X86_64_GOT32 = 3, /* word32, G + A */ + R_X86_64_PLT32 = 4, /* word32, L + A - P */ + R_X86_64_COPY = 5, /* none */ + R_X86_64_GLOB_DAT = 6, /* word64, S, set GOT entry to data address */ + R_X86_64_JMP_SLOT = 7, /* word64, S, set GOT entry to code address */ + R_X86_64_RELATIVE = 8, /* word64, B + A */ + R_X86_64_GOTPCREL = 9, /* word32, G + GOT + A - P */ + R_X86_64_32 = 10, /* word32 (zero extend), S + A */ + R_X86_64_32S = 11, /* word32 (sign extend), S + A */ + R_X86_64_16 = 12, /* word16, S + A */ + R_X86_64_PC16 = 13, /* word16, S + A - P */ + R_X86_64_8 = 14, /* word8, S + A */ + R_X86_64_PC8 = 15, /* word8, S + A - P */ + R_X86_64_DPTMOD64 = 16, /* word64, ID of module containing symbol */ + R_X86_64_DTPOFF64 = 17, /* word64, offset in TLS block */ + R_X86_64_TPOFF64 = 18, /* word64, offset in initial TLS block */ + R_X86_64_TLSGD = 19, /* word32, PC-rel offset to GD GOT block */ + R_X86_64_TLSLD = 20, /* word32, PC-rel offset to LD GOT block */ + R_X86_64_DTPOFF32 = 21, /* word32, offset to TLS block */ + R_X86_64_GOTTPOFF = 22, /* word32, PC-rel offset to IE GOT entry */ + R_X86_64_TPOFF32 = 23, /* word32, offset in initial TLS block */ + R_X86_64_PC64 = 24, /* word64, PC relative */ + R_X86_64_GOTOFF64 = 25, /* word64, offset to GOT */ + R_X86_64_GOTPC32 = 26, /* word32, signed pc relative to GOT */ + R_X86_64_GOT64 = 27, /* word64, GOT entry offset */ + R_X86_64_GOTPCREL64 = 28, /* word64, signed pc relative to GOT entry */ + R_X86_64_GOTPC64 = 29, /* word64, signed pc relative to GOT */ + R_X86_64_GOTPLT64 = 30, /* like GOT64, but indicates PLT entry needed */ + R_X86_64_PLTOFF64 = 31, /* word64, GOT relative offset to PLT entry */ + R_X86_64_GOTPC32_TLSDESC = 34, /* GOT offset for TLS descriptor */ + R_X86_64_TLSDESC_CALL = 35, /* Marker for call through TLS descriptor */ + R_X86_64_TLSDESC = 36 /* TLS descriptor */ +} elf_x86_64_relocation_type; + +struct elf_secthead { + elf_section_type type; + elf_section_flags flags; + elf_address offset; + yasm_intnum *size; + elf_section_index link; + elf_section_info info; /* see note ESD1 */ + unsigned long align; + elf_size entsize; + + yasm_symrec *sym; + elf_strtab_entry *name; + elf_section_index index; + + elf_strtab_entry *rel_name; + elf_section_index rel_index; + elf_address rel_offset; + unsigned long nreloc; +}; + +/* Note ESD1: + * for section types SHT_REL, SHT_RELA: + * link -> index of associated symbol table + * info -> index of relocated section + * for section types SHT_SYMTAB, SHT_DYNSYM: + * link -> index of associated string table + * info -> 1+index of last "local symbol" (bind == STB_LOCAL) + * (for section type SHT_DNAMIC: + * link -> index of string table + * info -> 0 ) + * (for section type SHT_HASH: + * link -> index of symbol table to which hash applies + * info -> 0 ) + * for all others: + * link -> SHN_UNDEF + * info -> 0 + */ + +struct elf_reloc_entry { + yasm_reloc reloc; + int rtype_rel; + size_t valsize; + yasm_intnum *addend; + /*@null@*/ yasm_symrec *wrt; + int is_GOT_sym; +}; + +STAILQ_HEAD(elf_strtab_head, elf_strtab_entry); +struct elf_strtab_entry { + STAILQ_ENTRY(elf_strtab_entry) qlink; + unsigned long index; + char *str; +}; + +STAILQ_HEAD(elf_symtab_head, elf_symtab_entry); +struct elf_symtab_entry { + STAILQ_ENTRY(elf_symtab_entry) qlink; + int in_table; + yasm_symrec *sym; + yasm_section *sect; + elf_strtab_entry *name; + elf_address value; + /*@dependent@*/ yasm_expr *xsize; + elf_size size; + elf_section_index index; + elf_symbol_binding bind; + elf_symbol_type type; + elf_symbol_vis vis; + elf_symbol_index symindex; +}; + +#endif /* defined(YASM_OBJFMT_ELF_INTERNAL) */ + +extern const yasm_assoc_data_callback elf_section_data; +extern const yasm_assoc_data_callback elf_symrec_data; +extern const yasm_assoc_data_callback elf_ssym_symrec_data; + + +const elf_machine_handler *elf_set_arch(struct yasm_arch *arch, + yasm_symtab *symtab, + int bits_pref); + +yasm_symrec *elf_get_special_sym(const char *name, const char *parser); + +/* reloc functions */ +int elf_is_wrt_sym_relative(yasm_symrec *wrt); +int elf_is_wrt_pos_adjusted(yasm_symrec *wrt); +elf_reloc_entry *elf_reloc_entry_create(yasm_symrec *sym, + /*@null@*/ yasm_symrec *wrt, + yasm_intnum *addr, + int rel, + size_t valsize, + int is_GOT_sym); +void elf_reloc_entry_destroy(void *entry); + +/* strtab functions */ +elf_strtab_entry *elf_strtab_entry_create(const char *str); +void elf_strtab_entry_set_str(elf_strtab_entry *entry, const char *str); +elf_strtab_head *elf_strtab_create(void); +elf_strtab_entry *elf_strtab_append_str(elf_strtab_head *head, const char *str); +void elf_strtab_destroy(elf_strtab_head *head); +unsigned long elf_strtab_output_to_file(FILE *f, elf_strtab_head *head); + +/* symtab functions */ +elf_symtab_entry *elf_symtab_entry_create(elf_strtab_entry *name, + struct yasm_symrec *sym); +elf_symtab_head *elf_symtab_create(void); +void elf_symtab_append_entry(elf_symtab_head *symtab, elf_symtab_entry *entry); +void elf_symtab_insert_local_sym(elf_symtab_head *symtab, + elf_symtab_entry *entry); +void elf_symtab_destroy(elf_symtab_head *head); +unsigned long elf_symtab_assign_indices(elf_symtab_head *symtab); +unsigned long elf_symtab_write_to_file(FILE *f, elf_symtab_head *symtab, + yasm_errwarns *errwarns); +void elf_symtab_set_nonzero(elf_symtab_entry *entry, + struct yasm_section *sect, + elf_section_index sectidx, + elf_symbol_binding bind, + elf_symbol_type type, + struct yasm_expr *size, + elf_address *value); +void elf_sym_set_visibility(elf_symtab_entry *entry, + elf_symbol_vis vis); +void elf_sym_set_type(elf_symtab_entry *entry, elf_symbol_type type); +void elf_sym_set_size(elf_symtab_entry *entry, struct yasm_expr *size); +int elf_sym_in_table(elf_symtab_entry *entry); + +/* section header functions */ +elf_secthead *elf_secthead_create(elf_strtab_entry *name, + elf_section_type type, + elf_section_flags flags, + elf_address offset, + elf_size size); +void elf_secthead_destroy(elf_secthead *esd); +unsigned long elf_secthead_write_to_file(FILE *f, elf_secthead *esd, + elf_section_index sindex); +void elf_secthead_append_reloc(yasm_section *sect, elf_secthead *shead, + elf_reloc_entry *reloc); +elf_section_type elf_secthead_get_type(elf_secthead *shead); +void elf_secthead_set_typeflags(elf_secthead *shead, elf_section_type type, + elf_section_flags flags); +int elf_secthead_is_empty(elf_secthead *shead); +struct yasm_symrec *elf_secthead_get_sym(elf_secthead *shead); +unsigned long elf_secthead_get_align(const elf_secthead *shead); +unsigned long elf_secthead_set_align(elf_secthead *shead, unsigned long align); +elf_section_index elf_secthead_get_index(elf_secthead *shead); +elf_section_info elf_secthead_set_info(elf_secthead *shead, + elf_section_info info); +elf_section_index elf_secthead_set_index(elf_secthead *shead, + elf_section_index sectidx); +elf_section_index elf_secthead_set_link(elf_secthead *shead, + elf_section_index link); +elf_section_index elf_secthead_set_rel_index(elf_secthead *shead, + elf_section_index sectidx); +elf_strtab_entry *elf_secthead_set_rel_name(elf_secthead *shead, + elf_strtab_entry *entry); +elf_size elf_secthead_set_entsize(elf_secthead *shead, elf_size size); +struct yasm_symrec *elf_secthead_set_sym(elf_secthead *shead, + struct yasm_symrec *sym); +void elf_secthead_add_size(elf_secthead *shead, yasm_intnum *size); +char *elf_secthead_name_reloc_section(const char *basesect); +void elf_handle_reloc_addend(yasm_intnum *intn, + elf_reloc_entry *reloc, + unsigned long offset); +unsigned long elf_secthead_write_rel_to_file(FILE *f, elf_section_index symtab, + yasm_section *sect, + elf_secthead *esd, + elf_section_index sindex); +unsigned long elf_secthead_write_relocs_to_file(FILE *f, yasm_section *sect, + elf_secthead *shead, + yasm_errwarns *errwarns); +long elf_secthead_set_file_offset(elf_secthead *shead, long pos); + +/* program header function */ +unsigned long +elf_proghead_get_size(void); +unsigned long +elf_proghead_write_to_file(FILE *f, + elf_offset secthead_addr, + unsigned long secthead_count, + elf_section_index shstrtab_index); + +#endif /* ELF_H_INCLUDED */ diff --git a/contrib/tools/yasm/modules/objfmts/macho/macho-objfmt.c b/contrib/tools/yasm/modules/objfmts/macho/macho-objfmt.c index 0a48d9b905..ad0215cff3 100644 --- a/contrib/tools/yasm/modules/objfmts/macho/macho-objfmt.c +++ b/contrib/tools/yasm/modules/objfmts/macho/macho-objfmt.c @@ -1,1620 +1,1620 @@ -/* - * Mac OS X ABI Mach-O File Format - * - * Copyright (C) 2007 Henryk Richter, built upon xdf objfmt (C) Peter Johnson - * - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/* - notes: This implementation is rather basic. There are several implementation - issues to be sorted out for full compliance and error resilience. - Some examples are given below (nasm syntax). - - 1) section placement - Mach-O requires BSS sections to be placed last in object files. This - has to be done manually. - Example: - - section .text - mov rax,[qword foo] - section .data - dw 0 - section .bss - foo dw 0 - - 2) addressing issues - - 2.1) symbol relative relocation (i.e. mov eax,[foo wrt bar]) - Not implemented yet. - - 2.2) data referencing in 64 bit mode - While ELF allows 32 bit absolute relocations in 64 bit mode, Mach-O - does not. Therefore code like - lea rbx,[_foo] ;48 8d 1c 25 00 00 00 00 - mov rcx,[_bar] ;48 8b 0c 25 00 00 00 00 - with a 32 bit address field cannot be relocated into an address >= 0x100000000 (OSX actually - uses that). - - Actually, the only register where a 64 bit displacement is allowed in x86-64, is rax - as in the example 1). - - A plausible workaround is either classic PIC (like in C), which is in turn - not implemented in this object format. The recommended was is PC relative - code (called RIP-relative in x86-64). So instead of the lines above, just write: - lea rbx,[_foo wrt rip] - mov rcx,[_bar wrt rip] - - 2.3) section/data alignment - Normally, you specify sections with a specific alignment - and get your data layed out as desired. Unfortunately, the - linker in MacOS X seems to ignore the section alignment requests. - The workaround is an explicit alignment at the end of the text section. - - section .text - movdqa xmm0,[_foo wrt rip] - - align 16 - section .data align=16 - _foo dw 32,32,32,32,32,32,32,32 - - FIXME: perform that operation implicitly! - - 2.4) cross section symbol differences unsupported in current implementation - [extern foo] - [extern bar] - section .data - dq bar-foo - - Will currently produce an error though the necessary means are provided - by the Mach-O specification. - -*/ - -#include <util.h> - -#include <libyasm.h> - -/* MACH-O DEFINES */ -/* Mach-O in-file header structure sizes (32 BIT, see below for 64 bit defs) */ -#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 - -/* 64 bit sizes */ -#define MACHO_HEADER64_SIZE 32 -#define MACHO_SEGCMD64_SIZE 72 -#define MACHO_SECTCMD64_SIZE 80 -#define MACHO_NLIST64_SIZE 16 -#define MACHO_RELINFO64_SIZE 8 - - -/* Mach-O file header values */ -#define MH_MAGIC 0xfeedface -#define MH_MAGIC_64 0xfeedfacf - -/* CPU machine type */ -#define CPU_TYPE_I386 7 /* x86 platform */ -#define CPU_TYPE_X86_64 (CPU_TYPE_I386|CPU_ARCH_ABI64) -#define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ - -/* CPU machine subtype, e.g. processor */ -#define CPU_SUBTYPE_I386_ALL 3 /* all-x86 compatible */ -#define CPU_SUBTYPE_X86_64_ALL CPU_SUBTYPE_I386_ALL -#define CPU_SUBTYPE_386 3 -#define CPU_SUBTYPE_486 4 -#define CPU_SUBTYPE_486SX (4 + 128) -#define CPU_SUBTYPE_586 5 -#define CPU_SUBTYPE_INTEL(f, m) ((f) + ((m) << 4)) -#define CPU_SUBTYPE_PENT CPU_SUBTYPE_INTEL(5, 0) -#define CPU_SUBTYPE_PENTPRO CPU_SUBTYPE_INTEL(6, 1) -#define CPU_SUBTYPE_PENTII_M3 CPU_SUBTYPE_INTEL(6, 3) -#define CPU_SUBTYPE_PENTII_M5 CPU_SUBTYPE_INTEL(6, 5) -#define CPU_SUBTYPE_PENTIUM_4 CPU_SUBTYPE_INTEL(10, 0) - -#define CPU_SUBTYPE_INTEL_FAMILY(x) ((x) & 15) -#define CPU_SUBTYPE_INTEL_FAMILY_MAX 15 - -#define CPU_SUBTYPE_INTEL_MODEL(x) ((x) >> 4) -#define CPU_SUBTYPE_INTEL_MODEL_ALL 0 - -#define MH_OBJECT 0x1 /* object file */ - -#define LC_SEGMENT 0x1 /* segment load command */ -#define LC_SYMTAB 0x2 /* symbol table load command */ -#define LC_SEGMENT_64 0x19 /* segment load command */ - - -#define VM_PROT_NONE 0x00 -#define VM_PROT_READ 0x01 -#define VM_PROT_WRITE 0x02 -#define VM_PROT_EXECUTE 0x04 - -#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) - -#define SECTION_TYPE 0x000000ff /* section type mask */ -#define SECTION_ATTRIBUTES 0xffffff00UL/* section attributes mask */ - -#define S_REGULAR 0x0 /* standard section */ -#define S_ZEROFILL 0x1 /* zerofill, in-memory only */ -#define S_CSTRING_LITERALS 0x2 /* literal C strings */ -#define S_4BYTE_LITERALS 0x3 /* only 4-byte literals */ -#define S_8BYTE_LITERALS 0x4 /* only 8-byte literals */ -#define S_LITERAL_POINTERS 0x5 /* only pointers to literals */ -#define S_NON_LAZY_SYMBOL_POINTERS 0x6 /* only non-lazy symbol pointers */ -#define S_LAZY_SYMBOL_POINTERS 0x7 /* only lazy symbol pointers */ -#define S_SYMBOL_STUBS 0x8 /* only symbol stubs; byte size of - * stub in the reserved2 field */ -#define S_MOD_INIT_FUNC_POINTERS 0x9 /* only function pointers for init */ -#define S_MOD_TERM_FUNC_POINTERS 0xa /* only function pointers for term */ -#define S_COALESCED 0xb /* symbols that are to be coalesced */ -#define S_GB_ZEROFILL 0xc /* >4GB zero fill on demand section */ -#define S_INTERPOSING 0xd /* only pairs of function pointers for - * interposing */ -#define S_16BYTE_LITERALS 0xe /* only 16 byte literals */ - -#define S_ATTR_DEBUG 0x02000000 /* a debug section */ -#define SECTION_ATTRIBUTES_SYS 0x00ffff00 /* system setable attributes */ -#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 /* section contains some - * machine instructions */ -#define S_ATTR_EXT_RELOC 0x00000200 /* section has external - * relocation entries */ -#define S_ATTR_LOC_RELOC 0x00000100 /* section has local - * relocation entries */ - -#define SECTION_ATTRIBUTES_USR 0xff000000UL /* User setable attributes */ -#define S_ATTR_PURE_INSTRUCTIONS 0x80000000UL /* only true machine insns */ -#define S_ATTR_NO_TOC 0x40000000UL /* coalesced symbols that are - * not to be in a ranlib table - * of contents */ -#define S_ATTR_STRIP_STATIC_SYMS 0x20000000UL /* ok to strip static symbols - * in this section in files - * with the MH_DYLDLINK flag */ -#define S_ATTR_NO_DEAD_STRIP 0x10000000UL /* no dead stripping */ -#define S_ATTR_LIVE_SUPPORT 0x08000000UL /* blocks are live if they - * reference live blocks */ -#define S_ATTR_SELF_MODIFYING_CODE 0x04000000UL /* Used with i386 code stubs - * written on by dyld */ - -/* macho references symbols in different ways whether they are linked at - * runtime (LAZY, read library functions) or at link time (NON_LAZY, mostly - * data) - * - * TODO: proper support for dynamically linkable modules would require the - * __import sections as well as the dsymtab command - */ -#define REFERENCE_FLAG_UNDEFINED_NON_LAZY 0x0 -#define REFERENCE_FLAG_UNDEFINED_LAZY 0x1 - -#define align(x, y) \ - (((x) + (y) - 1) & ~((y) - 1)) /* align x to multiple of y */ - -#define align32(x) \ - align(x, 4) /* align x to 32 bit boundary */ - -#define macho_MAGIC 0x87654322 - -/* Symbol table type field bit masks */ -#define N_STAB 0xe0 /* mask indicating stab entry */ -#define N_PEXT 0x10 /* private external bit */ -#define N_TYPE 0x0e /* mask for all the type bits */ -#define N_EXT 0x01 /* external (global) bit */ - -/* Symbol table type field values */ -#define N_UNDF 0x00 /* undefined */ -#define N_ABS 0x02 /* absolute address */ -#define N_SECT 0x0e /* symbol is defined in a section */ - -#define NO_SECT 0 /* no section for symbol in nlist */ - -#define REGULAR_OUTBUF_SIZE 1024 - - -typedef struct macho_reloc { - yasm_reloc reloc; - int pcrel; - int length; - int ext; - enum reloc_type_x86_64 { - /* x86 relocations */ - GENERIC_RELOC_VANILLA = 0, /* generic relocation */ - GENERIC_RELOC_PAIR = 1, /* Only follows a GENERIC_RELOC_SECTDIFF */ - GENERIC_RELOC_SECTDIFF = 2, - GENERIC_RELOC_PB_LA_PTR = 3, /* prebound lazy pointer */ - GENERIC_RELOC_LOCAL_SECTDIFF = 4, - - /* x86-64 relocations */ - X86_64_RELOC_UNSIGNED = 0, /* for absolute addresses */ - X86_64_RELOC_SIGNED = 1, /* for signed 32-bit displacement */ - X86_64_RELOC_BRANCH = 2, /* a CALL/JMP insn with 32-bit disp */ - X86_64_RELOC_GOT_LOAD = 3, /* a MOVQ load of a GOT entry */ - X86_64_RELOC_GOT = 4, /* other GOT references */ - X86_64_RELOC_SUBTRACTOR = 5, /* must be followed by a X86_64_RELOC_UNSIGNED */ - X86_64_RELOC_SIGNED_1 = 6, /* signed 32-bit disp, -1 addend */ - X86_64_RELOC_SIGNED_2 = 7, /* signed 32-bit disp, -2 addend */ - X86_64_RELOC_SIGNED_4 = 8 /* signed 32-bit disp, -4 addend */ - } type; -} macho_reloc; - -typedef struct macho_section_data { - /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ - long scnum; /* section number (0=first section) */ - /*@only@*/ char *segname; /* segment name in file */ - /*@only@*/ char *sectname; /* section name in file */ - unsigned long flags; /* S_* flags */ - unsigned long size; /* size of raw data (section data) in bytes */ - unsigned long offset; /* offset in raw data within file in bytes */ - unsigned long vmoff; /* memory offset */ - unsigned long nreloc; /* number of relocation entries */ - unsigned int extreloc; /* external relocations present (0/1) */ -} macho_section_data; - - -typedef struct macho_symrec_data { - unsigned long index; /* index in output order */ - yasm_intnum *value; /* valid after writing symtable to file */ - unsigned long length; /* length + 1 (plus auto underscore) */ -} macho_symrec_data; - - -typedef struct yasm_objfmt_macho { - yasm_objfmt_base objfmt; /* base structure */ - - long parse_scnum; /* sect numbering in parser */ - int bits; /* 32 / 64 */ - - yasm_symrec *gotpcrel_sym; /* ..gotpcrel */ -} yasm_objfmt_macho; - - -typedef struct macho_objfmt_output_info { - yasm_object *object; - yasm_objfmt_macho *objfmt_macho; - yasm_errwarns *errwarns; - /*@dependent@ */ FILE *f; - /*@only@ */ unsigned char *buf; - yasm_section *sect; - /*@dependent@ */ macho_section_data *msd; - - unsigned int is_64; /* write object in 64 bit mode */ - - /* vmsize and filesize available after traversing section count routine */ - unsigned long vmsize; /* raw size of all sections (including BSS) */ - unsigned long filesize; /* size of sections in file (excluding BSS) */ - unsigned long offset; /* offset within file */ - - /* forward offset tracking */ - unsigned long rel_base; /* first relocation in file */ - unsigned long s_reloff; /* in-file offset to relocations */ - - unsigned long indx; /* current symbol size in bytes (name length+1) */ - unsigned long symindex; /* current symbol index in output order */ - int all_syms; /* outputting all symbols? */ - unsigned long strlength; /* length of all strings */ -} macho_objfmt_output_info; - - -static void macho_section_data_destroy(/*@only@*/ void *d); -static void macho_section_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback macho_section_data_cb = { - macho_section_data_destroy, - macho_section_data_print -}; - -static void macho_symrec_data_destroy(/*@only@*/ void *d); -static void macho_symrec_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback macho_symrec_data_cb = { - macho_symrec_data_destroy, - macho_symrec_data_print -}; - -yasm_objfmt_module yasm_macho_LTX_objfmt; -yasm_objfmt_module yasm_macho32_LTX_objfmt; -yasm_objfmt_module yasm_macho64_LTX_objfmt; - -static yasm_objfmt * -macho_objfmt_create_common(yasm_object *object, yasm_objfmt_module *module, - int bits_pref) -{ - yasm_objfmt_macho *objfmt_macho = yasm_xmalloc(sizeof(yasm_objfmt_macho)); - - objfmt_macho->objfmt.module = module; - - /* Only support x86 arch for now */ - if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") != 0) { - yasm_xfree(objfmt_macho); - return NULL; - } - - /* Support x86 and amd64 machines of x86 arch */ - if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), "x86") == 0 && - (bits_pref == 0 || bits_pref == 32)) { - objfmt_macho->bits = 32; - objfmt_macho->gotpcrel_sym = NULL; - } else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), - "amd64") == 0 && - (bits_pref == 0 || bits_pref == 64)) { - objfmt_macho->bits = 64; - /* FIXME: misuse of NULL bytecode */ - objfmt_macho->gotpcrel_sym = - yasm_symtab_define_label(object->symtab, "..gotpcrel", NULL, 0, 0); - } else { - yasm_xfree(objfmt_macho); - return NULL; - } - - objfmt_macho->parse_scnum = 0; /* section numbering starts at 0 */ - return (yasm_objfmt *)objfmt_macho; -} - -static yasm_objfmt * -macho_objfmt_create(yasm_object *object) -{ - yasm_objfmt *objfmt; - yasm_objfmt_macho *objfmt_macho; - - objfmt = macho_objfmt_create_common(object, &yasm_macho_LTX_objfmt, 0); - if (objfmt) { - objfmt_macho = (yasm_objfmt_macho *)objfmt; - /* Figure out which bitness of object format to use */ - if (objfmt_macho->bits == 32) - objfmt_macho->objfmt.module = &yasm_macho32_LTX_objfmt; - else if (objfmt_macho->bits == 64) - objfmt_macho->objfmt.module = &yasm_macho64_LTX_objfmt; - } - return objfmt; -} - -static yasm_objfmt * -macho32_objfmt_create(yasm_object *object) -{ - return macho_objfmt_create_common(object, &yasm_macho32_LTX_objfmt, 32); -} - -static yasm_objfmt * -macho64_objfmt_create(yasm_object *object) -{ - return macho_objfmt_create_common(object, &yasm_macho64_LTX_objfmt, 64); -} - -static int -macho_objfmt_output_value(yasm_value *value, unsigned char *buf, - unsigned int destsize, unsigned long offset, - yasm_bytecode *bc, int warn, /*@null@*/ void *d) -{ - /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; - yasm_objfmt_macho *objfmt_macho; - /*@dependent@*/ /*@null@*/ yasm_intnum *intn; - unsigned long intn_minus = 0, intn_plus = 0; - int retval; - unsigned int valsize = value->size; - macho_reloc *reloc = NULL; - - assert(info != NULL); - objfmt_macho = info->objfmt_macho; - - if (value->abs) - value->abs = yasm_expr_simplify(value->abs, 1); - - /* Try to output constant and PC-relative section-local first. - * Note this does NOT output any value with a SEG, WRT, external, - * cross-section, or non-PC-relative reference (those are handled below). - */ - switch (yasm_value_output_basic(value, buf, destsize, bc, warn, - info->object->arch)) { - case -1: - return 1; - case 0: - break; - default: - return 0; - } - - if (value->section_rel) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("macho: relocation too complex for current implementation")); - return 1; - } - - if (value->rel) { - yasm_sym_vis vis = yasm_symrec_get_visibility(value->rel); - - reloc = yasm_xcalloc(sizeof(macho_reloc), 1); - reloc->reloc.addr = yasm_intnum_create_uint(bc->offset + offset); - reloc->reloc.sym = value->rel; - switch (valsize) { - case 64: - reloc->length = 3; - break; - case 32: - reloc->length = 2; - break; - case 16: - reloc->length = 1; - break; - case 8: - reloc->length = 0; - break; - default: - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("macho: relocation size unsupported")); - yasm_xfree(reloc); - return 1; - } - reloc->pcrel = 0; - reloc->ext = 0; - reloc->type = GENERIC_RELOC_VANILLA; - /* R_ABS */ - - if (value->rshift > 0) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("macho: shifted relocations not supported")); - yasm_xfree(reloc); - return 1; - } - - if (value->seg_of) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("macho: SEG not supported")); - yasm_xfree(reloc); - return 1; - } - - if (value->curpos_rel && objfmt_macho->gotpcrel_sym && - value->wrt == objfmt_macho->gotpcrel_sym) { - reloc->type = X86_64_RELOC_GOT; - value->wrt = NULL; - } else if (value->wrt) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("macho: invalid WRT")); - yasm_xfree(reloc); - return 1; - } - - if (value->curpos_rel) { - reloc->pcrel = 1; - if (!info->is_64) { - /* Adjust to start of section, so subtract out the bytecode - * offset. - */ - intn_minus = bc->offset; - } else { - /* Add in the offset plus value size to end up with 0. */ - intn_plus = offset+destsize; - if (reloc->type == X86_64_RELOC_GOT) { - /* XXX: This is a hack */ - if (offset >= 2 && buf[-2] == 0x8B) - reloc->type = X86_64_RELOC_GOT_LOAD; - } else if (value->jump_target) - reloc->type = X86_64_RELOC_BRANCH; - else - reloc->type = X86_64_RELOC_SIGNED; - } - } else if (info->is_64) { - if (valsize == 32) { - yasm_error_set(YASM_ERROR_NOT_CONSTANT, - N_("macho: sorry, cannot apply 32 bit absolute relocations in 64 bit mode, consider \"[_symbol wrt rip]\" for mem access, \"qword\" and \"dq _foo\" for pointers.")); - return 1; - } - reloc->type = X86_64_RELOC_UNSIGNED; - } - - /* It seems that x86-64 objects need to have all extern relocs? */ - if (info->is_64) - reloc->ext = 1; - - if ((vis & YASM_SYM_EXTERN) || (vis & YASM_SYM_COMMON)) { - reloc->ext = 1; - info->msd->extreloc = 1; /* section has external relocations */ - } else if (!info->is_64) { - /*@dependent@*/ /*@null@*/ yasm_bytecode *sym_precbc; - - /* Local symbols need valued to their actual address */ - if (yasm_symrec_get_label(value->rel, &sym_precbc)) { - yasm_section *sym_sect = yasm_bc_get_section(sym_precbc); - /*@null@*/ macho_section_data *msd; - msd = yasm_section_get_data(sym_sect, &macho_section_data_cb); - assert(msd != NULL); - intn_plus += msd->vmoff + yasm_bc_next_offset(sym_precbc); - } - } - - info->msd->nreloc++; - /*printf("reloc %s type %d ",yasm_symrec_get_name(reloc->reloc.sym),reloc->type);*/ - yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); - } - - if (intn_minus <= intn_plus) - intn = yasm_intnum_create_uint(intn_plus-intn_minus); - else { - intn = yasm_intnum_create_uint(intn_minus-intn_plus); - yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); - } - - if (value->abs) { - yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); - - if (!intn2) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("macho: relocation too complex")); - yasm_intnum_destroy(intn); - return 1; - } - yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); - } - - retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, - valsize, 0, bc, warn); - /*printf("val %ld\n",yasm_intnum_get_int(intn));*/ - yasm_intnum_destroy(intn); - return retval; -} - -static int -macho_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) -{ - /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; - /*@null@*/ /*@only@*/ unsigned char *bigbuf; - unsigned long size = REGULAR_OUTBUF_SIZE; - int gap; - - assert(info != NULL); - - bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, - macho_objfmt_output_value, NULL); - - /* Don't bother doing anything else if size ended up being 0. */ - if (size == 0) { - if (bigbuf) - yasm_xfree(bigbuf); - return 0; - } - - /* Warn that gaps are converted to 0 and write out the 0's. */ - if (gap) { - unsigned long left; - - yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, - N_("uninitialized space: zeroing")); - /* Write out in chunks */ - memset(info->buf, 0, REGULAR_OUTBUF_SIZE); - left = size; - while (left > REGULAR_OUTBUF_SIZE) { - fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); - left -= REGULAR_OUTBUF_SIZE; - } - fwrite(info->buf, left, 1, info->f); - } else { - /* Output buf (or bigbuf if non-NULL) to file */ - fwrite(bigbuf ? bigbuf : info->buf, (size_t) size, 1, info->f); - } - - /* If bigbuf was allocated, free it */ - if (bigbuf) - yasm_xfree(bigbuf); - - return 0; -} - -static int -macho_objfmt_output_section(yasm_section *sect, /*@null@ */ void *d) -{ - /*@null@ */ macho_objfmt_output_info *info = - (macho_objfmt_output_info *) d; - /*@dependent@ *//*@null@ */ macho_section_data *msd; - - assert(info != NULL); - msd = yasm_section_get_data(sect, &macho_section_data_cb); - assert(msd != NULL); - - if (!(msd->flags & S_ZEROFILL)) { - /* Output non-BSS sections */ - info->sect = sect; - info->msd = msd; - yasm_section_bcs_traverse(sect, info->errwarns, info, - macho_objfmt_output_bytecode); - } - return 0; -} - -static int -macho_objfmt_output_relocs(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ macho_section_data *msd; - macho_reloc *reloc; - - reloc = (macho_reloc *)yasm_section_relocs_first(sect); - while (reloc) { - unsigned char *localbuf = info->buf; - /*@null@*/ macho_symrec_data *xsymd; - unsigned long symnum; - - xsymd = yasm_symrec_get_data(reloc->reloc.sym, &macho_symrec_data_cb); - yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); - localbuf += 4; /* address of relocation */ - - if (reloc->ext) - symnum = xsymd->index; - else { - /* find section where the symbol relates to */ - /*@dependent@*/ /*@null@*/ yasm_section *dsect; - /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; - symnum = 0; /* default to absolute */ - if (yasm_symrec_get_label(reloc->reloc.sym, &precbc) && - (dsect = yasm_bc_get_section(precbc)) && - (msd = yasm_section_get_data(dsect, &macho_section_data_cb))) - symnum = msd->scnum+1; - } - YASM_WRITE_32_L(localbuf, - (symnum & 0x00ffffff) | - (((unsigned long)reloc->pcrel & 1) << 24) | - (((unsigned long)reloc->length & 3) << 25) | - (((unsigned long)reloc->ext & 1) << 27) | - (((unsigned long)reloc->type & 0xf) << 28)); - fwrite(info->buf, 8, 1, info->f); - reloc = (macho_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); - } - - return 0; -} - -static int -exp2_to_bits(unsigned long val) -{ - int ret = 0; - - while (val) { - val >>= 1; - ret++; - } - ret = (ret > 0) ? ret - 1 : 0; - - return ret; -} - -static int -macho_objfmt_is_section_label(yasm_symrec *sym) -{ - /*@dependent@*/ /*@null@*/ yasm_section *sect; - /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; - - /* Look at symrec for value/scnum/etc. */ - if (yasm_symrec_get_label(sym, &precbc)) { - if (precbc) - sect = yasm_bc_get_section(precbc); - else - sect = NULL; - /* it's a label: get value and offset. - * If there is not a section, leave as debugging symbol. - */ - if (sect) { - /*@dependent@*/ /*@null@*/ macho_section_data *msd; - - msd = yasm_section_get_data(sect, &macho_section_data_cb); - if (msd) { - if (msd->sym == sym) - return 1; /* don't store section names */ - } - } - } - return 0; -} - -static int -macho_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ macho_section_data *msd; - unsigned char *localbuf; - - assert(info != NULL); - msd = yasm_section_get_data(sect, &macho_section_data_cb); - assert(msd != NULL); - - localbuf = info->buf; - - memset(localbuf, 0, 16); - strncpy((char *)localbuf, msd->sectname, 16); - localbuf += 16; - memset(localbuf, 0, 16); - strncpy((char *)localbuf, msd->segname, 16); - localbuf += 16; - /* section address, size depend on 32/64 bit mode */ - YASM_WRITE_32_L(localbuf, msd->vmoff); /* address in memory */ - if (info->is_64) - YASM_WRITE_32_L(localbuf, 0); /* 64-bit mode: upper 32 bits = 0 */ - YASM_WRITE_32_L(localbuf, msd->size); /* size in memory */ - if (info->is_64) - YASM_WRITE_32_L(localbuf, 0); /* 64-bit mode: upper 32 bits = 0 */ - - /* offset,align,reloff,nreloc,flags,reserved1,reserved2 are 32 bit */ - if ((msd->flags & SECTION_TYPE) != S_ZEROFILL) { - YASM_WRITE_32_L(localbuf, msd->offset); - YASM_WRITE_32_L(localbuf, exp2_to_bits(yasm_section_get_align(sect))); - if (msd->nreloc) { - msd->flags |= S_ATTR_LOC_RELOC; - if (msd->extreloc) - msd->flags |= S_ATTR_EXT_RELOC; - YASM_WRITE_32_L(localbuf, - align32((long)(info->rel_base + info->s_reloff))); - YASM_WRITE_32_L(localbuf, msd->nreloc); /* nreloc */ - } else { - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, 0); - } - - info->s_reloff += msd->nreloc * MACHO_RELINFO_SIZE; /* nreloc */ - } else { - YASM_WRITE_32_L(localbuf, 0); /* these are zero in BSS */ - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, 0); - } - - YASM_WRITE_32_L(localbuf, msd->flags); /* flags */ - YASM_WRITE_32_L(localbuf, 0); /* reserved 1 */ - YASM_WRITE_32_L(localbuf, 0); /* reserved 2 */ - - if (info->is_64) - fwrite(info->buf, MACHO_SECTCMD64_SIZE, 1, info->f); - else - fwrite(info->buf, MACHO_SECTCMD_SIZE, 1, info->f); - - return 0; -} - - -static int -macho_objfmt_count_sym(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; - /*@only@*/ char *name; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - - assert(info != NULL); - if (info->all_syms || - vis & (YASM_SYM_GLOBAL | YASM_SYM_COMMON | YASM_SYM_EXTERN)) { - if (0 == macho_objfmt_is_section_label(sym)) { - /* Save index in symrec data */ - macho_symrec_data *sym_data = - yasm_symrec_get_data(sym, &macho_symrec_data_cb); - if (!sym_data) { - sym_data = yasm_xcalloc(sizeof(macho_symrec_data), 1); - yasm_symrec_add_data(sym, &macho_symrec_data_cb, sym_data); - } - sym_data->index = info->symindex; - info->symindex++; - - name = yasm_symrec_get_global_name(sym, info->object); - /*printf("%s\n",name); */ - /* name length + delimiter */ - sym_data->length = (unsigned long)strlen(name) + 1; - info->strlength += sym_data->length; - info->indx++; - yasm_xfree(name); - } - } - return 0; -} - - -static int -macho_objfmt_output_symtable(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - - assert(info != NULL); - - if (info->all_syms || - vis & (YASM_SYM_GLOBAL | YASM_SYM_COMMON | YASM_SYM_EXTERN)) { - const yasm_expr *equ_val; - const yasm_intnum *intn; - unsigned long value = 0; - long scnum = -3; /* -3 = debugging symbol */ - /*@dependent@*/ /*@null@*/ yasm_section *sect; - /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; - unsigned char *localbuf; - yasm_intnum *val; - unsigned int long_int_bytes = (info->is_64) ? 8 : 4; - unsigned int n_type = 0, n_sect = 0, n_desc = 0; - macho_symrec_data *symd; - - val = yasm_intnum_create_uint(0); - - symd = yasm_symrec_get_data(sym, &macho_symrec_data_cb); - - /* Look at symrec for value/scnum/etc. */ - if (yasm_symrec_get_label(sym, &precbc)) { - if (precbc) - sect = yasm_bc_get_section(precbc); - else - sect = NULL; - /* it's a label: get value and offset. - * If there is not a section, leave as debugging symbol. - */ - if (sect) { - /*@dependent@*/ /*@null@*/ macho_section_data *msd; - - msd = yasm_section_get_data(sect, &macho_section_data_cb); - if (msd) { - if (msd->sym == sym) { - /* don't store section names */ - yasm_intnum_destroy(val); - return 0; - } - scnum = msd->scnum; - n_type = N_SECT; - } else - yasm_internal_error(N_("didn't understand section")); - if (precbc) - value += yasm_bc_next_offset(precbc); - /* all values are subject to correction: base offset is first - * raw section, therefore add section offset - */ - if (msd) - value += msd->vmoff; - yasm_intnum_set_uint(val, value); - /*printf("%s offset %lx\n",name,value);*/ - } - } else if ((equ_val = yasm_symrec_get_equ(sym))) { - yasm_expr *equ_val_copy = yasm_expr_copy(equ_val); - - intn = yasm_expr_get_intnum(&equ_val_copy, 1); - if (!intn) { - if (vis & YASM_SYM_GLOBAL) { - yasm_error_set(YASM_ERROR_NOT_CONSTANT, - N_("global EQU value not an integer expression")); - yasm_errwarn_propagate(info->errwarns, equ_val->line); - } - } else - value = yasm_intnum_get_uint(intn); - yasm_expr_destroy(equ_val_copy); - yasm_intnum_set_uint(val, value); - n_type = N_ABS; - scnum = -2; /* -2 = absolute symbol */ - } - - if (vis & YASM_SYM_EXTERN) { - n_type = N_EXT; - scnum = -1; - /*n_desc = REFERENCE_FLAG_UNDEFINED_LAZY; * FIXME: see definition of REFERENCE_FLAG_* above */ - } else if (vis & YASM_SYM_COMMON) { - yasm_expr **csize = yasm_symrec_get_common_size(sym); - n_type = N_UNDF | N_EXT; - if (csize) { - intn = yasm_expr_get_intnum(csize, 1); - if (!intn) { - yasm_error_set(YASM_ERROR_NOT_CONSTANT, - N_("COMMON data size not an integer expression")); - yasm_errwarn_propagate(info->errwarns, (*csize)->line); - } else - yasm_intnum_set_uint(val, yasm_intnum_get_uint(intn)); - } - /*printf("common symbol %s val %lu\n", name, yasm_intnum_get_uint(val));*/ - } else if (vis & YASM_SYM_GLOBAL) { - yasm_valparamhead *valparams = - yasm_symrec_get_objext_valparams(sym); - - struct macho_global_data { - unsigned long flag; /* N_PEXT */ - } data; - - data.flag = 0; - - if (valparams) { - static const yasm_dir_help help[] = { - { "private_extern", 0, yasm_dir_helper_flag_set, - offsetof(struct macho_global_data, flag), N_PEXT }, - }; - yasm_dir_helper(sym, yasm_vps_first(valparams), - yasm_symrec_get_decl_line(sym), help, NELEMS(help), - &data, yasm_dir_helper_valparam_warn); - } - - n_type |= N_EXT | data.flag; - } - - localbuf = info->buf; - YASM_WRITE_32_L(localbuf, info->indx); /* offset in string table */ - YASM_WRITE_8(localbuf, n_type); /* type of symbol entry */ - n_sect = (scnum >= 0) ? scnum + 1 : NO_SECT; - YASM_WRITE_8(localbuf, n_sect); /* referring section where symbol is found */ - YASM_WRITE_16_L(localbuf, n_desc); /* extra description */ - yasm_intnum_get_sized(val, localbuf, long_int_bytes, ((long_int_bytes) << 3), 0, 0, 0); /* value/argument */ - localbuf += long_int_bytes; - if (symd) - symd->value = val; - else - yasm_intnum_destroy(val); - - info->indx += symd->length; - - fwrite(info->buf, 8 + long_int_bytes, 1, info->f); - } - - return 0; -} - - -static int -macho_objfmt_output_str(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - - assert(info != NULL); - - if (info->all_syms || - vis & (YASM_SYM_GLOBAL | YASM_SYM_COMMON | YASM_SYM_EXTERN)) { - if (0 == macho_objfmt_is_section_label(sym)) { - /*@only@*/ char *name = - yasm_symrec_get_global_name(sym, info->object); - size_t len = strlen(name); - - fwrite(name, len + 1, 1, info->f); - yasm_xfree(name); - } - } - return 0; -} - -static int -macho_objfmt_calc_sectsize(yasm_section *sect, /*@null@ */ void *d) -{ - /*@null@ */ macho_objfmt_output_info *info = - (macho_objfmt_output_info *) d; - /*@dependent@ *//*@null@ */ macho_section_data *msd; - unsigned long align; - - assert(info != NULL); - msd = yasm_section_get_data(sect, &macho_section_data_cb); - assert(msd != NULL); - - msd->size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); - if (!(msd->flags & S_ZEROFILL)) { - msd->offset = info->offset; - info->offset += msd->size; - info->filesize += msd->size; - } - - /* accumulate size in memory */ - msd->vmoff = info->vmsize; - info->vmsize += msd->size; - - /* align both start and end of section */ - align = yasm_section_get_align(sect); - if (align != 0) { - unsigned long delta = msd->vmoff % align; - if (delta > 0) { - msd->vmoff += align - delta; - info->vmsize += align - delta; - } - } - - return 0; -} - -/* write object */ -static void -macho_objfmt_output(yasm_object *object, FILE *f, int all_syms, - yasm_errwarns *errwarns) -{ - yasm_objfmt_macho *objfmt_macho = (yasm_objfmt_macho *)object->objfmt; - macho_objfmt_output_info info; - unsigned char *localbuf; - unsigned long symtab_count = 0; - unsigned long headsize; - unsigned int macho_segcmdsize, macho_sectcmdsize, macho_nlistsize; - unsigned int macho_segcmd; - unsigned int head_ncmds, head_sizeofcmds; - unsigned long fileoffset, fileoff_sections; - yasm_intnum *val; - unsigned long long_int_bytes; - const char pad_data[3] = "\0\0\0"; - - info.object = object; - info.objfmt_macho = objfmt_macho; - info.errwarns = errwarns; - info.f = f; - info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); - - if (objfmt_macho->parse_scnum == 0) { - yasm_internal_error(N_("no sections defined")); - /*@notreached@*/ - return; - } - - val = yasm_intnum_create_uint(0); - - /* - * MACH-O Header, Seg CMD, Sect CMDs, Sym Tab, Reloc Data - */ - info.is_64 = (objfmt_macho->bits == 32) ? 0 : 1; - if (info.is_64) { - /* this works only when SYMBOLS and SECTIONS present */ - headsize = - MACHO_HEADER64_SIZE + MACHO_SEGCMD64_SIZE + - (MACHO_SECTCMD64_SIZE * (objfmt_macho->parse_scnum)) + - MACHO_SYMCMD_SIZE; - macho_segcmd = LC_SEGMENT_64; - macho_segcmdsize = MACHO_SEGCMD64_SIZE; - macho_sectcmdsize = MACHO_SECTCMD64_SIZE; - macho_nlistsize = MACHO_NLIST64_SIZE; - long_int_bytes = 8; - } else { - headsize = - MACHO_HEADER_SIZE + MACHO_SEGCMD_SIZE + - (MACHO_SECTCMD_SIZE * (objfmt_macho->parse_scnum)) + - MACHO_SYMCMD_SIZE; - macho_segcmd = LC_SEGMENT; - macho_segcmdsize = MACHO_SEGCMD_SIZE; - macho_sectcmdsize = MACHO_SECTCMD_SIZE; - macho_nlistsize = MACHO_NLIST_SIZE; - long_int_bytes = 4; - } - - /* Get number of symbols */ - info.symindex = 0; - info.indx = 0; - info.strlength = 1; /* string table starts with a zero byte */ - info.all_syms = all_syms || info.is_64; - /*info.all_syms = 1; * force all syms into symbol table */ - yasm_symtab_traverse(object->symtab, &info, macho_objfmt_count_sym); - symtab_count = info.indx; - - /* write raw section data first */ - if (fseek(f, (long)headsize, SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@ */ - return; - } - - /* get size of sections in memory (including BSS) and size of sections - * in file (without BSS) - */ - info.vmsize = 0; - info.filesize = 0; - info.offset = headsize; - yasm_object_sections_traverse(object, &info, macho_objfmt_calc_sectsize); - - /* output sections to file */ - yasm_object_sections_traverse(object, &info, macho_objfmt_output_section); - - fileoff_sections = ftell(f); - - /* Write headers */ - if (fseek(f, 0, SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - localbuf = info.buf; - - /* header size is common to 32 bit and 64 bit variants */ - if (info.is_64) { - YASM_WRITE_32_L(localbuf, MH_MAGIC_64); /* magic number */ - /* i386 64-bit ABI */ - YASM_WRITE_32_L(localbuf, CPU_ARCH_ABI64 | CPU_TYPE_I386); - } else { - YASM_WRITE_32_L(localbuf, MH_MAGIC); /* magic number */ - YASM_WRITE_32_L(localbuf, CPU_TYPE_I386); /* i386 32-bit ABI */ - } - /* i386 all cpu subtype compatible */ - YASM_WRITE_32_L(localbuf, CPU_SUBTYPE_I386_ALL); - YASM_WRITE_32_L(localbuf, MH_OBJECT); /* MACH file type */ - - /* calculate number of commands and their size, put to stream */ - head_ncmds = 0; - head_sizeofcmds = 0; - if (objfmt_macho->parse_scnum > 0) { - head_ncmds++; - head_sizeofcmds += - macho_segcmdsize + macho_sectcmdsize * objfmt_macho->parse_scnum; - } - if (symtab_count > 0) { - head_ncmds++; - head_sizeofcmds += MACHO_SYMCMD_SIZE; - } - - YASM_WRITE_32_L(localbuf, head_ncmds); - YASM_WRITE_32_L(localbuf, head_sizeofcmds); - YASM_WRITE_32_L(localbuf, 0); /* no flags (yet) */ - if (info.is_64) { - YASM_WRITE_32_L(localbuf, 0); /* reserved in 64 bit */ - fileoffset = MACHO_HEADER64_SIZE + head_sizeofcmds; - } else { - /* initial offset to first section */ - fileoffset = MACHO_HEADER_SIZE + head_sizeofcmds; - } - - /* --------------- write segment header command ---------------- */ - YASM_WRITE_32_L(localbuf, macho_segcmd); /* command LC_SEGMENT */ - /* size of load command including section load commands */ - YASM_WRITE_32_L(localbuf, - macho_segcmdsize + - macho_sectcmdsize * objfmt_macho->parse_scnum); - /* in an MH_OBJECT file all sections are in one unnamed (name all zeros) - * segment (16x0) - */ - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, 0); - - /* in-memory offset, in-memory size */ - yasm_intnum_set_uint(val, 0); /* offset in memory (vmaddr) */ - yasm_intnum_get_sized(val, localbuf, long_int_bytes, - ((long_int_bytes) << 3), 0, 0, 0); - localbuf += long_int_bytes; - yasm_intnum_set_uint(val, info.vmsize); /* size in memory (vmsize) */ - yasm_intnum_get_sized(val, localbuf, long_int_bytes, - ((long_int_bytes) << 3), 0, 0, 0); - localbuf += long_int_bytes; - /* offset in file to first section */ - yasm_intnum_set_uint(val, fileoffset); - yasm_intnum_get_sized(val, localbuf, long_int_bytes, - ((long_int_bytes) << 3), 0, 0, 0); - localbuf += long_int_bytes; - yasm_intnum_set_uint(val, info.filesize); /* overall size in file */ - yasm_intnum_get_sized(val, localbuf, long_int_bytes, - ((long_int_bytes) << 3), 0, 0, 0); - localbuf += long_int_bytes; - - YASM_WRITE_32_L(localbuf, VM_PROT_DEFAULT); /* VM protection, maximum */ - YASM_WRITE_32_L(localbuf, VM_PROT_DEFAULT); /* VM protection, initial */ - /* number of sections */ - YASM_WRITE_32_L(localbuf, objfmt_macho->parse_scnum); - YASM_WRITE_32_L(localbuf, 0); /* no flags */ - - /* write MACH-O header and segment command to outfile */ - fwrite(info.buf, (size_t) (localbuf - info.buf), 1, f); - - /* next: section headers */ - /* offset to relocs for first section */ - info.rel_base = align32((long)fileoff_sections); - info.s_reloff = 0; /* offset for relocs of following sections */ - yasm_object_sections_traverse(object, &info, macho_objfmt_output_secthead); - - localbuf = info.buf; - /* write out symbol command */ - YASM_WRITE_32_L(localbuf, LC_SYMTAB); /* cmd == LC_SYMTAB */ - YASM_WRITE_32_L(localbuf, MACHO_SYMCMD_SIZE); - /* symbol table offset */ - YASM_WRITE_32_L(localbuf, info.rel_base + info.s_reloff); - YASM_WRITE_32_L(localbuf, symtab_count); /* number of symbols */ - - YASM_WRITE_32_L(localbuf, macho_nlistsize * symtab_count + info.rel_base + - info.s_reloff); /* string table offset */ - YASM_WRITE_32_L(localbuf, info.strlength); /* string table size */ - /* write symbol command */ - fwrite(info.buf, (size_t)(localbuf - info.buf), 1, f); - - /*printf("num symbols %d, vmsize %d, filesize %d\n",symtab_count, - info.vmsize, info.filesize ); */ - - /* get back to end of raw section data */ - if (fseek(f, (long)fileoff_sections, SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - /* padding to long boundary */ - if ((info.rel_base - fileoff_sections) > 0) { - fwrite(pad_data, info.rel_base - fileoff_sections, 1, f); - } - - /* relocation data */ - yasm_object_sections_traverse(object, &info, macho_objfmt_output_relocs); - - /* symbol table (NLIST) */ - info.indx = 1; /* restart symbol table indices */ - yasm_symtab_traverse(object->symtab, &info, macho_objfmt_output_symtable); - - /* symbol strings */ - fwrite(pad_data, 1, 1, f); - yasm_symtab_traverse(object->symtab, &info, macho_objfmt_output_str); - - yasm_intnum_destroy(val); - yasm_xfree(info.buf); -} - -static void -macho_objfmt_destroy(yasm_objfmt *objfmt) -{ - yasm_xfree(objfmt); -} - -static void -macho_objfmt_init_new_section(yasm_section *sect, unsigned long line) -{ - yasm_object *object = yasm_section_get_object(sect); - const char *sectname = yasm_section_get_name(sect); - yasm_objfmt_macho *objfmt_macho = (yasm_objfmt_macho *)object->objfmt; - macho_section_data *data; - yasm_symrec *sym; - - data = yasm_xmalloc(sizeof(macho_section_data)); - data->scnum = objfmt_macho->parse_scnum++; - data->segname = NULL; - data->sectname = NULL; - data->flags = S_REGULAR; - data->size = 0; - data->offset = 0; - data->vmoff = 0; - data->nreloc = 0; - data->extreloc = 0; - yasm_section_add_data(sect, &macho_section_data_cb, data); - - sym = yasm_symtab_define_label(object->symtab, sectname, - yasm_section_bcs_first(sect), 1, line); - data->sym = sym; -} - -static yasm_section * -macho_objfmt_add_default_section(yasm_object *object) -{ - yasm_section *retval; - macho_section_data *msd; - int isnew; - - retval = yasm_object_get_general(object, "LC_SEGMENT.__TEXT.__text", 0, 1, - 0, &isnew, 0); - if (isnew) { - msd = yasm_section_get_data(retval, &macho_section_data_cb); - msd->segname = yasm__xstrdup("__TEXT"); - msd->sectname = yasm__xstrdup("__text"); - msd->flags = S_ATTR_PURE_INSTRUCTIONS; - yasm_section_set_align(retval, 0, 0); - yasm_section_set_default(retval, 1); - } - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_section * -macho_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, - /*@unused@*/ /*@null@*/ - yasm_valparamhead *objext_valparams, - unsigned long line) -{ - yasm_valparam *vp; - yasm_section *retval; - int isnew; - /*@only@*/ char *f_sectname; - unsigned long flags; - unsigned long align; - int flags_override = 0; - const char *sectname; - char *realname; - int resonly = 0; - macho_section_data *msd; - size_t i; - - static const struct { - const char *in; - const char *seg; - const char *sect; - unsigned long flags; - unsigned long align; - } section_name_translation[] = { - {".text", "__TEXT", "__text", S_ATTR_PURE_INSTRUCTIONS, 0}, - {".const", "__TEXT", "__const", S_REGULAR, 0}, - {".static_const", "__TEXT", "__static_const", S_REGULAR, 0}, - {".cstring", "__TEXT", "__cstring", S_CSTRING_LITERALS, 0}, - {".literal4", "__TEXT", "__literal4", S_4BYTE_LITERALS, 4}, - {".literal8", "__TEXT", "__literal8", S_8BYTE_LITERALS, 8}, - {".literal16", "__TEXT", "__literal16", S_16BYTE_LITERALS, 16}, - {".constructor", "__TEXT", "__constructor", S_REGULAR, 0}, - {".destructor", "__TEXT", "__destructor", S_REGULAR, 0}, - {".fvmlib_init0", "__TEXT", "__fvmlib_init0", S_REGULAR, 0}, - {".fvmlib_init1", "__TEXT", "__fvmlib_init1", S_REGULAR, 0}, - {".mod_init_func", "__DATA", "__mod_init_func", - S_MOD_INIT_FUNC_POINTERS, 4}, - {".mod_term_func", "__DATA", "__mod_term_func", - S_MOD_TERM_FUNC_POINTERS, 4}, - {".dyld", "__DATA", "__dyld", S_REGULAR, 0}, - {".data", "__DATA", "__data", S_REGULAR, 0}, - {".static_data", "__DATA", "__static_data", S_REGULAR, 0}, - {".const_data", "__DATA", "__const", S_REGULAR, 0}, - {".rodata", "__DATA", "__const", S_REGULAR, 0}, - {".bss", "__DATA", "__bss", S_ZEROFILL, 0}, - {".objc_class_names", "__TEXT", "__cstring", S_CSTRING_LITERALS, 0}, - {".objc_meth_var_types","__TEXT", "__cstring", S_CSTRING_LITERALS, 0}, - {".objc_meth_var_names","__TEXT", "__cstring", S_CSTRING_LITERALS, 0}, - {".objc_selector_strs", "__OBJC", "__selector_strs", - S_CSTRING_LITERALS, 0}, - {".objc_class", "__OBJC", "__class", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_meta_class", "__OBJC", "__meta_class", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_string_object", "__OBJC", "__string_object", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_protocol", "__OBJC", "__protocol", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_cat_cls_meth", "__OBJC", "__cat_cls_meth", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_cat_inst_meth", "__OBJC", "__cat_inst_meth", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_cls_meth", "__OBJC", "__cls_meth", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_inst_meth", "__OBJC", "__inst_meth", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_message_refs", "__OBJC", "__message_refs", - S_LITERAL_POINTERS|S_ATTR_NO_DEAD_STRIP, 4}, - {".objc_cls_refs", "__OBJC", "__cls_refs", - S_LITERAL_POINTERS|S_ATTR_NO_DEAD_STRIP, 4}, - {".objc_module_info", "__OBJC", "__module_info", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_symbols", "__OBJC", "__symbols", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_category", "__OBJC", "__category", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_class_vars", "__OBJC", "__class_vars", - S_ATTR_NO_DEAD_STRIP, 0}, - {".objc_instance_vars", "__OBJC", "__instance_vars", - S_ATTR_NO_DEAD_STRIP, 0} - }; - - struct macho_section_switch_data { - /*@only@*/ /*@null@*/ char *f_segname; - /*@only@*/ /*@null@*/ yasm_intnum *align_intn; - } data; - - static const yasm_dir_help help[] = { - { "segname", 1, yasm_dir_helper_string, - offsetof(struct macho_section_switch_data, f_segname), 0 }, - { "align", 1, yasm_dir_helper_intn, - offsetof(struct macho_section_switch_data, align_intn), 0 } - }; - - data.f_segname = NULL; - data.align_intn = NULL; - - vp = yasm_vps_first(valparams); - sectname = yasm_vp_string(vp); - if (!sectname) - return NULL; - vp = yasm_vps_next(vp); - - /* translate .text,.data,.bss to __text,__data,__bss... */ - for (i=0; i<NELEMS(section_name_translation); i++) { - if (yasm__strcasecmp(sectname, section_name_translation[i].in) == 0) - break; - } - - if (i == NELEMS(section_name_translation)) { - const char *s; - if (vp && !vp->val && (s = yasm_vp_string(vp))) { - /* Treat as SEGNAME, SECTNAME */ - if (strlen(sectname) > 16) - yasm_warn_set(YASM_WARN_GENERAL, - N_("segment name is too long, max 16 chars; truncating")); - data.f_segname = yasm__xstrndup(sectname, 16); - if (strlen(s) > 16) - yasm_warn_set(YASM_WARN_GENERAL, - N_("section name is too long, max 16 chars; truncating")); - f_sectname = yasm__xstrndup(s, 16); - flags = S_REGULAR; - align = 0; - - sectname = s; - vp = yasm_vps_next(vp); - } else { - data.f_segname = NULL; - if (strlen(sectname) > 16) - yasm_warn_set(YASM_WARN_GENERAL, - N_("section name is too long, max 16 chars; truncating")); - f_sectname = yasm__xstrndup(sectname, 16); - flags = S_ATTR_SOME_INSTRUCTIONS; - align = 0; - } - } else { - data.f_segname = yasm__xstrdup(section_name_translation[i].seg); - f_sectname = yasm__xstrdup(section_name_translation[i].sect); - flags = section_name_translation[i].flags; - align = section_name_translation[i].align; - } - - flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), - &data, yasm_dir_helper_valparam_warn); - if (flags_override < 0) - return NULL; /* error occurred */ - - if (data.align_intn) { - align = yasm_intnum_get_uint(data.align_intn); - yasm_intnum_destroy(data.align_intn); - - /* Alignments must be a power of two. */ - if (!is_exp2(align)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("argument to `%s' is not a power of two"), - vp->val); - return NULL; - } - - /* Check to see if alignment is supported size */ - if (align > 16384) { - yasm_error_set(YASM_ERROR_VALUE, - N_("macho implementation does not support alignments > 16384")); - return NULL; - } - } - - if (!data.f_segname) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("Unknown section name, defaulting to __TEXT segment")); - data.f_segname = yasm__xstrdup("__TEXT"); - } - - /* Build a unique sectname from f_segname and f_sectname. */ - realname = yasm_xmalloc(strlen("LC_SEGMENT") + 1 + strlen(data.f_segname) + 1 + - strlen(f_sectname) + 1); - sprintf(realname, "LC_SEGMENT.%s.%s", data.f_segname, f_sectname); - retval = yasm_object_get_general(object, realname, align, 1, resonly, - &isnew, line); - yasm_xfree(realname); - - msd = yasm_section_get_data(retval, &macho_section_data_cb); - - if (isnew || yasm_section_is_default(retval)) { - yasm_section_set_default(retval, 0); - msd->segname = data.f_segname; - msd->sectname = f_sectname; - msd->flags = flags; - yasm_section_set_align(retval, align, line); - } else if (flags_override) { - /* align is the only value used from overrides. */ - if (yasm_section_get_align(retval) != align) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("section flags ignored on section redeclaration")); - } - } - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -macho_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - yasm_objfmt_macho *objfmt_macho = (yasm_objfmt_macho *)object->objfmt; - if (yasm__strcasecmp(name, "gotpcrel") == 0) { - return objfmt_macho->gotpcrel_sym; - } - return NULL; -} - -static void -macho_section_data_destroy(void *data) -{ - macho_section_data *msd = (macho_section_data *) data; - yasm_xfree(msd->segname); - yasm_xfree(msd->sectname); - yasm_xfree(data); -} - -static void -macho_section_data_print(void *data, FILE *f, int indent_level) -{ - macho_section_data *msd = (macho_section_data *) data; - - fprintf(f, "%*ssym=\n", indent_level, ""); - yasm_symrec_print(msd->sym, f, indent_level + 1); - fprintf(f, "%*sscnum=%ld\n", indent_level, "", msd->scnum); - fprintf(f, "%*sflags=0x%lx\n", indent_level, "", msd->flags); - fprintf(f, "%*ssize=%lu\n", indent_level, "", msd->size); - fprintf(f, "%*snreloc=%lu\n", indent_level, "", msd->nreloc); - fprintf(f, "%*soffset=%lu\n", indent_level, "", msd->offset); - fprintf(f, "%*sextreloc=%u\n", indent_level, "", msd->extreloc); -} - -static void -macho_symrec_data_destroy(void *data) -{ - yasm_xfree(data); -} - -static void -macho_symrec_data_print(void *data, FILE *f, int indent_level) -{ - macho_symrec_data *msd = (macho_symrec_data *)data; - - fprintf(f, "%*sindex=%ld\n", indent_level, "", msd->index); - fprintf(f, "%*svalue=", indent_level, ""); - if (msd->value) - fprintf(f, "%ld\n", yasm_intnum_get_int(msd->value)); - else - fprintf(f, "nil\n"); -} - - -/* Define valid debug formats to use with this object format */ -static const char *macho_objfmt_dbgfmt_keywords[] = { - "null", - NULL -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_macho_LTX_objfmt = { - "Mac OS X ABI Mach-O File Format", - "macho", - "o", - 32, - 0, - macho_objfmt_dbgfmt_keywords, - "null", - NULL, /* no directives */ - NULL, /* no standard macros */ - macho_objfmt_create, - macho_objfmt_output, - macho_objfmt_destroy, - macho_objfmt_add_default_section, - macho_objfmt_init_new_section, - macho_objfmt_section_switch, - macho_objfmt_get_special_sym -}; - -yasm_objfmt_module yasm_macho32_LTX_objfmt = { - "Mac OS X ABI Mach-O File Format (32-bit)", - "macho32", - "o", - 32, - 0, - macho_objfmt_dbgfmt_keywords, - "null", - NULL, /* no directives */ - NULL, /* no standard macros */ - macho32_objfmt_create, - macho_objfmt_output, - macho_objfmt_destroy, - macho_objfmt_add_default_section, - macho_objfmt_init_new_section, - macho_objfmt_section_switch, - macho_objfmt_get_special_sym -}; - -yasm_objfmt_module yasm_macho64_LTX_objfmt = { - "Mac OS X ABI Mach-O File Format (64-bit)", - "macho64", - "o", - 64, - 0, - macho_objfmt_dbgfmt_keywords, - "null", - NULL, /* no directives */ - NULL, /* no standard macros */ - macho64_objfmt_create, - macho_objfmt_output, - macho_objfmt_destroy, - macho_objfmt_add_default_section, - macho_objfmt_init_new_section, - macho_objfmt_section_switch, - macho_objfmt_get_special_sym -}; +/* + * Mac OS X ABI Mach-O File Format + * + * Copyright (C) 2007 Henryk Richter, built upon xdf objfmt (C) Peter Johnson + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + notes: This implementation is rather basic. There are several implementation + issues to be sorted out for full compliance and error resilience. + Some examples are given below (nasm syntax). + + 1) section placement + Mach-O requires BSS sections to be placed last in object files. This + has to be done manually. + Example: + + section .text + mov rax,[qword foo] + section .data + dw 0 + section .bss + foo dw 0 + + 2) addressing issues + + 2.1) symbol relative relocation (i.e. mov eax,[foo wrt bar]) + Not implemented yet. + + 2.2) data referencing in 64 bit mode + While ELF allows 32 bit absolute relocations in 64 bit mode, Mach-O + does not. Therefore code like + lea rbx,[_foo] ;48 8d 1c 25 00 00 00 00 + mov rcx,[_bar] ;48 8b 0c 25 00 00 00 00 + with a 32 bit address field cannot be relocated into an address >= 0x100000000 (OSX actually + uses that). + + Actually, the only register where a 64 bit displacement is allowed in x86-64, is rax + as in the example 1). + + A plausible workaround is either classic PIC (like in C), which is in turn + not implemented in this object format. The recommended was is PC relative + code (called RIP-relative in x86-64). So instead of the lines above, just write: + lea rbx,[_foo wrt rip] + mov rcx,[_bar wrt rip] + + 2.3) section/data alignment + Normally, you specify sections with a specific alignment + and get your data layed out as desired. Unfortunately, the + linker in MacOS X seems to ignore the section alignment requests. + The workaround is an explicit alignment at the end of the text section. + + section .text + movdqa xmm0,[_foo wrt rip] + + align 16 + section .data align=16 + _foo dw 32,32,32,32,32,32,32,32 + + FIXME: perform that operation implicitly! + + 2.4) cross section symbol differences unsupported in current implementation + [extern foo] + [extern bar] + section .data + dq bar-foo + + Will currently produce an error though the necessary means are provided + by the Mach-O specification. + +*/ + +#include <util.h> + +#include <libyasm.h> + +/* MACH-O DEFINES */ +/* Mach-O in-file header structure sizes (32 BIT, see below for 64 bit defs) */ +#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 + +/* 64 bit sizes */ +#define MACHO_HEADER64_SIZE 32 +#define MACHO_SEGCMD64_SIZE 72 +#define MACHO_SECTCMD64_SIZE 80 +#define MACHO_NLIST64_SIZE 16 +#define MACHO_RELINFO64_SIZE 8 + + +/* Mach-O file header values */ +#define MH_MAGIC 0xfeedface +#define MH_MAGIC_64 0xfeedfacf + +/* CPU machine type */ +#define CPU_TYPE_I386 7 /* x86 platform */ +#define CPU_TYPE_X86_64 (CPU_TYPE_I386|CPU_ARCH_ABI64) +#define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ + +/* CPU machine subtype, e.g. processor */ +#define CPU_SUBTYPE_I386_ALL 3 /* all-x86 compatible */ +#define CPU_SUBTYPE_X86_64_ALL CPU_SUBTYPE_I386_ALL +#define CPU_SUBTYPE_386 3 +#define CPU_SUBTYPE_486 4 +#define CPU_SUBTYPE_486SX (4 + 128) +#define CPU_SUBTYPE_586 5 +#define CPU_SUBTYPE_INTEL(f, m) ((f) + ((m) << 4)) +#define CPU_SUBTYPE_PENT CPU_SUBTYPE_INTEL(5, 0) +#define CPU_SUBTYPE_PENTPRO CPU_SUBTYPE_INTEL(6, 1) +#define CPU_SUBTYPE_PENTII_M3 CPU_SUBTYPE_INTEL(6, 3) +#define CPU_SUBTYPE_PENTII_M5 CPU_SUBTYPE_INTEL(6, 5) +#define CPU_SUBTYPE_PENTIUM_4 CPU_SUBTYPE_INTEL(10, 0) + +#define CPU_SUBTYPE_INTEL_FAMILY(x) ((x) & 15) +#define CPU_SUBTYPE_INTEL_FAMILY_MAX 15 + +#define CPU_SUBTYPE_INTEL_MODEL(x) ((x) >> 4) +#define CPU_SUBTYPE_INTEL_MODEL_ALL 0 + +#define MH_OBJECT 0x1 /* object file */ + +#define LC_SEGMENT 0x1 /* segment load command */ +#define LC_SYMTAB 0x2 /* symbol table load command */ +#define LC_SEGMENT_64 0x19 /* segment load command */ + + +#define VM_PROT_NONE 0x00 +#define VM_PROT_READ 0x01 +#define VM_PROT_WRITE 0x02 +#define VM_PROT_EXECUTE 0x04 + +#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) + +#define SECTION_TYPE 0x000000ff /* section type mask */ +#define SECTION_ATTRIBUTES 0xffffff00UL/* section attributes mask */ + +#define S_REGULAR 0x0 /* standard section */ +#define S_ZEROFILL 0x1 /* zerofill, in-memory only */ +#define S_CSTRING_LITERALS 0x2 /* literal C strings */ +#define S_4BYTE_LITERALS 0x3 /* only 4-byte literals */ +#define S_8BYTE_LITERALS 0x4 /* only 8-byte literals */ +#define S_LITERAL_POINTERS 0x5 /* only pointers to literals */ +#define S_NON_LAZY_SYMBOL_POINTERS 0x6 /* only non-lazy symbol pointers */ +#define S_LAZY_SYMBOL_POINTERS 0x7 /* only lazy symbol pointers */ +#define S_SYMBOL_STUBS 0x8 /* only symbol stubs; byte size of + * stub in the reserved2 field */ +#define S_MOD_INIT_FUNC_POINTERS 0x9 /* only function pointers for init */ +#define S_MOD_TERM_FUNC_POINTERS 0xa /* only function pointers for term */ +#define S_COALESCED 0xb /* symbols that are to be coalesced */ +#define S_GB_ZEROFILL 0xc /* >4GB zero fill on demand section */ +#define S_INTERPOSING 0xd /* only pairs of function pointers for + * interposing */ +#define S_16BYTE_LITERALS 0xe /* only 16 byte literals */ + +#define S_ATTR_DEBUG 0x02000000 /* a debug section */ +#define SECTION_ATTRIBUTES_SYS 0x00ffff00 /* system setable attributes */ +#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 /* section contains some + * machine instructions */ +#define S_ATTR_EXT_RELOC 0x00000200 /* section has external + * relocation entries */ +#define S_ATTR_LOC_RELOC 0x00000100 /* section has local + * relocation entries */ + +#define SECTION_ATTRIBUTES_USR 0xff000000UL /* User setable attributes */ +#define S_ATTR_PURE_INSTRUCTIONS 0x80000000UL /* only true machine insns */ +#define S_ATTR_NO_TOC 0x40000000UL /* coalesced symbols that are + * not to be in a ranlib table + * of contents */ +#define S_ATTR_STRIP_STATIC_SYMS 0x20000000UL /* ok to strip static symbols + * in this section in files + * with the MH_DYLDLINK flag */ +#define S_ATTR_NO_DEAD_STRIP 0x10000000UL /* no dead stripping */ +#define S_ATTR_LIVE_SUPPORT 0x08000000UL /* blocks are live if they + * reference live blocks */ +#define S_ATTR_SELF_MODIFYING_CODE 0x04000000UL /* Used with i386 code stubs + * written on by dyld */ + +/* macho references symbols in different ways whether they are linked at + * runtime (LAZY, read library functions) or at link time (NON_LAZY, mostly + * data) + * + * TODO: proper support for dynamically linkable modules would require the + * __import sections as well as the dsymtab command + */ +#define REFERENCE_FLAG_UNDEFINED_NON_LAZY 0x0 +#define REFERENCE_FLAG_UNDEFINED_LAZY 0x1 + +#define align(x, y) \ + (((x) + (y) - 1) & ~((y) - 1)) /* align x to multiple of y */ + +#define align32(x) \ + align(x, 4) /* align x to 32 bit boundary */ + +#define macho_MAGIC 0x87654322 + +/* Symbol table type field bit masks */ +#define N_STAB 0xe0 /* mask indicating stab entry */ +#define N_PEXT 0x10 /* private external bit */ +#define N_TYPE 0x0e /* mask for all the type bits */ +#define N_EXT 0x01 /* external (global) bit */ + +/* Symbol table type field values */ +#define N_UNDF 0x00 /* undefined */ +#define N_ABS 0x02 /* absolute address */ +#define N_SECT 0x0e /* symbol is defined in a section */ + +#define NO_SECT 0 /* no section for symbol in nlist */ + +#define REGULAR_OUTBUF_SIZE 1024 + + +typedef struct macho_reloc { + yasm_reloc reloc; + int pcrel; + int length; + int ext; + enum reloc_type_x86_64 { + /* x86 relocations */ + GENERIC_RELOC_VANILLA = 0, /* generic relocation */ + GENERIC_RELOC_PAIR = 1, /* Only follows a GENERIC_RELOC_SECTDIFF */ + GENERIC_RELOC_SECTDIFF = 2, + GENERIC_RELOC_PB_LA_PTR = 3, /* prebound lazy pointer */ + GENERIC_RELOC_LOCAL_SECTDIFF = 4, + + /* x86-64 relocations */ + X86_64_RELOC_UNSIGNED = 0, /* for absolute addresses */ + X86_64_RELOC_SIGNED = 1, /* for signed 32-bit displacement */ + X86_64_RELOC_BRANCH = 2, /* a CALL/JMP insn with 32-bit disp */ + X86_64_RELOC_GOT_LOAD = 3, /* a MOVQ load of a GOT entry */ + X86_64_RELOC_GOT = 4, /* other GOT references */ + X86_64_RELOC_SUBTRACTOR = 5, /* must be followed by a X86_64_RELOC_UNSIGNED */ + X86_64_RELOC_SIGNED_1 = 6, /* signed 32-bit disp, -1 addend */ + X86_64_RELOC_SIGNED_2 = 7, /* signed 32-bit disp, -2 addend */ + X86_64_RELOC_SIGNED_4 = 8 /* signed 32-bit disp, -4 addend */ + } type; +} macho_reloc; + +typedef struct macho_section_data { + /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ + long scnum; /* section number (0=first section) */ + /*@only@*/ char *segname; /* segment name in file */ + /*@only@*/ char *sectname; /* section name in file */ + unsigned long flags; /* S_* flags */ + unsigned long size; /* size of raw data (section data) in bytes */ + unsigned long offset; /* offset in raw data within file in bytes */ + unsigned long vmoff; /* memory offset */ + unsigned long nreloc; /* number of relocation entries */ + unsigned int extreloc; /* external relocations present (0/1) */ +} macho_section_data; + + +typedef struct macho_symrec_data { + unsigned long index; /* index in output order */ + yasm_intnum *value; /* valid after writing symtable to file */ + unsigned long length; /* length + 1 (plus auto underscore) */ +} macho_symrec_data; + + +typedef struct yasm_objfmt_macho { + yasm_objfmt_base objfmt; /* base structure */ + + long parse_scnum; /* sect numbering in parser */ + int bits; /* 32 / 64 */ + + yasm_symrec *gotpcrel_sym; /* ..gotpcrel */ +} yasm_objfmt_macho; + + +typedef struct macho_objfmt_output_info { + yasm_object *object; + yasm_objfmt_macho *objfmt_macho; + yasm_errwarns *errwarns; + /*@dependent@ */ FILE *f; + /*@only@ */ unsigned char *buf; + yasm_section *sect; + /*@dependent@ */ macho_section_data *msd; + + unsigned int is_64; /* write object in 64 bit mode */ + + /* vmsize and filesize available after traversing section count routine */ + unsigned long vmsize; /* raw size of all sections (including BSS) */ + unsigned long filesize; /* size of sections in file (excluding BSS) */ + unsigned long offset; /* offset within file */ + + /* forward offset tracking */ + unsigned long rel_base; /* first relocation in file */ + unsigned long s_reloff; /* in-file offset to relocations */ + + unsigned long indx; /* current symbol size in bytes (name length+1) */ + unsigned long symindex; /* current symbol index in output order */ + int all_syms; /* outputting all symbols? */ + unsigned long strlength; /* length of all strings */ +} macho_objfmt_output_info; + + +static void macho_section_data_destroy(/*@only@*/ void *d); +static void macho_section_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback macho_section_data_cb = { + macho_section_data_destroy, + macho_section_data_print +}; + +static void macho_symrec_data_destroy(/*@only@*/ void *d); +static void macho_symrec_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback macho_symrec_data_cb = { + macho_symrec_data_destroy, + macho_symrec_data_print +}; + +yasm_objfmt_module yasm_macho_LTX_objfmt; +yasm_objfmt_module yasm_macho32_LTX_objfmt; +yasm_objfmt_module yasm_macho64_LTX_objfmt; + +static yasm_objfmt * +macho_objfmt_create_common(yasm_object *object, yasm_objfmt_module *module, + int bits_pref) +{ + yasm_objfmt_macho *objfmt_macho = yasm_xmalloc(sizeof(yasm_objfmt_macho)); + + objfmt_macho->objfmt.module = module; + + /* Only support x86 arch for now */ + if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") != 0) { + yasm_xfree(objfmt_macho); + return NULL; + } + + /* Support x86 and amd64 machines of x86 arch */ + if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), "x86") == 0 && + (bits_pref == 0 || bits_pref == 32)) { + objfmt_macho->bits = 32; + objfmt_macho->gotpcrel_sym = NULL; + } else if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), + "amd64") == 0 && + (bits_pref == 0 || bits_pref == 64)) { + objfmt_macho->bits = 64; + /* FIXME: misuse of NULL bytecode */ + objfmt_macho->gotpcrel_sym = + yasm_symtab_define_label(object->symtab, "..gotpcrel", NULL, 0, 0); + } else { + yasm_xfree(objfmt_macho); + return NULL; + } + + objfmt_macho->parse_scnum = 0; /* section numbering starts at 0 */ + return (yasm_objfmt *)objfmt_macho; +} + +static yasm_objfmt * +macho_objfmt_create(yasm_object *object) +{ + yasm_objfmt *objfmt; + yasm_objfmt_macho *objfmt_macho; + + objfmt = macho_objfmt_create_common(object, &yasm_macho_LTX_objfmt, 0); + if (objfmt) { + objfmt_macho = (yasm_objfmt_macho *)objfmt; + /* Figure out which bitness of object format to use */ + if (objfmt_macho->bits == 32) + objfmt_macho->objfmt.module = &yasm_macho32_LTX_objfmt; + else if (objfmt_macho->bits == 64) + objfmt_macho->objfmt.module = &yasm_macho64_LTX_objfmt; + } + return objfmt; +} + +static yasm_objfmt * +macho32_objfmt_create(yasm_object *object) +{ + return macho_objfmt_create_common(object, &yasm_macho32_LTX_objfmt, 32); +} + +static yasm_objfmt * +macho64_objfmt_create(yasm_object *object) +{ + return macho_objfmt_create_common(object, &yasm_macho64_LTX_objfmt, 64); +} + +static int +macho_objfmt_output_value(yasm_value *value, unsigned char *buf, + unsigned int destsize, unsigned long offset, + yasm_bytecode *bc, int warn, /*@null@*/ void *d) +{ + /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; + yasm_objfmt_macho *objfmt_macho; + /*@dependent@*/ /*@null@*/ yasm_intnum *intn; + unsigned long intn_minus = 0, intn_plus = 0; + int retval; + unsigned int valsize = value->size; + macho_reloc *reloc = NULL; + + assert(info != NULL); + objfmt_macho = info->objfmt_macho; + + if (value->abs) + value->abs = yasm_expr_simplify(value->abs, 1); + + /* Try to output constant and PC-relative section-local first. + * Note this does NOT output any value with a SEG, WRT, external, + * cross-section, or non-PC-relative reference (those are handled below). + */ + switch (yasm_value_output_basic(value, buf, destsize, bc, warn, + info->object->arch)) { + case -1: + return 1; + case 0: + break; + default: + return 0; + } + + if (value->section_rel) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("macho: relocation too complex for current implementation")); + return 1; + } + + if (value->rel) { + yasm_sym_vis vis = yasm_symrec_get_visibility(value->rel); + + reloc = yasm_xcalloc(sizeof(macho_reloc), 1); + reloc->reloc.addr = yasm_intnum_create_uint(bc->offset + offset); + reloc->reloc.sym = value->rel; + switch (valsize) { + case 64: + reloc->length = 3; + break; + case 32: + reloc->length = 2; + break; + case 16: + reloc->length = 1; + break; + case 8: + reloc->length = 0; + break; + default: + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("macho: relocation size unsupported")); + yasm_xfree(reloc); + return 1; + } + reloc->pcrel = 0; + reloc->ext = 0; + reloc->type = GENERIC_RELOC_VANILLA; + /* R_ABS */ + + if (value->rshift > 0) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("macho: shifted relocations not supported")); + yasm_xfree(reloc); + return 1; + } + + if (value->seg_of) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("macho: SEG not supported")); + yasm_xfree(reloc); + return 1; + } + + if (value->curpos_rel && objfmt_macho->gotpcrel_sym && + value->wrt == objfmt_macho->gotpcrel_sym) { + reloc->type = X86_64_RELOC_GOT; + value->wrt = NULL; + } else if (value->wrt) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("macho: invalid WRT")); + yasm_xfree(reloc); + return 1; + } + + if (value->curpos_rel) { + reloc->pcrel = 1; + if (!info->is_64) { + /* Adjust to start of section, so subtract out the bytecode + * offset. + */ + intn_minus = bc->offset; + } else { + /* Add in the offset plus value size to end up with 0. */ + intn_plus = offset+destsize; + if (reloc->type == X86_64_RELOC_GOT) { + /* XXX: This is a hack */ + if (offset >= 2 && buf[-2] == 0x8B) + reloc->type = X86_64_RELOC_GOT_LOAD; + } else if (value->jump_target) + reloc->type = X86_64_RELOC_BRANCH; + else + reloc->type = X86_64_RELOC_SIGNED; + } + } else if (info->is_64) { + if (valsize == 32) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("macho: sorry, cannot apply 32 bit absolute relocations in 64 bit mode, consider \"[_symbol wrt rip]\" for mem access, \"qword\" and \"dq _foo\" for pointers.")); + return 1; + } + reloc->type = X86_64_RELOC_UNSIGNED; + } + + /* It seems that x86-64 objects need to have all extern relocs? */ + if (info->is_64) + reloc->ext = 1; + + if ((vis & YASM_SYM_EXTERN) || (vis & YASM_SYM_COMMON)) { + reloc->ext = 1; + info->msd->extreloc = 1; /* section has external relocations */ + } else if (!info->is_64) { + /*@dependent@*/ /*@null@*/ yasm_bytecode *sym_precbc; + + /* Local symbols need valued to their actual address */ + if (yasm_symrec_get_label(value->rel, &sym_precbc)) { + yasm_section *sym_sect = yasm_bc_get_section(sym_precbc); + /*@null@*/ macho_section_data *msd; + msd = yasm_section_get_data(sym_sect, &macho_section_data_cb); + assert(msd != NULL); + intn_plus += msd->vmoff + yasm_bc_next_offset(sym_precbc); + } + } + + info->msd->nreloc++; + /*printf("reloc %s type %d ",yasm_symrec_get_name(reloc->reloc.sym),reloc->type);*/ + yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); + } + + if (intn_minus <= intn_plus) + intn = yasm_intnum_create_uint(intn_plus-intn_minus); + else { + intn = yasm_intnum_create_uint(intn_minus-intn_plus); + yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); + } + + if (value->abs) { + yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); + + if (!intn2) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("macho: relocation too complex")); + yasm_intnum_destroy(intn); + return 1; + } + yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); + } + + retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, + valsize, 0, bc, warn); + /*printf("val %ld\n",yasm_intnum_get_int(intn));*/ + yasm_intnum_destroy(intn); + return retval; +} + +static int +macho_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) +{ + /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; + /*@null@*/ /*@only@*/ unsigned char *bigbuf; + unsigned long size = REGULAR_OUTBUF_SIZE; + int gap; + + assert(info != NULL); + + bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, + macho_objfmt_output_value, NULL); + + /* Don't bother doing anything else if size ended up being 0. */ + if (size == 0) { + if (bigbuf) + yasm_xfree(bigbuf); + return 0; + } + + /* Warn that gaps are converted to 0 and write out the 0's. */ + if (gap) { + unsigned long left; + + yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, + N_("uninitialized space: zeroing")); + /* Write out in chunks */ + memset(info->buf, 0, REGULAR_OUTBUF_SIZE); + left = size; + while (left > REGULAR_OUTBUF_SIZE) { + fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); + left -= REGULAR_OUTBUF_SIZE; + } + fwrite(info->buf, left, 1, info->f); + } else { + /* Output buf (or bigbuf if non-NULL) to file */ + fwrite(bigbuf ? bigbuf : info->buf, (size_t) size, 1, info->f); + } + + /* If bigbuf was allocated, free it */ + if (bigbuf) + yasm_xfree(bigbuf); + + return 0; +} + +static int +macho_objfmt_output_section(yasm_section *sect, /*@null@ */ void *d) +{ + /*@null@ */ macho_objfmt_output_info *info = + (macho_objfmt_output_info *) d; + /*@dependent@ *//*@null@ */ macho_section_data *msd; + + assert(info != NULL); + msd = yasm_section_get_data(sect, &macho_section_data_cb); + assert(msd != NULL); + + if (!(msd->flags & S_ZEROFILL)) { + /* Output non-BSS sections */ + info->sect = sect; + info->msd = msd; + yasm_section_bcs_traverse(sect, info->errwarns, info, + macho_objfmt_output_bytecode); + } + return 0; +} + +static int +macho_objfmt_output_relocs(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ macho_section_data *msd; + macho_reloc *reloc; + + reloc = (macho_reloc *)yasm_section_relocs_first(sect); + while (reloc) { + unsigned char *localbuf = info->buf; + /*@null@*/ macho_symrec_data *xsymd; + unsigned long symnum; + + xsymd = yasm_symrec_get_data(reloc->reloc.sym, &macho_symrec_data_cb); + yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); + localbuf += 4; /* address of relocation */ + + if (reloc->ext) + symnum = xsymd->index; + else { + /* find section where the symbol relates to */ + /*@dependent@*/ /*@null@*/ yasm_section *dsect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + symnum = 0; /* default to absolute */ + if (yasm_symrec_get_label(reloc->reloc.sym, &precbc) && + (dsect = yasm_bc_get_section(precbc)) && + (msd = yasm_section_get_data(dsect, &macho_section_data_cb))) + symnum = msd->scnum+1; + } + YASM_WRITE_32_L(localbuf, + (symnum & 0x00ffffff) | + (((unsigned long)reloc->pcrel & 1) << 24) | + (((unsigned long)reloc->length & 3) << 25) | + (((unsigned long)reloc->ext & 1) << 27) | + (((unsigned long)reloc->type & 0xf) << 28)); + fwrite(info->buf, 8, 1, info->f); + reloc = (macho_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); + } + + return 0; +} + +static int +exp2_to_bits(unsigned long val) +{ + int ret = 0; + + while (val) { + val >>= 1; + ret++; + } + ret = (ret > 0) ? ret - 1 : 0; + + return ret; +} + +static int +macho_objfmt_is_section_label(yasm_symrec *sym) +{ + /*@dependent@*/ /*@null@*/ yasm_section *sect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + + /* Look at symrec for value/scnum/etc. */ + if (yasm_symrec_get_label(sym, &precbc)) { + if (precbc) + sect = yasm_bc_get_section(precbc); + else + sect = NULL; + /* it's a label: get value and offset. + * If there is not a section, leave as debugging symbol. + */ + if (sect) { + /*@dependent@*/ /*@null@*/ macho_section_data *msd; + + msd = yasm_section_get_data(sect, &macho_section_data_cb); + if (msd) { + if (msd->sym == sym) + return 1; /* don't store section names */ + } + } + } + return 0; +} + +static int +macho_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ macho_section_data *msd; + unsigned char *localbuf; + + assert(info != NULL); + msd = yasm_section_get_data(sect, &macho_section_data_cb); + assert(msd != NULL); + + localbuf = info->buf; + + memset(localbuf, 0, 16); + strncpy((char *)localbuf, msd->sectname, 16); + localbuf += 16; + memset(localbuf, 0, 16); + strncpy((char *)localbuf, msd->segname, 16); + localbuf += 16; + /* section address, size depend on 32/64 bit mode */ + YASM_WRITE_32_L(localbuf, msd->vmoff); /* address in memory */ + if (info->is_64) + YASM_WRITE_32_L(localbuf, 0); /* 64-bit mode: upper 32 bits = 0 */ + YASM_WRITE_32_L(localbuf, msd->size); /* size in memory */ + if (info->is_64) + YASM_WRITE_32_L(localbuf, 0); /* 64-bit mode: upper 32 bits = 0 */ + + /* offset,align,reloff,nreloc,flags,reserved1,reserved2 are 32 bit */ + if ((msd->flags & SECTION_TYPE) != S_ZEROFILL) { + YASM_WRITE_32_L(localbuf, msd->offset); + YASM_WRITE_32_L(localbuf, exp2_to_bits(yasm_section_get_align(sect))); + if (msd->nreloc) { + msd->flags |= S_ATTR_LOC_RELOC; + if (msd->extreloc) + msd->flags |= S_ATTR_EXT_RELOC; + YASM_WRITE_32_L(localbuf, + align32((long)(info->rel_base + info->s_reloff))); + YASM_WRITE_32_L(localbuf, msd->nreloc); /* nreloc */ + } else { + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, 0); + } + + info->s_reloff += msd->nreloc * MACHO_RELINFO_SIZE; /* nreloc */ + } else { + YASM_WRITE_32_L(localbuf, 0); /* these are zero in BSS */ + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, 0); + } + + YASM_WRITE_32_L(localbuf, msd->flags); /* flags */ + YASM_WRITE_32_L(localbuf, 0); /* reserved 1 */ + YASM_WRITE_32_L(localbuf, 0); /* reserved 2 */ + + if (info->is_64) + fwrite(info->buf, MACHO_SECTCMD64_SIZE, 1, info->f); + else + fwrite(info->buf, MACHO_SECTCMD_SIZE, 1, info->f); + + return 0; +} + + +static int +macho_objfmt_count_sym(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; + /*@only@*/ char *name; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + + assert(info != NULL); + if (info->all_syms || + vis & (YASM_SYM_GLOBAL | YASM_SYM_COMMON | YASM_SYM_EXTERN)) { + if (0 == macho_objfmt_is_section_label(sym)) { + /* Save index in symrec data */ + macho_symrec_data *sym_data = + yasm_symrec_get_data(sym, &macho_symrec_data_cb); + if (!sym_data) { + sym_data = yasm_xcalloc(sizeof(macho_symrec_data), 1); + yasm_symrec_add_data(sym, &macho_symrec_data_cb, sym_data); + } + sym_data->index = info->symindex; + info->symindex++; + + name = yasm_symrec_get_global_name(sym, info->object); + /*printf("%s\n",name); */ + /* name length + delimiter */ + sym_data->length = (unsigned long)strlen(name) + 1; + info->strlength += sym_data->length; + info->indx++; + yasm_xfree(name); + } + } + return 0; +} + + +static int +macho_objfmt_output_symtable(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + + assert(info != NULL); + + if (info->all_syms || + vis & (YASM_SYM_GLOBAL | YASM_SYM_COMMON | YASM_SYM_EXTERN)) { + const yasm_expr *equ_val; + const yasm_intnum *intn; + unsigned long value = 0; + long scnum = -3; /* -3 = debugging symbol */ + /*@dependent@*/ /*@null@*/ yasm_section *sect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + unsigned char *localbuf; + yasm_intnum *val; + unsigned int long_int_bytes = (info->is_64) ? 8 : 4; + unsigned int n_type = 0, n_sect = 0, n_desc = 0; + macho_symrec_data *symd; + + val = yasm_intnum_create_uint(0); + + symd = yasm_symrec_get_data(sym, &macho_symrec_data_cb); + + /* Look at symrec for value/scnum/etc. */ + if (yasm_symrec_get_label(sym, &precbc)) { + if (precbc) + sect = yasm_bc_get_section(precbc); + else + sect = NULL; + /* it's a label: get value and offset. + * If there is not a section, leave as debugging symbol. + */ + if (sect) { + /*@dependent@*/ /*@null@*/ macho_section_data *msd; + + msd = yasm_section_get_data(sect, &macho_section_data_cb); + if (msd) { + if (msd->sym == sym) { + /* don't store section names */ + yasm_intnum_destroy(val); + return 0; + } + scnum = msd->scnum; + n_type = N_SECT; + } else + yasm_internal_error(N_("didn't understand section")); + if (precbc) + value += yasm_bc_next_offset(precbc); + /* all values are subject to correction: base offset is first + * raw section, therefore add section offset + */ + if (msd) + value += msd->vmoff; + yasm_intnum_set_uint(val, value); + /*printf("%s offset %lx\n",name,value);*/ + } + } else if ((equ_val = yasm_symrec_get_equ(sym))) { + yasm_expr *equ_val_copy = yasm_expr_copy(equ_val); + + intn = yasm_expr_get_intnum(&equ_val_copy, 1); + if (!intn) { + if (vis & YASM_SYM_GLOBAL) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("global EQU value not an integer expression")); + yasm_errwarn_propagate(info->errwarns, equ_val->line); + } + } else + value = yasm_intnum_get_uint(intn); + yasm_expr_destroy(equ_val_copy); + yasm_intnum_set_uint(val, value); + n_type = N_ABS; + scnum = -2; /* -2 = absolute symbol */ + } + + if (vis & YASM_SYM_EXTERN) { + n_type = N_EXT; + scnum = -1; + /*n_desc = REFERENCE_FLAG_UNDEFINED_LAZY; * FIXME: see definition of REFERENCE_FLAG_* above */ + } else if (vis & YASM_SYM_COMMON) { + yasm_expr **csize = yasm_symrec_get_common_size(sym); + n_type = N_UNDF | N_EXT; + if (csize) { + intn = yasm_expr_get_intnum(csize, 1); + if (!intn) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("COMMON data size not an integer expression")); + yasm_errwarn_propagate(info->errwarns, (*csize)->line); + } else + yasm_intnum_set_uint(val, yasm_intnum_get_uint(intn)); + } + /*printf("common symbol %s val %lu\n", name, yasm_intnum_get_uint(val));*/ + } else if (vis & YASM_SYM_GLOBAL) { + yasm_valparamhead *valparams = + yasm_symrec_get_objext_valparams(sym); + + struct macho_global_data { + unsigned long flag; /* N_PEXT */ + } data; + + data.flag = 0; + + if (valparams) { + static const yasm_dir_help help[] = { + { "private_extern", 0, yasm_dir_helper_flag_set, + offsetof(struct macho_global_data, flag), N_PEXT }, + }; + yasm_dir_helper(sym, yasm_vps_first(valparams), + yasm_symrec_get_decl_line(sym), help, NELEMS(help), + &data, yasm_dir_helper_valparam_warn); + } + + n_type |= N_EXT | data.flag; + } + + localbuf = info->buf; + YASM_WRITE_32_L(localbuf, info->indx); /* offset in string table */ + YASM_WRITE_8(localbuf, n_type); /* type of symbol entry */ + n_sect = (scnum >= 0) ? scnum + 1 : NO_SECT; + YASM_WRITE_8(localbuf, n_sect); /* referring section where symbol is found */ + YASM_WRITE_16_L(localbuf, n_desc); /* extra description */ + yasm_intnum_get_sized(val, localbuf, long_int_bytes, ((long_int_bytes) << 3), 0, 0, 0); /* value/argument */ + localbuf += long_int_bytes; + if (symd) + symd->value = val; + else + yasm_intnum_destroy(val); + + info->indx += symd->length; + + fwrite(info->buf, 8 + long_int_bytes, 1, info->f); + } + + return 0; +} + + +static int +macho_objfmt_output_str(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ macho_objfmt_output_info *info = (macho_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + + assert(info != NULL); + + if (info->all_syms || + vis & (YASM_SYM_GLOBAL | YASM_SYM_COMMON | YASM_SYM_EXTERN)) { + if (0 == macho_objfmt_is_section_label(sym)) { + /*@only@*/ char *name = + yasm_symrec_get_global_name(sym, info->object); + size_t len = strlen(name); + + fwrite(name, len + 1, 1, info->f); + yasm_xfree(name); + } + } + return 0; +} + +static int +macho_objfmt_calc_sectsize(yasm_section *sect, /*@null@ */ void *d) +{ + /*@null@ */ macho_objfmt_output_info *info = + (macho_objfmt_output_info *) d; + /*@dependent@ *//*@null@ */ macho_section_data *msd; + unsigned long align; + + assert(info != NULL); + msd = yasm_section_get_data(sect, &macho_section_data_cb); + assert(msd != NULL); + + msd->size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); + if (!(msd->flags & S_ZEROFILL)) { + msd->offset = info->offset; + info->offset += msd->size; + info->filesize += msd->size; + } + + /* accumulate size in memory */ + msd->vmoff = info->vmsize; + info->vmsize += msd->size; + + /* align both start and end of section */ + align = yasm_section_get_align(sect); + if (align != 0) { + unsigned long delta = msd->vmoff % align; + if (delta > 0) { + msd->vmoff += align - delta; + info->vmsize += align - delta; + } + } + + return 0; +} + +/* write object */ +static void +macho_objfmt_output(yasm_object *object, FILE *f, int all_syms, + yasm_errwarns *errwarns) +{ + yasm_objfmt_macho *objfmt_macho = (yasm_objfmt_macho *)object->objfmt; + macho_objfmt_output_info info; + unsigned char *localbuf; + unsigned long symtab_count = 0; + unsigned long headsize; + unsigned int macho_segcmdsize, macho_sectcmdsize, macho_nlistsize; + unsigned int macho_segcmd; + unsigned int head_ncmds, head_sizeofcmds; + unsigned long fileoffset, fileoff_sections; + yasm_intnum *val; + unsigned long long_int_bytes; + const char pad_data[3] = "\0\0\0"; + + info.object = object; + info.objfmt_macho = objfmt_macho; + info.errwarns = errwarns; + info.f = f; + info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); + + if (objfmt_macho->parse_scnum == 0) { + yasm_internal_error(N_("no sections defined")); + /*@notreached@*/ + return; + } + + val = yasm_intnum_create_uint(0); + + /* + * MACH-O Header, Seg CMD, Sect CMDs, Sym Tab, Reloc Data + */ + info.is_64 = (objfmt_macho->bits == 32) ? 0 : 1; + if (info.is_64) { + /* this works only when SYMBOLS and SECTIONS present */ + headsize = + MACHO_HEADER64_SIZE + MACHO_SEGCMD64_SIZE + + (MACHO_SECTCMD64_SIZE * (objfmt_macho->parse_scnum)) + + MACHO_SYMCMD_SIZE; + macho_segcmd = LC_SEGMENT_64; + macho_segcmdsize = MACHO_SEGCMD64_SIZE; + macho_sectcmdsize = MACHO_SECTCMD64_SIZE; + macho_nlistsize = MACHO_NLIST64_SIZE; + long_int_bytes = 8; + } else { + headsize = + MACHO_HEADER_SIZE + MACHO_SEGCMD_SIZE + + (MACHO_SECTCMD_SIZE * (objfmt_macho->parse_scnum)) + + MACHO_SYMCMD_SIZE; + macho_segcmd = LC_SEGMENT; + macho_segcmdsize = MACHO_SEGCMD_SIZE; + macho_sectcmdsize = MACHO_SECTCMD_SIZE; + macho_nlistsize = MACHO_NLIST_SIZE; + long_int_bytes = 4; + } + + /* Get number of symbols */ + info.symindex = 0; + info.indx = 0; + info.strlength = 1; /* string table starts with a zero byte */ + info.all_syms = all_syms || info.is_64; + /*info.all_syms = 1; * force all syms into symbol table */ + yasm_symtab_traverse(object->symtab, &info, macho_objfmt_count_sym); + symtab_count = info.indx; + + /* write raw section data first */ + if (fseek(f, (long)headsize, SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@ */ + return; + } + + /* get size of sections in memory (including BSS) and size of sections + * in file (without BSS) + */ + info.vmsize = 0; + info.filesize = 0; + info.offset = headsize; + yasm_object_sections_traverse(object, &info, macho_objfmt_calc_sectsize); + + /* output sections to file */ + yasm_object_sections_traverse(object, &info, macho_objfmt_output_section); + + fileoff_sections = ftell(f); + + /* Write headers */ + if (fseek(f, 0, SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + localbuf = info.buf; + + /* header size is common to 32 bit and 64 bit variants */ + if (info.is_64) { + YASM_WRITE_32_L(localbuf, MH_MAGIC_64); /* magic number */ + /* i386 64-bit ABI */ + YASM_WRITE_32_L(localbuf, CPU_ARCH_ABI64 | CPU_TYPE_I386); + } else { + YASM_WRITE_32_L(localbuf, MH_MAGIC); /* magic number */ + YASM_WRITE_32_L(localbuf, CPU_TYPE_I386); /* i386 32-bit ABI */ + } + /* i386 all cpu subtype compatible */ + YASM_WRITE_32_L(localbuf, CPU_SUBTYPE_I386_ALL); + YASM_WRITE_32_L(localbuf, MH_OBJECT); /* MACH file type */ + + /* calculate number of commands and their size, put to stream */ + head_ncmds = 0; + head_sizeofcmds = 0; + if (objfmt_macho->parse_scnum > 0) { + head_ncmds++; + head_sizeofcmds += + macho_segcmdsize + macho_sectcmdsize * objfmt_macho->parse_scnum; + } + if (symtab_count > 0) { + head_ncmds++; + head_sizeofcmds += MACHO_SYMCMD_SIZE; + } + + YASM_WRITE_32_L(localbuf, head_ncmds); + YASM_WRITE_32_L(localbuf, head_sizeofcmds); + YASM_WRITE_32_L(localbuf, 0); /* no flags (yet) */ + if (info.is_64) { + YASM_WRITE_32_L(localbuf, 0); /* reserved in 64 bit */ + fileoffset = MACHO_HEADER64_SIZE + head_sizeofcmds; + } else { + /* initial offset to first section */ + fileoffset = MACHO_HEADER_SIZE + head_sizeofcmds; + } + + /* --------------- write segment header command ---------------- */ + YASM_WRITE_32_L(localbuf, macho_segcmd); /* command LC_SEGMENT */ + /* size of load command including section load commands */ + YASM_WRITE_32_L(localbuf, + macho_segcmdsize + + macho_sectcmdsize * objfmt_macho->parse_scnum); + /* in an MH_OBJECT file all sections are in one unnamed (name all zeros) + * segment (16x0) + */ + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, 0); + + /* in-memory offset, in-memory size */ + yasm_intnum_set_uint(val, 0); /* offset in memory (vmaddr) */ + yasm_intnum_get_sized(val, localbuf, long_int_bytes, + ((long_int_bytes) << 3), 0, 0, 0); + localbuf += long_int_bytes; + yasm_intnum_set_uint(val, info.vmsize); /* size in memory (vmsize) */ + yasm_intnum_get_sized(val, localbuf, long_int_bytes, + ((long_int_bytes) << 3), 0, 0, 0); + localbuf += long_int_bytes; + /* offset in file to first section */ + yasm_intnum_set_uint(val, fileoffset); + yasm_intnum_get_sized(val, localbuf, long_int_bytes, + ((long_int_bytes) << 3), 0, 0, 0); + localbuf += long_int_bytes; + yasm_intnum_set_uint(val, info.filesize); /* overall size in file */ + yasm_intnum_get_sized(val, localbuf, long_int_bytes, + ((long_int_bytes) << 3), 0, 0, 0); + localbuf += long_int_bytes; + + YASM_WRITE_32_L(localbuf, VM_PROT_DEFAULT); /* VM protection, maximum */ + YASM_WRITE_32_L(localbuf, VM_PROT_DEFAULT); /* VM protection, initial */ + /* number of sections */ + YASM_WRITE_32_L(localbuf, objfmt_macho->parse_scnum); + YASM_WRITE_32_L(localbuf, 0); /* no flags */ + + /* write MACH-O header and segment command to outfile */ + fwrite(info.buf, (size_t) (localbuf - info.buf), 1, f); + + /* next: section headers */ + /* offset to relocs for first section */ + info.rel_base = align32((long)fileoff_sections); + info.s_reloff = 0; /* offset for relocs of following sections */ + yasm_object_sections_traverse(object, &info, macho_objfmt_output_secthead); + + localbuf = info.buf; + /* write out symbol command */ + YASM_WRITE_32_L(localbuf, LC_SYMTAB); /* cmd == LC_SYMTAB */ + YASM_WRITE_32_L(localbuf, MACHO_SYMCMD_SIZE); + /* symbol table offset */ + YASM_WRITE_32_L(localbuf, info.rel_base + info.s_reloff); + YASM_WRITE_32_L(localbuf, symtab_count); /* number of symbols */ + + YASM_WRITE_32_L(localbuf, macho_nlistsize * symtab_count + info.rel_base + + info.s_reloff); /* string table offset */ + YASM_WRITE_32_L(localbuf, info.strlength); /* string table size */ + /* write symbol command */ + fwrite(info.buf, (size_t)(localbuf - info.buf), 1, f); + + /*printf("num symbols %d, vmsize %d, filesize %d\n",symtab_count, + info.vmsize, info.filesize ); */ + + /* get back to end of raw section data */ + if (fseek(f, (long)fileoff_sections, SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + /* padding to long boundary */ + if ((info.rel_base - fileoff_sections) > 0) { + fwrite(pad_data, info.rel_base - fileoff_sections, 1, f); + } + + /* relocation data */ + yasm_object_sections_traverse(object, &info, macho_objfmt_output_relocs); + + /* symbol table (NLIST) */ + info.indx = 1; /* restart symbol table indices */ + yasm_symtab_traverse(object->symtab, &info, macho_objfmt_output_symtable); + + /* symbol strings */ + fwrite(pad_data, 1, 1, f); + yasm_symtab_traverse(object->symtab, &info, macho_objfmt_output_str); + + yasm_intnum_destroy(val); + yasm_xfree(info.buf); +} + +static void +macho_objfmt_destroy(yasm_objfmt *objfmt) +{ + yasm_xfree(objfmt); +} + +static void +macho_objfmt_init_new_section(yasm_section *sect, unsigned long line) +{ + yasm_object *object = yasm_section_get_object(sect); + const char *sectname = yasm_section_get_name(sect); + yasm_objfmt_macho *objfmt_macho = (yasm_objfmt_macho *)object->objfmt; + macho_section_data *data; + yasm_symrec *sym; + + data = yasm_xmalloc(sizeof(macho_section_data)); + data->scnum = objfmt_macho->parse_scnum++; + data->segname = NULL; + data->sectname = NULL; + data->flags = S_REGULAR; + data->size = 0; + data->offset = 0; + data->vmoff = 0; + data->nreloc = 0; + data->extreloc = 0; + yasm_section_add_data(sect, &macho_section_data_cb, data); + + sym = yasm_symtab_define_label(object->symtab, sectname, + yasm_section_bcs_first(sect), 1, line); + data->sym = sym; +} + +static yasm_section * +macho_objfmt_add_default_section(yasm_object *object) +{ + yasm_section *retval; + macho_section_data *msd; + int isnew; + + retval = yasm_object_get_general(object, "LC_SEGMENT.__TEXT.__text", 0, 1, + 0, &isnew, 0); + if (isnew) { + msd = yasm_section_get_data(retval, &macho_section_data_cb); + msd->segname = yasm__xstrdup("__TEXT"); + msd->sectname = yasm__xstrdup("__text"); + msd->flags = S_ATTR_PURE_INSTRUCTIONS; + yasm_section_set_align(retval, 0, 0); + yasm_section_set_default(retval, 1); + } + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_section * +macho_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, + /*@unused@*/ /*@null@*/ + yasm_valparamhead *objext_valparams, + unsigned long line) +{ + yasm_valparam *vp; + yasm_section *retval; + int isnew; + /*@only@*/ char *f_sectname; + unsigned long flags; + unsigned long align; + int flags_override = 0; + const char *sectname; + char *realname; + int resonly = 0; + macho_section_data *msd; + size_t i; + + static const struct { + const char *in; + const char *seg; + const char *sect; + unsigned long flags; + unsigned long align; + } section_name_translation[] = { + {".text", "__TEXT", "__text", S_ATTR_PURE_INSTRUCTIONS, 0}, + {".const", "__TEXT", "__const", S_REGULAR, 0}, + {".static_const", "__TEXT", "__static_const", S_REGULAR, 0}, + {".cstring", "__TEXT", "__cstring", S_CSTRING_LITERALS, 0}, + {".literal4", "__TEXT", "__literal4", S_4BYTE_LITERALS, 4}, + {".literal8", "__TEXT", "__literal8", S_8BYTE_LITERALS, 8}, + {".literal16", "__TEXT", "__literal16", S_16BYTE_LITERALS, 16}, + {".constructor", "__TEXT", "__constructor", S_REGULAR, 0}, + {".destructor", "__TEXT", "__destructor", S_REGULAR, 0}, + {".fvmlib_init0", "__TEXT", "__fvmlib_init0", S_REGULAR, 0}, + {".fvmlib_init1", "__TEXT", "__fvmlib_init1", S_REGULAR, 0}, + {".mod_init_func", "__DATA", "__mod_init_func", + S_MOD_INIT_FUNC_POINTERS, 4}, + {".mod_term_func", "__DATA", "__mod_term_func", + S_MOD_TERM_FUNC_POINTERS, 4}, + {".dyld", "__DATA", "__dyld", S_REGULAR, 0}, + {".data", "__DATA", "__data", S_REGULAR, 0}, + {".static_data", "__DATA", "__static_data", S_REGULAR, 0}, + {".const_data", "__DATA", "__const", S_REGULAR, 0}, + {".rodata", "__DATA", "__const", S_REGULAR, 0}, + {".bss", "__DATA", "__bss", S_ZEROFILL, 0}, + {".objc_class_names", "__TEXT", "__cstring", S_CSTRING_LITERALS, 0}, + {".objc_meth_var_types","__TEXT", "__cstring", S_CSTRING_LITERALS, 0}, + {".objc_meth_var_names","__TEXT", "__cstring", S_CSTRING_LITERALS, 0}, + {".objc_selector_strs", "__OBJC", "__selector_strs", + S_CSTRING_LITERALS, 0}, + {".objc_class", "__OBJC", "__class", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_meta_class", "__OBJC", "__meta_class", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_string_object", "__OBJC", "__string_object", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_protocol", "__OBJC", "__protocol", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_cat_cls_meth", "__OBJC", "__cat_cls_meth", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_cat_inst_meth", "__OBJC", "__cat_inst_meth", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_cls_meth", "__OBJC", "__cls_meth", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_inst_meth", "__OBJC", "__inst_meth", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_message_refs", "__OBJC", "__message_refs", + S_LITERAL_POINTERS|S_ATTR_NO_DEAD_STRIP, 4}, + {".objc_cls_refs", "__OBJC", "__cls_refs", + S_LITERAL_POINTERS|S_ATTR_NO_DEAD_STRIP, 4}, + {".objc_module_info", "__OBJC", "__module_info", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_symbols", "__OBJC", "__symbols", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_category", "__OBJC", "__category", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_class_vars", "__OBJC", "__class_vars", + S_ATTR_NO_DEAD_STRIP, 0}, + {".objc_instance_vars", "__OBJC", "__instance_vars", + S_ATTR_NO_DEAD_STRIP, 0} + }; + + struct macho_section_switch_data { + /*@only@*/ /*@null@*/ char *f_segname; + /*@only@*/ /*@null@*/ yasm_intnum *align_intn; + } data; + + static const yasm_dir_help help[] = { + { "segname", 1, yasm_dir_helper_string, + offsetof(struct macho_section_switch_data, f_segname), 0 }, + { "align", 1, yasm_dir_helper_intn, + offsetof(struct macho_section_switch_data, align_intn), 0 } + }; + + data.f_segname = NULL; + data.align_intn = NULL; + + vp = yasm_vps_first(valparams); + sectname = yasm_vp_string(vp); + if (!sectname) + return NULL; + vp = yasm_vps_next(vp); + + /* translate .text,.data,.bss to __text,__data,__bss... */ + for (i=0; i<NELEMS(section_name_translation); i++) { + if (yasm__strcasecmp(sectname, section_name_translation[i].in) == 0) + break; + } + + if (i == NELEMS(section_name_translation)) { + const char *s; + if (vp && !vp->val && (s = yasm_vp_string(vp))) { + /* Treat as SEGNAME, SECTNAME */ + if (strlen(sectname) > 16) + yasm_warn_set(YASM_WARN_GENERAL, + N_("segment name is too long, max 16 chars; truncating")); + data.f_segname = yasm__xstrndup(sectname, 16); + if (strlen(s) > 16) + yasm_warn_set(YASM_WARN_GENERAL, + N_("section name is too long, max 16 chars; truncating")); + f_sectname = yasm__xstrndup(s, 16); + flags = S_REGULAR; + align = 0; + + sectname = s; + vp = yasm_vps_next(vp); + } else { + data.f_segname = NULL; + if (strlen(sectname) > 16) + yasm_warn_set(YASM_WARN_GENERAL, + N_("section name is too long, max 16 chars; truncating")); + f_sectname = yasm__xstrndup(sectname, 16); + flags = S_ATTR_SOME_INSTRUCTIONS; + align = 0; + } + } else { + data.f_segname = yasm__xstrdup(section_name_translation[i].seg); + f_sectname = yasm__xstrdup(section_name_translation[i].sect); + flags = section_name_translation[i].flags; + align = section_name_translation[i].align; + } + + flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), + &data, yasm_dir_helper_valparam_warn); + if (flags_override < 0) + return NULL; /* error occurred */ + + if (data.align_intn) { + align = yasm_intnum_get_uint(data.align_intn); + yasm_intnum_destroy(data.align_intn); + + /* Alignments must be a power of two. */ + if (!is_exp2(align)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("argument to `%s' is not a power of two"), + vp->val); + return NULL; + } + + /* Check to see if alignment is supported size */ + if (align > 16384) { + yasm_error_set(YASM_ERROR_VALUE, + N_("macho implementation does not support alignments > 16384")); + return NULL; + } + } + + if (!data.f_segname) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("Unknown section name, defaulting to __TEXT segment")); + data.f_segname = yasm__xstrdup("__TEXT"); + } + + /* Build a unique sectname from f_segname and f_sectname. */ + realname = yasm_xmalloc(strlen("LC_SEGMENT") + 1 + strlen(data.f_segname) + 1 + + strlen(f_sectname) + 1); + sprintf(realname, "LC_SEGMENT.%s.%s", data.f_segname, f_sectname); + retval = yasm_object_get_general(object, realname, align, 1, resonly, + &isnew, line); + yasm_xfree(realname); + + msd = yasm_section_get_data(retval, &macho_section_data_cb); + + if (isnew || yasm_section_is_default(retval)) { + yasm_section_set_default(retval, 0); + msd->segname = data.f_segname; + msd->sectname = f_sectname; + msd->flags = flags; + yasm_section_set_align(retval, align, line); + } else if (flags_override) { + /* align is the only value used from overrides. */ + if (yasm_section_get_align(retval) != align) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("section flags ignored on section redeclaration")); + } + } + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +macho_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + yasm_objfmt_macho *objfmt_macho = (yasm_objfmt_macho *)object->objfmt; + if (yasm__strcasecmp(name, "gotpcrel") == 0) { + return objfmt_macho->gotpcrel_sym; + } + return NULL; +} + +static void +macho_section_data_destroy(void *data) +{ + macho_section_data *msd = (macho_section_data *) data; + yasm_xfree(msd->segname); + yasm_xfree(msd->sectname); + yasm_xfree(data); +} + +static void +macho_section_data_print(void *data, FILE *f, int indent_level) +{ + macho_section_data *msd = (macho_section_data *) data; + + fprintf(f, "%*ssym=\n", indent_level, ""); + yasm_symrec_print(msd->sym, f, indent_level + 1); + fprintf(f, "%*sscnum=%ld\n", indent_level, "", msd->scnum); + fprintf(f, "%*sflags=0x%lx\n", indent_level, "", msd->flags); + fprintf(f, "%*ssize=%lu\n", indent_level, "", msd->size); + fprintf(f, "%*snreloc=%lu\n", indent_level, "", msd->nreloc); + fprintf(f, "%*soffset=%lu\n", indent_level, "", msd->offset); + fprintf(f, "%*sextreloc=%u\n", indent_level, "", msd->extreloc); +} + +static void +macho_symrec_data_destroy(void *data) +{ + yasm_xfree(data); +} + +static void +macho_symrec_data_print(void *data, FILE *f, int indent_level) +{ + macho_symrec_data *msd = (macho_symrec_data *)data; + + fprintf(f, "%*sindex=%ld\n", indent_level, "", msd->index); + fprintf(f, "%*svalue=", indent_level, ""); + if (msd->value) + fprintf(f, "%ld\n", yasm_intnum_get_int(msd->value)); + else + fprintf(f, "nil\n"); +} + + +/* Define valid debug formats to use with this object format */ +static const char *macho_objfmt_dbgfmt_keywords[] = { + "null", + NULL +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_macho_LTX_objfmt = { + "Mac OS X ABI Mach-O File Format", + "macho", + "o", + 32, + 0, + macho_objfmt_dbgfmt_keywords, + "null", + NULL, /* no directives */ + NULL, /* no standard macros */ + macho_objfmt_create, + macho_objfmt_output, + macho_objfmt_destroy, + macho_objfmt_add_default_section, + macho_objfmt_init_new_section, + macho_objfmt_section_switch, + macho_objfmt_get_special_sym +}; + +yasm_objfmt_module yasm_macho32_LTX_objfmt = { + "Mac OS X ABI Mach-O File Format (32-bit)", + "macho32", + "o", + 32, + 0, + macho_objfmt_dbgfmt_keywords, + "null", + NULL, /* no directives */ + NULL, /* no standard macros */ + macho32_objfmt_create, + macho_objfmt_output, + macho_objfmt_destroy, + macho_objfmt_add_default_section, + macho_objfmt_init_new_section, + macho_objfmt_section_switch, + macho_objfmt_get_special_sym +}; + +yasm_objfmt_module yasm_macho64_LTX_objfmt = { + "Mac OS X ABI Mach-O File Format (64-bit)", + "macho64", + "o", + 64, + 0, + macho_objfmt_dbgfmt_keywords, + "null", + NULL, /* no directives */ + NULL, /* no standard macros */ + macho64_objfmt_create, + macho_objfmt_output, + macho_objfmt_destroy, + macho_objfmt_add_default_section, + macho_objfmt_init_new_section, + macho_objfmt_section_switch, + macho_objfmt_get_special_sym +}; diff --git a/contrib/tools/yasm/modules/objfmts/rdf/rdf-objfmt.c b/contrib/tools/yasm/modules/objfmts/rdf/rdf-objfmt.c index eb3c66688c..a65c4b94dd 100644 --- a/contrib/tools/yasm/modules/objfmts/rdf/rdf-objfmt.c +++ b/contrib/tools/yasm/modules/objfmts/rdf/rdf-objfmt.c @@ -1,1088 +1,1088 @@ -/* - * Relocatable Dynamic Object File Format (RDOFF) version 2 format - * - * Copyright (C) 2006-2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include <util.h> - -#include <libyasm.h> - - -#define REGULAR_OUTBUF_SIZE 1024 - -#define RDF_MAGIC "RDOFF2" - -/* Maximum size of an import/export label (including trailing zero) */ -#define EXIM_LABEL_MAX 64 - -/* Maximum size of library or module name (including trailing zero) */ -#define MODLIB_NAME_MAX 128 - -/* Maximum number of segments that we can handle in one file */ -#define RDF_MAXSEGS 64 - -/* Record types that may present the RDOFF header */ -#define RDFREC_GENERIC 0 -#define RDFREC_RELOC 1 -#define RDFREC_IMPORT 2 -#define RDFREC_GLOBAL 3 -#define RDFREC_DLL 4 -#define RDFREC_BSS 5 -#define RDFREC_SEGRELOC 6 -#define RDFREC_FARIMPORT 7 -#define RDFREC_MODNAME 8 -#define RDFREC_COMMON 10 - -/* Flags for ExportRec/ImportRec */ -#define SYM_DATA 1 -#define SYM_FUNCTION 2 - -/* Flags for ExportRec */ -#define SYM_GLOBAL 4 - -/* Flags for ImportRec */ -#define SYM_IMPORT 8 -#define SYM_FAR 16 - -typedef struct rdf_reloc { - yasm_reloc reloc; - enum { - RDF_RELOC_NORM, /* normal */ - RDF_RELOC_REL, /* relative to current position */ - RDF_RELOC_SEG /* segment containing symbol */ - } type; /* type of relocation */ - unsigned int size; - unsigned int refseg; -} rdf_reloc; - -typedef struct rdf_section_data { - /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ - long scnum; /* section number (0=first section) */ - enum { - RDF_SECT_BSS = 0, - RDF_SECT_CODE = 1, - RDF_SECT_DATA = 2, - RDF_SECT_COMMENT = 3, - RDF_SECT_LCOMMENT = 4, - RDF_SECT_PCOMMENT = 5, - RDF_SECT_SYMDEBUG = 6, - RDF_SECT_LINEDEBUG = 7 - } type; /* section type */ - unsigned int reserved; /* reserved data */ - unsigned long size; /* size of raw data (section data) in bytes */ - - unsigned char *raw_data; /* raw section data, only used during output */ -} rdf_section_data; - -typedef struct rdf_symrec_data { - unsigned int segment; /* assigned RDF "segment" index */ -} rdf_symrec_data; - -typedef STAILQ_HEAD(xdf_str_head, xdf_str) xdf_str_head; -typedef struct xdf_str { - STAILQ_ENTRY(xdf_str) link; - /*@owned@*/ char *str; -} xdf_str; - -typedef struct yasm_objfmt_rdf { - yasm_objfmt_base objfmt; /* base structure */ - - long parse_scnum; /* sect numbering in parser */ - - /*@owned@*/ xdf_str_head module_names; - /*@owned@*/ xdf_str_head library_names; -} yasm_objfmt_rdf; - -typedef struct rdf_objfmt_output_info { - yasm_object *object; - yasm_objfmt_rdf *objfmt_rdf; - yasm_errwarns *errwarns; - /*@dependent@*/ FILE *f; - /*@only@*/ unsigned char *buf; - yasm_section *sect; - /*@dependent@*/ rdf_section_data *rsd; - - unsigned long indx; /* symbol "segment" (extern/common only) */ - - unsigned long bss_size; /* total BSS size */ -} rdf_objfmt_output_info; - -static void rdf_section_data_destroy(/*@only@*/ void *d); -static void rdf_section_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback rdf_section_data_cb = { - rdf_section_data_destroy, - rdf_section_data_print -}; - -static void rdf_symrec_data_destroy(/*@only@*/ void *d); -static void rdf_symrec_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback rdf_symrec_data_cb = { - rdf_symrec_data_destroy, - rdf_symrec_data_print -}; - -yasm_objfmt_module yasm_rdf_LTX_objfmt; - - -static /*@dependent@*/ rdf_symrec_data * -rdf_objfmt_sym_set_data(yasm_symrec *sym, unsigned int segment) -{ - rdf_symrec_data *rsymd = yasm_xmalloc(sizeof(rdf_symrec_data)); - - rsymd->segment = segment; - - yasm_symrec_add_data(sym, &rdf_symrec_data_cb, rsymd); - return rsymd; -} - -static yasm_objfmt * -rdf_objfmt_create(yasm_object *object) -{ - yasm_objfmt_rdf *objfmt_rdf = yasm_xmalloc(sizeof(yasm_objfmt_rdf)); - - /* We theoretically support all arches, so don't check. - * Really we only support byte-addressable ones. - */ - - objfmt_rdf->parse_scnum = 0; /* section numbering starts at 0 */ - - STAILQ_INIT(&objfmt_rdf->module_names); - STAILQ_INIT(&objfmt_rdf->library_names); - - objfmt_rdf->objfmt.module = &yasm_rdf_LTX_objfmt; - - return (yasm_objfmt *)objfmt_rdf; -} - -static int -rdf_objfmt_output_value(yasm_value *value, unsigned char *buf, - unsigned int destsize, unsigned long offset, - yasm_bytecode *bc, int warn, /*@null@*/ void *d) -{ - /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ yasm_intnum *intn; - unsigned long intn_minus; - unsigned long intn_plus; - int retval; - unsigned int valsize = value->size; - - assert(info != NULL); - - if (value->abs) - value->abs = yasm_expr_simplify(value->abs, 1); - - /* Try to output constant and PC-relative section-local first. - * Note this does NOT output any value with a SEG, WRT, external, - * cross-section, or non-PC-relative reference (those are handled below). - */ - switch (yasm_value_output_basic(value, buf, destsize, bc, warn, - info->object->arch)) { - case -1: - return 1; - case 0: - break; - default: - return 0; - } - - if (value->section_rel) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("rdf: relocation too complex")); - return 1; - } - - if (value->rel && value->wrt) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("rdf: WRT not supported")); - return 1; - } - - intn_minus = 0; - intn_plus = 0; - if (value->rel) { - rdf_reloc *reloc; - /*@null@*/ rdf_symrec_data *rsymd; - /*@dependent@*/ yasm_bytecode *precbc; - - reloc = yasm_xmalloc(sizeof(rdf_reloc)); - reloc->reloc.addr = yasm_intnum_create_uint(bc->offset + offset); - reloc->reloc.sym = value->rel; - reloc->size = valsize/8; - - if (value->seg_of) - reloc->type = RDF_RELOC_SEG; - else if (value->curpos_rel) { - reloc->type = RDF_RELOC_REL; - /* Adjust to start of section, so subtract out the bytecode - * offset. - */ - intn_minus = bc->offset; - } else - reloc->type = RDF_RELOC_NORM; - - if (yasm_symrec_get_label(value->rel, &precbc)) { - /* local, set the value to be the offset, and the refseg to the - * segment number. - */ - /*@dependent@*/ /*@null@*/ rdf_section_data *csectd; - /*@dependent@*/ yasm_section *sect; - - sect = yasm_bc_get_section(precbc); - csectd = yasm_section_get_data(sect, &rdf_section_data_cb); - if (!csectd) - yasm_internal_error(N_("didn't understand section")); - reloc->refseg = csectd->scnum; - intn_plus = yasm_bc_next_offset(precbc); - } else { - /* must be common/external */ - rsymd = yasm_symrec_get_data(reloc->reloc.sym, - &rdf_symrec_data_cb); - if (!rsymd) - yasm_internal_error( - N_("rdf: no symbol data for relocated symbol")); - reloc->refseg = rsymd->segment; - } - - yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); - } - - if (intn_minus > 0) { - intn = yasm_intnum_create_uint(intn_minus); - yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); - } else - intn = yasm_intnum_create_uint(intn_plus); - - if (value->abs) { - yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); - if (!intn2) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("rdf: relocation too complex")); - yasm_intnum_destroy(intn); - return 1; - } - yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); - } - - retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, - valsize, 0, bc, warn); - yasm_intnum_destroy(intn); - return retval; -} - -static int -rdf_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) -{ - /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; - /*@null@*/ /*@only@*/ unsigned char *bigbuf; - unsigned long size = REGULAR_OUTBUF_SIZE; - int gap; - - assert(info != NULL); - - bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, - rdf_objfmt_output_value, NULL); - - /* Don't bother doing anything else if size ended up being 0. */ - if (size == 0) { - if (bigbuf) - yasm_xfree(bigbuf); - return 0; - } - - /* Warn that gaps are converted to 0 and write out the 0's. */ - if (gap) { - yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, - N_("uninitialized space: zeroing")); - /* Write out in chunks */ - memset(&info->rsd->raw_data[info->rsd->size], 0, size); - } else { - /* Output buf (or bigbuf if non-NULL) to file */ - memcpy(&info->rsd->raw_data[info->rsd->size], - bigbuf ? bigbuf : info->buf, (size_t)size); - } - - info->rsd->size += size; - - /* If bigbuf was allocated, free it */ - if (bigbuf) - yasm_xfree(bigbuf); - - return 0; -} - -static int -rdf_objfmt_output_section_mem(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ rdf_section_data *rsd; - unsigned long size; - - assert(info != NULL); - rsd = yasm_section_get_data(sect, &rdf_section_data_cb); - assert(rsd != NULL); - - size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); - - if (rsd->type == RDF_SECT_BSS) { - /* Don't output BSS sections, but remember length - * TODO: Check for non-reserve bytecodes? - */ - info->bss_size += size; - return 0; - } - - /* Empty? Go on to next section */ - if (size == 0) - return 0; - - /* See UGH comment in output() for why we're doing this */ - rsd->raw_data = yasm_xmalloc(size); - rsd->size = 0; - - info->sect = sect; - info->rsd = rsd; - yasm_section_bcs_traverse(sect, info->errwarns, info, - rdf_objfmt_output_bytecode); - - /* Sanity check final section size */ - if (rsd->size != size) - yasm_internal_error( - N_("rdf: section computed size did not match actual size")); - - return 0; -} - -static int -rdf_objfmt_output_section_reloc(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ rdf_section_data *rsd; - rdf_reloc *reloc; - - assert(info != NULL); - rsd = yasm_section_get_data(sect, &rdf_section_data_cb); - assert(rsd != NULL); - - if (rsd->type == RDF_SECT_BSS) { - /* Don't output BSS sections. */ - return 0; - } - - /* Empty? Go on to next section */ - if (rsd->size == 0) - return 0; - - reloc = (rdf_reloc *)yasm_section_relocs_first(sect); - while (reloc) { - unsigned char *localbuf = info->buf; - - if (reloc->type == RDF_RELOC_SEG) - YASM_WRITE_8(localbuf, RDFREC_SEGRELOC); - else - YASM_WRITE_8(localbuf, RDFREC_RELOC); - YASM_WRITE_8(localbuf, 8); /* record length */ - /* Section number, +0x40 if relative reloc */ - YASM_WRITE_8(localbuf, rsd->scnum + - (reloc->type == RDF_RELOC_REL ? 0x40 : 0)); - yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); - localbuf += 4; /* offset of relocation */ - YASM_WRITE_8(localbuf, reloc->size); /* size of relocation */ - YASM_WRITE_16_L(localbuf, reloc->refseg); /* relocated symbol */ - fwrite(info->buf, 10, 1, info->f); - - reloc = (rdf_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); - } - - return 0; -} - -static int -rdf_objfmt_output_section_file(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ rdf_section_data *rsd; - unsigned char *localbuf; - - assert(info != NULL); - rsd = yasm_section_get_data(sect, &rdf_section_data_cb); - assert(rsd != NULL); - - if (rsd->type == RDF_SECT_BSS) { - /* Don't output BSS sections. */ - return 0; - } - - /* Empty? Go on to next section */ - if (rsd->size == 0) - return 0; - - /* Section header */ - localbuf = info->buf; - YASM_WRITE_16_L(localbuf, rsd->type); /* type */ - YASM_WRITE_16_L(localbuf, rsd->scnum); /* number */ - YASM_WRITE_16_L(localbuf, rsd->reserved); /* reserved */ - YASM_WRITE_32_L(localbuf, rsd->size); /* length */ - fwrite(info->buf, 10, 1, info->f); - - /* Section data */ - fwrite(rsd->raw_data, rsd->size, 1, info->f); - - /* Free section data */ - yasm_xfree(rsd->raw_data); - rsd->raw_data = NULL; - - return 0; -} - -#define FLAG_EXT 0x1000 -#define FLAG_GLOB 0x2000 -#define FLAG_SET 0x4000 -#define FLAG_CLR 0x8000 -#define FLAG_MASK 0x0fff - -static int -rdf_helper_flag(void *obj, yasm_valparam *vp, unsigned long line, void *d, - uintptr_t flag) -{ - yasm_symrec *sym = (yasm_symrec *)obj; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - unsigned int *flags = (unsigned int *)d; - - if (((vis & YASM_SYM_GLOBAL) && (flag & FLAG_GLOB)) || - ((vis & YASM_SYM_EXTERN) && (flag & FLAG_EXT))) { - if (flag & FLAG_SET) - *flags |= flag & FLAG_MASK; - else if (flag & FLAG_CLR) - *flags &= ~(flag & FLAG_MASK); - } - return 0; -} - -static unsigned int -rdf_parse_flags(yasm_symrec *sym) -{ - /*@dependent@*/ /*@null@*/ yasm_valparamhead *objext_valparams = - yasm_symrec_get_objext_valparams(sym); - unsigned int flags = 0; - - static const yasm_dir_help help[] = { - { "data", 0, rdf_helper_flag, 0, - FLAG_EXT|FLAG_GLOB|FLAG_SET|SYM_DATA }, - { "object", 0, rdf_helper_flag, 0, - FLAG_EXT|FLAG_GLOB|FLAG_SET|SYM_DATA }, - { "proc", 0, rdf_helper_flag, 0, - FLAG_EXT|FLAG_GLOB|FLAG_SET|SYM_FUNCTION }, - { "function", 0, rdf_helper_flag, 0, - FLAG_EXT|FLAG_GLOB|FLAG_SET|SYM_FUNCTION }, - { "import", 0, rdf_helper_flag, 0, FLAG_EXT|FLAG_SET|SYM_IMPORT }, - { "export", 0, rdf_helper_flag, 0, FLAG_GLOB|FLAG_SET|SYM_GLOBAL }, - { "far", 0, rdf_helper_flag, 0, FLAG_EXT|FLAG_SET|SYM_FAR }, - { "near", 0, rdf_helper_flag, 0, FLAG_EXT|FLAG_CLR|SYM_FAR } - }; - - if (!objext_valparams) - return 0; - - yasm_dir_helper(sym, yasm_vps_first(objext_valparams), 0, help, - NELEMS(help), &flags, yasm_dir_helper_valparam_warn); - - return flags; -} - -static int -rdf_objfmt_output_sym(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - /*@only@*/ char *name; - size_t len; - unsigned long value = 0; - unsigned int scnum = 0; - /*@dependent@*/ /*@null@*/ yasm_section *sect; - /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; - unsigned char *localbuf; - - assert(info != NULL); - - if (vis == YASM_SYM_LOCAL || vis == YASM_SYM_DLOCAL) - return 0; /* skip local syms */ - - /* Look at symrec for value/scnum/etc. */ - if (yasm_symrec_get_label(sym, &precbc)) { - /*@dependent@*/ /*@null@*/ rdf_section_data *csectd; - - if (precbc) - sect = yasm_bc_get_section(precbc); - else - sect = NULL; - if (!sect) - return 0; - - /* it's a label: get value and offset. */ - csectd = yasm_section_get_data(sect, &rdf_section_data_cb); - if (csectd) - scnum = csectd->scnum; - else - yasm_internal_error(N_("didn't understand section")); - value = yasm_bc_next_offset(precbc); - } else if (yasm_symrec_get_equ(sym)) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("rdf does not support exporting EQU/absolute values")); - yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); - return 0; - } - - name = yasm_symrec_get_global_name(sym, info->object); - len = strlen(name); - - if (len > EXIM_LABEL_MAX-1) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("label name too long, truncating to %d bytes"), - EXIM_LABEL_MAX); - len = EXIM_LABEL_MAX-1; - } - - localbuf = info->buf; - if (vis & YASM_SYM_GLOBAL) { - YASM_WRITE_8(localbuf, RDFREC_GLOBAL); - YASM_WRITE_8(localbuf, 6+len+1); /* record length */ - YASM_WRITE_8(localbuf, rdf_parse_flags(sym)); /* flags */ - YASM_WRITE_8(localbuf, scnum); /* segment referred to */ - YASM_WRITE_32_L(localbuf, value); /* offset */ - } else { - /* Save symbol segment in symrec data (for later reloc gen) */ - scnum = info->indx++; - rdf_objfmt_sym_set_data(sym, scnum); - - if (vis & YASM_SYM_COMMON) { - /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; - const yasm_intnum *intn; - /*@dependent@*/ /*@null@*/ yasm_valparamhead *objext_valparams = - yasm_symrec_get_objext_valparams(sym); - unsigned long addralign = 0; - - YASM_WRITE_8(localbuf, RDFREC_COMMON); - YASM_WRITE_8(localbuf, 8+len+1); /* record length */ - YASM_WRITE_16_L(localbuf, scnum); /* segment allocated */ - - /* size */ - csize_expr = yasm_symrec_get_common_size(sym); - assert(csize_expr != NULL); - intn = yasm_expr_get_intnum(csize_expr, 1); - if (!intn) { - yasm_error_set(YASM_ERROR_NOT_CONSTANT, - N_("COMMON data size not an integer expression")); - } else - value = yasm_intnum_get_uint(intn); - YASM_WRITE_32_L(localbuf, value); - - /* alignment */ - if (objext_valparams) { - yasm_valparam *vp = yasm_vps_first(objext_valparams); - for (; vp; vp = yasm_vps_next(vp)) { - if (!vp->val) { - /*@only@*/ /*@null@*/ yasm_expr *align_expr; - /*@dependent@*/ /*@null@*/ - const yasm_intnum *align_intn; - - if (!(align_expr = yasm_vp_expr(vp, - info->object->symtab, - yasm_symrec_get_decl_line(sym))) || - !(align_intn = yasm_expr_get_intnum(&align_expr, - 0))) { - yasm_error_set(YASM_ERROR_VALUE, - N_("argument to `%s' is not an integer"), - vp->val); - if (align_expr) - yasm_expr_destroy(align_expr); - continue; - } - addralign = yasm_intnum_get_uint(align_intn); - yasm_expr_destroy(align_expr); - - /* Alignments must be a power of two. */ - if (!is_exp2(addralign)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("alignment constraint is not a power of two")); - continue; - } - } else - yasm_warn_set(YASM_WARN_GENERAL, - N_("Unrecognized qualifier `%s'"), vp->val); - } - } - YASM_WRITE_16_L(localbuf, addralign); - } else if (vis & YASM_SYM_EXTERN) { - unsigned int flags = rdf_parse_flags(sym); - if (flags & SYM_FAR) { - YASM_WRITE_8(localbuf, RDFREC_FARIMPORT); - flags &= ~SYM_FAR; - } else - YASM_WRITE_8(localbuf, RDFREC_IMPORT); - YASM_WRITE_8(localbuf, 3+len+1); /* record length */ - YASM_WRITE_8(localbuf, flags); /* flags */ - YASM_WRITE_16_L(localbuf, scnum); /* segment allocated */ - } - } - - /* Symbol name */ - memcpy(localbuf, name, len); - localbuf += len; - YASM_WRITE_8(localbuf, 0); /* 0-terminated name */ - yasm_xfree(name); - - fwrite(info->buf, (unsigned long)(localbuf-info->buf), 1, info->f); - - yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); - return 0; -} - -static void -rdf_objfmt_output(yasm_object *object, FILE *f, int all_syms, - yasm_errwarns *errwarns) -{ - yasm_objfmt_rdf *objfmt_rdf = (yasm_objfmt_rdf *)object->objfmt; - rdf_objfmt_output_info info; - unsigned char *localbuf; - long headerlen, filelen; - xdf_str *cur; - size_t len; - - info.object = object; - info.objfmt_rdf = objfmt_rdf; - info.errwarns = errwarns; - info.f = f; - info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); - info.bss_size = 0; - - /* Allocate space for file header by seeking forward */ - if (fseek(f, (long)strlen(RDF_MAGIC)+8, SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - /* Output custom header records (library and module, etc) */ - cur = STAILQ_FIRST(&objfmt_rdf->module_names); - while (cur) { - len = strlen(cur->str)+1; - localbuf = info.buf; - YASM_WRITE_8(localbuf, RDFREC_MODNAME); /* record type */ - YASM_WRITE_8(localbuf, len); /* record length */ - fwrite(info.buf, 2, 1, f); - fwrite(cur->str, len, 1, f); - cur = STAILQ_NEXT(cur, link); - } - - cur = STAILQ_FIRST(&objfmt_rdf->library_names); - while (cur) { - len = strlen(cur->str)+1; - localbuf = info.buf; - YASM_WRITE_8(localbuf, RDFREC_DLL); /* record type */ - YASM_WRITE_8(localbuf, len); /* record length */ - fwrite(info.buf, 2, 1, f); - fwrite(cur->str, len, 1, f); - cur = STAILQ_NEXT(cur, link); - } - - /* Output symbol table */ - info.indx = objfmt_rdf->parse_scnum; - yasm_symtab_traverse(object->symtab, &info, rdf_objfmt_output_sym); - - /* UGH! Due to the fact the relocs go at the beginning of the file, and - * we only know if we have relocs when we output the sections, we have - * to output the section data before we have output the relocs. But - * we also don't know how much space to preallocate for relocs, so.... - * we output into memory buffers first (thus the UGH). - * - * Stupid object format design, if you ask me (basically all other - * object formats put the relocs *after* the section data to avoid this - * exact problem). - * - * We also calculate the total size of all BSS sections here. - */ - if (yasm_object_sections_traverse(object, &info, - rdf_objfmt_output_section_mem)) - return; - - /* Output all relocs */ - if (yasm_object_sections_traverse(object, &info, - rdf_objfmt_output_section_reloc)) - return; - - /* Output BSS record */ - if (info.bss_size > 0) { - localbuf = info.buf; - YASM_WRITE_8(localbuf, RDFREC_BSS); /* record type */ - YASM_WRITE_8(localbuf, 4); /* record length */ - YASM_WRITE_32_L(localbuf, info.bss_size); /* total BSS size */ - fwrite(info.buf, 6, 1, f); - } - - /* Determine header length */ - headerlen = ftell(f); - if (headerlen == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return; - } - - /* Section data (to file) */ - if (yasm_object_sections_traverse(object, &info, - rdf_objfmt_output_section_file)) - return; - - /* NULL section to end file */ - memset(info.buf, 0, 10); - fwrite(info.buf, 10, 1, f); - - /* Determine object length */ - filelen = ftell(f); - if (filelen == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return; - } - - /* Write file header */ - if (fseek(f, 0, SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - fwrite(RDF_MAGIC, strlen(RDF_MAGIC), 1, f); - localbuf = info.buf; - YASM_WRITE_32_L(localbuf, filelen-10); /* object size */ - YASM_WRITE_32_L(localbuf, headerlen-14); /* header size */ - fwrite(info.buf, 8, 1, f); - - yasm_xfree(info.buf); -} - -static void -rdf_objfmt_destroy(yasm_objfmt *objfmt) -{ - yasm_objfmt_rdf *objfmt_rdf = (yasm_objfmt_rdf *)objfmt; - xdf_str *cur, *next; - - cur = STAILQ_FIRST(&objfmt_rdf->module_names); - while (cur) { - next = STAILQ_NEXT(cur, link); - yasm_xfree(cur->str); - yasm_xfree(cur); - cur = next; - } - - cur = STAILQ_FIRST(&objfmt_rdf->library_names); - while (cur) { - next = STAILQ_NEXT(cur, link); - yasm_xfree(cur->str); - yasm_xfree(cur); - cur = next; - } - - yasm_xfree(objfmt); -} - -static void -rdf_objfmt_init_new_section(yasm_section *sect, unsigned long line) -{ - yasm_object *object = yasm_section_get_object(sect); - const char *sectname = yasm_section_get_name(sect); - yasm_objfmt_rdf *objfmt_rdf = (yasm_objfmt_rdf *)object->objfmt; - rdf_section_data *data; - yasm_symrec *sym; - - data = yasm_xmalloc(sizeof(rdf_section_data)); - data->scnum = objfmt_rdf->parse_scnum++; - data->type = 0; - data->reserved = 0; - data->size = 0; - data->raw_data = NULL; - yasm_section_add_data(sect, &rdf_section_data_cb, data); - - sym = yasm_symtab_define_label(object->symtab, sectname, - yasm_section_bcs_first(sect), 1, line); - data->sym = sym; -} - -static yasm_section * -rdf_objfmt_add_default_section(yasm_object *object) -{ - yasm_section *retval; - rdf_section_data *rsd; - int isnew; - - retval = yasm_object_get_general(object, ".text", 0, 1, 0, &isnew, 0); - if (isnew) { - rsd = yasm_section_get_data(retval, &rdf_section_data_cb); - rsd->type = RDF_SECT_CODE; - rsd->reserved = 0; - yasm_section_set_default(retval, 1); - } - return retval; -} - -static int -rdf_helper_set_type(void *obj, yasm_valparam *vp, unsigned long line, - void *d, uintptr_t newtype) -{ - unsigned int *type = (unsigned int *)d; - *type = newtype; - return 0; -} - -struct rdf_section_switch_data { - /*@only@*/ /*@null@*/ yasm_intnum *reserved_intn; - unsigned int type; -}; - -static int -rdf_helper_set_reserved(void *obj, yasm_valparam *vp, unsigned long line, - void *d) -{ - struct rdf_section_switch_data *data = (struct rdf_section_switch_data *)d; - - if (!vp->val && vp->type == YASM_PARAM_EXPR) - return yasm_dir_helper_intn(obj, vp, line, &data->reserved_intn, 0); - else - return yasm_dir_helper_valparam_warn(obj, vp, line, d); -} - -static /*@observer@*/ /*@null@*/ yasm_section * -rdf_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, - /*@unused@*/ /*@null@*/ - yasm_valparamhead *objext_valparams, - unsigned long line) -{ - yasm_valparam *vp = yasm_vps_first(valparams); - yasm_section *retval; - int isnew; - unsigned int reserved = 0; - int flags_override = 0; - const char *sectname; - rdf_section_data *rsd; - - struct rdf_section_switch_data data; - - static const yasm_dir_help help[] = { - { "bss", 0, rdf_helper_set_type, - offsetof(struct rdf_section_switch_data, type), RDF_SECT_BSS }, - { "code", 0, rdf_helper_set_type, - offsetof(struct rdf_section_switch_data, type), RDF_SECT_CODE }, - { "text", 0, rdf_helper_set_type, - offsetof(struct rdf_section_switch_data, type), RDF_SECT_CODE }, - { "data", 0, rdf_helper_set_type, - offsetof(struct rdf_section_switch_data, type), RDF_SECT_DATA }, - { "comment", 0, rdf_helper_set_type, - offsetof(struct rdf_section_switch_data, type), RDF_SECT_COMMENT }, - { "lcomment", 0, rdf_helper_set_type, - offsetof(struct rdf_section_switch_data, type), RDF_SECT_LCOMMENT }, - { "pcomment", 0, rdf_helper_set_type, - offsetof(struct rdf_section_switch_data, type), RDF_SECT_PCOMMENT }, - { "symdebug", 0, rdf_helper_set_type, - offsetof(struct rdf_section_switch_data, type), RDF_SECT_SYMDEBUG }, - { "linedebug", 0, rdf_helper_set_type, - offsetof(struct rdf_section_switch_data, type), RDF_SECT_LINEDEBUG }, - { "reserved", 1, yasm_dir_helper_intn, - offsetof(struct rdf_section_switch_data, reserved_intn), 0 } - }; - - data.reserved_intn = NULL; - data.type = 0xffff; - - vp = yasm_vps_first(valparams); - sectname = yasm_vp_string(vp); - if (!sectname) - return NULL; - vp = yasm_vps_next(vp); - - if (strcmp(sectname, ".text") == 0) - data.type = RDF_SECT_CODE; - else if (strcmp(sectname, ".data") == 0) - data.type = RDF_SECT_DATA; - else if (strcmp(sectname, ".bss") == 0) - data.type = RDF_SECT_BSS; - - flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), - &data, rdf_helper_set_reserved); - if (flags_override < 0) - return NULL; /* error occurred */ - - if (data.type == 0xffff) { - yasm_error_set(YASM_ERROR_VALUE, - N_("new segment declared without type code")); - data.type = RDF_SECT_DATA; - } - - if (data.reserved_intn) { - reserved = yasm_intnum_get_uint(data.reserved_intn); - yasm_intnum_destroy(data.reserved_intn); - } - - retval = yasm_object_get_general(object, sectname, 0, 1, - data.type == RDF_SECT_BSS, &isnew, line); - - rsd = yasm_section_get_data(retval, &rdf_section_data_cb); - - if (isnew || yasm_section_is_default(retval)) { - yasm_section_set_default(retval, 0); - rsd->type = data.type; - rsd->reserved = reserved; - } else if (flags_override) - yasm_warn_set(YASM_WARN_GENERAL, - N_("section flags ignored on section redeclaration")); - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -rdf_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - return NULL; -} - -static void -rdf_section_data_destroy(void *data) -{ - rdf_section_data *rsd = (rdf_section_data *)data; - if (rsd->raw_data) - yasm_xfree(rsd->raw_data); - yasm_xfree(data); -} - -static void -rdf_section_data_print(void *data, FILE *f, int indent_level) -{ - rdf_section_data *rsd = (rdf_section_data *)data; - - fprintf(f, "%*ssym=\n", indent_level, ""); - yasm_symrec_print(rsd->sym, f, indent_level+1); - fprintf(f, "%*sscnum=%ld\n", indent_level, "", rsd->scnum); - fprintf(f, "%*stype=0x%x\n", indent_level, "", rsd->type); - fprintf(f, "%*sreserved=0x%x\n", indent_level, "", rsd->reserved); - fprintf(f, "%*ssize=%ld\n", indent_level, "", rsd->size); -} - -static void -rdf_symrec_data_destroy(void *data) -{ - yasm_xfree(data); -} - -static void -rdf_symrec_data_print(void *data, FILE *f, int indent_level) -{ - rdf_symrec_data *rsymd = (rdf_symrec_data *)data; - - fprintf(f, "%*ssymtab segment=%u\n", indent_level, "", rsymd->segment); -} - -static void -rdf_objfmt_add_libmodule(yasm_object *object, char *name, int lib) -{ - yasm_objfmt_rdf *objfmt_rdf = (yasm_objfmt_rdf *)object->objfmt; - xdf_str *str; - - /* Add to list */ - str = yasm_xmalloc(sizeof(xdf_str)); - str->str = name; - if (lib) - STAILQ_INSERT_TAIL(&objfmt_rdf->library_names, str, link); - else - STAILQ_INSERT_TAIL(&objfmt_rdf->module_names, str, link); - - if (strlen(str->str) > MODLIB_NAME_MAX-1) { - yasm_warn_set(YASM_WARN_GENERAL, - N_("name too long, truncating to %d bytes"), - MODLIB_NAME_MAX); - str->str[MODLIB_NAME_MAX-1] = '\0'; - } -} - -static void -dir_library(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_valparam *vp = yasm_vps_first(valparams); - rdf_objfmt_add_libmodule(object, yasm__xstrdup(yasm_vp_string(vp)), 1); -} - -static void -dir_module(yasm_object *object, yasm_valparamhead *valparams, - yasm_valparamhead *objext_valparams, unsigned long line) -{ - yasm_valparam *vp = yasm_vps_first(valparams); - rdf_objfmt_add_libmodule(object, yasm__xstrdup(yasm_vp_string(vp)), 0); -} - -/* Define valid debug formats to use with this object format */ -static const char *rdf_objfmt_dbgfmt_keywords[] = { - "null", - NULL -}; - -static const yasm_directive rdf_objfmt_directives[] = { - { "library", "nasm", dir_library, YASM_DIR_ARG_REQUIRED }, - { "module", "nasm", dir_module, YASM_DIR_ARG_REQUIRED }, - { NULL, NULL, NULL, 0 } -}; - -static const char *rdf_nasm_stdmac[] = { - "%imacro library 1+.nolist", - "[library %1]", - "%endmacro", - "%imacro module 1+.nolist", - "[module %1]", - "%endmacro", - NULL -}; - -static const yasm_stdmac rdf_objfmt_stdmacs[] = { - { "nasm", "nasm", rdf_nasm_stdmac }, - { NULL, NULL, NULL } -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_rdf_LTX_objfmt = { - "Relocatable Dynamic Object File Format (RDOFF) v2.0", - "rdf", - "rdf", - 32, - 0, - rdf_objfmt_dbgfmt_keywords, - "null", - rdf_objfmt_directives, - rdf_objfmt_stdmacs, - rdf_objfmt_create, - rdf_objfmt_output, - rdf_objfmt_destroy, - rdf_objfmt_add_default_section, - rdf_objfmt_init_new_section, - rdf_objfmt_section_switch, - rdf_objfmt_get_special_sym -}; +/* + * Relocatable Dynamic Object File Format (RDOFF) version 2 format + * + * Copyright (C) 2006-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <util.h> + +#include <libyasm.h> + + +#define REGULAR_OUTBUF_SIZE 1024 + +#define RDF_MAGIC "RDOFF2" + +/* Maximum size of an import/export label (including trailing zero) */ +#define EXIM_LABEL_MAX 64 + +/* Maximum size of library or module name (including trailing zero) */ +#define MODLIB_NAME_MAX 128 + +/* Maximum number of segments that we can handle in one file */ +#define RDF_MAXSEGS 64 + +/* Record types that may present the RDOFF header */ +#define RDFREC_GENERIC 0 +#define RDFREC_RELOC 1 +#define RDFREC_IMPORT 2 +#define RDFREC_GLOBAL 3 +#define RDFREC_DLL 4 +#define RDFREC_BSS 5 +#define RDFREC_SEGRELOC 6 +#define RDFREC_FARIMPORT 7 +#define RDFREC_MODNAME 8 +#define RDFREC_COMMON 10 + +/* Flags for ExportRec/ImportRec */ +#define SYM_DATA 1 +#define SYM_FUNCTION 2 + +/* Flags for ExportRec */ +#define SYM_GLOBAL 4 + +/* Flags for ImportRec */ +#define SYM_IMPORT 8 +#define SYM_FAR 16 + +typedef struct rdf_reloc { + yasm_reloc reloc; + enum { + RDF_RELOC_NORM, /* normal */ + RDF_RELOC_REL, /* relative to current position */ + RDF_RELOC_SEG /* segment containing symbol */ + } type; /* type of relocation */ + unsigned int size; + unsigned int refseg; +} rdf_reloc; + +typedef struct rdf_section_data { + /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ + long scnum; /* section number (0=first section) */ + enum { + RDF_SECT_BSS = 0, + RDF_SECT_CODE = 1, + RDF_SECT_DATA = 2, + RDF_SECT_COMMENT = 3, + RDF_SECT_LCOMMENT = 4, + RDF_SECT_PCOMMENT = 5, + RDF_SECT_SYMDEBUG = 6, + RDF_SECT_LINEDEBUG = 7 + } type; /* section type */ + unsigned int reserved; /* reserved data */ + unsigned long size; /* size of raw data (section data) in bytes */ + + unsigned char *raw_data; /* raw section data, only used during output */ +} rdf_section_data; + +typedef struct rdf_symrec_data { + unsigned int segment; /* assigned RDF "segment" index */ +} rdf_symrec_data; + +typedef STAILQ_HEAD(xdf_str_head, xdf_str) xdf_str_head; +typedef struct xdf_str { + STAILQ_ENTRY(xdf_str) link; + /*@owned@*/ char *str; +} xdf_str; + +typedef struct yasm_objfmt_rdf { + yasm_objfmt_base objfmt; /* base structure */ + + long parse_scnum; /* sect numbering in parser */ + + /*@owned@*/ xdf_str_head module_names; + /*@owned@*/ xdf_str_head library_names; +} yasm_objfmt_rdf; + +typedef struct rdf_objfmt_output_info { + yasm_object *object; + yasm_objfmt_rdf *objfmt_rdf; + yasm_errwarns *errwarns; + /*@dependent@*/ FILE *f; + /*@only@*/ unsigned char *buf; + yasm_section *sect; + /*@dependent@*/ rdf_section_data *rsd; + + unsigned long indx; /* symbol "segment" (extern/common only) */ + + unsigned long bss_size; /* total BSS size */ +} rdf_objfmt_output_info; + +static void rdf_section_data_destroy(/*@only@*/ void *d); +static void rdf_section_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback rdf_section_data_cb = { + rdf_section_data_destroy, + rdf_section_data_print +}; + +static void rdf_symrec_data_destroy(/*@only@*/ void *d); +static void rdf_symrec_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback rdf_symrec_data_cb = { + rdf_symrec_data_destroy, + rdf_symrec_data_print +}; + +yasm_objfmt_module yasm_rdf_LTX_objfmt; + + +static /*@dependent@*/ rdf_symrec_data * +rdf_objfmt_sym_set_data(yasm_symrec *sym, unsigned int segment) +{ + rdf_symrec_data *rsymd = yasm_xmalloc(sizeof(rdf_symrec_data)); + + rsymd->segment = segment; + + yasm_symrec_add_data(sym, &rdf_symrec_data_cb, rsymd); + return rsymd; +} + +static yasm_objfmt * +rdf_objfmt_create(yasm_object *object) +{ + yasm_objfmt_rdf *objfmt_rdf = yasm_xmalloc(sizeof(yasm_objfmt_rdf)); + + /* We theoretically support all arches, so don't check. + * Really we only support byte-addressable ones. + */ + + objfmt_rdf->parse_scnum = 0; /* section numbering starts at 0 */ + + STAILQ_INIT(&objfmt_rdf->module_names); + STAILQ_INIT(&objfmt_rdf->library_names); + + objfmt_rdf->objfmt.module = &yasm_rdf_LTX_objfmt; + + return (yasm_objfmt *)objfmt_rdf; +} + +static int +rdf_objfmt_output_value(yasm_value *value, unsigned char *buf, + unsigned int destsize, unsigned long offset, + yasm_bytecode *bc, int warn, /*@null@*/ void *d) +{ + /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ yasm_intnum *intn; + unsigned long intn_minus; + unsigned long intn_plus; + int retval; + unsigned int valsize = value->size; + + assert(info != NULL); + + if (value->abs) + value->abs = yasm_expr_simplify(value->abs, 1); + + /* Try to output constant and PC-relative section-local first. + * Note this does NOT output any value with a SEG, WRT, external, + * cross-section, or non-PC-relative reference (those are handled below). + */ + switch (yasm_value_output_basic(value, buf, destsize, bc, warn, + info->object->arch)) { + case -1: + return 1; + case 0: + break; + default: + return 0; + } + + if (value->section_rel) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("rdf: relocation too complex")); + return 1; + } + + if (value->rel && value->wrt) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("rdf: WRT not supported")); + return 1; + } + + intn_minus = 0; + intn_plus = 0; + if (value->rel) { + rdf_reloc *reloc; + /*@null@*/ rdf_symrec_data *rsymd; + /*@dependent@*/ yasm_bytecode *precbc; + + reloc = yasm_xmalloc(sizeof(rdf_reloc)); + reloc->reloc.addr = yasm_intnum_create_uint(bc->offset + offset); + reloc->reloc.sym = value->rel; + reloc->size = valsize/8; + + if (value->seg_of) + reloc->type = RDF_RELOC_SEG; + else if (value->curpos_rel) { + reloc->type = RDF_RELOC_REL; + /* Adjust to start of section, so subtract out the bytecode + * offset. + */ + intn_minus = bc->offset; + } else + reloc->type = RDF_RELOC_NORM; + + if (yasm_symrec_get_label(value->rel, &precbc)) { + /* local, set the value to be the offset, and the refseg to the + * segment number. + */ + /*@dependent@*/ /*@null@*/ rdf_section_data *csectd; + /*@dependent@*/ yasm_section *sect; + + sect = yasm_bc_get_section(precbc); + csectd = yasm_section_get_data(sect, &rdf_section_data_cb); + if (!csectd) + yasm_internal_error(N_("didn't understand section")); + reloc->refseg = csectd->scnum; + intn_plus = yasm_bc_next_offset(precbc); + } else { + /* must be common/external */ + rsymd = yasm_symrec_get_data(reloc->reloc.sym, + &rdf_symrec_data_cb); + if (!rsymd) + yasm_internal_error( + N_("rdf: no symbol data for relocated symbol")); + reloc->refseg = rsymd->segment; + } + + yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); + } + + if (intn_minus > 0) { + intn = yasm_intnum_create_uint(intn_minus); + yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); + } else + intn = yasm_intnum_create_uint(intn_plus); + + if (value->abs) { + yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); + if (!intn2) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("rdf: relocation too complex")); + yasm_intnum_destroy(intn); + return 1; + } + yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); + } + + retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, + valsize, 0, bc, warn); + yasm_intnum_destroy(intn); + return retval; +} + +static int +rdf_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) +{ + /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; + /*@null@*/ /*@only@*/ unsigned char *bigbuf; + unsigned long size = REGULAR_OUTBUF_SIZE; + int gap; + + assert(info != NULL); + + bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, + rdf_objfmt_output_value, NULL); + + /* Don't bother doing anything else if size ended up being 0. */ + if (size == 0) { + if (bigbuf) + yasm_xfree(bigbuf); + return 0; + } + + /* Warn that gaps are converted to 0 and write out the 0's. */ + if (gap) { + yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, + N_("uninitialized space: zeroing")); + /* Write out in chunks */ + memset(&info->rsd->raw_data[info->rsd->size], 0, size); + } else { + /* Output buf (or bigbuf if non-NULL) to file */ + memcpy(&info->rsd->raw_data[info->rsd->size], + bigbuf ? bigbuf : info->buf, (size_t)size); + } + + info->rsd->size += size; + + /* If bigbuf was allocated, free it */ + if (bigbuf) + yasm_xfree(bigbuf); + + return 0; +} + +static int +rdf_objfmt_output_section_mem(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ rdf_section_data *rsd; + unsigned long size; + + assert(info != NULL); + rsd = yasm_section_get_data(sect, &rdf_section_data_cb); + assert(rsd != NULL); + + size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); + + if (rsd->type == RDF_SECT_BSS) { + /* Don't output BSS sections, but remember length + * TODO: Check for non-reserve bytecodes? + */ + info->bss_size += size; + return 0; + } + + /* Empty? Go on to next section */ + if (size == 0) + return 0; + + /* See UGH comment in output() for why we're doing this */ + rsd->raw_data = yasm_xmalloc(size); + rsd->size = 0; + + info->sect = sect; + info->rsd = rsd; + yasm_section_bcs_traverse(sect, info->errwarns, info, + rdf_objfmt_output_bytecode); + + /* Sanity check final section size */ + if (rsd->size != size) + yasm_internal_error( + N_("rdf: section computed size did not match actual size")); + + return 0; +} + +static int +rdf_objfmt_output_section_reloc(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ rdf_section_data *rsd; + rdf_reloc *reloc; + + assert(info != NULL); + rsd = yasm_section_get_data(sect, &rdf_section_data_cb); + assert(rsd != NULL); + + if (rsd->type == RDF_SECT_BSS) { + /* Don't output BSS sections. */ + return 0; + } + + /* Empty? Go on to next section */ + if (rsd->size == 0) + return 0; + + reloc = (rdf_reloc *)yasm_section_relocs_first(sect); + while (reloc) { + unsigned char *localbuf = info->buf; + + if (reloc->type == RDF_RELOC_SEG) + YASM_WRITE_8(localbuf, RDFREC_SEGRELOC); + else + YASM_WRITE_8(localbuf, RDFREC_RELOC); + YASM_WRITE_8(localbuf, 8); /* record length */ + /* Section number, +0x40 if relative reloc */ + YASM_WRITE_8(localbuf, rsd->scnum + + (reloc->type == RDF_RELOC_REL ? 0x40 : 0)); + yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); + localbuf += 4; /* offset of relocation */ + YASM_WRITE_8(localbuf, reloc->size); /* size of relocation */ + YASM_WRITE_16_L(localbuf, reloc->refseg); /* relocated symbol */ + fwrite(info->buf, 10, 1, info->f); + + reloc = (rdf_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); + } + + return 0; +} + +static int +rdf_objfmt_output_section_file(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ rdf_section_data *rsd; + unsigned char *localbuf; + + assert(info != NULL); + rsd = yasm_section_get_data(sect, &rdf_section_data_cb); + assert(rsd != NULL); + + if (rsd->type == RDF_SECT_BSS) { + /* Don't output BSS sections. */ + return 0; + } + + /* Empty? Go on to next section */ + if (rsd->size == 0) + return 0; + + /* Section header */ + localbuf = info->buf; + YASM_WRITE_16_L(localbuf, rsd->type); /* type */ + YASM_WRITE_16_L(localbuf, rsd->scnum); /* number */ + YASM_WRITE_16_L(localbuf, rsd->reserved); /* reserved */ + YASM_WRITE_32_L(localbuf, rsd->size); /* length */ + fwrite(info->buf, 10, 1, info->f); + + /* Section data */ + fwrite(rsd->raw_data, rsd->size, 1, info->f); + + /* Free section data */ + yasm_xfree(rsd->raw_data); + rsd->raw_data = NULL; + + return 0; +} + +#define FLAG_EXT 0x1000 +#define FLAG_GLOB 0x2000 +#define FLAG_SET 0x4000 +#define FLAG_CLR 0x8000 +#define FLAG_MASK 0x0fff + +static int +rdf_helper_flag(void *obj, yasm_valparam *vp, unsigned long line, void *d, + uintptr_t flag) +{ + yasm_symrec *sym = (yasm_symrec *)obj; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + unsigned int *flags = (unsigned int *)d; + + if (((vis & YASM_SYM_GLOBAL) && (flag & FLAG_GLOB)) || + ((vis & YASM_SYM_EXTERN) && (flag & FLAG_EXT))) { + if (flag & FLAG_SET) + *flags |= flag & FLAG_MASK; + else if (flag & FLAG_CLR) + *flags &= ~(flag & FLAG_MASK); + } + return 0; +} + +static unsigned int +rdf_parse_flags(yasm_symrec *sym) +{ + /*@dependent@*/ /*@null@*/ yasm_valparamhead *objext_valparams = + yasm_symrec_get_objext_valparams(sym); + unsigned int flags = 0; + + static const yasm_dir_help help[] = { + { "data", 0, rdf_helper_flag, 0, + FLAG_EXT|FLAG_GLOB|FLAG_SET|SYM_DATA }, + { "object", 0, rdf_helper_flag, 0, + FLAG_EXT|FLAG_GLOB|FLAG_SET|SYM_DATA }, + { "proc", 0, rdf_helper_flag, 0, + FLAG_EXT|FLAG_GLOB|FLAG_SET|SYM_FUNCTION }, + { "function", 0, rdf_helper_flag, 0, + FLAG_EXT|FLAG_GLOB|FLAG_SET|SYM_FUNCTION }, + { "import", 0, rdf_helper_flag, 0, FLAG_EXT|FLAG_SET|SYM_IMPORT }, + { "export", 0, rdf_helper_flag, 0, FLAG_GLOB|FLAG_SET|SYM_GLOBAL }, + { "far", 0, rdf_helper_flag, 0, FLAG_EXT|FLAG_SET|SYM_FAR }, + { "near", 0, rdf_helper_flag, 0, FLAG_EXT|FLAG_CLR|SYM_FAR } + }; + + if (!objext_valparams) + return 0; + + yasm_dir_helper(sym, yasm_vps_first(objext_valparams), 0, help, + NELEMS(help), &flags, yasm_dir_helper_valparam_warn); + + return flags; +} + +static int +rdf_objfmt_output_sym(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ rdf_objfmt_output_info *info = (rdf_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + /*@only@*/ char *name; + size_t len; + unsigned long value = 0; + unsigned int scnum = 0; + /*@dependent@*/ /*@null@*/ yasm_section *sect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + unsigned char *localbuf; + + assert(info != NULL); + + if (vis == YASM_SYM_LOCAL || vis == YASM_SYM_DLOCAL) + return 0; /* skip local syms */ + + /* Look at symrec for value/scnum/etc. */ + if (yasm_symrec_get_label(sym, &precbc)) { + /*@dependent@*/ /*@null@*/ rdf_section_data *csectd; + + if (precbc) + sect = yasm_bc_get_section(precbc); + else + sect = NULL; + if (!sect) + return 0; + + /* it's a label: get value and offset. */ + csectd = yasm_section_get_data(sect, &rdf_section_data_cb); + if (csectd) + scnum = csectd->scnum; + else + yasm_internal_error(N_("didn't understand section")); + value = yasm_bc_next_offset(precbc); + } else if (yasm_symrec_get_equ(sym)) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("rdf does not support exporting EQU/absolute values")); + yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); + return 0; + } + + name = yasm_symrec_get_global_name(sym, info->object); + len = strlen(name); + + if (len > EXIM_LABEL_MAX-1) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("label name too long, truncating to %d bytes"), + EXIM_LABEL_MAX); + len = EXIM_LABEL_MAX-1; + } + + localbuf = info->buf; + if (vis & YASM_SYM_GLOBAL) { + YASM_WRITE_8(localbuf, RDFREC_GLOBAL); + YASM_WRITE_8(localbuf, 6+len+1); /* record length */ + YASM_WRITE_8(localbuf, rdf_parse_flags(sym)); /* flags */ + YASM_WRITE_8(localbuf, scnum); /* segment referred to */ + YASM_WRITE_32_L(localbuf, value); /* offset */ + } else { + /* Save symbol segment in symrec data (for later reloc gen) */ + scnum = info->indx++; + rdf_objfmt_sym_set_data(sym, scnum); + + if (vis & YASM_SYM_COMMON) { + /*@dependent@*/ /*@null@*/ yasm_expr **csize_expr; + const yasm_intnum *intn; + /*@dependent@*/ /*@null@*/ yasm_valparamhead *objext_valparams = + yasm_symrec_get_objext_valparams(sym); + unsigned long addralign = 0; + + YASM_WRITE_8(localbuf, RDFREC_COMMON); + YASM_WRITE_8(localbuf, 8+len+1); /* record length */ + YASM_WRITE_16_L(localbuf, scnum); /* segment allocated */ + + /* size */ + csize_expr = yasm_symrec_get_common_size(sym); + assert(csize_expr != NULL); + intn = yasm_expr_get_intnum(csize_expr, 1); + if (!intn) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("COMMON data size not an integer expression")); + } else + value = yasm_intnum_get_uint(intn); + YASM_WRITE_32_L(localbuf, value); + + /* alignment */ + if (objext_valparams) { + yasm_valparam *vp = yasm_vps_first(objext_valparams); + for (; vp; vp = yasm_vps_next(vp)) { + if (!vp->val) { + /*@only@*/ /*@null@*/ yasm_expr *align_expr; + /*@dependent@*/ /*@null@*/ + const yasm_intnum *align_intn; + + if (!(align_expr = yasm_vp_expr(vp, + info->object->symtab, + yasm_symrec_get_decl_line(sym))) || + !(align_intn = yasm_expr_get_intnum(&align_expr, + 0))) { + yasm_error_set(YASM_ERROR_VALUE, + N_("argument to `%s' is not an integer"), + vp->val); + if (align_expr) + yasm_expr_destroy(align_expr); + continue; + } + addralign = yasm_intnum_get_uint(align_intn); + yasm_expr_destroy(align_expr); + + /* Alignments must be a power of two. */ + if (!is_exp2(addralign)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("alignment constraint is not a power of two")); + continue; + } + } else + yasm_warn_set(YASM_WARN_GENERAL, + N_("Unrecognized qualifier `%s'"), vp->val); + } + } + YASM_WRITE_16_L(localbuf, addralign); + } else if (vis & YASM_SYM_EXTERN) { + unsigned int flags = rdf_parse_flags(sym); + if (flags & SYM_FAR) { + YASM_WRITE_8(localbuf, RDFREC_FARIMPORT); + flags &= ~SYM_FAR; + } else + YASM_WRITE_8(localbuf, RDFREC_IMPORT); + YASM_WRITE_8(localbuf, 3+len+1); /* record length */ + YASM_WRITE_8(localbuf, flags); /* flags */ + YASM_WRITE_16_L(localbuf, scnum); /* segment allocated */ + } + } + + /* Symbol name */ + memcpy(localbuf, name, len); + localbuf += len; + YASM_WRITE_8(localbuf, 0); /* 0-terminated name */ + yasm_xfree(name); + + fwrite(info->buf, (unsigned long)(localbuf-info->buf), 1, info->f); + + yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); + return 0; +} + +static void +rdf_objfmt_output(yasm_object *object, FILE *f, int all_syms, + yasm_errwarns *errwarns) +{ + yasm_objfmt_rdf *objfmt_rdf = (yasm_objfmt_rdf *)object->objfmt; + rdf_objfmt_output_info info; + unsigned char *localbuf; + long headerlen, filelen; + xdf_str *cur; + size_t len; + + info.object = object; + info.objfmt_rdf = objfmt_rdf; + info.errwarns = errwarns; + info.f = f; + info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); + info.bss_size = 0; + + /* Allocate space for file header by seeking forward */ + if (fseek(f, (long)strlen(RDF_MAGIC)+8, SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + /* Output custom header records (library and module, etc) */ + cur = STAILQ_FIRST(&objfmt_rdf->module_names); + while (cur) { + len = strlen(cur->str)+1; + localbuf = info.buf; + YASM_WRITE_8(localbuf, RDFREC_MODNAME); /* record type */ + YASM_WRITE_8(localbuf, len); /* record length */ + fwrite(info.buf, 2, 1, f); + fwrite(cur->str, len, 1, f); + cur = STAILQ_NEXT(cur, link); + } + + cur = STAILQ_FIRST(&objfmt_rdf->library_names); + while (cur) { + len = strlen(cur->str)+1; + localbuf = info.buf; + YASM_WRITE_8(localbuf, RDFREC_DLL); /* record type */ + YASM_WRITE_8(localbuf, len); /* record length */ + fwrite(info.buf, 2, 1, f); + fwrite(cur->str, len, 1, f); + cur = STAILQ_NEXT(cur, link); + } + + /* Output symbol table */ + info.indx = objfmt_rdf->parse_scnum; + yasm_symtab_traverse(object->symtab, &info, rdf_objfmt_output_sym); + + /* UGH! Due to the fact the relocs go at the beginning of the file, and + * we only know if we have relocs when we output the sections, we have + * to output the section data before we have output the relocs. But + * we also don't know how much space to preallocate for relocs, so.... + * we output into memory buffers first (thus the UGH). + * + * Stupid object format design, if you ask me (basically all other + * object formats put the relocs *after* the section data to avoid this + * exact problem). + * + * We also calculate the total size of all BSS sections here. + */ + if (yasm_object_sections_traverse(object, &info, + rdf_objfmt_output_section_mem)) + return; + + /* Output all relocs */ + if (yasm_object_sections_traverse(object, &info, + rdf_objfmt_output_section_reloc)) + return; + + /* Output BSS record */ + if (info.bss_size > 0) { + localbuf = info.buf; + YASM_WRITE_8(localbuf, RDFREC_BSS); /* record type */ + YASM_WRITE_8(localbuf, 4); /* record length */ + YASM_WRITE_32_L(localbuf, info.bss_size); /* total BSS size */ + fwrite(info.buf, 6, 1, f); + } + + /* Determine header length */ + headerlen = ftell(f); + if (headerlen == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return; + } + + /* Section data (to file) */ + if (yasm_object_sections_traverse(object, &info, + rdf_objfmt_output_section_file)) + return; + + /* NULL section to end file */ + memset(info.buf, 0, 10); + fwrite(info.buf, 10, 1, f); + + /* Determine object length */ + filelen = ftell(f); + if (filelen == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return; + } + + /* Write file header */ + if (fseek(f, 0, SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + fwrite(RDF_MAGIC, strlen(RDF_MAGIC), 1, f); + localbuf = info.buf; + YASM_WRITE_32_L(localbuf, filelen-10); /* object size */ + YASM_WRITE_32_L(localbuf, headerlen-14); /* header size */ + fwrite(info.buf, 8, 1, f); + + yasm_xfree(info.buf); +} + +static void +rdf_objfmt_destroy(yasm_objfmt *objfmt) +{ + yasm_objfmt_rdf *objfmt_rdf = (yasm_objfmt_rdf *)objfmt; + xdf_str *cur, *next; + + cur = STAILQ_FIRST(&objfmt_rdf->module_names); + while (cur) { + next = STAILQ_NEXT(cur, link); + yasm_xfree(cur->str); + yasm_xfree(cur); + cur = next; + } + + cur = STAILQ_FIRST(&objfmt_rdf->library_names); + while (cur) { + next = STAILQ_NEXT(cur, link); + yasm_xfree(cur->str); + yasm_xfree(cur); + cur = next; + } + + yasm_xfree(objfmt); +} + +static void +rdf_objfmt_init_new_section(yasm_section *sect, unsigned long line) +{ + yasm_object *object = yasm_section_get_object(sect); + const char *sectname = yasm_section_get_name(sect); + yasm_objfmt_rdf *objfmt_rdf = (yasm_objfmt_rdf *)object->objfmt; + rdf_section_data *data; + yasm_symrec *sym; + + data = yasm_xmalloc(sizeof(rdf_section_data)); + data->scnum = objfmt_rdf->parse_scnum++; + data->type = 0; + data->reserved = 0; + data->size = 0; + data->raw_data = NULL; + yasm_section_add_data(sect, &rdf_section_data_cb, data); + + sym = yasm_symtab_define_label(object->symtab, sectname, + yasm_section_bcs_first(sect), 1, line); + data->sym = sym; +} + +static yasm_section * +rdf_objfmt_add_default_section(yasm_object *object) +{ + yasm_section *retval; + rdf_section_data *rsd; + int isnew; + + retval = yasm_object_get_general(object, ".text", 0, 1, 0, &isnew, 0); + if (isnew) { + rsd = yasm_section_get_data(retval, &rdf_section_data_cb); + rsd->type = RDF_SECT_CODE; + rsd->reserved = 0; + yasm_section_set_default(retval, 1); + } + return retval; +} + +static int +rdf_helper_set_type(void *obj, yasm_valparam *vp, unsigned long line, + void *d, uintptr_t newtype) +{ + unsigned int *type = (unsigned int *)d; + *type = newtype; + return 0; +} + +struct rdf_section_switch_data { + /*@only@*/ /*@null@*/ yasm_intnum *reserved_intn; + unsigned int type; +}; + +static int +rdf_helper_set_reserved(void *obj, yasm_valparam *vp, unsigned long line, + void *d) +{ + struct rdf_section_switch_data *data = (struct rdf_section_switch_data *)d; + + if (!vp->val && vp->type == YASM_PARAM_EXPR) + return yasm_dir_helper_intn(obj, vp, line, &data->reserved_intn, 0); + else + return yasm_dir_helper_valparam_warn(obj, vp, line, d); +} + +static /*@observer@*/ /*@null@*/ yasm_section * +rdf_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, + /*@unused@*/ /*@null@*/ + yasm_valparamhead *objext_valparams, + unsigned long line) +{ + yasm_valparam *vp = yasm_vps_first(valparams); + yasm_section *retval; + int isnew; + unsigned int reserved = 0; + int flags_override = 0; + const char *sectname; + rdf_section_data *rsd; + + struct rdf_section_switch_data data; + + static const yasm_dir_help help[] = { + { "bss", 0, rdf_helper_set_type, + offsetof(struct rdf_section_switch_data, type), RDF_SECT_BSS }, + { "code", 0, rdf_helper_set_type, + offsetof(struct rdf_section_switch_data, type), RDF_SECT_CODE }, + { "text", 0, rdf_helper_set_type, + offsetof(struct rdf_section_switch_data, type), RDF_SECT_CODE }, + { "data", 0, rdf_helper_set_type, + offsetof(struct rdf_section_switch_data, type), RDF_SECT_DATA }, + { "comment", 0, rdf_helper_set_type, + offsetof(struct rdf_section_switch_data, type), RDF_SECT_COMMENT }, + { "lcomment", 0, rdf_helper_set_type, + offsetof(struct rdf_section_switch_data, type), RDF_SECT_LCOMMENT }, + { "pcomment", 0, rdf_helper_set_type, + offsetof(struct rdf_section_switch_data, type), RDF_SECT_PCOMMENT }, + { "symdebug", 0, rdf_helper_set_type, + offsetof(struct rdf_section_switch_data, type), RDF_SECT_SYMDEBUG }, + { "linedebug", 0, rdf_helper_set_type, + offsetof(struct rdf_section_switch_data, type), RDF_SECT_LINEDEBUG }, + { "reserved", 1, yasm_dir_helper_intn, + offsetof(struct rdf_section_switch_data, reserved_intn), 0 } + }; + + data.reserved_intn = NULL; + data.type = 0xffff; + + vp = yasm_vps_first(valparams); + sectname = yasm_vp_string(vp); + if (!sectname) + return NULL; + vp = yasm_vps_next(vp); + + if (strcmp(sectname, ".text") == 0) + data.type = RDF_SECT_CODE; + else if (strcmp(sectname, ".data") == 0) + data.type = RDF_SECT_DATA; + else if (strcmp(sectname, ".bss") == 0) + data.type = RDF_SECT_BSS; + + flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), + &data, rdf_helper_set_reserved); + if (flags_override < 0) + return NULL; /* error occurred */ + + if (data.type == 0xffff) { + yasm_error_set(YASM_ERROR_VALUE, + N_("new segment declared without type code")); + data.type = RDF_SECT_DATA; + } + + if (data.reserved_intn) { + reserved = yasm_intnum_get_uint(data.reserved_intn); + yasm_intnum_destroy(data.reserved_intn); + } + + retval = yasm_object_get_general(object, sectname, 0, 1, + data.type == RDF_SECT_BSS, &isnew, line); + + rsd = yasm_section_get_data(retval, &rdf_section_data_cb); + + if (isnew || yasm_section_is_default(retval)) { + yasm_section_set_default(retval, 0); + rsd->type = data.type; + rsd->reserved = reserved; + } else if (flags_override) + yasm_warn_set(YASM_WARN_GENERAL, + N_("section flags ignored on section redeclaration")); + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +rdf_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + return NULL; +} + +static void +rdf_section_data_destroy(void *data) +{ + rdf_section_data *rsd = (rdf_section_data *)data; + if (rsd->raw_data) + yasm_xfree(rsd->raw_data); + yasm_xfree(data); +} + +static void +rdf_section_data_print(void *data, FILE *f, int indent_level) +{ + rdf_section_data *rsd = (rdf_section_data *)data; + + fprintf(f, "%*ssym=\n", indent_level, ""); + yasm_symrec_print(rsd->sym, f, indent_level+1); + fprintf(f, "%*sscnum=%ld\n", indent_level, "", rsd->scnum); + fprintf(f, "%*stype=0x%x\n", indent_level, "", rsd->type); + fprintf(f, "%*sreserved=0x%x\n", indent_level, "", rsd->reserved); + fprintf(f, "%*ssize=%ld\n", indent_level, "", rsd->size); +} + +static void +rdf_symrec_data_destroy(void *data) +{ + yasm_xfree(data); +} + +static void +rdf_symrec_data_print(void *data, FILE *f, int indent_level) +{ + rdf_symrec_data *rsymd = (rdf_symrec_data *)data; + + fprintf(f, "%*ssymtab segment=%u\n", indent_level, "", rsymd->segment); +} + +static void +rdf_objfmt_add_libmodule(yasm_object *object, char *name, int lib) +{ + yasm_objfmt_rdf *objfmt_rdf = (yasm_objfmt_rdf *)object->objfmt; + xdf_str *str; + + /* Add to list */ + str = yasm_xmalloc(sizeof(xdf_str)); + str->str = name; + if (lib) + STAILQ_INSERT_TAIL(&objfmt_rdf->library_names, str, link); + else + STAILQ_INSERT_TAIL(&objfmt_rdf->module_names, str, link); + + if (strlen(str->str) > MODLIB_NAME_MAX-1) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("name too long, truncating to %d bytes"), + MODLIB_NAME_MAX); + str->str[MODLIB_NAME_MAX-1] = '\0'; + } +} + +static void +dir_library(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp = yasm_vps_first(valparams); + rdf_objfmt_add_libmodule(object, yasm__xstrdup(yasm_vp_string(vp)), 1); +} + +static void +dir_module(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp = yasm_vps_first(valparams); + rdf_objfmt_add_libmodule(object, yasm__xstrdup(yasm_vp_string(vp)), 0); +} + +/* Define valid debug formats to use with this object format */ +static const char *rdf_objfmt_dbgfmt_keywords[] = { + "null", + NULL +}; + +static const yasm_directive rdf_objfmt_directives[] = { + { "library", "nasm", dir_library, YASM_DIR_ARG_REQUIRED }, + { "module", "nasm", dir_module, YASM_DIR_ARG_REQUIRED }, + { NULL, NULL, NULL, 0 } +}; + +static const char *rdf_nasm_stdmac[] = { + "%imacro library 1+.nolist", + "[library %1]", + "%endmacro", + "%imacro module 1+.nolist", + "[module %1]", + "%endmacro", + NULL +}; + +static const yasm_stdmac rdf_objfmt_stdmacs[] = { + { "nasm", "nasm", rdf_nasm_stdmac }, + { NULL, NULL, NULL } +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_rdf_LTX_objfmt = { + "Relocatable Dynamic Object File Format (RDOFF) v2.0", + "rdf", + "rdf", + 32, + 0, + rdf_objfmt_dbgfmt_keywords, + "null", + rdf_objfmt_directives, + rdf_objfmt_stdmacs, + rdf_objfmt_create, + rdf_objfmt_output, + rdf_objfmt_destroy, + rdf_objfmt_add_default_section, + rdf_objfmt_init_new_section, + rdf_objfmt_section_switch, + rdf_objfmt_get_special_sym +}; diff --git a/contrib/tools/yasm/modules/objfmts/xdf/xdf-objfmt.c b/contrib/tools/yasm/modules/objfmts/xdf/xdf-objfmt.c index 9ad0e60afa..b2ac442511 100644 --- a/contrib/tools/yasm/modules/objfmts/xdf/xdf-objfmt.c +++ b/contrib/tools/yasm/modules/objfmts/xdf/xdf-objfmt.c @@ -1,842 +1,842 @@ -/* - * Extended Dynamic Object format - * - * Copyright (C) 2004-2007 Peter Johnson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include <util.h> - -#include <libyasm.h> - - -#define REGULAR_OUTBUF_SIZE 1024 - -#define XDF_MAGIC 0x87654322 - -#define XDF_SYM_EXTERN 1 -#define XDF_SYM_GLOBAL 2 -#define XDF_SYM_EQU 4 - -typedef struct xdf_reloc { - yasm_reloc reloc; - /*@null@*/ yasm_symrec *base; /* base symbol (for WRT) */ - enum { - XDF_RELOC_REL = 1, /* relative to segment */ - XDF_RELOC_WRT = 2, /* relative to symbol */ - XDF_RELOC_RIP = 4, /* RIP-relative */ - XDF_RELOC_SEG = 8 /* segment containing symbol */ - } type; /* type of relocation */ - enum { - XDF_RELOC_8 = 1, - XDF_RELOC_16 = 2, - XDF_RELOC_32 = 4, - XDF_RELOC_64 = 8 - } size; /* size of relocation */ - unsigned int shift; /* relocation shift (0,4,8,16,24,32) */ -} xdf_reloc; - -typedef struct xdf_section_data { - /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ - yasm_intnum *addr; /* starting memory address */ - yasm_intnum *vaddr; /* starting virtual address */ - long scnum; /* section number (0=first section) */ - enum { - XDF_SECT_ABSOLUTE = 0x01, - XDF_SECT_FLAT = 0x02, - XDF_SECT_BSS = 0x04, - XDF_SECT_EQU = 0x08, - XDF_SECT_USE_16 = 0x10, - XDF_SECT_USE_32 = 0x20, - XDF_SECT_USE_64 = 0x40 - } flags; /* section flags */ - unsigned long scnptr; /* file ptr to raw data */ - unsigned long size; /* size of raw data (section data) in bytes */ - unsigned long relptr; /* file ptr to relocation */ - unsigned long nreloc; /* number of relocation entries >64k -> error */ -} xdf_section_data; - -typedef struct xdf_symrec_data { - unsigned long index; /* assigned XDF symbol table index */ -} xdf_symrec_data; - -typedef struct yasm_objfmt_xdf { - yasm_objfmt_base objfmt; /* base structure */ - - long parse_scnum; /* sect numbering in parser */ -} yasm_objfmt_xdf; - -typedef struct xdf_objfmt_output_info { - yasm_object *object; - yasm_objfmt_xdf *objfmt_xdf; - yasm_errwarns *errwarns; - /*@dependent@*/ FILE *f; - /*@only@*/ unsigned char *buf; - yasm_section *sect; - /*@dependent@*/ xdf_section_data *xsd; - - unsigned long indx; /* current symbol index */ - int all_syms; /* outputting all symbols? */ - unsigned long strtab_offset; /* current string table offset */ -} xdf_objfmt_output_info; - -static void xdf_section_data_destroy(/*@only@*/ void *d); -static void xdf_section_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback xdf_section_data_cb = { - xdf_section_data_destroy, - xdf_section_data_print -}; - -static void xdf_symrec_data_destroy(/*@only@*/ void *d); -static void xdf_symrec_data_print(void *data, FILE *f, int indent_level); - -static const yasm_assoc_data_callback xdf_symrec_data_cb = { - xdf_symrec_data_destroy, - xdf_symrec_data_print -}; - -yasm_objfmt_module yasm_xdf_LTX_objfmt; - - -static yasm_objfmt * -xdf_objfmt_create(yasm_object *object) -{ - yasm_objfmt_xdf *objfmt_xdf = yasm_xmalloc(sizeof(yasm_objfmt_xdf)); - - /* Only support x86 arch */ - if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") != 0) { - yasm_xfree(objfmt_xdf); - return NULL; - } - - /* Support x86 and amd64 machines of x86 arch */ - if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), "x86") && - yasm__strcasecmp(yasm_arch_get_machine(object->arch), "amd64")) { - yasm_xfree(objfmt_xdf); - return NULL; - } - - objfmt_xdf->parse_scnum = 0; /* section numbering starts at 0 */ - - objfmt_xdf->objfmt.module = &yasm_xdf_LTX_objfmt; - - return (yasm_objfmt *)objfmt_xdf; -} - -static int -xdf_objfmt_output_value(yasm_value *value, unsigned char *buf, - unsigned int destsize, unsigned long offset, - yasm_bytecode *bc, int warn, /*@null@*/ void *d) -{ - /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ yasm_intnum *intn; - unsigned long intn_minus; - int retval; - unsigned int valsize = value->size; - - assert(info != NULL); - - if (value->abs) - value->abs = yasm_expr_simplify(value->abs, 1); - - /* Try to output constant and PC-relative section-local first. - * Note this does NOT output any value with a SEG, WRT, external, - * cross-section, or non-PC-relative reference (those are handled below). - */ - switch (yasm_value_output_basic(value, buf, destsize, bc, warn, - info->object->arch)) { - case -1: - return 1; - case 0: - break; - default: - return 0; - } - - if (value->section_rel) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("xdf: relocation too complex")); - return 1; - } - - intn_minus = 0; - if (value->rel) { - xdf_reloc *reloc; - - reloc = yasm_xmalloc(sizeof(xdf_reloc)); - reloc->reloc.addr = yasm_intnum_create_uint(bc->offset + offset); - reloc->reloc.sym = value->rel; - reloc->base = NULL; - reloc->size = valsize/8; - reloc->shift = value->rshift; - - if (value->seg_of) - reloc->type = XDF_RELOC_SEG; - else if (value->wrt) { - reloc->base = value->wrt; - reloc->type = XDF_RELOC_WRT; - } else if (value->curpos_rel) { - reloc->type = XDF_RELOC_RIP; - /* Adjust to start of section, so subtract out the bytecode - * offset. - */ - intn_minus = bc->offset; - } else - reloc->type = XDF_RELOC_REL; - info->xsd->nreloc++; - yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); - } - - if (intn_minus > 0) { - intn = yasm_intnum_create_uint(intn_minus); - yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); - } else - intn = yasm_intnum_create_uint(0); - - if (value->abs) { - yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); - if (!intn2) { - yasm_error_set(YASM_ERROR_TOO_COMPLEX, - N_("xdf: relocation too complex")); - yasm_intnum_destroy(intn); - return 1; - } - yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); - } - - retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, - valsize, 0, bc, warn); - yasm_intnum_destroy(intn); - return retval; -} - -static int -xdf_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) -{ - /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; - /*@null@*/ /*@only@*/ unsigned char *bigbuf; - unsigned long size = REGULAR_OUTBUF_SIZE; - int gap; - - assert(info != NULL); - - bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, - xdf_objfmt_output_value, NULL); - - /* Don't bother doing anything else if size ended up being 0. */ - if (size == 0) { - if (bigbuf) - yasm_xfree(bigbuf); - return 0; - } - - info->xsd->size += size; - - /* Warn that gaps are converted to 0 and write out the 0's. */ - if (gap) { - unsigned long left; - yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, - N_("uninitialized space: zeroing")); - /* Write out in chunks */ - memset(info->buf, 0, REGULAR_OUTBUF_SIZE); - left = size; - while (left > REGULAR_OUTBUF_SIZE) { - fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); - left -= REGULAR_OUTBUF_SIZE; - } - fwrite(info->buf, left, 1, info->f); - } else { - /* Output buf (or bigbuf if non-NULL) to file */ - fwrite(bigbuf ? bigbuf : info->buf, (size_t)size, 1, info->f); - } - - /* If bigbuf was allocated, free it */ - if (bigbuf) - yasm_xfree(bigbuf); - - return 0; -} - -static int -xdf_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ xdf_section_data *xsd; - long pos; - xdf_reloc *reloc; - - assert(info != NULL); - xsd = yasm_section_get_data(sect, &xdf_section_data_cb); - assert(xsd != NULL); - - if (xsd->flags & XDF_SECT_BSS) { - /* Don't output BSS sections. - * TODO: Check for non-reserve bytecodes? - */ - pos = 0; /* position = 0 because it's not in the file */ - xsd->size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); - } else { - pos = ftell(info->f); - if (pos == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return 1; - } - - info->sect = sect; - info->xsd = xsd; - yasm_section_bcs_traverse(sect, info->errwarns, info, - xdf_objfmt_output_bytecode); - - /* Sanity check final section size */ - if (xsd->size != yasm_bc_next_offset(yasm_section_bcs_last(sect))) - yasm_internal_error( - N_("xdf: section computed size did not match actual size")); - } - - /* Empty? Go on to next section */ - if (xsd->size == 0) - return 0; - - xsd->scnptr = (unsigned long)pos; - - /* No relocations to output? Go on to next section */ - if (xsd->nreloc == 0) - return 0; - - pos = ftell(info->f); - if (pos == -1) { - yasm__fatal(N_("could not get file position on output file")); - /*@notreached@*/ - return 1; - } - xsd->relptr = (unsigned long)pos; - - reloc = (xdf_reloc *)yasm_section_relocs_first(sect); - while (reloc) { - unsigned char *localbuf = info->buf; - /*@null@*/ xdf_symrec_data *xsymd; - - xsymd = yasm_symrec_get_data(reloc->reloc.sym, &xdf_symrec_data_cb); - if (!xsymd) - yasm_internal_error( - N_("xdf: no symbol data for relocated symbol")); - - yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); - localbuf += 4; /* address of relocation */ - YASM_WRITE_32_L(localbuf, xsymd->index); /* relocated symbol */ - if (reloc->base) { - xsymd = yasm_symrec_get_data(reloc->base, &xdf_symrec_data_cb); - if (!xsymd) - yasm_internal_error( - N_("xdf: no symbol data for relocated base symbol")); - YASM_WRITE_32_L(localbuf, xsymd->index); /* base symbol */ - } else { - if (reloc->type == XDF_RELOC_WRT) - yasm_internal_error( - N_("xdf: no base symbol for WRT relocation")); - YASM_WRITE_32_L(localbuf, 0); /* no base symbol */ - } - YASM_WRITE_8(localbuf, reloc->type); /* type of relocation */ - YASM_WRITE_8(localbuf, reloc->size); /* size of relocation */ - YASM_WRITE_8(localbuf, reloc->shift); /* relocation shift */ - YASM_WRITE_8(localbuf, 0); /* flags */ - fwrite(info->buf, 16, 1, info->f); - - reloc = (xdf_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); - } - - return 0; -} - -static int -xdf_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) -{ - /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; - /*@dependent@*/ /*@null@*/ xdf_section_data *xsd; - /*@null@*/ xdf_symrec_data *xsymd; - unsigned char *localbuf; - - assert(info != NULL); - xsd = yasm_section_get_data(sect, &xdf_section_data_cb); - assert(xsd != NULL); - - localbuf = info->buf; - xsymd = yasm_symrec_get_data(xsd->sym, &xdf_symrec_data_cb); - assert(xsymd != NULL); - - YASM_WRITE_32_L(localbuf, xsymd->index); /* section name symbol */ - if (xsd->addr) { - yasm_intnum_get_sized(xsd->addr, localbuf, 8, 64, 0, 0, 0); - localbuf += 8; /* physical address */ - } else { - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, 0); - } - if (xsd->vaddr) { - yasm_intnum_get_sized(xsd->vaddr, localbuf, 8, 64, 0, 0, 0); - localbuf += 8; /* virtual address */ - } else if (xsd->addr) { - yasm_intnum_get_sized(xsd->addr, localbuf, 8, 64, 0, 0, 0); - localbuf += 8; /* VA=PA */ - } else { - YASM_WRITE_32_L(localbuf, 0); - YASM_WRITE_32_L(localbuf, 0); - } - YASM_WRITE_16_L(localbuf, yasm_section_get_align(sect)); /* alignment */ - YASM_WRITE_16_L(localbuf, xsd->flags); /* flags */ - YASM_WRITE_32_L(localbuf, xsd->scnptr); /* file ptr to data */ - YASM_WRITE_32_L(localbuf, xsd->size); /* section size */ - YASM_WRITE_32_L(localbuf, xsd->relptr); /* file ptr to relocs */ - YASM_WRITE_32_L(localbuf, xsd->nreloc); /* num of relocation entries */ - fwrite(info->buf, 40, 1, info->f); - - return 0; -} - -static int -xdf_objfmt_count_sym(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - assert(info != NULL); - if (vis & YASM_SYM_COMMON) { - yasm_error_set(YASM_ERROR_GENERAL, - N_("XDF object format does not support common variables")); - yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); - return 0; - } - if (info->all_syms || - (vis != YASM_SYM_LOCAL && !(vis & YASM_SYM_DLOCAL))) { - /* Save index in symrec data */ - xdf_symrec_data *sym_data = yasm_xmalloc(sizeof(xdf_symrec_data)); - sym_data->index = info->indx; - yasm_symrec_add_data(sym, &xdf_symrec_data_cb, sym_data); - - info->indx++; - } - return 0; -} - -static int -xdf_objfmt_output_sym(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - - assert(info != NULL); - - if (info->all_syms || vis != YASM_SYM_LOCAL) { - /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); - const yasm_expr *equ_val; - const yasm_intnum *intn; - size_t len = strlen(name); - unsigned long value = 0; - long scnum = -3; /* -3 = debugging symbol */ - /*@dependent@*/ /*@null@*/ yasm_section *sect; - /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; - unsigned long flags = 0; - unsigned char *localbuf; - - if (vis & YASM_SYM_GLOBAL) - flags = XDF_SYM_GLOBAL; - - /* Look at symrec for value/scnum/etc. */ - if (yasm_symrec_get_label(sym, &precbc)) { - if (precbc) - sect = yasm_bc_get_section(precbc); - else - sect = NULL; - /* it's a label: get value and offset. - * If there is not a section, leave as debugging symbol. - */ - if (sect) { - /*@dependent@*/ /*@null@*/ xdf_section_data *csectd; - csectd = yasm_section_get_data(sect, &xdf_section_data_cb); - if (csectd) - scnum = csectd->scnum; - else - yasm_internal_error(N_("didn't understand section")); - if (precbc) - value += yasm_bc_next_offset(precbc); - } - } else if ((equ_val = yasm_symrec_get_equ(sym))) { - yasm_expr *equ_val_copy = yasm_expr_copy(equ_val); - intn = yasm_expr_get_intnum(&equ_val_copy, 1); - if (!intn) { - if (vis & YASM_SYM_GLOBAL) { - yasm_error_set(YASM_ERROR_NOT_CONSTANT, - N_("global EQU value not an integer expression")); - yasm_errwarn_propagate(info->errwarns, equ_val->line); - } - } else - value = yasm_intnum_get_uint(intn); - yasm_expr_destroy(equ_val_copy); - - flags |= XDF_SYM_EQU; - scnum = -2; /* -2 = absolute symbol */ - } else { - if (vis & YASM_SYM_EXTERN) { - flags = XDF_SYM_EXTERN; - scnum = -1; - } - } - - localbuf = info->buf; - YASM_WRITE_32_L(localbuf, scnum); /* section number */ - YASM_WRITE_32_L(localbuf, value); /* value */ - YASM_WRITE_32_L(localbuf, info->strtab_offset); - info->strtab_offset += (unsigned long)(len+1); - YASM_WRITE_32_L(localbuf, flags); /* flags */ - fwrite(info->buf, 16, 1, info->f); - yasm_xfree(name); - } - return 0; -} - -static int -xdf_objfmt_output_str(yasm_symrec *sym, /*@null@*/ void *d) -{ - /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; - yasm_sym_vis vis = yasm_symrec_get_visibility(sym); - - assert(info != NULL); - - if (info->all_syms || vis != YASM_SYM_LOCAL) { - /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); - size_t len = strlen(name); - fwrite(name, len+1, 1, info->f); - yasm_xfree(name); - } - return 0; -} - -static void -xdf_objfmt_output(yasm_object *object, FILE *f, int all_syms, - yasm_errwarns *errwarns) -{ - yasm_objfmt_xdf *objfmt_xdf = (yasm_objfmt_xdf *)object->objfmt; - xdf_objfmt_output_info info; - unsigned char *localbuf; - unsigned long symtab_count = 0; - - info.object = object; - info.objfmt_xdf = objfmt_xdf; - info.errwarns = errwarns; - info.f = f; - info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); - - /* Allocate space for headers by seeking forward */ - if (fseek(f, (long)(16+40*(objfmt_xdf->parse_scnum)), SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - /* Get number of symbols */ - info.indx = 0; - info.all_syms = 1; /* force all syms into symbol table */ - yasm_symtab_traverse(object->symtab, &info, xdf_objfmt_count_sym); - symtab_count = info.indx; - - /* Get file offset of start of string table */ - info.strtab_offset = 16+40*(objfmt_xdf->parse_scnum)+16*symtab_count; - - /* Output symbol table */ - yasm_symtab_traverse(object->symtab, &info, xdf_objfmt_output_sym); - - /* Output string table */ - yasm_symtab_traverse(object->symtab, &info, xdf_objfmt_output_str); - - /* Section data/relocs */ - if (yasm_object_sections_traverse(object, &info, - xdf_objfmt_output_section)) - return; - - /* Write headers */ - if (fseek(f, 0, SEEK_SET) < 0) { - yasm__fatal(N_("could not seek on output file")); - /*@notreached@*/ - return; - } - - localbuf = info.buf; - YASM_WRITE_32_L(localbuf, XDF_MAGIC); /* magic number */ - YASM_WRITE_32_L(localbuf, objfmt_xdf->parse_scnum); /* number of sects */ - YASM_WRITE_32_L(localbuf, symtab_count); /* number of symtabs */ - /* size of sect headers + symbol table + strings */ - YASM_WRITE_32_L(localbuf, info.strtab_offset-16); - fwrite(info.buf, 16, 1, f); - - yasm_object_sections_traverse(object, &info, xdf_objfmt_output_secthead); - - yasm_xfree(info.buf); -} - -static void -xdf_objfmt_destroy(yasm_objfmt *objfmt) -{ - yasm_xfree(objfmt); -} - -static void -xdf_objfmt_init_new_section(yasm_section *sect, unsigned long line) -{ - yasm_object *object = yasm_section_get_object(sect); - const char *sectname = yasm_section_get_name(sect); - yasm_objfmt_xdf *objfmt_xdf = (yasm_objfmt_xdf *)object->objfmt; - xdf_section_data *data; - yasm_symrec *sym; - - data = yasm_xmalloc(sizeof(xdf_section_data)); - data->scnum = objfmt_xdf->parse_scnum++; - data->flags = 0; - data->addr = NULL; - data->vaddr = NULL; - data->scnptr = 0; - data->size = 0; - data->relptr = 0; - data->nreloc = 0; - yasm_section_add_data(sect, &xdf_section_data_cb, data); - - sym = yasm_symtab_define_label(object->symtab, sectname, - yasm_section_bcs_first(sect), 1, line); - data->sym = sym; -} - -static yasm_section * -xdf_objfmt_add_default_section(yasm_object *object) -{ - yasm_section *retval; - int isnew; - - retval = yasm_object_get_general(object, ".text", 0, 1, 0, &isnew, 0); - if (isnew) - yasm_section_set_default(retval, 1); - return retval; -} - -static int -xdf_helper_use(void *obj, yasm_valparam *vp, unsigned long line, void *d, - uintptr_t bits) -{ - yasm_object *object = (yasm_object *)obj; - unsigned long *flags = (unsigned long *)d; - *flags &= ~(XDF_SECT_USE_16|XDF_SECT_USE_32|XDF_SECT_USE_64); - switch (bits) { - case 16: *flags |= XDF_SECT_USE_16; break; - case 32: *flags |= XDF_SECT_USE_32; break; - case 64: *flags |= XDF_SECT_USE_64; break; - }; - yasm_arch_set_var(object->arch, "mode_bits", bits); - return 0; -} - -static /*@observer@*/ /*@null@*/ yasm_section * -xdf_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, - /*@unused@*/ /*@null@*/ - yasm_valparamhead *objext_valparams, - unsigned long line) -{ - yasm_valparam *vp; - yasm_section *retval; - int isnew; - int flags_override = 0; - const char *sectname; - int resonly = 0; - xdf_section_data *xsd; - unsigned long align = 0; - - struct xdf_section_switch_data { - /*@only@*/ /*@null@*/ yasm_intnum *absaddr; - /*@only@*/ /*@null@*/ yasm_intnum *vaddr; - /*@only@*/ /*@null@*/ yasm_intnum *align_intn; - unsigned long flags; - } data; - - static const yasm_dir_help help[] = { - { "use16", 0, xdf_helper_use, - offsetof(struct xdf_section_switch_data, flags), 16 }, - { "use32", 0, xdf_helper_use, - offsetof(struct xdf_section_switch_data, flags), 32 }, - { "use64", 0, xdf_helper_use, - offsetof(struct xdf_section_switch_data, flags), 64 }, - { "bss", 0, yasm_dir_helper_flag_or, - offsetof(struct xdf_section_switch_data, flags), XDF_SECT_BSS }, - { "flat", 0, yasm_dir_helper_flag_or, - offsetof(struct xdf_section_switch_data, flags), XDF_SECT_FLAT }, - { "absolute", 1, yasm_dir_helper_intn, - offsetof(struct xdf_section_switch_data, absaddr), 0 }, - { "virtual", 1, yasm_dir_helper_intn, - offsetof(struct xdf_section_switch_data, vaddr), 0 }, - { "align", 1, yasm_dir_helper_intn, - offsetof(struct xdf_section_switch_data, align_intn), 0 } - }; - - data.absaddr = NULL; - data.vaddr = NULL; - data.align_intn = NULL; - data.flags = 0; - - vp = yasm_vps_first(valparams); - sectname = yasm_vp_string(vp); - if (!sectname) - return NULL; - vp = yasm_vps_next(vp); - - flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), - &data, yasm_dir_helper_valparam_warn); - if (flags_override < 0) - return NULL; /* error occurred */ - - if (data.absaddr) - data.flags |= XDF_SECT_ABSOLUTE; - if (data.align_intn) { - align = yasm_intnum_get_uint(data.align_intn); - yasm_intnum_destroy(data.align_intn); - - /* Alignments must be a power of two. */ - if (!is_exp2(align)) { - yasm_error_set(YASM_ERROR_VALUE, - N_("argument to `%s' is not a power of two"), - "align"); - if (data.vaddr) - yasm_intnum_destroy(data.vaddr); - if (data.absaddr) - yasm_intnum_destroy(data.absaddr); - return NULL; - } - - /* Check to see if alignment is supported size */ - if (align > 4096) { - yasm_error_set(YASM_ERROR_VALUE, - N_("XDF does not support alignments > 4096")); - if (data.vaddr) - yasm_intnum_destroy(data.vaddr); - if (data.absaddr) - yasm_intnum_destroy(data.absaddr); - return NULL; - } - } - - retval = yasm_object_get_general(object, sectname, align, 1, resonly, - &isnew, line); - - xsd = yasm_section_get_data(retval, &xdf_section_data_cb); - - if (isnew || yasm_section_is_default(retval)) { - yasm_section_set_default(retval, 0); - xsd->flags = data.flags; - if (data.absaddr) { - if (xsd->addr) - yasm_intnum_destroy(xsd->addr); - xsd->addr = data.absaddr; - } - if (data.vaddr) { - if (xsd->vaddr) - yasm_intnum_destroy(xsd->vaddr); - xsd->vaddr = data.vaddr; - } - yasm_section_set_align(retval, align, line); - } else if (flags_override) - yasm_warn_set(YASM_WARN_GENERAL, - N_("section flags ignored on section redeclaration")); - return retval; -} - -static /*@observer@*/ /*@null@*/ yasm_symrec * -xdf_objfmt_get_special_sym(yasm_object *object, const char *name, - const char *parser) -{ - return NULL; -} - -static void -xdf_section_data_destroy(void *data) -{ - xdf_section_data *xsd = (xdf_section_data *)data; - if (xsd->addr) - yasm_intnum_destroy(xsd->addr); - if (xsd->vaddr) - yasm_intnum_destroy(xsd->vaddr); - yasm_xfree(data); -} - -static void -xdf_section_data_print(void *data, FILE *f, int indent_level) -{ - xdf_section_data *xsd = (xdf_section_data *)data; - - fprintf(f, "%*ssym=\n", indent_level, ""); - yasm_symrec_print(xsd->sym, f, indent_level+1); - fprintf(f, "%*sscnum=%ld\n", indent_level, "", xsd->scnum); - fprintf(f, "%*sflags=0x%x\n", indent_level, "", xsd->flags); - fprintf(f, "%*saddr=", indent_level, ""); - yasm_intnum_print(xsd->addr, f); - fprintf(f, "%*svaddr=", indent_level, ""); - yasm_intnum_print(xsd->vaddr, f); - fprintf(f, "%*sscnptr=0x%lx\n", indent_level, "", xsd->scnptr); - fprintf(f, "%*ssize=%ld\n", indent_level, "", xsd->size); - fprintf(f, "%*srelptr=0x%lx\n", indent_level, "", xsd->relptr); - fprintf(f, "%*snreloc=%ld\n", indent_level, "", xsd->nreloc); -} - -static void -xdf_symrec_data_destroy(void *data) -{ - yasm_xfree(data); -} - -static void -xdf_symrec_data_print(void *data, FILE *f, int indent_level) -{ - xdf_symrec_data *xsd = (xdf_symrec_data *)data; - - fprintf(f, "%*ssymtab index=%lu\n", indent_level, "", xsd->index); -} - -/* Define valid debug formats to use with this object format */ -static const char *xdf_objfmt_dbgfmt_keywords[] = { - "null", - NULL -}; - -/* Define objfmt structure -- see objfmt.h for details */ -yasm_objfmt_module yasm_xdf_LTX_objfmt = { - "Extended Dynamic Object", - "xdf", - "xdf", - 32, - 0, - xdf_objfmt_dbgfmt_keywords, - "null", - NULL, /* no directives */ - NULL, /* no standard macros */ - xdf_objfmt_create, - xdf_objfmt_output, - xdf_objfmt_destroy, - xdf_objfmt_add_default_section, - xdf_objfmt_init_new_section, - xdf_objfmt_section_switch, - xdf_objfmt_get_special_sym -}; +/* + * Extended Dynamic Object format + * + * Copyright (C) 2004-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <util.h> + +#include <libyasm.h> + + +#define REGULAR_OUTBUF_SIZE 1024 + +#define XDF_MAGIC 0x87654322 + +#define XDF_SYM_EXTERN 1 +#define XDF_SYM_GLOBAL 2 +#define XDF_SYM_EQU 4 + +typedef struct xdf_reloc { + yasm_reloc reloc; + /*@null@*/ yasm_symrec *base; /* base symbol (for WRT) */ + enum { + XDF_RELOC_REL = 1, /* relative to segment */ + XDF_RELOC_WRT = 2, /* relative to symbol */ + XDF_RELOC_RIP = 4, /* RIP-relative */ + XDF_RELOC_SEG = 8 /* segment containing symbol */ + } type; /* type of relocation */ + enum { + XDF_RELOC_8 = 1, + XDF_RELOC_16 = 2, + XDF_RELOC_32 = 4, + XDF_RELOC_64 = 8 + } size; /* size of relocation */ + unsigned int shift; /* relocation shift (0,4,8,16,24,32) */ +} xdf_reloc; + +typedef struct xdf_section_data { + /*@dependent@*/ yasm_symrec *sym; /* symbol created for this section */ + yasm_intnum *addr; /* starting memory address */ + yasm_intnum *vaddr; /* starting virtual address */ + long scnum; /* section number (0=first section) */ + enum { + XDF_SECT_ABSOLUTE = 0x01, + XDF_SECT_FLAT = 0x02, + XDF_SECT_BSS = 0x04, + XDF_SECT_EQU = 0x08, + XDF_SECT_USE_16 = 0x10, + XDF_SECT_USE_32 = 0x20, + XDF_SECT_USE_64 = 0x40 + } flags; /* section flags */ + unsigned long scnptr; /* file ptr to raw data */ + unsigned long size; /* size of raw data (section data) in bytes */ + unsigned long relptr; /* file ptr to relocation */ + unsigned long nreloc; /* number of relocation entries >64k -> error */ +} xdf_section_data; + +typedef struct xdf_symrec_data { + unsigned long index; /* assigned XDF symbol table index */ +} xdf_symrec_data; + +typedef struct yasm_objfmt_xdf { + yasm_objfmt_base objfmt; /* base structure */ + + long parse_scnum; /* sect numbering in parser */ +} yasm_objfmt_xdf; + +typedef struct xdf_objfmt_output_info { + yasm_object *object; + yasm_objfmt_xdf *objfmt_xdf; + yasm_errwarns *errwarns; + /*@dependent@*/ FILE *f; + /*@only@*/ unsigned char *buf; + yasm_section *sect; + /*@dependent@*/ xdf_section_data *xsd; + + unsigned long indx; /* current symbol index */ + int all_syms; /* outputting all symbols? */ + unsigned long strtab_offset; /* current string table offset */ +} xdf_objfmt_output_info; + +static void xdf_section_data_destroy(/*@only@*/ void *d); +static void xdf_section_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback xdf_section_data_cb = { + xdf_section_data_destroy, + xdf_section_data_print +}; + +static void xdf_symrec_data_destroy(/*@only@*/ void *d); +static void xdf_symrec_data_print(void *data, FILE *f, int indent_level); + +static const yasm_assoc_data_callback xdf_symrec_data_cb = { + xdf_symrec_data_destroy, + xdf_symrec_data_print +}; + +yasm_objfmt_module yasm_xdf_LTX_objfmt; + + +static yasm_objfmt * +xdf_objfmt_create(yasm_object *object) +{ + yasm_objfmt_xdf *objfmt_xdf = yasm_xmalloc(sizeof(yasm_objfmt_xdf)); + + /* Only support x86 arch */ + if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") != 0) { + yasm_xfree(objfmt_xdf); + return NULL; + } + + /* Support x86 and amd64 machines of x86 arch */ + if (yasm__strcasecmp(yasm_arch_get_machine(object->arch), "x86") && + yasm__strcasecmp(yasm_arch_get_machine(object->arch), "amd64")) { + yasm_xfree(objfmt_xdf); + return NULL; + } + + objfmt_xdf->parse_scnum = 0; /* section numbering starts at 0 */ + + objfmt_xdf->objfmt.module = &yasm_xdf_LTX_objfmt; + + return (yasm_objfmt *)objfmt_xdf; +} + +static int +xdf_objfmt_output_value(yasm_value *value, unsigned char *buf, + unsigned int destsize, unsigned long offset, + yasm_bytecode *bc, int warn, /*@null@*/ void *d) +{ + /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ yasm_intnum *intn; + unsigned long intn_minus; + int retval; + unsigned int valsize = value->size; + + assert(info != NULL); + + if (value->abs) + value->abs = yasm_expr_simplify(value->abs, 1); + + /* Try to output constant and PC-relative section-local first. + * Note this does NOT output any value with a SEG, WRT, external, + * cross-section, or non-PC-relative reference (those are handled below). + */ + switch (yasm_value_output_basic(value, buf, destsize, bc, warn, + info->object->arch)) { + case -1: + return 1; + case 0: + break; + default: + return 0; + } + + if (value->section_rel) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("xdf: relocation too complex")); + return 1; + } + + intn_minus = 0; + if (value->rel) { + xdf_reloc *reloc; + + reloc = yasm_xmalloc(sizeof(xdf_reloc)); + reloc->reloc.addr = yasm_intnum_create_uint(bc->offset + offset); + reloc->reloc.sym = value->rel; + reloc->base = NULL; + reloc->size = valsize/8; + reloc->shift = value->rshift; + + if (value->seg_of) + reloc->type = XDF_RELOC_SEG; + else if (value->wrt) { + reloc->base = value->wrt; + reloc->type = XDF_RELOC_WRT; + } else if (value->curpos_rel) { + reloc->type = XDF_RELOC_RIP; + /* Adjust to start of section, so subtract out the bytecode + * offset. + */ + intn_minus = bc->offset; + } else + reloc->type = XDF_RELOC_REL; + info->xsd->nreloc++; + yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); + } + + if (intn_minus > 0) { + intn = yasm_intnum_create_uint(intn_minus); + yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); + } else + intn = yasm_intnum_create_uint(0); + + if (value->abs) { + yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); + if (!intn2) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("xdf: relocation too complex")); + yasm_intnum_destroy(intn); + return 1; + } + yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); + } + + retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, + valsize, 0, bc, warn); + yasm_intnum_destroy(intn); + return retval; +} + +static int +xdf_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) +{ + /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; + /*@null@*/ /*@only@*/ unsigned char *bigbuf; + unsigned long size = REGULAR_OUTBUF_SIZE; + int gap; + + assert(info != NULL); + + bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &gap, info, + xdf_objfmt_output_value, NULL); + + /* Don't bother doing anything else if size ended up being 0. */ + if (size == 0) { + if (bigbuf) + yasm_xfree(bigbuf); + return 0; + } + + info->xsd->size += size; + + /* Warn that gaps are converted to 0 and write out the 0's. */ + if (gap) { + unsigned long left; + yasm_warn_set(YASM_WARN_UNINIT_CONTENTS, + N_("uninitialized space: zeroing")); + /* Write out in chunks */ + memset(info->buf, 0, REGULAR_OUTBUF_SIZE); + left = size; + while (left > REGULAR_OUTBUF_SIZE) { + fwrite(info->buf, REGULAR_OUTBUF_SIZE, 1, info->f); + left -= REGULAR_OUTBUF_SIZE; + } + fwrite(info->buf, left, 1, info->f); + } else { + /* Output buf (or bigbuf if non-NULL) to file */ + fwrite(bigbuf ? bigbuf : info->buf, (size_t)size, 1, info->f); + } + + /* If bigbuf was allocated, free it */ + if (bigbuf) + yasm_xfree(bigbuf); + + return 0; +} + +static int +xdf_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ xdf_section_data *xsd; + long pos; + xdf_reloc *reloc; + + assert(info != NULL); + xsd = yasm_section_get_data(sect, &xdf_section_data_cb); + assert(xsd != NULL); + + if (xsd->flags & XDF_SECT_BSS) { + /* Don't output BSS sections. + * TODO: Check for non-reserve bytecodes? + */ + pos = 0; /* position = 0 because it's not in the file */ + xsd->size = yasm_bc_next_offset(yasm_section_bcs_last(sect)); + } else { + pos = ftell(info->f); + if (pos == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return 1; + } + + info->sect = sect; + info->xsd = xsd; + yasm_section_bcs_traverse(sect, info->errwarns, info, + xdf_objfmt_output_bytecode); + + /* Sanity check final section size */ + if (xsd->size != yasm_bc_next_offset(yasm_section_bcs_last(sect))) + yasm_internal_error( + N_("xdf: section computed size did not match actual size")); + } + + /* Empty? Go on to next section */ + if (xsd->size == 0) + return 0; + + xsd->scnptr = (unsigned long)pos; + + /* No relocations to output? Go on to next section */ + if (xsd->nreloc == 0) + return 0; + + pos = ftell(info->f); + if (pos == -1) { + yasm__fatal(N_("could not get file position on output file")); + /*@notreached@*/ + return 1; + } + xsd->relptr = (unsigned long)pos; + + reloc = (xdf_reloc *)yasm_section_relocs_first(sect); + while (reloc) { + unsigned char *localbuf = info->buf; + /*@null@*/ xdf_symrec_data *xsymd; + + xsymd = yasm_symrec_get_data(reloc->reloc.sym, &xdf_symrec_data_cb); + if (!xsymd) + yasm_internal_error( + N_("xdf: no symbol data for relocated symbol")); + + yasm_intnum_get_sized(reloc->reloc.addr, localbuf, 4, 32, 0, 0, 0); + localbuf += 4; /* address of relocation */ + YASM_WRITE_32_L(localbuf, xsymd->index); /* relocated symbol */ + if (reloc->base) { + xsymd = yasm_symrec_get_data(reloc->base, &xdf_symrec_data_cb); + if (!xsymd) + yasm_internal_error( + N_("xdf: no symbol data for relocated base symbol")); + YASM_WRITE_32_L(localbuf, xsymd->index); /* base symbol */ + } else { + if (reloc->type == XDF_RELOC_WRT) + yasm_internal_error( + N_("xdf: no base symbol for WRT relocation")); + YASM_WRITE_32_L(localbuf, 0); /* no base symbol */ + } + YASM_WRITE_8(localbuf, reloc->type); /* type of relocation */ + YASM_WRITE_8(localbuf, reloc->size); /* size of relocation */ + YASM_WRITE_8(localbuf, reloc->shift); /* relocation shift */ + YASM_WRITE_8(localbuf, 0); /* flags */ + fwrite(info->buf, 16, 1, info->f); + + reloc = (xdf_reloc *)yasm_section_reloc_next((yasm_reloc *)reloc); + } + + return 0; +} + +static int +xdf_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d) +{ + /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; + /*@dependent@*/ /*@null@*/ xdf_section_data *xsd; + /*@null@*/ xdf_symrec_data *xsymd; + unsigned char *localbuf; + + assert(info != NULL); + xsd = yasm_section_get_data(sect, &xdf_section_data_cb); + assert(xsd != NULL); + + localbuf = info->buf; + xsymd = yasm_symrec_get_data(xsd->sym, &xdf_symrec_data_cb); + assert(xsymd != NULL); + + YASM_WRITE_32_L(localbuf, xsymd->index); /* section name symbol */ + if (xsd->addr) { + yasm_intnum_get_sized(xsd->addr, localbuf, 8, 64, 0, 0, 0); + localbuf += 8; /* physical address */ + } else { + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, 0); + } + if (xsd->vaddr) { + yasm_intnum_get_sized(xsd->vaddr, localbuf, 8, 64, 0, 0, 0); + localbuf += 8; /* virtual address */ + } else if (xsd->addr) { + yasm_intnum_get_sized(xsd->addr, localbuf, 8, 64, 0, 0, 0); + localbuf += 8; /* VA=PA */ + } else { + YASM_WRITE_32_L(localbuf, 0); + YASM_WRITE_32_L(localbuf, 0); + } + YASM_WRITE_16_L(localbuf, yasm_section_get_align(sect)); /* alignment */ + YASM_WRITE_16_L(localbuf, xsd->flags); /* flags */ + YASM_WRITE_32_L(localbuf, xsd->scnptr); /* file ptr to data */ + YASM_WRITE_32_L(localbuf, xsd->size); /* section size */ + YASM_WRITE_32_L(localbuf, xsd->relptr); /* file ptr to relocs */ + YASM_WRITE_32_L(localbuf, xsd->nreloc); /* num of relocation entries */ + fwrite(info->buf, 40, 1, info->f); + + return 0; +} + +static int +xdf_objfmt_count_sym(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + assert(info != NULL); + if (vis & YASM_SYM_COMMON) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("XDF object format does not support common variables")); + yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym)); + return 0; + } + if (info->all_syms || + (vis != YASM_SYM_LOCAL && !(vis & YASM_SYM_DLOCAL))) { + /* Save index in symrec data */ + xdf_symrec_data *sym_data = yasm_xmalloc(sizeof(xdf_symrec_data)); + sym_data->index = info->indx; + yasm_symrec_add_data(sym, &xdf_symrec_data_cb, sym_data); + + info->indx++; + } + return 0; +} + +static int +xdf_objfmt_output_sym(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + + assert(info != NULL); + + if (info->all_syms || vis != YASM_SYM_LOCAL) { + /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); + const yasm_expr *equ_val; + const yasm_intnum *intn; + size_t len = strlen(name); + unsigned long value = 0; + long scnum = -3; /* -3 = debugging symbol */ + /*@dependent@*/ /*@null@*/ yasm_section *sect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + unsigned long flags = 0; + unsigned char *localbuf; + + if (vis & YASM_SYM_GLOBAL) + flags = XDF_SYM_GLOBAL; + + /* Look at symrec for value/scnum/etc. */ + if (yasm_symrec_get_label(sym, &precbc)) { + if (precbc) + sect = yasm_bc_get_section(precbc); + else + sect = NULL; + /* it's a label: get value and offset. + * If there is not a section, leave as debugging symbol. + */ + if (sect) { + /*@dependent@*/ /*@null@*/ xdf_section_data *csectd; + csectd = yasm_section_get_data(sect, &xdf_section_data_cb); + if (csectd) + scnum = csectd->scnum; + else + yasm_internal_error(N_("didn't understand section")); + if (precbc) + value += yasm_bc_next_offset(precbc); + } + } else if ((equ_val = yasm_symrec_get_equ(sym))) { + yasm_expr *equ_val_copy = yasm_expr_copy(equ_val); + intn = yasm_expr_get_intnum(&equ_val_copy, 1); + if (!intn) { + if (vis & YASM_SYM_GLOBAL) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("global EQU value not an integer expression")); + yasm_errwarn_propagate(info->errwarns, equ_val->line); + } + } else + value = yasm_intnum_get_uint(intn); + yasm_expr_destroy(equ_val_copy); + + flags |= XDF_SYM_EQU; + scnum = -2; /* -2 = absolute symbol */ + } else { + if (vis & YASM_SYM_EXTERN) { + flags = XDF_SYM_EXTERN; + scnum = -1; + } + } + + localbuf = info->buf; + YASM_WRITE_32_L(localbuf, scnum); /* section number */ + YASM_WRITE_32_L(localbuf, value); /* value */ + YASM_WRITE_32_L(localbuf, info->strtab_offset); + info->strtab_offset += (unsigned long)(len+1); + YASM_WRITE_32_L(localbuf, flags); /* flags */ + fwrite(info->buf, 16, 1, info->f); + yasm_xfree(name); + } + return 0; +} + +static int +xdf_objfmt_output_str(yasm_symrec *sym, /*@null@*/ void *d) +{ + /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; + yasm_sym_vis vis = yasm_symrec_get_visibility(sym); + + assert(info != NULL); + + if (info->all_syms || vis != YASM_SYM_LOCAL) { + /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); + size_t len = strlen(name); + fwrite(name, len+1, 1, info->f); + yasm_xfree(name); + } + return 0; +} + +static void +xdf_objfmt_output(yasm_object *object, FILE *f, int all_syms, + yasm_errwarns *errwarns) +{ + yasm_objfmt_xdf *objfmt_xdf = (yasm_objfmt_xdf *)object->objfmt; + xdf_objfmt_output_info info; + unsigned char *localbuf; + unsigned long symtab_count = 0; + + info.object = object; + info.objfmt_xdf = objfmt_xdf; + info.errwarns = errwarns; + info.f = f; + info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE); + + /* Allocate space for headers by seeking forward */ + if (fseek(f, (long)(16+40*(objfmt_xdf->parse_scnum)), SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + /* Get number of symbols */ + info.indx = 0; + info.all_syms = 1; /* force all syms into symbol table */ + yasm_symtab_traverse(object->symtab, &info, xdf_objfmt_count_sym); + symtab_count = info.indx; + + /* Get file offset of start of string table */ + info.strtab_offset = 16+40*(objfmt_xdf->parse_scnum)+16*symtab_count; + + /* Output symbol table */ + yasm_symtab_traverse(object->symtab, &info, xdf_objfmt_output_sym); + + /* Output string table */ + yasm_symtab_traverse(object->symtab, &info, xdf_objfmt_output_str); + + /* Section data/relocs */ + if (yasm_object_sections_traverse(object, &info, + xdf_objfmt_output_section)) + return; + + /* Write headers */ + if (fseek(f, 0, SEEK_SET) < 0) { + yasm__fatal(N_("could not seek on output file")); + /*@notreached@*/ + return; + } + + localbuf = info.buf; + YASM_WRITE_32_L(localbuf, XDF_MAGIC); /* magic number */ + YASM_WRITE_32_L(localbuf, objfmt_xdf->parse_scnum); /* number of sects */ + YASM_WRITE_32_L(localbuf, symtab_count); /* number of symtabs */ + /* size of sect headers + symbol table + strings */ + YASM_WRITE_32_L(localbuf, info.strtab_offset-16); + fwrite(info.buf, 16, 1, f); + + yasm_object_sections_traverse(object, &info, xdf_objfmt_output_secthead); + + yasm_xfree(info.buf); +} + +static void +xdf_objfmt_destroy(yasm_objfmt *objfmt) +{ + yasm_xfree(objfmt); +} + +static void +xdf_objfmt_init_new_section(yasm_section *sect, unsigned long line) +{ + yasm_object *object = yasm_section_get_object(sect); + const char *sectname = yasm_section_get_name(sect); + yasm_objfmt_xdf *objfmt_xdf = (yasm_objfmt_xdf *)object->objfmt; + xdf_section_data *data; + yasm_symrec *sym; + + data = yasm_xmalloc(sizeof(xdf_section_data)); + data->scnum = objfmt_xdf->parse_scnum++; + data->flags = 0; + data->addr = NULL; + data->vaddr = NULL; + data->scnptr = 0; + data->size = 0; + data->relptr = 0; + data->nreloc = 0; + yasm_section_add_data(sect, &xdf_section_data_cb, data); + + sym = yasm_symtab_define_label(object->symtab, sectname, + yasm_section_bcs_first(sect), 1, line); + data->sym = sym; +} + +static yasm_section * +xdf_objfmt_add_default_section(yasm_object *object) +{ + yasm_section *retval; + int isnew; + + retval = yasm_object_get_general(object, ".text", 0, 1, 0, &isnew, 0); + if (isnew) + yasm_section_set_default(retval, 1); + return retval; +} + +static int +xdf_helper_use(void *obj, yasm_valparam *vp, unsigned long line, void *d, + uintptr_t bits) +{ + yasm_object *object = (yasm_object *)obj; + unsigned long *flags = (unsigned long *)d; + *flags &= ~(XDF_SECT_USE_16|XDF_SECT_USE_32|XDF_SECT_USE_64); + switch (bits) { + case 16: *flags |= XDF_SECT_USE_16; break; + case 32: *flags |= XDF_SECT_USE_32; break; + case 64: *flags |= XDF_SECT_USE_64; break; + }; + yasm_arch_set_var(object->arch, "mode_bits", bits); + return 0; +} + +static /*@observer@*/ /*@null@*/ yasm_section * +xdf_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams, + /*@unused@*/ /*@null@*/ + yasm_valparamhead *objext_valparams, + unsigned long line) +{ + yasm_valparam *vp; + yasm_section *retval; + int isnew; + int flags_override = 0; + const char *sectname; + int resonly = 0; + xdf_section_data *xsd; + unsigned long align = 0; + + struct xdf_section_switch_data { + /*@only@*/ /*@null@*/ yasm_intnum *absaddr; + /*@only@*/ /*@null@*/ yasm_intnum *vaddr; + /*@only@*/ /*@null@*/ yasm_intnum *align_intn; + unsigned long flags; + } data; + + static const yasm_dir_help help[] = { + { "use16", 0, xdf_helper_use, + offsetof(struct xdf_section_switch_data, flags), 16 }, + { "use32", 0, xdf_helper_use, + offsetof(struct xdf_section_switch_data, flags), 32 }, + { "use64", 0, xdf_helper_use, + offsetof(struct xdf_section_switch_data, flags), 64 }, + { "bss", 0, yasm_dir_helper_flag_or, + offsetof(struct xdf_section_switch_data, flags), XDF_SECT_BSS }, + { "flat", 0, yasm_dir_helper_flag_or, + offsetof(struct xdf_section_switch_data, flags), XDF_SECT_FLAT }, + { "absolute", 1, yasm_dir_helper_intn, + offsetof(struct xdf_section_switch_data, absaddr), 0 }, + { "virtual", 1, yasm_dir_helper_intn, + offsetof(struct xdf_section_switch_data, vaddr), 0 }, + { "align", 1, yasm_dir_helper_intn, + offsetof(struct xdf_section_switch_data, align_intn), 0 } + }; + + data.absaddr = NULL; + data.vaddr = NULL; + data.align_intn = NULL; + data.flags = 0; + + vp = yasm_vps_first(valparams); + sectname = yasm_vp_string(vp); + if (!sectname) + return NULL; + vp = yasm_vps_next(vp); + + flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help), + &data, yasm_dir_helper_valparam_warn); + if (flags_override < 0) + return NULL; /* error occurred */ + + if (data.absaddr) + data.flags |= XDF_SECT_ABSOLUTE; + if (data.align_intn) { + align = yasm_intnum_get_uint(data.align_intn); + yasm_intnum_destroy(data.align_intn); + + /* Alignments must be a power of two. */ + if (!is_exp2(align)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("argument to `%s' is not a power of two"), + "align"); + if (data.vaddr) + yasm_intnum_destroy(data.vaddr); + if (data.absaddr) + yasm_intnum_destroy(data.absaddr); + return NULL; + } + + /* Check to see if alignment is supported size */ + if (align > 4096) { + yasm_error_set(YASM_ERROR_VALUE, + N_("XDF does not support alignments > 4096")); + if (data.vaddr) + yasm_intnum_destroy(data.vaddr); + if (data.absaddr) + yasm_intnum_destroy(data.absaddr); + return NULL; + } + } + + retval = yasm_object_get_general(object, sectname, align, 1, resonly, + &isnew, line); + + xsd = yasm_section_get_data(retval, &xdf_section_data_cb); + + if (isnew || yasm_section_is_default(retval)) { + yasm_section_set_default(retval, 0); + xsd->flags = data.flags; + if (data.absaddr) { + if (xsd->addr) + yasm_intnum_destroy(xsd->addr); + xsd->addr = data.absaddr; + } + if (data.vaddr) { + if (xsd->vaddr) + yasm_intnum_destroy(xsd->vaddr); + xsd->vaddr = data.vaddr; + } + yasm_section_set_align(retval, align, line); + } else if (flags_override) + yasm_warn_set(YASM_WARN_GENERAL, + N_("section flags ignored on section redeclaration")); + return retval; +} + +static /*@observer@*/ /*@null@*/ yasm_symrec * +xdf_objfmt_get_special_sym(yasm_object *object, const char *name, + const char *parser) +{ + return NULL; +} + +static void +xdf_section_data_destroy(void *data) +{ + xdf_section_data *xsd = (xdf_section_data *)data; + if (xsd->addr) + yasm_intnum_destroy(xsd->addr); + if (xsd->vaddr) + yasm_intnum_destroy(xsd->vaddr); + yasm_xfree(data); +} + +static void +xdf_section_data_print(void *data, FILE *f, int indent_level) +{ + xdf_section_data *xsd = (xdf_section_data *)data; + + fprintf(f, "%*ssym=\n", indent_level, ""); + yasm_symrec_print(xsd->sym, f, indent_level+1); + fprintf(f, "%*sscnum=%ld\n", indent_level, "", xsd->scnum); + fprintf(f, "%*sflags=0x%x\n", indent_level, "", xsd->flags); + fprintf(f, "%*saddr=", indent_level, ""); + yasm_intnum_print(xsd->addr, f); + fprintf(f, "%*svaddr=", indent_level, ""); + yasm_intnum_print(xsd->vaddr, f); + fprintf(f, "%*sscnptr=0x%lx\n", indent_level, "", xsd->scnptr); + fprintf(f, "%*ssize=%ld\n", indent_level, "", xsd->size); + fprintf(f, "%*srelptr=0x%lx\n", indent_level, "", xsd->relptr); + fprintf(f, "%*snreloc=%ld\n", indent_level, "", xsd->nreloc); +} + +static void +xdf_symrec_data_destroy(void *data) +{ + yasm_xfree(data); +} + +static void +xdf_symrec_data_print(void *data, FILE *f, int indent_level) +{ + xdf_symrec_data *xsd = (xdf_symrec_data *)data; + + fprintf(f, "%*ssymtab index=%lu\n", indent_level, "", xsd->index); +} + +/* Define valid debug formats to use with this object format */ +static const char *xdf_objfmt_dbgfmt_keywords[] = { + "null", + NULL +}; + +/* Define objfmt structure -- see objfmt.h for details */ +yasm_objfmt_module yasm_xdf_LTX_objfmt = { + "Extended Dynamic Object", + "xdf", + "xdf", + 32, + 0, + xdf_objfmt_dbgfmt_keywords, + "null", + NULL, /* no directives */ + NULL, /* no standard macros */ + xdf_objfmt_create, + xdf_objfmt_output, + xdf_objfmt_destroy, + xdf_objfmt_add_default_section, + xdf_objfmt_init_new_section, + xdf_objfmt_section_switch, + xdf_objfmt_get_special_sym +}; |