mirror of
https://github.com/netwide-assembler/nasm.git
synced 2025-09-22 10:43:39 -04:00
outmacho: always use symbol-relative relocations for MachO64
It seems that the MachO64 linker really doesn't like segment-relative relocations under certain circumstances, so force relocations to be converted to "external" (symbol-relative); error out if no symbol is available (if this is a problem, we can consider inserting a synthetic symbol if necessary.) Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
This commit is contained in:
@@ -138,6 +138,7 @@ struct macho_fmt {
|
|||||||
uint32_t reloc_abs; /* Absolute relocation type */
|
uint32_t reloc_abs; /* Absolute relocation type */
|
||||||
uint32_t reloc_rel; /* Relative relocation type */
|
uint32_t reloc_rel; /* Relative relocation type */
|
||||||
uint32_t reloc_tlv; /* Thread local relocation type */
|
uint32_t reloc_tlv; /* Thread local relocation type */
|
||||||
|
bool forcesym; /* Always use "external" (symbol-relative) relocations */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct macho_fmt fmt;
|
static struct macho_fmt fmt;
|
||||||
@@ -154,7 +155,7 @@ struct section {
|
|||||||
int32_t index;
|
int32_t index;
|
||||||
int32_t fileindex;
|
int32_t fileindex;
|
||||||
struct reloc *relocs;
|
struct reloc *relocs;
|
||||||
struct rbtree *gsyms; /* Global symbols in section */
|
struct rbtree *syms[2]; /* All/global symbols symbols in section */
|
||||||
int align;
|
int align;
|
||||||
bool by_name; /* This section was specified by full MachO name */
|
bool by_name; /* This section was specified by full MachO name */
|
||||||
|
|
||||||
@@ -215,7 +216,7 @@ struct reloc {
|
|||||||
|
|
||||||
struct symbol {
|
struct symbol {
|
||||||
/* nasm internal data */
|
/* nasm internal data */
|
||||||
struct rbtree symv; /* Global symbol rbtree; "key" contains the
|
struct rbtree symv[2]; /* All/global symbol rbtrees; "key" contains the
|
||||||
symbol offset. */
|
symbol offset. */
|
||||||
struct symbol *next; /* next symbol in the list */
|
struct symbol *next; /* next symbol in the list */
|
||||||
char *name; /* name of this symbol */
|
char *name; /* name of this symbol */
|
||||||
@@ -412,21 +413,22 @@ static void sect_write(struct section *sect,
|
|||||||
/*
|
/*
|
||||||
* Find a suitable global symbol for a ..gotpcrel or ..tlvp reference
|
* Find a suitable global symbol for a ..gotpcrel or ..tlvp reference
|
||||||
*/
|
*/
|
||||||
static struct symbol *macho_find_gsym(struct section *s,
|
static struct symbol *macho_find_sym(struct section *s, uint64_t offset,
|
||||||
uint64_t offset, bool exact)
|
bool global, bool exact)
|
||||||
{
|
{
|
||||||
struct rbtree *srb;
|
struct rbtree *srb;
|
||||||
|
|
||||||
srb = rb_search(s->gsyms, offset);
|
srb = rb_search(s->syms[global], offset);
|
||||||
|
|
||||||
if (!srb || (exact && srb->key != offset)) {
|
if (!srb || (exact && srb->key != offset)) {
|
||||||
nasm_error(ERR_NONFATAL, "unable to find a suitable %s symbol"
|
nasm_error(ERR_NONFATAL, "unable to find a suitable%s%s symbol"
|
||||||
" for this reference",
|
" for this reference",
|
||||||
s == &absolute_sect ? "absolute" : "global");
|
global ? " global" : "",
|
||||||
|
s == &absolute_sect ? " absolute " : "");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return container_of(srb, struct symbol, symv);
|
return container_of(srb - global, struct symbol, symv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t add_reloc(struct section *sect, int32_t section,
|
static int64_t add_reloc(struct section *sect, int32_t section,
|
||||||
@@ -469,7 +471,7 @@ static int64_t add_reloc(struct section *sect, int32_t section,
|
|||||||
if (section == NO_SEG) {
|
if (section == NO_SEG) {
|
||||||
/* absolute (can this even happen?) */
|
/* absolute (can this even happen?) */
|
||||||
r->ext = 0;
|
r->ext = 0;
|
||||||
r->snum = R_ABS;
|
r->snum = NO_SECT;
|
||||||
} else if (fi == NO_SECT) {
|
} else if (fi == NO_SECT) {
|
||||||
/* external */
|
/* external */
|
||||||
r->snum = raa_read(extsyms, section);
|
r->snum = raa_read(extsyms, section);
|
||||||
@@ -493,8 +495,8 @@ static int64_t add_reloc(struct section *sect, int32_t section,
|
|||||||
#if 0
|
#if 0
|
||||||
/* This "seems" to be how it ought to work... */
|
/* This "seems" to be how it ought to work... */
|
||||||
|
|
||||||
struct symbol *sym = macho_find_gsym(&absolute_sect,
|
struct symbol *sym = macho_find_sym(&absolute_sect, offset,
|
||||||
offset, false);
|
false, false);
|
||||||
if (!sym)
|
if (!sym)
|
||||||
goto bail;
|
goto bail;
|
||||||
|
|
||||||
@@ -543,15 +545,27 @@ static int64_t add_reloc(struct section *sect, int32_t section,
|
|||||||
/* external */
|
/* external */
|
||||||
r->snum = raa_read(extsyms, section);
|
r->snum = raa_read(extsyms, section);
|
||||||
} else {
|
} else {
|
||||||
/* internal */
|
/* internal - does it really need to be global? */
|
||||||
struct symbol *sym = macho_find_gsym(s, offset, reltype != RL_TLV);
|
struct symbol *sym = macho_find_sym(s, offset, true,
|
||||||
|
reltype != RL_TLV);
|
||||||
if (!sym)
|
if (!sym)
|
||||||
goto bail;
|
goto bail;
|
||||||
|
|
||||||
r->snum = sym->initial_snum;
|
r->snum = sym->initial_snum;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* For 64-bit Mach-O, force a symbol reference if at all possible */
|
||||||
|
if (!r->ext && r->snum != NO_SECT && fmt.forcesym) {
|
||||||
|
struct symbol *sym = macho_find_sym(s, offset, false, false);
|
||||||
|
if (sym) {
|
||||||
|
adjust = bytes - sym->symv[0].key;
|
||||||
|
r->snum = sym->initial_snum;
|
||||||
|
r->ext = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* NeXT as puts relocs in reversed order (address-wise) into the
|
/* NeXT as puts relocs in reversed order (address-wise) into the
|
||||||
** files, so we do the same, doesn't seem to make much of a
|
** files, so we do the same, doesn't seem to make much of a
|
||||||
** difference either way */
|
** difference either way */
|
||||||
@@ -645,7 +659,7 @@ static void macho_output(int32_t secto, const void *data,
|
|||||||
if (section != NO_SEG) {
|
if (section != NO_SEG) {
|
||||||
if (section % 2) {
|
if (section % 2) {
|
||||||
nasm_error(ERR_NONFATAL, "Mach-O format does not support"
|
nasm_error(ERR_NONFATAL, "Mach-O format does not support"
|
||||||
" section base references");
|
" section base references");
|
||||||
} else if (wrt == NO_SEG) {
|
} else if (wrt == NO_SEG) {
|
||||||
if (fmt.ptrsize == 8 && asize != 8) {
|
if (fmt.ptrsize == 8 && asize != 8) {
|
||||||
nasm_error(ERR_NONFATAL,
|
nasm_error(ERR_NONFATAL,
|
||||||
@@ -968,6 +982,7 @@ static void macho_symdef(char *name, int32_t section, int64_t offset,
|
|||||||
int is_global, char *special)
|
int is_global, char *special)
|
||||||
{
|
{
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
|
struct section *s;
|
||||||
|
|
||||||
if (special) {
|
if (special) {
|
||||||
nasm_error(ERR_NONFATAL, "The Mach-O output format does "
|
nasm_error(ERR_NONFATAL, "The Mach-O output format does "
|
||||||
@@ -1000,7 +1015,8 @@ static void macho_symdef(char *name, int32_t section, int64_t offset,
|
|||||||
sym->strx = strslen;
|
sym->strx = strslen;
|
||||||
sym->type = 0;
|
sym->type = 0;
|
||||||
sym->desc = 0;
|
sym->desc = 0;
|
||||||
sym->symv.key = offset;
|
sym->symv[0].key = offset;
|
||||||
|
sym->symv[1].key = offset;
|
||||||
sym->initial_snum = -1;
|
sym->initial_snum = -1;
|
||||||
|
|
||||||
/* external and common symbols get N_EXT */
|
/* external and common symbols get N_EXT */
|
||||||
@@ -1013,10 +1029,9 @@ static void macho_symdef(char *name, int32_t section, int64_t offset,
|
|||||||
sym->type |= N_ABS;
|
sym->type |= N_ABS;
|
||||||
sym->sect = NO_SECT;
|
sym->sect = NO_SECT;
|
||||||
|
|
||||||
/* all absolute symbols are available to use as references */
|
s = &absolute_sect;
|
||||||
absolute_sect.gsyms = rb_insert(absolute_sect.gsyms, &sym->symv);
|
|
||||||
} else {
|
} else {
|
||||||
struct section *s = get_section_by_index(section);
|
s = get_section_by_index(section);
|
||||||
|
|
||||||
sym->type |= N_SECT;
|
sym->type |= N_SECT;
|
||||||
|
|
||||||
@@ -1038,7 +1053,7 @@ static void macho_symdef(char *name, int32_t section, int64_t offset,
|
|||||||
case 2:
|
case 2:
|
||||||
/* there isn't actually a difference between global
|
/* there isn't actually a difference between global
|
||||||
** and common symbols, both even have their size in
|
** and common symbols, both even have their size in
|
||||||
** sym->symv.key */
|
** sym->symv[0].key */
|
||||||
sym->type = N_EXT;
|
sym->type = N_EXT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1049,10 +1064,15 @@ static void macho_symdef(char *name, int32_t section, int64_t offset,
|
|||||||
nasm_panic(0, "in-file index for section %d not found, is_global = %d", section, is_global);
|
nasm_panic(0, "in-file index for section %d not found, is_global = %d", section, is_global);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (is_global) {
|
|
||||||
s->gsyms = rb_insert(s->gsyms, &sym->symv);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
s->syms[0] = rb_insert(s->syms[0], &sym->symv[0]);
|
||||||
|
if (is_global)
|
||||||
|
s->syms[1] = rb_insert(s->syms[1], &sym->symv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
++nsyms;
|
++nsyms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1467,10 +1487,10 @@ static void macho_write_symtab (void)
|
|||||||
sizes. */
|
sizes. */
|
||||||
if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) {
|
if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) {
|
||||||
nasm_assert(sym->sect <= seg_nsects);
|
nasm_assert(sym->sect <= seg_nsects);
|
||||||
sym->symv.key += sectstab[sym->sect]->addr;
|
sym->symv[0].key += sectstab[sym->sect]->addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fwriteptr(sym->symv.key, ofile); /* value (i.e. offset) */
|
fwriteptr(sym->symv[0].key, ofile); /* value (i.e. offset) */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1485,10 +1505,10 @@ static void macho_write_symtab (void)
|
|||||||
sizes. */
|
sizes. */
|
||||||
if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) {
|
if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) {
|
||||||
nasm_assert(sym->sect <= seg_nsects);
|
nasm_assert(sym->sect <= seg_nsects);
|
||||||
sym->symv.key += sectstab[sym->sect]->addr;
|
sym->symv[0].key += sectstab[sym->sect]->addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fwriteptr(sym->symv.key, ofile); /* value (i.e. offset) */
|
fwriteptr(sym->symv[0].key, ofile); /* value (i.e. offset) */
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < nundefsym; i++) {
|
for (i = 0; i < nundefsym; i++) {
|
||||||
@@ -1502,10 +1522,10 @@ static void macho_write_symtab (void)
|
|||||||
sizes. */
|
sizes. */
|
||||||
if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) {
|
if (((sym->type & N_TYPE) == N_SECT) && (sym->sect != NO_SECT)) {
|
||||||
nasm_assert(sym->sect <= seg_nsects);
|
nasm_assert(sym->sect <= seg_nsects);
|
||||||
sym->symv.key += sectstab[sym->sect]->addr;
|
sym->symv[0].key += sectstab[sym->sect]->addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fwriteptr(sym->symv.key, ofile); /* value (i.e. offset) */
|
fwriteptr(sym->symv[0].key, ofile); /* value (i.e. offset) */
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2232,7 +2252,8 @@ static const struct macho_fmt macho32_fmt = {
|
|||||||
RL_MAX_32,
|
RL_MAX_32,
|
||||||
GENERIC_RELOC_VANILLA,
|
GENERIC_RELOC_VANILLA,
|
||||||
GENERIC_RELOC_VANILLA,
|
GENERIC_RELOC_VANILLA,
|
||||||
GENERIC_RELOC_TLV
|
GENERIC_RELOC_TLV,
|
||||||
|
false /* Allow segment-relative relocations */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void macho32_init(void)
|
static void macho32_init(void)
|
||||||
@@ -2277,7 +2298,7 @@ const struct ofmt of_macho32 = {
|
|||||||
null_directive,
|
null_directive,
|
||||||
macho_filename,
|
macho_filename,
|
||||||
macho_cleanup,
|
macho_cleanup,
|
||||||
macho_pragma_list,
|
macho_pragma_list
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2294,7 +2315,8 @@ static const struct macho_fmt macho64_fmt = {
|
|||||||
RL_MAX_64,
|
RL_MAX_64,
|
||||||
X86_64_RELOC_UNSIGNED,
|
X86_64_RELOC_UNSIGNED,
|
||||||
X86_64_RELOC_SIGNED,
|
X86_64_RELOC_SIGNED,
|
||||||
X86_64_RELOC_TLV
|
X86_64_RELOC_TLV,
|
||||||
|
true /* Force symbol-relative relocations */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void macho64_init(void)
|
static void macho64_init(void)
|
||||||
|
Reference in New Issue
Block a user