diff options
-rw-r--r-- | output/outelf64.c | 174 |
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; |