summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-10-17 19:32:10 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-10-17 19:32:10 -0700
commit8140afbaf11836aa13e633c3c6a50813b3f8f59d (patch)
tree4a23ad35378c3b87120a831e923cc466f11b6b15
parentedb58f7813b20f1ec1ad640b53fcd58b680d8103 (diff)
downloadnasm-8140afbaf11836aa13e633c3c6a50813b3f8f59d.tar.gz
ELF64: unbreak generating no-segment addresses
When generating an address that is *not* tied to a symbol, we just want to emit the bytes. I believe the assembler is already supposed to do that for us, but just in case, do it right here too. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--output/outelf64.c249
1 files changed, 138 insertions, 111 deletions
diff --git a/output/outelf64.c b/output/outelf64.c
index dac77cc1..ec9c9f73 100644
--- a/output/outelf64.c
+++ b/output/outelf64.c
@@ -225,8 +225,8 @@ static int elf_nsect, nsections;
static int64_t elf_foffs;
static void elf_write(void);
-static void elf_sect_write(struct Section *, const void *,
- uint64_t);
+static void elf_sect_write(struct Section *, const void *, size_t);
+static void elf_sect_writeaddr(struct Section *, int64_t, size_t);
static void elf_section_header(int, int, uint64_t, void *, bool, uint64_t, int, int,
int, int);
static void elf_write_sections(void);
@@ -941,10 +941,19 @@ static void elf_out(int32_t segto, const void *data,
if (s->type == SHT_NOBITS && type != OUT_RESERVE) {
error(ERR_WARNING, "attempt to initialize memory in"
" BSS section `%s': ignored", s->name);
- if (type == OUT_REL2ADR)
- size = 2;
- else if (type == OUT_REL4ADR)
- size = 4;
+ switch (type) {
+ case OUT_REL2ADR:
+ size = 2;
+ break;
+ case OUT_REL4ADR:
+ size = 4;
+ break;
+ case OUT_REL8ADR:
+ size = 8;
+ break;
+ default:
+ break; /* size is already set */
+ }
s->len += size;
return;
}
@@ -962,164 +971,177 @@ static void elf_out(int32_t segto, const void *data,
elf_sect_write(s, data, size);
} else if (type == OUT_ADDRESS) {
addr = *(int64_t *)data;
- if (segment != NO_SEG) {
- if (segment % 2) {
- error(ERR_NONFATAL, "ELF format does not support"
- " segment base references");
- } else {
- if (wrt == NO_SEG) {
- switch ((int)size) {
- case 1:
- elf_add_reloc(s, segment, addr, R_X86_64_8);
- break;
- case 2:
- elf_add_reloc(s, segment, addr, R_X86_64_16);
- break;
- case 4:
- elf_add_reloc(s, segment, addr, R_X86_64_32);
- break;
- case 8:
- elf_add_reloc(s, segment, addr, R_X86_64_64);
- break;
- default:
- error(ERR_PANIC, "internal error elf64-hpa-871");
- break;
- }
- } else if (wrt == elf_gotpc_sect + 1) {
- /*
- * The user will supply GOT relative to $$. ELF
- * will let us have GOT relative to $. So we
- * need to fix up the data item by $-$$.
- */
- addr += s->len;
- elf_add_reloc(s, segment, addr, R_X86_64_GOTPC32);
- } else if (wrt == elf_gotoff_sect + 1) {
- if (size != 8) {
- error(ERR_NONFATAL, "ELF64 requires ..gotoff "
- "references to be qword absolute");
- wrt = NO_SEG;
- } else {
- elf_add_reloc(s, segment, addr, R_X86_64_GOTOFF64);
- }
- } else if (wrt == elf_got_sect + 1) {
- switch ((int)size) {
- case 4:
- elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_GOT32, true);
- break;
- case 8:
- elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_GOT64, true);
- break;
- default:
- error(ERR_NONFATAL, "invalid ..got reference");
- wrt = NO_SEG;
- break;
- }
- } else if (wrt == elf_sym_sect + 1) {
- switch ((int)size) {
- case 1:
- elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_8, false);
- break;
- case 2:
- elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_16, false);
- break;
- case 4:
- elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_32, false);
- break;
- case 8:
- elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_64, false);
- break;
- default:
- error(ERR_PANIC, "internal error elf64-hpa-903");
- break;
- }
- } else if (wrt == elf_plt_sect + 1) {
- error(ERR_NONFATAL, "ELF format cannot produce non-PC-"
- "relative PLT references");
- } else {
- error(ERR_NONFATAL, "ELF format does not support this"
- " use of WRT");
- wrt = NO_SEG; /* we can at least _try_ to continue */
- }
- }
- }
- elf_sect_write(s, &zero, size);
+ if (segment == NO_SEG) {
+ /* Do nothing */
+ } else if (segment % 2) {
+ error(ERR_NONFATAL, "ELF format does not support"
+ " segment base references");
+ } else {
+ if (wrt == NO_SEG) {
+ switch ((int)size) {
+ case 1:
+ elf_add_reloc(s, segment, addr, R_X86_64_8);
+ break;
+ case 2:
+ elf_add_reloc(s, segment, addr, R_X86_64_16);
+ break;
+ case 4:
+ elf_add_reloc(s, segment, addr, R_X86_64_32);
+ break;
+ case 8:
+ elf_add_reloc(s, segment, addr, R_X86_64_64);
+ break;
+ default:
+ error(ERR_PANIC, "internal error elf64-hpa-871");
+ break;
+ }
+ addr = 0;
+ } else if (wrt == elf_gotpc_sect + 1) {
+ /*
+ * The user will supply GOT relative to $$. ELF
+ * will let us have GOT relative to $. So we
+ * need to fix up the data item by $-$$.
+ */
+ addr += s->len;
+ elf_add_reloc(s, segment, addr, R_X86_64_GOTPC32);
+ addr = 0;
+ } else if (wrt == elf_gotoff_sect + 1) {
+ if (size != 8) {
+ error(ERR_NONFATAL, "ELF64 requires ..gotoff "
+ "references to be qword absolute");
+ } else {
+ elf_add_reloc(s, segment, addr, R_X86_64_GOTOFF64);
+ addr = 0;
+ }
+ } else if (wrt == elf_got_sect + 1) {
+ switch ((int)size) {
+ case 4:
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_GOT32, true);
+ addr = 0;
+ break;
+ case 8:
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_GOT64, true);
+ addr = 0;
+ break;
+ default:
+ error(ERR_NONFATAL, "invalid ..got reference");
+ break;
+ }
+ } else if (wrt == elf_sym_sect + 1) {
+ switch ((int)size) {
+ case 1:
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_8, false);
+ addr = 0;
+ break;
+ case 2:
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_16, false);
+ addr = 0;
+ break;
+ case 4:
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_32, false);
+ addr = 0;
+ break;
+ case 8:
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_64, false);
+ addr = 0;
+ break;
+ default:
+ error(ERR_PANIC, "internal error elf64-hpa-903");
+ break;
+ }
+ } else if (wrt == elf_plt_sect + 1) {
+ error(ERR_NONFATAL, "ELF format cannot produce non-PC-"
+ "relative PLT references");
+ } else {
+ error(ERR_NONFATAL, "ELF format does not support this"
+ " use of WRT");
+ }
+ }
+ elf_sect_writeaddr(s, addr, size);
} else if (type == OUT_REL2ADR) {
+ addr = *(int64_t *)data - size;
if (segment == segto)
error(ERR_PANIC, "intra-segment OUT_REL2ADR");
- if (segment != NO_SEG && segment % 2) {
+ if (segment == NO_SEG) {
+ /* Do nothing */
+ } else if (segment % 2) {
error(ERR_NONFATAL, "ELF format does not support"
" segment base references");
} else {
if (wrt == NO_SEG) {
- elf_add_reloc(s, segment,
- *(int64_t *)data - size, R_X86_64_PC16);
+ elf_add_reloc(s, segment, addr, R_X86_64_PC16);
+ addr = 0;
} else {
error(ERR_NONFATAL,
"Unsupported non-32-bit ELF relocation [2]");
}
}
- elf_sect_write(s, &zero, 2);
+ elf_sect_writeaddr(s, addr, size);
} else if (type == OUT_REL4ADR) {
+ addr = *(int64_t *)data - size;
if (segment == segto)
error(ERR_PANIC, "intra-segment OUT_REL4ADR");
- if (segment != NO_SEG && segment % 2) {
+ if (segment == NO_SEG) {
+ /* Do nothing */
+ } else if (segment % 2) {
error(ERR_NONFATAL, "ELF64 format does not support"
" segment base references");
} else {
if (wrt == NO_SEG) {
- elf_add_reloc(s, segment, *(int64_t *)data - size,
- R_X86_64_PC32);
+ elf_add_reloc(s, segment, addr, R_X86_64_PC32);
+ addr = 0;
} else if (wrt == elf_plt_sect + 1) {
- elf_add_reloc(s, segment, *(int64_t *)data - size,
- R_X86_64_PLT32);
+ elf_add_reloc(s, segment, addr, R_X86_64_PLT32);
+ addr = 0;
} else if (wrt == elf_gotpc_sect + 1 ||
wrt == elf_got_sect + 1) {
- elf_add_reloc(s, segment, *(int64_t *)data - size,
- R_X86_64_GOTPCREL);
+ elf_add_reloc(s, segment, addr, R_X86_64_GOTPCREL);
+ addr = 0;
} else if (wrt == elf_gotoff_sect + 1 ||
wrt == elf_got_sect + 1) {
error(ERR_NONFATAL, "ELF64 requires ..gotoff references to be "
"qword absolute");
- wrt = NO_SEG;
} else {
error(ERR_NONFATAL, "ELF64 format does not support this"
" use of WRT");
- wrt = NO_SEG; /* we can at least _try_ to continue */
}
}
- elf_sect_write(s, &zero, 4);
+ elf_sect_writeaddr(s, addr, size);
} else if (type == OUT_REL8ADR) {
+ addr = *(int64_t *)data - size;
if (segment == segto)
error(ERR_PANIC, "intra-segment OUT_REL8ADR");
- if (segment != NO_SEG && segment % 2) {
+ if (segment == NO_SEG) {
+ /* Do nothing */
+ } else if (segment % 2) {
error(ERR_NONFATAL, "ELF64 format does not support"
" segment base references");
} else {
if (wrt == NO_SEG) {
elf_add_reloc(s, segment, *(int64_t *)data - size,
R_X86_64_PC64);
+ addr = 0;
} else if (wrt == elf_gotpc_sect + 1 ||
wrt == elf_got_sect + 1) {
elf_add_reloc(s, segment, *(int64_t *)data - size,
R_X86_64_GOTPCREL64);
+ addr = 0;
} else if (wrt == elf_gotoff_sect + 1 ||
wrt == elf_got_sect + 1) {
error(ERR_NONFATAL, "ELF64 requires ..gotoff references to be "
"qword absolute");
- wrt = NO_SEG;
} else {
error(ERR_NONFATAL, "ELF64 format does not support this"
" use of WRT");
- wrt = NO_SEG; /* we can at least _try_ to continue */
}
}
- elf_sect_write(s, &zero, 8);
+ elf_sect_writeaddr(s, addr, size);
}
}
@@ -1533,6 +1555,11 @@ static void elf_sect_write(struct Section *sect, const void *data, size_t len)
saa_wbytes(sect->data, data, len);
sect->len += len;
}
+static void elf_sect_writeaddr(struct Section *sect, int64_t data, size_t len)
+{
+ saa_writeaddr(sect->data, data, len);
+ sect->len += len;
+}
static int32_t elf_segbase(int32_t segment)
{