#include <stdio.h>
#include <sys/elf.h>
#include <sys/elf_SPARC.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <synch.h>
#include <string.h>
#include <debug.h>
#include <reloc.h>
#include <conv.h>
#include "_rtld.h"
#include "_audit.h"
#include "_elf.h"
#include "_inline_gen.h"
#include "_inline_reloc.h"
#include "msg.h"
extern void iflush_range(caddr_t, size_t);
extern void plt_upper_32(uintptr_t, uintptr_t);
extern void plt_upper_44(uintptr_t, uintptr_t);
extern void plt_full_range(uintptr_t, uintptr_t);
extern void elf_rtbndr(Rt_map *, ulong_t, caddr_t);
extern void elf_rtbndr_far(Rt_map *, ulong_t, caddr_t);
int
elf_mach_flags_check(Rej_desc *rej, Ehdr *ehdr)
{
if (ehdr->e_flags & EF_SPARC_EXT_MASK) {
if (ehdr->e_flags & EF_SPARC_HAL_R1) {
rej->rej_type = SGS_REJ_HAL;
rej->rej_info = (uint_t)ehdr->e_flags;
return (0);
}
if ((ehdr->e_flags & EF_SPARC_SUN_US3) & ~at_flags) {
rej->rej_type = SGS_REJ_US3;
rej->rej_info = (uint_t)ehdr->e_flags;
return (0);
}
if ((ehdr->e_flags & EF_SPARC_EXT_MASK) & ~at_flags) {
rej->rej_type = SGS_REJ_BADFLAG;
rej->rej_info = (uint_t)ehdr->e_flags;
return (0);
}
} else if ((ehdr->e_flags & ~EF_SPARCV9_MM) != 0) {
rej->rej_type = SGS_REJ_BADFLAG;
rej->rej_info = (uint_t)ehdr->e_flags;
return (0);
}
return (1);
}
void
ldso_plt_init(Rt_map *lmp)
{
if (PLTGOT(lmp)) {
Xword pltoff;
pltoff = (2 * M_PLT_ENTSIZE) / M_PLT_INSSIZE;
elf_plt2_init(PLTGOT(lmp) + pltoff, lmp);
elf_plt_init(PLTGOT(lmp), (caddr_t)elf_rtbndr_far);
pltoff = M_PLT_ENTSIZE / M_PLT_INSSIZE;
elf_plt_init(PLTGOT(lmp) + pltoff, (caddr_t)elf_rtbndr);
}
}
Pltbindtype
elf_plt_write(uintptr_t addr, uintptr_t vaddr, void *rptr, uintptr_t symval,
Xword pltndx)
{
Rela *rel = (Rela *)rptr;
uintptr_t nsym = ~symval;
uintptr_t vpltaddr, pltaddr;
long disp;
pltaddr = addr + rel->r_offset;
vpltaddr = vaddr + rel->r_offset;
disp = symval - vpltaddr - 4;
if (pltndx >= (M64_PLT_NEARPLTS - M_PLT_XNumber)) {
*((Sxword *)pltaddr) = (uintptr_t)symval +
(uintptr_t)rel->r_addend - vaddr;
DBG_CALL(pltcntfar++);
return (PLT_T_FAR);
}
if (S_INRANGE(disp, 23) && !(rtld_flags & RT_FL_NOBAPLT)) {
uint_t *pltent, bainstr;
Pltbindtype rc;
pltent = (uint_t *)pltaddr;
if (S_INRANGE(disp, 20)) {
bainstr = M_BA_A_PT;
bainstr |= (uint_t)(S_MASK(19) & (disp >> 2));
rc = PLT_T_21D;
DBG_CALL(pltcnt21d++);
} else {
bainstr = M_BA_A;
bainstr |= (uint_t)(S_MASK(22) & (disp >> 2));
rc = PLT_T_24D;
DBG_CALL(pltcnt24d++);
}
pltent[2] = M_NOP;
pltent[1] = bainstr;
iflush_range((char *)(&pltent[1]), 4);
pltent[0] = M_NOP;
iflush_range((char *)(&pltent[0]), 4);
return (rc);
}
if ((nsym >> 32) == 0) {
plt_upper_32(pltaddr, symval);
DBG_CALL(pltcntu32++);
return (PLT_T_U32);
}
if ((nsym >> 44) == 0) {
plt_upper_44(pltaddr, symval);
DBG_CALL(pltcntu44++);
return (PLT_T_U44);
}
plt_full_range(pltaddr, symval);
DBG_CALL(pltcntfull++);
return (PLT_T_FULL);
}
#define VAL64_TO_G1 \
0x0b, 0x00, 0x00, 0x00, \
0x8a, 0x11, 0x60, 0x00, \
0x8b, 0x29, 0x70, 0x20, \
0x03, 0x00, 0x00, 0x00, \
0x82, 0x10, 0x60, 0x00, \
0x82, 0x10, 0x40, 0x05
static const Byte dyn_plt_template[] = {
0x2a, 0xcf, 0x80, 0x03,
0x82, 0x27, 0x80, 0x0e,
0x82, 0x10, 0x20, 0xb0,
0x9d, 0xe3, 0xbf, 0x40,
0xc2, 0x77, 0xa7, 0xef,
VAL64_TO_G1,
0xc2, 0x77, 0xa7, 0xf7,
VAL64_TO_G1,
0x9f, 0xc0, 0x60, 0x00,
0x01, 0x00, 0x00, 0x00
};
int dyn_plt_ent_size = sizeof (dyn_plt_template) +
sizeof (Addr) +
sizeof (Addr) +
sizeof (Word) +
sizeof (Word) +
sizeof (Sym);
static int
reloc_val64_to_g1(uchar_t *off, Addr *value, const char *sym, Lm_list *lml)
{
Xword tmp_value;
tmp_value = (Xword)value;
if (do_reloc_rtld(R_SPARC_HH22, off, &tmp_value, sym,
MSG_ORIG(MSG_SPECFIL_DYNPLT), lml) == 0) {
return (0);
}
tmp_value = (Xword)value;
if (do_reloc_rtld(R_SPARC_HM10, off + 4, &tmp_value, sym,
MSG_ORIG(MSG_SPECFIL_DYNPLT), lml) == 0) {
return (0);
}
tmp_value = (Xword)value;
if (do_reloc_rtld(R_SPARC_LM22, off + 12, &tmp_value, sym,
MSG_ORIG(MSG_SPECFIL_DYNPLT), lml) == 0) {
return (0);
}
tmp_value = (Xword)value;
if (do_reloc_rtld(R_SPARC_LO10, off + 16, &tmp_value, sym,
MSG_ORIG(MSG_SPECFIL_DYNPLT), lml) == 0) {
return (0);
}
return (1);
}
static caddr_t
elf_plt_trace_write(caddr_t addr, Rela *rptr, Rt_map *rlmp, Rt_map *dlmp,
Sym *sym, uint_t symndx, ulong_t pltndx, caddr_t to, uint_t sb_flags,
int *fail)
{
extern ulong_t elf_plt_trace();
uchar_t *dyn_plt;
uintptr_t *dyndata;
if ((sb_flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)) ==
(LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)) {
(void) elf_plt_write((uintptr_t)addr, (uintptr_t)addr,
rptr, (uintptr_t)to, pltndx);
return (to);
}
dyn_plt = (uchar_t *)((uintptr_t)AUDINFO(rlmp)->ai_dynplts +
(pltndx * dyn_plt_ent_size));
if (*dyn_plt == 0) {
Sym *symp;
Lm_list *lml = LIST(rlmp);
(void) memcpy((void *)dyn_plt, dyn_plt_template,
sizeof (dyn_plt_template));
dyndata = (uintptr_t *)((uintptr_t)dyn_plt +
sizeof (dyn_plt_template));
if (!(reloc_val64_to_g1((dyn_plt + 0x14), dyndata,
MSG_ORIG(MSG_SYM_LADYNDATA), lml) &&
reloc_val64_to_g1((dyn_plt + 0x30), (Addr *)&elf_plt_trace,
MSG_ORIG(MSG_SYM_ELFPLTTRACE), lml))) {
*fail = 1;
return (0);
}
*dyndata++ = (Addr)rlmp;
*dyndata++ = (Addr)dlmp;
*dyndata = (Addr)sb_flags;
*(Word *)dyndata = symndx;
dyndata++;
symp = (Sym *)dyndata;
*symp = *sym;
symp->st_value = (Addr)to;
iflush_range((void *)dyn_plt, sizeof (dyn_plt_template));
}
(void) elf_plt_write((uintptr_t)addr, (uintptr_t)addr, rptr,
(uintptr_t)dyn_plt, pltndx);
return ((caddr_t)dyn_plt);
}
ulong_t
elf_bndr(Rt_map *lmp, ulong_t pltoff, caddr_t from)
{
Rt_map *nlmp, *llmp;
Addr addr, vaddr, reloff, symval;
char *name;
Rela *rptr;
Sym *rsym, *nsym;
Xword pltndx;
uint_t binfo, sb_flags = 0, dbg_class;
ulong_t rsymndx;
Slookup sl;
Sresult sr;
Pltbindtype pbtype;
int entry, lmflags, farplt = 0;
Lm_list *lml;
entry = enter(0);
lml = LIST(lmp);
if ((lmflags = lml->lm_flags) & LML_FLG_RTLDLM) {
dbg_class = dbg_desc->d_class;
dbg_desc->d_class = 0;
}
addr = pltoff - M_PLT_RESERVSZ;
if (pltoff < (M64_PLT_NEARPLTS * M_PLT_ENTSIZE)) {
pltndx = addr / M_PLT_ENTSIZE;
} else {
ulong_t pltblockoff;
pltblockoff = pltoff - (M64_PLT_NEARPLTS * M_PLT_ENTSIZE);
pltndx = M64_PLT_NEARPLTS +
((pltblockoff / M64_PLT_FBLOCKSZ) * M64_PLT_FBLKCNTS) +
((pltblockoff % M64_PLT_FBLOCKSZ) / M64_PLT_FENTSIZE) -
M_PLT_XNumber;
farplt = 1;
}
if (!lmp || (!farplt && (addr % M_PLT_ENTSIZE) != 0) ||
(farplt && (addr % M_PLT_INSSIZE))) {
Conv_inv_buf_t inv_buf;
eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_PLTREF),
conv_reloc_SPARC_type(R_SPARC_JMP_SLOT, 0, &inv_buf),
EC_NATPTR(lmp), EC_XWORD(pltoff), EC_NATPTR(from));
rtldexit(lml, 1);
}
reloff = pltndx * sizeof (Rela);
addr = (ulong_t)JMPREL(lmp);
rptr = (Rela *)(addr + reloff);
rsymndx = ELF_R_SYM(rptr->r_info);
rsym = (Sym *)((ulong_t)SYMTAB(lmp) + (rsymndx * SYMENT(lmp)));
name = (char *)(STRTAB(lmp) + rsym->st_name);
llmp = lml->lm_tail;
SLOOKUP_INIT(sl, name, lmp, lml->lm_head, ld_entry_cnt, 0,
rsymndx, rsym, 0, LKUP_DEFT);
SRESULT_INIT(sr, name);
if (lookup_sym(&sl, &sr, &binfo, NULL) == 0) {
eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_NOSYM), NAME(lmp),
demangle(name));
rtldexit(lml, 1);
}
name = (char *)sr.sr_name;
nlmp = sr.sr_dmap;
nsym = sr.sr_sym;
symval = nsym->st_value;
if (!(FLAGS(nlmp) & FLG_RT_FIXED) &&
(nsym->st_shndx != SHN_ABS))
symval += ADDR(nlmp);
if ((lmp != nlmp) && ((FLAGS1(nlmp) & FL1_RT_NOINIFIN) == 0)) {
if (bind_one(lmp, nlmp, BND_REFER) == 0)
rtldexit(lml, 1);
}
if ((lml->lm_tflags | AFLAGS(lmp) | AFLAGS(nlmp)) &
LML_TFLG_AUD_SYMBIND) {
uint_t symndx = (uint_t)(((uintptr_t)nsym -
(uintptr_t)SYMTAB(nlmp)) / SYMENT(nlmp));
symval = audit_symbind(lmp, nlmp, nsym, symndx, symval,
&sb_flags);
}
if (FLAGS(lmp) & FLG_RT_FIXED)
vaddr = 0;
else
vaddr = ADDR(lmp);
pbtype = PLT_T_NONE;
if (!(rtld_flags & RT_FL_NOBIND)) {
if (((lml->lm_tflags | AFLAGS(lmp)) &
(LML_TFLG_AUD_PLTENTER | LML_TFLG_AUD_PLTEXIT)) &&
AUDINFO(lmp)->ai_dynplts) {
int fail = 0;
uint_t symndx = (uint_t)(((uintptr_t)nsym -
(uintptr_t)SYMTAB(nlmp)) / SYMENT(nlmp));
symval = (ulong_t)elf_plt_trace_write((caddr_t)vaddr,
rptr, lmp, nlmp, nsym, symndx, pltndx,
(caddr_t)symval, sb_flags, &fail);
if (fail)
rtldexit(lml, 1);
} else {
pbtype = elf_plt_write((uintptr_t)vaddr,
(uintptr_t)vaddr, rptr, symval, pltndx);
}
}
DBG_CALL(Dbg_bind_global(lmp, (Addr)from, (Off)(from - ADDR(lmp)),
(Xword)pltndx, pbtype, nlmp, (Addr)symval, nsym->st_value,
name, binfo));
if (entry)
load_completion(llmp);
if ((LIST(nlmp)->lm_flags & LML_FLG_RTLDLM) && LIST(nlmp)->lm_init)
load_completion(nlmp);
if (entry) {
is_dep_init(nlmp, lmp);
leave(lml, 0);
}
if (lmflags & LML_FLG_RTLDLM)
dbg_desc->d_class = dbg_class;
return (symval);
}
static int
bindpltpad(Rt_map *lmp, Alist **padlist, Addr value, void **pltaddr,
const char *fname, const char *sname)
{
Aliste idx = 0;
Pltpadinfo ppi, *ppip;
void *plt;
uintptr_t pltoff;
Rela rel;
int i;
for (ALIST_TRAVERSE(*padlist, idx, ppip)) {
if (ppip->pp_addr == value) {
*pltaddr = ppip->pp_plt;
DBG_CALL(Dbg_bind_pltpad_from(lmp, (Addr)*pltaddr,
sname));
return (1);
}
if (ppip->pp_addr > value)
break;
}
plt = PLTPAD(lmp);
pltoff = (uintptr_t)plt - (uintptr_t)ADDR(lmp);
PLTPAD(lmp) = (void *)((uintptr_t)PLTPAD(lmp) + M_PLT_ENTSIZE);
if (PLTPAD(lmp) > PLTPADEND(lmp)) {
*pltaddr = (void *)value;
return (1);
}
rel.r_offset = pltoff;
rel.r_info = 0;
rel.r_addend = 0;
for (i = 0; i < (M_PLT_ENTSIZE / sizeof (uint_t)); i++) {
((uint_t *)plt)[i] = M_NOP;
}
iflush_range((caddr_t)plt, M_PLT_ENTSIZE);
(void) elf_plt_write(ADDR(lmp), ADDR(lmp), &rel, value, 0);
ppi.pp_addr = value;
ppi.pp_plt = plt;
if (alist_insert(padlist, &ppi, sizeof (Pltpadinfo),
AL_CNT_PLTPAD, idx) == NULL)
return (0);
*pltaddr = plt;
DBG_CALL(Dbg_bind_pltpad_to(lmp, (Addr)*pltaddr, fname, sname));
return (1);
}
int
elf_reloc(Rt_map *lmp, uint_t plt, int *in_nfavl, APlist **textrel)
{
ulong_t relbgn, relend, relsiz, basebgn, pltbgn, pltend;
ulong_t pltndx, roffset, rsymndx, psymndx = 0;
uint_t dsymndx, binfo, pbinfo;
uchar_t rtype;
long reladd;
Addr value, pvalue;
Sym *symref, *psymref, *symdef, *psymdef;
Syminfo *sip;
char *name, *pname;
Rt_map *_lmp, *plmp;
int ret = 1, noplt = 0;
long relacount = RELACOUNT(lmp);
Rela *rel;
Pltbindtype pbtype;
Alist *pltpadlist = NULL;
APlist *bound = NULL;
if ((plt == 0) && (FLAGS(lmp) & FLG_RT_REGSYMS)) {
if (elf_regsyms(lmp) == 0)
return (0);
}
if ((plt == 0) && PLTGOT(lmp)) {
mmapobj_result_t *mpp;
Xword pltoff;
if ((((mpp =
find_segment((caddr_t)PLTGOT(lmp), lmp)) != NULL) &&
((mpp->mr_prot & PROT_WRITE) == 0)) &&
((set_prot(lmp, mpp, 1) == 0) ||
(aplist_append(textrel, mpp, AL_CNT_TEXTREL) == NULL)))
return (0);
pltoff = (2 * M_PLT_ENTSIZE) / M_PLT_INSSIZE;
elf_plt2_init(PLTGOT(lmp) + pltoff, lmp);
elf_plt_init(PLTGOT(lmp), (caddr_t)elf_rtbndr_far);
pltoff = M_PLT_ENTSIZE / M_PLT_INSSIZE;
elf_plt_init(PLTGOT(lmp) + pltoff, (caddr_t)elf_rtbndr);
}
if ((pltbgn = (ulong_t)JMPREL(lmp)) != 0)
pltend = pltbgn + (ulong_t)(PLTRELSZ(lmp));
if (plt) {
relbgn = pltbgn;
relend = pltend;
} else {
relbgn = (ulong_t)(REL(lmp));
relend = relbgn + (ulong_t)(RELSZ(lmp));
if (pltbgn) {
if (!relbgn || (relbgn > pltbgn))
relbgn = pltbgn;
if (!relbgn || (relend < pltend))
relend = pltend;
}
}
if (!relbgn || (relbgn == relend)) {
DBG_CALL(Dbg_reloc_run(lmp, 0, plt, DBG_REL_NONE));
return (1);
}
relsiz = (ulong_t)(RELENT(lmp));
basebgn = ADDR(lmp);
DBG_CALL(Dbg_reloc_run(lmp, M_REL_SHT_TYPE, plt, DBG_REL_START));
if (pltbgn && ((MODE(lmp) & RTLD_NOW) == 0))
noplt = 1;
sip = SYMINFO(lmp);
while (relbgn < relend) {
mmapobj_result_t *mpp;
uint_t sb_flags = 0;
Addr vaddr;
rtype = ELF_R_TYPE(((Rela *)relbgn)->r_info, M_MACH);
if ((rtype == R_SPARC_RELATIVE) &&
((FLAGS(lmp) & FLG_RT_FIXED) == 0) && (DBG_ENABLED == 0)) {
if (relacount) {
relbgn = elf_reloc_relative_count(relbgn,
relacount, relsiz, basebgn, lmp,
textrel, 0);
relacount = 0;
} else {
relbgn = elf_reloc_relative(relbgn, relend,
relsiz, basebgn, lmp, textrel, 0);
}
if (relbgn >= relend)
break;
rtype = ELF_R_TYPE(((Rela *)relbgn)->r_info, M_MACH);
}
roffset = ((Rela *)relbgn)->r_offset;
reladd = (long)(((Rela *)relbgn)->r_addend);
rsymndx = ELF_R_SYM(((Rela *)relbgn)->r_info);
rel = (Rela *)relbgn;
relbgn += relsiz;
if (rtype == R_SPARC_NONE)
continue;
if (noplt && ((ulong_t)rel >= pltbgn) &&
((ulong_t)rel < pltend)) {
relbgn = pltend;
continue;
}
if (rtype != R_SPARC_REGISTER) {
if (!(FLAGS(lmp) & FLG_RT_FIXED))
roffset += basebgn;
if ((mpp = find_segment((caddr_t)roffset,
lmp)) == NULL) {
elf_reloc_bad(lmp, (void *)rel, rtype, roffset,
rsymndx);
continue;
}
}
if (plt) {
uchar_t *_roffset = (uchar_t *)roffset;
_roffset += M_PLT_INSSIZE;
if ((*(uint_t *)_roffset &
(~(S_MASK(19)))) != M_BA_A_XCC)
continue;
}
binfo = 0;
pltndx = (ulong_t)-1;
pbtype = PLT_T_NONE;
if (rsymndx) {
if (sip && is_sym_deferred((ulong_t)rel, basebgn, lmp,
textrel, sip, rsymndx))
continue;
symref = (Sym *)((ulong_t)SYMTAB(lmp) +
(rsymndx * SYMENT(lmp)));
if (ELF_ST_BIND(symref->st_info) == STB_LOCAL) {
value = basebgn;
name = NULL;
if ((rtype == R_SPARC_TLS_DTPMOD32) ||
(rtype == R_SPARC_TLS_DTPMOD64)) {
value = TLSMODID(lmp);
} else if ((rtype == R_SPARC_TLS_TPOFF32) ||
(rtype == R_SPARC_TLS_TPOFF64)) {
if ((value = elf_static_tls(lmp, symref,
rel, rtype, 0, roffset, 0)) == 0) {
ret = 0;
break;
}
}
} else {
if ((rsymndx == psymndx) &&
(rtype != R_SPARC_COPY)) {
if (psymdef == 0) {
DBG_CALL(Dbg_bind_weak(lmp,
(Addr)roffset, (Addr)
(roffset - basebgn), name));
continue;
}
value = pvalue;
name = pname;
symdef = psymdef;
symref = psymref;
_lmp = plmp;
binfo = pbinfo;
if ((LIST(_lmp)->lm_tflags |
AFLAGS(_lmp)) &
LML_TFLG_AUD_SYMBIND) {
value = audit_symbind(lmp, _lmp,
symdef, dsymndx, value,
&sb_flags);
}
} else {
Slookup sl;
Sresult sr;
name = (char *)(STRTAB(lmp) +
symref->st_name);
SLOOKUP_INIT(sl, name, lmp, 0,
ld_entry_cnt, 0, rsymndx, symref,
rtype, LKUP_STDRELOC);
SRESULT_INIT(sr, name);
symdef = NULL;
if (lookup_sym(&sl, &sr, &binfo,
in_nfavl)) {
name = (char *)sr.sr_name;
_lmp = sr.sr_dmap;
symdef = sr.sr_sym;
}
if (symdef == 0) {
if (sl.sl_bind != STB_WEAK) {
if (elf_reloc_error(lmp, name,
rel, binfo))
continue;
ret = 0;
break;
} else {
psymndx = rsymndx;
psymdef = 0;
DBG_CALL(Dbg_bind_weak(lmp,
(Addr)roffset, (Addr)
(roffset - basebgn), name));
continue;
}
}
if ((lmp != _lmp) && ((FLAGS1(_lmp) &
FL1_RT_NOINIFIN) == 0)) {
if (aplist_test(&bound, _lmp,
AL_CNT_RELBIND) == 0) {
ret = 0;
break;
}
}
if (IS_SIZE(rtype))
value = symdef->st_size;
else
value = symdef->st_value;
if (!(FLAGS(_lmp) & FLG_RT_FIXED) &&
!(IS_SIZE(rtype)) &&
(symdef->st_shndx != SHN_ABS) &&
(ELF_ST_TYPE(symdef->st_info) !=
STT_TLS))
value += ADDR(_lmp);
if (rtype != R_SPARC_COPY) {
psymndx = rsymndx;
pvalue = value;
pname = name;
psymdef = symdef;
psymref = symref;
plmp = _lmp;
pbinfo = binfo;
}
if ((LIST(_lmp)->lm_tflags |
AFLAGS(_lmp)) &
LML_TFLG_AUD_SYMBIND) {
dsymndx = (((uintptr_t)symdef -
(uintptr_t)SYMTAB(_lmp)) /
SYMENT(_lmp));
value = audit_symbind(lmp, _lmp,
symdef, dsymndx, value,
&sb_flags);
}
}
if (IS_PC_RELATIVE(rtype))
value -= roffset;
if ((rtype == R_SPARC_TLS_DTPMOD32) ||
(rtype == R_SPARC_TLS_DTPMOD64)) {
value = TLSMODID(_lmp);
} else if ((rtype == R_SPARC_TLS_TPOFF64) ||
(rtype == R_SPARC_TLS_TPOFF32)) {
if ((value = elf_static_tls(_lmp,
symdef, rel, rtype, name, roffset,
value)) == 0) {
ret = 0;
break;
}
}
}
} else {
if (rtype == R_SPARC_REGISTER) {
value = 0;
} else if ((rtype == R_SPARC_TLS_DTPMOD32) ||
(rtype == R_SPARC_TLS_DTPMOD64)) {
value = TLSMODID(lmp);
} else
value = basebgn;
name = NULL;
}
DBG_CALL(Dbg_reloc_in(LIST(lmp), ELF_DBG_RTLD, M_MACH,
M_REL_SHT_TYPE, rel, NULL, 0, name));
if ((rtype != R_SPARC_REGISTER) &&
((mpp->mr_prot & PROT_WRITE) == 0) &&
((set_prot(lmp, mpp, 1) == 0) ||
(aplist_append(textrel, mpp, AL_CNT_TEXTREL) == NULL))) {
ret = 0;
break;
}
switch (rtype) {
case R_SPARC_REGISTER:
value += reladd;
if (roffset == STO_SPARC_REGISTER_G1) {
set_sparc_g1(value);
} else if (roffset == STO_SPARC_REGISTER_G2) {
set_sparc_g2(value);
} else if (roffset == STO_SPARC_REGISTER_G3) {
set_sparc_g3(value);
} else if (roffset == STO_SPARC_REGISTER_G4) {
set_sparc_g4(value);
} else if (roffset == STO_SPARC_REGISTER_G5) {
set_sparc_g5(value);
} else if (roffset == STO_SPARC_REGISTER_G6) {
set_sparc_g6(value);
} else if (roffset == STO_SPARC_REGISTER_G7) {
set_sparc_g7(value);
} else {
eprintf(LIST(lmp), ERR_FATAL,
MSG_INTL(MSG_REL_BADREG), NAME(lmp),
EC_ADDR(roffset));
ret = 0;
break;
}
DBG_CALL(Dbg_reloc_apply_reg(LIST(lmp), ELF_DBG_RTLD,
M_MACH, (Xword)roffset, (Xword)value));
break;
case R_SPARC_COPY:
if (elf_copy_reloc(name, symref, lmp, (void *)roffset,
symdef, _lmp, (const void *)value) == 0)
ret = 0;
break;
case R_SPARC_JMP_SLOT:
pltndx = ((uintptr_t)rel -
(uintptr_t)JMPREL(lmp)) / relsiz;
if (FLAGS(lmp) & FLG_RT_FIXED)
vaddr = 0;
else
vaddr = ADDR(lmp);
if (((LIST(lmp)->lm_tflags | AFLAGS(lmp)) &
(LML_TFLG_AUD_PLTENTER | LML_TFLG_AUD_PLTEXIT)) &&
AUDINFO(lmp)->ai_dynplts) {
int fail = 0;
uint_t symndx = (uint_t)(((uintptr_t)symdef -
(uintptr_t)SYMTAB(_lmp)) / SYMENT(_lmp));
(void) elf_plt_trace_write((caddr_t)vaddr,
(Rela *)rel, lmp, _lmp, symdef, symndx,
pltndx, (caddr_t)value, sb_flags, &fail);
if (fail)
ret = 0;
} else {
DBG_CALL(Dbg_reloc_apply_val(LIST(lmp),
ELF_DBG_RTLD, (Xword)roffset,
(Xword)value));
pbtype = elf_plt_write((uintptr_t)vaddr,
(uintptr_t)vaddr, (void *)rel, value,
pltndx);
}
break;
case R_SPARC_WDISP30:
if (PLTPAD(lmp) &&
(S_INRANGE((Sxword)value, 29) == 0)) {
void * plt = 0;
if (bindpltpad(lmp, &pltpadlist,
value + roffset, &plt,
NAME(_lmp), name) == 0) {
ret = 0;
break;
}
value = (Addr)((Addr)plt - roffset);
}
default:
value += reladd;
if (IS_EXTOFFSET(rtype))
value += (Word)ELF_R_TYPE_DATA(rel->r_info);
if ((rtype == R_SPARC_GLOB_DAT) ||
(rtype == R_SPARC_64)) {
if (roffset & 0x7) {
Conv_inv_buf_t inv_buf;
eprintf(LIST(lmp), ERR_FATAL,
MSG_INTL(MSG_REL_NONALIGN),
conv_reloc_SPARC_type(rtype,
0, &inv_buf),
NAME(lmp), demangle(name),
EC_OFF(roffset));
ret = 0;
} else
*(ulong_t *)roffset += value;
} else {
if (do_reloc_rtld(rtype, (uchar_t *)roffset,
(Xword *)&value, name,
NAME(lmp), LIST(lmp)) == 0)
ret = 0;
}
DBG_CALL(Dbg_reloc_apply_val(LIST(lmp), ELF_DBG_RTLD,
(Xword)roffset, (Xword)value));
if (textrel)
iflush_range((caddr_t)roffset, 0x4);
}
if ((ret == 0) &&
((LIST(lmp)->lm_flags & LML_FLG_TRC_WARN) == 0))
break;
if (binfo) {
DBG_CALL(Dbg_bind_global(lmp, (Addr)roffset,
(Off)(roffset - basebgn), pltndx, pbtype,
_lmp, (Addr)value, symdef->st_value, name, binfo));
}
}
if (pltpadlist)
free(pltpadlist);
return (relocate_finish(lmp, bound, ret));
}
const char *
_conv_reloc_type(uint_t rel)
{
static Conv_inv_buf_t inv_buf;
return (conv_reloc_SPARC_type(rel, 0, &inv_buf));
}