diff options
author | somov <somov@yandex-team.ru> | 2022-02-10 16:45:49 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:49 +0300 |
commit | 7489e4682331202b9c7d863c0898eb83d7b12c2b (patch) | |
tree | 9142afc54d335ea52910662635b898e79e192e49 /contrib/tools/yasm/modules/objfmts/bin | |
parent | a5950576e397b1909261050b8c7da16db58f10b1 (diff) | |
download | ydb-7489e4682331202b9c7d863c0898eb83d7b12c2b.tar.gz |
Restoring authorship annotation for <somov@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/tools/yasm/modules/objfmts/bin')
-rw-r--r-- | contrib/tools/yasm/modules/objfmts/bin/bin-objfmt.c | 3944 |
1 files changed, 1972 insertions, 1972 deletions
diff --git a/contrib/tools/yasm/modules/objfmts/bin/bin-objfmt.c b/contrib/tools/yasm/modules/objfmts/bin/bin-objfmt.c index 702bda1590..5f422245d3 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 +}; |