| /* |
| * 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> |
| /*@unused@*/ RCSID("$Id: bin-objfmt.c 2310 2010-03-28 19:28:54Z peter $"); |
| |
| #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 |
| }; |