summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2017-02-21 11:30:22 -0800
committerH. Peter Anvin <hpa@zytor.com>2017-02-21 11:30:22 -0800
commit8930a8fc151e095788de9242cb163f8d1779b9d4 (patch)
tree443e80afa580cb3544e6b4ce542f715a124a0882
parented71316e2bf64d77b57b93880f8ae384cf407da0 (diff)
downloadnasm-8930a8fc151e095788de9242cb163f8d1779b9d4.tar.gz
Properly keep track of the base of relative relocations
For expressions like [foo - $] or [bar - $$] our relocation base is not the same as the end of the instruction. Make that explicit. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--asm/assemble.c62
-rw-r--r--include/nasm.h24
-rw-r--r--output/legacy.c16
3 files changed, 67 insertions, 35 deletions
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:
- 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;
+ 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 == data->segment && data->twrt == NO_SEG) {
+ if (data->tsegment == NO_SEG && 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);
+ warn_overflow_out(addrval, asize, data->sign);
- 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: