diff --git a/asm/assemble.c b/asm/assemble.c index 1748a0ec..caed64f0 100644 --- a/asm/assemble.c +++ b/asm/assemble.c @@ -307,6 +307,29 @@ static void warn_overflow_opd(const struct operand *o, int size) } } +static void warn_overflow_out(int64_t data, int size, enum out_sign sign) +{ + bool err; + + switch (sign) { + case OUT_WRAP: + err = overflow_general(data, size); + break; + case OUT_SIGNED: + err = overflow_signed(data, size); + break; + case OUT_UNSIGNED: + err = overflow_unsigned(data, size); + break; + default: + panic(); + break; + } + + if (err) + warn_overflow(ERR_PASS2, size); +} + /* * This routine wrappers the real output format's output routine, * in order to pass a copy of the data off to the listing file @@ -324,6 +347,7 @@ static void out(struct out_data *data) uint64_t q; } xdata; uint64_t size = data->size; + int64_t addrval; if (!data->size) return; /* Nothing to do */ @@ -334,31 +358,22 @@ static void out(struct out_data *data) */ switch (data->type) { case OUT_ADDRESS: + addrval = data->toffset; + goto address; + + case OUT_RELADDR: + addrval = data->toffset - data->relbase; + goto address; + + address: asize = data->size; nasm_assert(asize <= 8); if (data->tsegment == NO_SEG && data->twrt == NO_SEG) { - /* XXX: check for overflow */ uint8_t *q = xdata.b; - WRITEADDR(q, data->toffset, asize); - data->data = xdata.b; - data->type = OUT_RAWDATA; - asize = 0; /* No longer an address */ - } - break; + warn_overflow_out(addrval, asize, data->sign); - case OUT_RELADDR: - asize = data->size; - nasm_assert(asize <= 8); - if (data->tsegment == data->segment && data->twrt == NO_SEG) { - uint8_t *q = xdata.b; - int64_t delta = data->toffset - data->offset - - (data->inslen - data->insoffs); - - if (overflow_signed(delta, asize)) - warn_overflow(ERR_PASS2, asize); - - WRITEADDR(q, delta, asize); + WRITEADDR(q, addrval, asize); data->data = xdata.b; data->type = OUT_RAWDATA; asize = 0; /* No longer an address */ @@ -442,6 +457,14 @@ static inline void out_imm(struct out_data *data, const struct operand *opx, data->toffset = opx->offset; data->tsegment = opx->segment; data->twrt = opx->wrt; + /* + * XXX: improve this if at some point in the future we can + * distinguish the subtrahend in expressions like [foo - bar] + * where bar is a symbol in the current segment. However, at the + * current point, if OPFLAG_RELATIVE is set that subtraction has + * already occurred. + */ + data->relbase = 0; out(data); } @@ -457,6 +480,7 @@ static void out_reladdr(struct out_data *data, const struct operand *opx, data->toffset = opx->offset; data->tsegment = opx->segment; data->twrt = opx->wrt; + data->relbase = data->offset + (data->inslen - data->insoffs); out(data); } @@ -523,6 +547,7 @@ int64_t assemble(int32_t segment, int64_t start, int bits, iflag_t cp, cpu = cp; + nasm_zero(&data); data.offset = start; data.segment = segment; data.itemp = NULL; @@ -553,6 +578,7 @@ int64_t assemble(int32_t segment, int64_t start, int bits, iflag_t cp, data.toffset = e->offset; data.tsegment = e->segment; data.twrt = e->wrt; + data.relbase = 0; out(&data); } } else if (e->type == EOT_DB_STRING || diff --git a/include/nasm.h b/include/nasm.h index e373d767..f49d9feb 100644 --- a/include/nasm.h +++ b/include/nasm.h @@ -84,29 +84,20 @@ struct ofmt; /* * Values for the `type' parameter to an output function. - * - * Exceptions are OUT_RELxADR, which denote an x-byte relocation - * which will be a relative jump. For this we need to know the - * distance in bytes from the start of the relocated record until - * the end of the containing instruction. _This_ is what is stored - * in the size part of the parameter, in this case. - * - * Also OUT_RESERVE denotes reservation of N bytes of BSS space, - * and the contents of the "data" parameter is irrelevant. - * - * The "data" parameter for the output function points to a "int32_t", - * containing the address in question, unless the type is - * OUT_RAWDATA, in which case it points to an "uint8_t" - * array. */ enum out_type { OUT_RAWDATA, /* Plain bytes */ OUT_RESERVE, /* Reserved bytes (RESB et al) */ OUT_ADDRESS, /* An address (symbol value) */ - OUT_RELADDR, /* A relative address (relative to instruction end) */ + OUT_RELADDR, /* A relative address */ OUT_SEGMENT, /* A segment number */ - /* These are temporary until the backend change */ + /* + * These values are used by the legacy backend interface only; + * see output/legacy.c for more information. These should never + * be used otherwise. Once all backends have been migrated to the + * new interface they should be removed. + */ OUT_REL1ADR, OUT_REL2ADR, OUT_REL4ADR, @@ -138,6 +129,7 @@ struct out_data { uint64_t toffset; /* Target address offset for relocation */ int32_t tsegment; /* Target segment for relocation */ int32_t twrt; /* Relocation with respect to */ + int64_t relbase; /* Relative base for OUT_RELADDR */ }; /* diff --git a/output/legacy.c b/output/legacy.c index 9546dd97..3fc95ee8 100644 --- a/output/legacy.c +++ b/output/legacy.c @@ -36,6 +36,20 @@ * * Mangle a struct out_data to match the rather bizarre legacy * backend interface. + * + * The "data" parameter for the output function points to a "int64_t", + * containing the address of the target in question, unless the type is + * OUT_RAWDATA, in which case it points to an "uint8_t" + * array. + * + * Exceptions are OUT_RELxADR, which denote an x-byte relocation + * which will be a relative jump. For this we need to know the + * distance in bytes from the start of the relocated record until + * the end of the containing instruction. _This_ is what is stored + * in the size part of the parameter, in this case. + * + * Also OUT_RESERVE denotes reservation of N bytes of BSS space, + * and the contents of the "data" parameter is irrelevant. */ #include "nasm.h" @@ -70,7 +84,7 @@ void nasm_do_legacy_output(const struct out_data *data) } dptr = &data->toffset; - size = data->inslen - data->insoffs; + size = data->relbase - data->offset; break; case OUT_SEGMENT: