summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-10-17 18:23:29 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-10-17 18:23:29 -0700
commit64fa04f0199cdab91ee5dea3d29dd8a6970edde3 (patch)
tree2574ea26a5b5e1464e9d11d394063473d8a7bf6b
parente41b69beaf51a725a98b1ba4480751333b24a06f (diff)
downloadnasm-64fa04f0199cdab91ee5dea3d29dd8a6970edde3.tar.gz
ELF64: use the RELA addend field instead of relying on the code stream
The x86-64 ABI wants the symbol addend to reside in the addend field of the RELA relocation, not in the code stream. Apparently it's something one can get away with, but the linker would still botch it for some cases. Change it so we pass the proper output and emit zero into the code stream. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--output/outelf64.c174
1 files changed, 94 insertions, 80 deletions
diff --git a/output/outelf64.c b/output/outelf64.c
index b70550f5..dac77cc1 100644
--- a/output/outelf64.c
+++ b/output/outelf64.c
@@ -114,16 +114,16 @@ typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Off;
typedef struct
{
- Elf64_Word sh_name; /* Section name (string tbl index) */
- Elf64_Word sh_type; /* Section type */
- Elf64_Xword sh_flags; /* Section flags */
- Elf64_Addr sh_addr; /* Section virtual addr at execution */
- Elf64_Off sh_offset; /* Section file offset */
- Elf64_Xword sh_size; /* Section size in bytes */
- Elf64_Word sh_link; /* Link to another section */
- Elf64_Word sh_info; /* Additional section information */
- Elf64_Xword sh_addralign; /* Section alignment */
- Elf64_Xword sh_entsize; /* Entry size if section holds table */
+ Elf64_Word sh_name; /* Section name (string tbl index) */
+ Elf64_Word sh_type; /* Section type */
+ Elf64_Xword sh_flags; /* Section flags */
+ Elf64_Addr sh_addr; /* Section virtual addr at execution */
+ Elf64_Off sh_offset; /* Section file offset */
+ Elf64_Xword sh_size; /* Section size in bytes */
+ Elf64_Word sh_link; /* Link to another section */
+ Elf64_Word sh_info; /* Additional section information */
+ Elf64_Xword sh_addralign; /* Section alignment */
+ Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
@@ -132,22 +132,23 @@ typedef struct
struct Reloc {
struct Reloc *next;
- int64_t address; /* relative to _start_ of section */
- int64_t symbol; /* symbol index */
- int type; /* type of relocation */
+ int64_t address; /* relative to _start_ of section */
+ int64_t symbol; /* symbol index */
+ int64_t offset; /* symbol addend */
+ int type; /* type of relocation */
};
struct Symbol {
- int32_t strpos; /* string table position of name */
- int32_t section; /* section ID of the symbol */
- int type; /* symbol type */
- int other; /* symbol visibility */
- int64_t value; /* address, or COMMON variable align */
- int32_t size; /* size of symbol */
- int32_t globnum; /* symbol table offset if global */
- struct Symbol *next; /* list of globals in each section */
+ int32_t strpos; /* string table position of name */
+ int32_t section; /* section ID of the symbol */
+ int type; /* symbol type */
+ int other; /* symbol visibility */
+ int64_t value; /* address, or COMMON variable align */
+ int32_t size; /* size of symbol */
+ int32_t globnum; /* symbol table offset if global */
+ struct Symbol *next; /* list of globals in each section */
struct Symbol *nextfwd; /* list of unresolved-size symbols */
- char *name; /* used temporarily if in above list */
+ char *name; /* used temporarily if in above list */
};
@@ -155,10 +156,10 @@ struct Section {
struct SAA *data;
uint64_t len, size;
uint32_t nrelocs;
- int32_t index; /* index into sects array */
- uint32_t type; /* SHT_PROGBITS or SHT_NOBITS */
- uint64_t align; /* alignment: power of two */
- uint64_t flags; /* section flags */
+ int32_t index; /* index into sects array */
+ uint32_t type; /* SHT_PROGBITS or SHT_NOBITS */
+ uint64_t align; /* alignment: power of two */
+ uint64_t flags; /* section flags */
char *name;
struct SAA *rel;
uint64_t rellen;
@@ -224,7 +225,7 @@ static int elf_nsect, nsections;
static int64_t elf_foffs;
static void elf_write(void);
-static void elf_sect_write(struct Section *, const uint8_t *,
+static void elf_sect_write(struct Section *, const void *,
uint64_t);
static void elf_section_header(int, int, uint64_t, void *, bool, uint64_t, int, int,
int, int);
@@ -771,7 +772,8 @@ static void elf_deflabel(char *name, int32_t segment, int64_t offset,
error(ERR_NONFATAL, "no special symbol features supported here");
}
-static void elf_add_reloc(struct Section *sect, int32_t segment, int type)
+static void elf_add_reloc(struct Section *sect, int32_t segment,
+ int64_t offset, int type)
{
struct Reloc *r;
r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
@@ -779,6 +781,7 @@ static void elf_add_reloc(struct Section *sect, int32_t segment, int type)
r->next = NULL;
r->address = sect->len;
+ r->offset = offset;
if (segment == NO_SEG)
r->symbol = 0;
else {
@@ -817,7 +820,7 @@ static void elf_add_reloc(struct Section *sect, int32_t segment, int type)
* Inefficiency: we search, currently, using a linked list which
* isn't even necessarily sorted.
*/
-static int32_t elf_add_gsym_reloc(struct Section *sect,
+static int64_t elf_add_gsym_reloc(struct Section *sect,
int32_t segment, int64_t offset,
int type, bool exact)
{
@@ -840,11 +843,7 @@ static int32_t elf_add_gsym_reloc(struct Section *sect,
break;
}
if (!s) {
- if (exact && offset != 0)
- error(ERR_NONFATAL, "unable to find a suitable global symbol"
- " for this reference");
- else
- elf_add_reloc(sect, segment, type);
+ elf_add_reloc(sect, segment, offset, type);
return offset;
}
@@ -875,6 +874,7 @@ static int32_t elf_add_gsym_reloc(struct Section *sect,
r->next = NULL;
r->address = sect->len;
+ r->offset = offset - sym->value;
r->symbol = GLOBAL_TEMP_BASE + sym->globnum;
r->type = type;
@@ -888,11 +888,12 @@ static void elf_out(int32_t segto, const void *data,
int32_t segment, int32_t wrt)
{
struct Section *s;
- int64_t addr;
- uint8_t mydata[16], *p;
+ int64_t addr, zero;
int i;
static struct symlininfo sinfo;
+ zero = 0;
+
#if defined(DEBUG) && DEBUG>2
if (data) fprintf(stderr,
" elf_out line: %d type: %x seg: %d segto: %d bytes: %x data: %"PRIx64"\n",
@@ -960,7 +961,6 @@ static void elf_out(int32_t segto, const void *data,
error(ERR_PANIC, "OUT_RAWDATA with other than NO_SEG");
elf_sect_write(s, data, size);
} else if (type == OUT_ADDRESS) {
- bool gnu16 = false;
addr = *(int64_t *)data;
if (segment != NO_SEG) {
if (segment % 2) {
@@ -970,16 +970,16 @@ static void elf_out(int32_t segto, const void *data,
if (wrt == NO_SEG) {
switch ((int)size) {
case 1:
- elf_add_reloc(s, segment, R_X86_64_8);
+ elf_add_reloc(s, segment, addr, R_X86_64_8);
break;
case 2:
- elf_add_reloc(s, segment, R_X86_64_16);
+ elf_add_reloc(s, segment, addr, R_X86_64_16);
break;
case 4:
- elf_add_reloc(s, segment, R_X86_64_32);
+ elf_add_reloc(s, segment, addr, R_X86_64_32);
break;
case 8:
- elf_add_reloc(s, segment, R_X86_64_64);
+ elf_add_reloc(s, segment, addr, R_X86_64_64);
break;
default:
error(ERR_PANIC, "internal error elf64-hpa-871");
@@ -992,24 +992,24 @@ static void elf_out(int32_t segto, const void *data,
* need to fix up the data item by $-$$.
*/
addr += s->len;
- elf_add_reloc(s, segment, R_X86_64_GOTPC32);
+ 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, R_X86_64_GOTOFF64);
+ elf_add_reloc(s, segment, addr, R_X86_64_GOTOFF64);
}
} else if (wrt == elf_got_sect + 1) {
switch ((int)size) {
case 4:
- addr = elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_GOT32, true);
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_GOT32, true);
break;
case 8:
- addr = elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_GOT64, true);
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_GOT64, true);
break;
default:
error(ERR_NONFATAL, "invalid ..got reference");
@@ -1019,20 +1019,20 @@ static void elf_out(int32_t segto, const void *data,
} else if (wrt == elf_sym_sect + 1) {
switch ((int)size) {
case 1:
- addr = elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_8, false);
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_8, false);
break;
case 2:
- addr = elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_16, false);
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_16, false);
break;
case 4:
- addr = elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_32, false);
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_32, false);
break;
case 8:
- addr = elf_add_gsym_reloc(s, segment, addr,
- R_X86_64_64, false);
+ elf_add_gsym_reloc(s, segment, addr,
+ R_X86_64_64, false);
break;
default:
error(ERR_PANIC, "internal error elf64-hpa-903");
@@ -1048,18 +1048,7 @@ static void elf_out(int32_t segto, const void *data,
}
}
}
- p = mydata;
- if (gnu16) {
- WRITESHORT(p, addr);
- } else {
- if (size != 8 && size != 4 && segment != NO_SEG) {
- error(ERR_NONFATAL,
- "Unsupported non-64-bit ELF relocation");
- }
- if (size == 4) WRITELONG(p, addr);
- else WRITEDLONG(p, (int64_t)addr);
- }
- elf_sect_write(s, mydata, size);
+ elf_sect_write(s, &zero, size);
} else if (type == OUT_REL2ADR) {
if (segment == segto)
error(ERR_PANIC, "intra-segment OUT_REL2ADR");
@@ -1068,15 +1057,14 @@ static void elf_out(int32_t segto, const void *data,
" segment base references");
} else {
if (wrt == NO_SEG) {
- elf_add_reloc(s, segment, R_X86_64_PC16);
+ elf_add_reloc(s, segment,
+ *(int64_t *)data - size, R_X86_64_PC16);
} else {
error(ERR_NONFATAL,
"Unsupported non-32-bit ELF relocation [2]");
}
}
- p = mydata;
- WRITESHORT(p, *(int64_t *)data - size);
- elf_sect_write(s, mydata, 2L);
+ elf_sect_write(s, &zero, 2);
} else if (type == OUT_REL4ADR) {
if (segment == segto)
error(ERR_PANIC, "intra-segment OUT_REL4ADR");
@@ -1085,12 +1073,41 @@ static void elf_out(int32_t segto, const void *data,
" segment base references");
} else {
if (wrt == NO_SEG) {
- elf_add_reloc(s, segment, R_X86_64_PC32);
+ elf_add_reloc(s, segment, *(int64_t *)data - size,
+ R_X86_64_PC32);
} else if (wrt == elf_plt_sect + 1) {
- elf_add_reloc(s, segment, R_X86_64_PLT32);
+ elf_add_reloc(s, segment, *(int64_t *)data - size,
+ R_X86_64_PLT32);
+ } 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);
+ } 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);
+ } else if (type == OUT_REL8ADR) {
+ if (segment == segto)
+ error(ERR_PANIC, "intra-segment OUT_REL8ADR");
+ if (segment != NO_SEG && 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);
} else if (wrt == elf_gotpc_sect + 1 ||
wrt == elf_got_sect + 1) {
- elf_add_reloc(s, segment, R_X86_64_GOTPCREL);
+ elf_add_reloc(s, segment, *(int64_t *)data - size,
+ R_X86_64_GOTPCREL64);
} else if (wrt == elf_gotoff_sect + 1 ||
wrt == elf_got_sect + 1) {
error(ERR_NONFATAL, "ELF64 requires ..gotoff references to be "
@@ -1102,9 +1119,7 @@ static void elf_out(int32_t segto, const void *data,
wrt = NO_SEG; /* we can at least _try_ to continue */
}
}
- p = mydata;
- WRITELONG(p, *(int64_t *)data - size);
- elf_sect_write(s, mydata, 4L);
+ elf_sect_write(s, &zero, 8);
}
}
@@ -1464,7 +1479,7 @@ static struct SAA *elf_build_reltab(uint64_t *len, struct Reloc *r)
p = entry;
WRITEDLONG(p, r->address);
WRITEDLONG(p, (sym << 32) + r->type);
- WRITEDLONG(p, (uint64_t) 0);
+ WRITEDLONG(p, r->offset);
saa_wbytes(s, entry, 24L);
*len += 24;
@@ -1513,8 +1528,7 @@ static void elf_write_sections(void)
}
}
-static void elf_sect_write(struct Section *sect,
- const uint8_t *data, uint64_t len)
+static void elf_sect_write(struct Section *sect, const void *data, size_t len)
{
saa_wbytes(sect->data, data, len);
sect->len += len;