mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-07-16 20:47:21 +02:00
1973 lines
64 KiB
C
1973 lines
64 KiB
C
/*
|
|
* 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
|
|
};
|