mirror of
https://github.com/yasm/yasm
synced 2026-08-26 22:26:05 -04:00
528 lines
16 KiB
C
528 lines
16 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>
|
|
/*@unused@*/ RCSID("$Id$");
|
|
|
|
#define YASM_LIB_INTERNAL
|
|
#define YASM_BC_INTERNAL
|
|
#define YASM_EXPR_INTERNAL
|
|
#include <libyasm.h>
|
|
|
|
|
|
#define REGULAR_OUTBUF_SIZE 1024
|
|
|
|
typedef struct yasm_objfmt_bin {
|
|
yasm_objfmt_base objfmt; /* base structure */
|
|
} yasm_objfmt_bin;
|
|
|
|
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;
|
|
return (yasm_objfmt *)objfmt_bin;
|
|
}
|
|
|
|
/* Aligns sect to either its specified alignment. Uses prevsect and base to
|
|
* both determine the new starting address (returned) and the total length of
|
|
* prevsect after sect has been aligned.
|
|
*/
|
|
static unsigned long
|
|
bin_objfmt_align_section(yasm_section *sect, yasm_section *prevsect,
|
|
unsigned long base,
|
|
/*@out@*/ unsigned long *prevsectlen,
|
|
/*@out@*/ unsigned long *padamt)
|
|
{
|
|
unsigned long start;
|
|
unsigned long align;
|
|
|
|
/* Figure out the size of .text by looking at the last bytecode's offset
|
|
* plus its length. Add the start and size together to get the new start.
|
|
*/
|
|
*prevsectlen = yasm_bc_next_offset(yasm_section_bcs_last(prevsect));
|
|
start = base + *prevsectlen;
|
|
|
|
/* Round new start up to alignment of .data section, and adjust textlen to
|
|
* indicate padded size. Because aignment is always a power of two, we
|
|
* can use some bit trickery to do this easily.
|
|
*/
|
|
align = yasm_section_get_align(sect);
|
|
|
|
if (start & (align-1))
|
|
start = (start & ~(align-1)) + align;
|
|
|
|
*padamt = start - (base + *prevsectlen);
|
|
|
|
return start;
|
|
}
|
|
|
|
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 */
|
|
unsigned long abs_start; /* what absolutes go against */
|
|
} bin_objfmt_output_info;
|
|
|
|
static /*@only@*/ yasm_expr *
|
|
bin_objfmt_expr_xform(/*@returned@*/ /*@only@*/ yasm_expr *e,
|
|
/*@unused@*/ /*@null@*/ void *d)
|
|
{
|
|
int i;
|
|
/*@dependent@*/ yasm_section *sect;
|
|
/*@dependent@*/ /*@null@*/ yasm_bytecode *precbc;
|
|
/*@null@*/ yasm_intnum *dist;
|
|
|
|
for (i=0; i<e->numterms; i++) {
|
|
/* Transform symrecs or precbcs that reference sections into
|
|
* start expr + 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))) {
|
|
const yasm_expr *start = yasm_section_get_start(sect);
|
|
e->terms[i].type = YASM_EXPR_EXPR;
|
|
e->terms[i].data.expn =
|
|
yasm_expr_create(YASM_EXPR_ADD,
|
|
yasm_expr_expr(yasm_expr_copy(start)),
|
|
yasm_expr_int(dist), e->line);
|
|
}
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
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;
|
|
|
|
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
|
|
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;
|
|
} else
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
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 void
|
|
bin_objfmt_output(yasm_object *object, FILE *f, /*@unused@*/ int all_syms,
|
|
yasm_errwarns *errwarns)
|
|
{
|
|
/*@observer@*/ /*@null@*/ yasm_section *text, *data, *bss, *prevsect;
|
|
/*@null@*/ yasm_expr *startexpr;
|
|
/*@dependent@*/ /*@null@*/ const yasm_intnum *startnum;
|
|
unsigned long start = 0, textstart = 0, datastart = 0;
|
|
unsigned long textlen = 0, textpad = 0, datalen = 0, datapad = 0;
|
|
unsigned long *prevsectlenptr, *prevsectpadptr;
|
|
unsigned long i;
|
|
bin_objfmt_output_info info;
|
|
|
|
info.object = object;
|
|
info.errwarns = errwarns;
|
|
info.f = f;
|
|
info.buf = yasm_xmalloc(REGULAR_OUTBUF_SIZE);
|
|
|
|
/* Check symbol table */
|
|
yasm_symtab_traverse(object->symtab, &info, bin_objfmt_check_sym);
|
|
|
|
text = yasm_object_find_general(object, ".text");
|
|
data = yasm_object_find_general(object, ".data");
|
|
bss = yasm_object_find_general(object, ".bss");
|
|
|
|
if (!text)
|
|
yasm_internal_error(N_("No `.text' section in bin objfmt output"));
|
|
|
|
/* First determine the actual starting offsets for .data and .bss.
|
|
* As the order in the file is .text -> .data -> .bss (not present),
|
|
* use the last bytecode in .text (and the .text section start) to
|
|
* determine the starting offset in .data, and likewise for .bss.
|
|
* Also compensate properly for alignment.
|
|
*/
|
|
|
|
/* Find out the start of .text */
|
|
startexpr = yasm_expr_copy(yasm_section_get_start(text));
|
|
assert(startexpr != NULL);
|
|
startnum = yasm_expr_get_intnum(&startexpr, 0);
|
|
if (!startnum) {
|
|
yasm_error_set(YASM_ERROR_TOO_COMPLEX,
|
|
N_("ORG expression too complex"));
|
|
yasm_errwarn_propagate(errwarns, startexpr->line);
|
|
return;
|
|
}
|
|
start = yasm_intnum_get_uint(startnum);
|
|
yasm_expr_destroy(startexpr);
|
|
info.abs_start = start;
|
|
textstart = start;
|
|
|
|
/* Align .data and .bss (if present) by adjusting their starts. */
|
|
prevsect = text;
|
|
prevsectlenptr = &textlen;
|
|
prevsectpadptr = &textpad;
|
|
if (data) {
|
|
start = bin_objfmt_align_section(data, prevsect, start,
|
|
prevsectlenptr, prevsectpadptr);
|
|
yasm_section_set_start(data, yasm_expr_create_ident(
|
|
yasm_expr_int(yasm_intnum_create_uint(start)), 0), 0);
|
|
datastart = start;
|
|
prevsect = data;
|
|
prevsectlenptr = &datalen;
|
|
prevsectpadptr = &datapad;
|
|
}
|
|
if (bss) {
|
|
start = bin_objfmt_align_section(bss, prevsect, start,
|
|
prevsectlenptr, prevsectpadptr);
|
|
yasm_section_set_start(bss, yasm_expr_create_ident(
|
|
yasm_expr_int(yasm_intnum_create_uint(start)), 0), 0);
|
|
}
|
|
|
|
/* Output .text first. */
|
|
info.sect = text;
|
|
info.start = textstart;
|
|
yasm_section_bcs_traverse(text, errwarns, &info,
|
|
bin_objfmt_output_bytecode);
|
|
|
|
/* If .data is present, output it */
|
|
if (data) {
|
|
/* Add padding to align .data. Just use a for loop, as this will
|
|
* seldom be very many bytes.
|
|
*/
|
|
for (i=0; i<textpad; i++)
|
|
fputc(0, f);
|
|
|
|
/* Output .data bytecodes */
|
|
info.sect = data;
|
|
info.start = datastart;
|
|
yasm_section_bcs_traverse(data, errwarns,
|
|
&info, bin_objfmt_output_bytecode);
|
|
}
|
|
|
|
/* If .bss is present, check it for non-reserve bytecodes */
|
|
|
|
|
|
yasm_xfree(info.buf);
|
|
}
|
|
|
|
static void
|
|
bin_objfmt_destroy(yasm_objfmt *objfmt)
|
|
{
|
|
yasm_xfree(objfmt);
|
|
}
|
|
|
|
static void
|
|
bin_objfmt_init_new_section(yasm_object *object, yasm_section *sect,
|
|
const char *sectname, unsigned long line)
|
|
{
|
|
yasm_symtab_define_label(object->symtab, sectname,
|
|
yasm_section_bcs_first(sect), 1, 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, 16, 1, 0, &isnew, 0);
|
|
if (isnew) {
|
|
bin_objfmt_init_new_section(object, retval, ".text", 0);
|
|
yasm_section_set_default(retval, 1);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
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;
|
|
unsigned long start;
|
|
const char *sectname;
|
|
int resonly = 0;
|
|
/*@only@*/ /*@null@*/ yasm_intnum *align_intn = NULL;
|
|
unsigned long align = 4;
|
|
int have_align = 0;
|
|
|
|
static const yasm_dir_help help[] = {
|
|
{ "align", 1, yasm_dir_helper_intn, 0, 0 }
|
|
};
|
|
|
|
vp = yasm_vps_first(valparams);
|
|
sectname = yasm_vp_string(vp);
|
|
if (!sectname)
|
|
return NULL;
|
|
vp = yasm_vps_next(vp);
|
|
|
|
/* If it's the first section output (.text) start at 0, otherwise
|
|
* make sure the start is > 128.
|
|
*/
|
|
if (strcmp(sectname, ".text") == 0)
|
|
start = 0;
|
|
else if (strcmp(sectname, ".data") == 0)
|
|
start = 200;
|
|
else if (strcmp(sectname, ".bss") == 0) {
|
|
start = 200;
|
|
resonly = 1;
|
|
} else {
|
|
/* other section names not recognized. */
|
|
yasm_error_set(YASM_ERROR_GENERAL,
|
|
N_("segment name `%s' not recognized"), sectname);
|
|
return NULL;
|
|
}
|
|
|
|
have_align = yasm_dir_helper(object, vp, line, help, NELEMS(help),
|
|
&align_intn, yasm_dir_helper_valparam_warn);
|
|
if (have_align < 0)
|
|
return NULL; /* error occurred */
|
|
|
|
if (align_intn) {
|
|
align = yasm_intnum_get_uint(align_intn);
|
|
yasm_intnum_destroy(align_intn);
|
|
|
|
/* Alignments must be a power of two. */
|
|
if (!is_exp2(align)) {
|
|
yasm_error_set(YASM_ERROR_VALUE,
|
|
N_("argument to `%s' is not a power of two"),
|
|
"align");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
retval = yasm_object_get_general(object, sectname,
|
|
yasm_expr_create_ident(
|
|
yasm_expr_int(yasm_intnum_create_uint(start)), line), align,
|
|
strcmp(sectname, ".text") == 0, resonly, &isnew, line);
|
|
|
|
if (isnew)
|
|
bin_objfmt_init_new_section(object, retval, sectname, line);
|
|
|
|
if (isnew || yasm_section_is_default(retval)) {
|
|
yasm_section_set_default(retval, 0);
|
|
yasm_section_set_align(retval, align, line);
|
|
} else if (have_align)
|
|
yasm_warn_set(YASM_WARN_GENERAL,
|
|
N_("alignment value ignored on section redeclaration"));
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
bin_objfmt_dir_org(yasm_object *object,
|
|
/*@null@*/ yasm_valparamhead *valparams,
|
|
/*@unused@*/ /*@null@*/
|
|
yasm_valparamhead *objext_valparams, unsigned long line)
|
|
{
|
|
yasm_section *sect;
|
|
yasm_valparam *vp;
|
|
/*@only@*/ /*@null@*/ yasm_expr *start;
|
|
|
|
/* ORG takes just a simple integer as param */
|
|
vp = yasm_vps_first(valparams);
|
|
start = yasm_vp_expr(vp, object->symtab, line);
|
|
if (!start) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("argument to ORG must be expression"));
|
|
return;
|
|
}
|
|
|
|
/* ORG changes the start of the .text section */
|
|
sect = yasm_object_find_general(object, ".text");
|
|
if (!sect)
|
|
yasm_internal_error(
|
|
N_("bin objfmt: .text section does not exist before ORG is called?"));
|
|
yasm_section_set_start(sect, start, line);
|
|
}
|
|
|
|
|
|
/* 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 },
|
|
{ NULL, NULL, NULL, 0 }
|
|
};
|
|
|
|
/* Define objfmt structure -- see objfmt.h for details */
|
|
yasm_objfmt_module yasm_bin_LTX_objfmt = {
|
|
"Flat format binary",
|
|
"bin",
|
|
NULL,
|
|
16,
|
|
bin_objfmt_dbgfmt_keywords,
|
|
"null",
|
|
bin_objfmt_directives,
|
|
bin_objfmt_create,
|
|
bin_objfmt_output,
|
|
bin_objfmt_destroy,
|
|
bin_objfmt_add_default_section,
|
|
bin_objfmt_section_switch
|
|
};
|