mirror of
https://github.com/netwide-assembler/nasm.git
synced 2025-03-19 18:00:23 +08:00
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>
This commit is contained in:
parent
ed71316e2b
commit
8930a8fc15
@ -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 ||
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user