summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-10-17 20:01:16 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-10-17 20:01:16 -0700
commit70e6c2a6cb954edcbc2d403519a0eb662987af61 (patch)
tree846eb08821c8ba36de93e3950890728af5ed81b8
parent8140afbaf11836aa13e633c3c6a50813b3f8f59d (diff)
downloadnasm-70e6c2a6cb954edcbc2d403519a0eb662987af61.tar.gz
nasm-70e6c2a6cb954edcbc2d403519a0eb662987af61.tar.bz2
nasm-70e6c2a6cb954edcbc2d403519a0eb662987af61.zip
ELF64: GOT and PLT references need a symbol (a slot!) to reference
GOT and PLT references need a symbol; after all, they reference a GOT or PLT slot. Thus, they need elf_add_gsym_reloc(). Mungify the interface so that they can communicate the need for the PC-shifted offset into the relocation. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--output/outelf64.c59
1 files changed, 33 insertions, 26 deletions
diff --git a/output/outelf64.c b/output/outelf64.c
index ec9c9f7..3e9f776 100644
--- a/output/outelf64.c
+++ b/output/outelf64.c
@@ -820,9 +820,9 @@ static void elf_add_reloc(struct Section *sect, int32_t segment,
* Inefficiency: we search, currently, using a linked list which
* isn't even necessarily sorted.
*/
-static int64_t elf_add_gsym_reloc(struct Section *sect,
- int32_t segment, int64_t offset,
- int type, bool exact)
+static void elf_add_gsym_reloc(struct Section *sect,
+ int32_t segment, int64_t offset, int64_t pcrel,
+ int type, bool exact)
{
struct Reloc *r;
struct Section *s;
@@ -842,9 +842,13 @@ static int64_t elf_add_gsym_reloc(struct Section *sect,
s = sects[i];
break;
}
+
if (!s) {
- elf_add_reloc(sect, segment, offset, type);
- return offset;
+ if (exact && offset)
+ error(ERR_NONFATAL, "invalid access to an external symbol");
+ else
+ elf_add_reloc(sect, segment, offset - pcrel, type);
+ return;
}
if (exact) {
@@ -854,6 +858,11 @@ static int64_t elf_add_gsym_reloc(struct Section *sect,
for (sym = s->gsyms; sym; sym = sym->next)
if (sym->value == offset)
break;
+ if (!sym) {
+ error(ERR_NONFATAL, "unable to find a suitable global symbol"
+ " for this reference");
+ return;
+ }
} else {
/*
* Find the nearest symbol below this one.
@@ -863,24 +872,17 @@ static int64_t elf_add_gsym_reloc(struct Section *sect,
if (sm->value <= offset && (!sym || sm->value > sym->value))
sym = sm;
}
- if (!sym && exact) {
- error(ERR_NONFATAL, "unable to find a suitable global symbol"
- " for this reference");
- return 0;
- }
r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
sect->tail = &r->next;
r->next = NULL;
r->address = sect->len;
- r->offset = offset - sym->value;
+ r->offset = offset - pcrel - sym->value;
r->symbol = GLOBAL_TEMP_BASE + sym->globnum;
r->type = type;
sect->nrelocs++;
-
- return offset - sym->value;
}
static void elf_out(int32_t segto, const void *data,
@@ -1010,18 +1012,19 @@ static void elf_out(int32_t segto, const void *data,
error(ERR_NONFATAL, "ELF64 requires ..gotoff "
"references to be qword absolute");
} else {
- elf_add_reloc(s, segment, addr, R_X86_64_GOTOFF64);
+ elf_add_gsym_reloc(s, segment, addr, 0,
+ R_X86_64_GOTOFF64, true);
addr = 0;
}
} else if (wrt == elf_got_sect + 1) {
switch ((int)size) {
case 4:
- elf_add_gsym_reloc(s, segment, addr,
+ elf_add_gsym_reloc(s, segment, addr, 0,
R_X86_64_GOT32, true);
addr = 0;
break;
case 8:
- elf_add_gsym_reloc(s, segment, addr,
+ elf_add_gsym_reloc(s, segment, addr, 0,
R_X86_64_GOT64, true);
addr = 0;
break;
@@ -1032,22 +1035,22 @@ static void elf_out(int32_t segto, const void *data,
} else if (wrt == elf_sym_sect + 1) {
switch ((int)size) {
case 1:
- elf_add_gsym_reloc(s, segment, addr,
+ elf_add_gsym_reloc(s, segment, addr, 0,
R_X86_64_8, false);
addr = 0;
break;
case 2:
- elf_add_gsym_reloc(s, segment, addr,
+ elf_add_gsym_reloc(s, segment, addr, 0,
R_X86_64_16, false);
addr = 0;
break;
case 4:
- elf_add_gsym_reloc(s, segment, addr,
+ elf_add_gsym_reloc(s, segment, addr, 0,
R_X86_64_32, false);
addr = 0;
break;
case 8:
- elf_add_gsym_reloc(s, segment, addr,
+ elf_add_gsym_reloc(s, segment, addr, 0,
R_X86_64_64, false);
addr = 0;
break;
@@ -1097,11 +1100,15 @@ static void elf_out(int32_t segto, const void *data,
elf_add_reloc(s, segment, addr, R_X86_64_PC32);
addr = 0;
} else if (wrt == elf_plt_sect + 1) {
- elf_add_reloc(s, segment, addr, R_X86_64_PLT32);
+ int64_t pcrel = s->len + size;
+ elf_add_gsym_reloc(s, segment, addr+pcrel, pcrel,
+ R_X86_64_PLT32, false);
addr = 0;
} else if (wrt == elf_gotpc_sect + 1 ||
wrt == elf_got_sect + 1) {
- elf_add_reloc(s, segment, addr, R_X86_64_GOTPCREL);
+ int64_t pcrel = s->len + size;
+ elf_add_gsym_reloc(s, segment, addr+pcrel, pcrel,
+ R_X86_64_GOTPCREL, false);
addr = 0;
} else if (wrt == elf_gotoff_sect + 1 ||
wrt == elf_got_sect + 1) {
@@ -1124,13 +1131,13 @@ static void elf_out(int32_t segto, const void *data,
" segment base references");
} else {
if (wrt == NO_SEG) {
- elf_add_reloc(s, segment, *(int64_t *)data - size,
- R_X86_64_PC64);
+ elf_add_reloc(s, segment, addr, 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);
+ int64_t pcrel = s->len + size;
+ elf_add_gsym_reloc(s, segment, addr+pcrel, pcrel,
+ R_X86_64_GOTPCREL64, false);
addr = 0;
} else if (wrt == elf_gotoff_sect + 1 ||
wrt == elf_got_sect + 1) {