summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2016-02-11 21:07:40 -0800
committerH. Peter Anvin <hpa@zytor.com>2016-02-11 21:07:40 -0800
commitb64125022d9743df8428d2fa925b36632b8ffc2a (patch)
treea68a6482601ffdd68191ef3600f9911c46a6646f
parent33814133bdc8db9fcbdc1be097f7b4a226d8e143 (diff)
downloadnasm-b64125022d9743df8428d2fa925b36632b8ffc2a.tar.gz
assemble.c: handle oversized relative relocations
Handle the case of oversized (larger than permitted by the output format) relative relocations. Relative relocations are always signed, and quite likely to actually have the sign bits set, so zero-extending them is not an option. Fortunately oversized relative relocations are rare, as no CPU instruction support 64-bit relative addresses. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--assemble.c29
1 files changed, 26 insertions, 3 deletions
diff --git a/assemble.c b/assemble.c
index 05359445..b1e68217 100644
--- a/assemble.c
+++ b/assemble.c
@@ -306,6 +306,27 @@ static void warn_overflow_opd(const struct operand *o, int size)
}
/*
+ * Size of an address relocation, or zero if not an address
+ */
+static int addrsize(enum out_type type, uint64_t size)
+{
+ switch (type) {
+ case OUT_ADDRESS:
+ return abs((int)size);
+ case OUT_REL1ADR:
+ return 1;
+ case OUT_REL2ADR:
+ return 2;
+ case OUT_REL4ADR:
+ return 4;
+ case OUT_REL8ADR:
+ return 8;
+ default:
+ return 0;
+ }
+}
+
+/*
* This routine wrappers the real output format's output routine,
* in order to pass a copy of the data off to the listing file
* generator at the same time, flatten unnecessary relocations,
@@ -318,7 +339,7 @@ static void out(int64_t offset, int32_t segto, const void *data,
static int32_t lineno = 0; /* static!!! */
static char *lnfname = NULL;
uint8_t p[8];
- const int asize = abs((int)size); /* True address size */
+ int asize = addrsize(type, size); /* Address size in bytes */
const int amax = outfmt->maxbits >> 3; /* Maximum address size in bytes */
if (type == OUT_ADDRESS && segment == NO_SEG && wrt == NO_SEG) {
@@ -336,6 +357,8 @@ static void out(int64_t offset, int32_t segto, const void *data,
WRITEADDR(q, *(int64_t *)data, asize);
data = p;
type = OUT_RAWDATA;
+
+ asize = 0; /* No longer an address */
}
list->output(offset, data, type, size);
@@ -352,8 +375,8 @@ static void out(int64_t offset, int32_t segto, const void *data,
if (src_get(&lineno, &lnfname))
outfmt->current_dfmt->linenum(lnfname, lineno, segto);
- if (type == OUT_ADDRESS && asize > amax) {
- if (asize < 0) {
+ if (asize && asize > amax) {
+ if (type != OUT_ADDRESS || (int)size < 0) {
errfunc(ERR_NONFATAL,
"%d-bit signed relocation unsupported by output format %s\n",
asize << 3, outfmt->shortname);