# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # BASEPLAT = amd64 # Object lists are organized into primary (most frequently used code) and # secondary lists (less frequently used code). P_COMOBJS= debugdata.o \ analyze.o elf.o external.o globals.o \ malloc.o paths.o setup.o util.o \ dlfcns.o config_elf.o locale.o tsort.o \ remove.o move.o tls.o cap.o S_COMOBJS= debug.o audit.o object.o G_MACHOBJS= doreloc.o P_MACHOBJS= amd64_elf.o _setup.o dlamd64getunwind.o CP_MACHOBJS= S_MACHOBJS= P_ASOBJS= boot.o boot_elf.o caller.o S_ASOBJS= CRTSRCS= ../../../../lib/crt/amd64 CRTI= pics/crti.o CRTN= pics/crtn.o CRTS= $(CRTI) $(CRTN) include $(SRC)/cmd/sgs/rtld/Makefile.com include $(SRC)/lib/Makefile.lib.64 MAPFILE-ORDER = ../common/mapfile-order-gcc # Add any machine specific flags. ASFLAGS += -D__amd64 -D_ELF64 $(amd64_ASFLAGS) ADBGENFLAGS += -mlp64 ADBSUB= $(ADBSUB64) CPPFLAGS += -D_ELF64 # Hammerhead: 64-bit only - runtime linker at flattened path SONAME= /lib/ld.so.1 SGSMSGTARG += $(SGSMSGINTEL) $(SGSMSGINTEL64) $(SGSMSG64) LDLIB = -L ../../libld/$(MACH64) RTLDLIB = -L ../../librtld/$(MACH64) CPICLIB = $(CPICLIB64) LDDBGLIBDIR = $(LDDBGLIBDIR64) CONVLIBDIR = $(CONVLIBDIR64) .KEEP_STATE: all: $(RTLD) install: all $(ROOTDYNLIB64) $(ROOTCOMPATLINKS) $(ROOTCOMPATLINKS64) adbmacros: adb .WAIT $(ADBSCRIPTS) adbinstall: adbmacros .WAIT $(ROOTADB64) include $(SRC)/cmd/sgs/rtld/Makefile.targ include $(SRC)/Makefile.master.64 # Hammerhead: ld.so.1 MUST be linked by illumos ld (sunld), not GNU ld. # GNU ld produces ELFOSABI_NONE, missing PT_SUNWDTRACE/PT_SUNW_UNWIND program # headers, and wrong segment alignment — the kernel cannot properly map the # result. Override LD after Makefile.master.64 (which resets LD=$(LD64)). # # `unexport LD_ALTEXEC` does NOT reliably prevent sunld from seeing the # variable in recipe subshells (gmake may still pass it through). When # LD_ALTEXEC is set to gnu-ld-wrapper, sunld re-execs that wrapper, # which silently ignores `-z dtrace=dtrace_data` — the resulting # ld.so.1 has PT_SUNWDTRACE with p_memsz=0, and the kernel's # dtrace_safe_phdr() rejects it with silent ENOEXEC when exec'ing any # binary that uses ld.so.1 as its interpreter (including /sbin/init). # # Use the same wrapper script kernel linking uses: a single-token path # that unsets LD_ALTEXEC before exec'ing /usr/bin/sunld. See # `usr/src/tools/scripts/sunld-kernel.sh`. unexport LD_ALTEXEC LD = $(SRC)/tools/scripts/sunld-kernel.sh GSHARED = -G # Redefine DYNFLAGS in illumos ld native syntax (no -Wl, prefix). # The Makefile.targ link rule calls $(LD) directly, not $(CC). # Note: illumos ld requires space between -z and the keyword. DYNFLAGS = -h $(SONAME) -z textoff -z defs \ $(MAPFILES:%=-M %) $(MAPFILE.PGA:%=-M %) \ -i -e _rt_boot $(VERSREF) $(ZNODLOPEN) \ $(ZINTERPOSE) -z dtrace=dtrace_data '-R$$ORIGIN' \ -Bsymbolic ZNODLOPEN = -z nodlopen ZINTERPOSE = -z interpose # Hammerhead: GNU strip corrupts ld.so.1 in two places. # # 1. POST_PROCESS_SO runs STRIP_STABS ($(STRIP) -x $@) after the link # rule in Makefile.targ. On Hammerhead $(STRIP) is GNU strip. # 2. INS.file in Makefile.master passes `-s` to install(1), which # invokes GNU strip again at install time. # # GNU strip handles the Solaris PT_SUNWDTRACE program header # incorrectly: # # - sunld with `-z dtrace=dtrace_data` creates a PT_SUNWDTRACE # program header with p_filesz=0 (NOBITS — dtrace_data is in # .bss), p_memsz=0x40 (= FASTTRAP_SUNWDTRACE_SIZE), p_vaddr set to # the symbol address. # - GNU strip sees p_filesz=0 and 'optimizes' by zeroing p_memsz as # well, and drops the `dtrace_data` symbol entirely. # - The kernel's dtrace_safe_phdr() at elf.c:123 rejects any # interpreter whose PT_SUNWDTRACE p_memsz < PT_SUNWDTRACE_SIZE (64). # - Result: every exec() of a dynamic binary that uses ld.so.1 as # its interpreter fails with ENOEXEC (including /sbin/init). # # Disable both strip calls. The rest of POST_PROCESS_SO (mcs -d, # elfsign) still runs. ld.so.1 ends up larger (~1.4 MB unstripped vs # ~474 KB stripped) but exec works. STRIP_STABS = : INS.file = $(RM) $@; $(INS) -m $(FILEMODE) -f $(@D) $< /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2018, Joyent, Inc. * Copyright 2022 Oxide Computer Company */ /* * amd64 specific setup routine - relocate ld.so's symbols, setup its * environment, map in loadable sections of the executable. * * Takes base address ld.so was loaded at, address of ld.so's dynamic * structure, address of process environment pointers, address of auxiliary * vector and * argv[0] (process name). * If errors occur, send process signal - otherwise * return executable's entry point to the bootstrap routine. */ #include #include #include #include #include #include #include #include "_rtld.h" #include "_audit.h" #include "msg.h" /* * Number of bytes to save for register usage. */ uint_t _plt_save_size; void (*_plt_fp_save)(void *); void (*_plt_fp_restore)(void *); extern void _elf_rtbndr_fp_save_orig(void *); extern void _elf_rtbndr_fp_restore_orig(void *); extern void _elf_rtbndr_fp_fxsave(void *); extern void _elf_rtbndr_fp_fxrestore(void *); extern void _elf_rtbndr_fp_xsave(void *); extern void _elf_rtbndr_fp_xrestore(void *); /* * Based on what the kernel has told us, go through and set up the various * pointers that we'll need for elf_rtbndr for the FPU. */ static void _setup_plt_fpu(int kind, size_t len) { /* * If we didn't get a length for some reason, fall back to the old * implementation. */ if (len == 0) kind = -1; switch (kind) { case AT_386_FPINFO_FXSAVE: _plt_fp_save = _elf_rtbndr_fp_fxsave; _plt_fp_restore = _elf_rtbndr_fp_fxrestore; _plt_save_size = len; break; /* * We can treat processors that don't correctly handle the exception * information in xsave the same way we do others. The information * that may or may not be properly saved and restored should not be * relevant to us because of the ABI. */ case AT_386_FPINFO_XSAVE: case AT_386_FPINFO_XSAVE_AMD: _plt_fp_save = _elf_rtbndr_fp_xsave; _plt_fp_restore = _elf_rtbndr_fp_xrestore; _plt_save_size = len; break; default: _plt_fp_save = _elf_rtbndr_fp_save_orig; _plt_fp_restore = _elf_rtbndr_fp_restore_orig; /* * The ABI says that 8 floating point registers are used for * passing arguments (%xmm0 through %xmm7). Because these * registers on some platforms may shadow the %ymm and %zmm * registers, we end up needing to size this for the maximally * sized register we care about, a 512-bit (64-byte) zmm * register. */ _plt_save_size = 64 * 8; break; } } /* VARARGS */ unsigned long _setup(Boot *ebp, Dyn *ld_dyn) { ulong_t reladdr, relacount, ld_base = 0; ulong_t relaent = 0, pltrelsz = 0; ulong_t strtab, soname, interp_base = 0; char *_rt_name, **_envp, **_argv; int _syspagsz = 0, fd = -1; uint_t _flags = 0; uint_t hwcap[3] = { 0, 0, 0 }; Dyn *dyn_ptr; Phdr *phdr = NULL; Rt_map *lmp; auxv_t *auxv, *_auxv; uid_t uid = (uid_t)-1, euid = (uid_t)-1; gid_t gid = (gid_t)-1, egid = (gid_t)-1; char *_platform = NULL, *_execname = NULL, *_emulator = NULL; int auxflags = -1, fpkind = -1; size_t fpsize = 0; /* * Scan the bootstrap structure to pick up the basics. */ for (; ebp->eb_tag != EB_NULL; ebp++) switch (ebp->eb_tag) { case EB_LDSO_BASE: ld_base = (unsigned long)ebp->eb_un.eb_val; break; case EB_ARGV: _argv = (char **)ebp->eb_un.eb_ptr; break; case EB_ENVP: _envp = (char **)ebp->eb_un.eb_ptr; break; case EB_AUXV: _auxv = (auxv_t *)ebp->eb_un.eb_ptr; break; case EB_PAGESIZE: _syspagsz = (int)ebp->eb_un.eb_val; break; } /* * Search the aux. vector for the information passed by exec. */ for (auxv = _auxv; auxv->a_type != AT_NULL; auxv++) { switch (auxv->a_type) { case AT_EXECFD: /* this is the old exec that passes a file descriptor */ fd = (int)auxv->a_un.a_val; break; case AT_FLAGS: /* processor flags (MAU available, etc) */ _flags = auxv->a_un.a_val; break; case AT_PAGESZ: /* system page size */ _syspagsz = (int)auxv->a_un.a_val; break; case AT_PHDR: /* address of the segment table */ phdr = (Phdr *)auxv->a_un.a_ptr; break; case AT_BASE: /* interpreter base address */ if (ld_base == 0) ld_base = auxv->a_un.a_val; interp_base = auxv->a_un.a_val; break; case AT_SUN_UID: /* effective user id for the executable */ euid = (uid_t)auxv->a_un.a_val; break; case AT_SUN_RUID: /* real user id for the executable */ uid = (uid_t)auxv->a_un.a_val; break; case AT_SUN_GID: /* effective group id for the executable */ egid = (gid_t)auxv->a_un.a_val; break; case AT_SUN_RGID: /* real group id for the executable */ gid = (gid_t)auxv->a_un.a_val; break; case AT_SUN_PLATFORM: /* platform name */ _platform = auxv->a_un.a_ptr; break; case AT_SUN_EXECNAME: /* full pathname of execed object */ _execname = auxv->a_un.a_ptr; break; case AT_SUN_AUXFLAGS: /* auxiliary flags */ auxflags = (int)auxv->a_un.a_val; break; case AT_SUN_HWCAP: /* hardware capabilities */ hwcap[0] = (uint_t)auxv->a_un.a_val; break; case AT_SUN_HWCAP2: /* hardware capabilities */ hwcap[1] = (uint_t)auxv->a_un.a_val; break; case AT_SUN_HWCAP3: /* hardware capabilities */ hwcap[2] = (uint_t)auxv->a_un.a_val; break; case AT_SUN_EMULATOR: /* name of emulation library, if any */ _emulator = auxv->a_un.a_ptr; break; case AT_SUN_FPTYPE: fpkind = (int)auxv->a_un.a_val; break; case AT_SUN_FPSIZE: fpsize = (size_t)auxv->a_un.a_val; break; } } /* * Get needed info from ld.so's dynamic structure. */ /* LINTED */ dyn_ptr = (Dyn *)((char *)ld_dyn + ld_base); for (ld_dyn = dyn_ptr; ld_dyn->d_tag != DT_NULL; ld_dyn++) { switch (ld_dyn->d_tag) { case DT_RELA: reladdr = ld_dyn->d_un.d_ptr + ld_base; break; case DT_RELACOUNT: relacount = ld_dyn->d_un.d_val; break; case DT_RELAENT: relaent = ld_dyn->d_un.d_val; break; case DT_PLTRELSZ: pltrelsz = ld_dyn->d_un.d_val; break; case DT_STRTAB: strtab = ld_dyn->d_un.d_ptr + ld_base; break; case DT_SONAME: soname = ld_dyn->d_un.d_val; break; } } _rt_name = (char *)strtab + soname; /* * If we don't have a RELAENT, just assume the size. */ if (relaent == 0) relaent = sizeof (Rela); /* * As all global symbol references within ld.so.1 are protected * (symbolic), only RELATIVE and JMPSLOT relocations should be left * to process at runtime. Process all relocations now. */ relacount += (pltrelsz / relaent); for (; relacount; relacount--) { ulong_t roffset; roffset = ((Rela *)reladdr)->r_offset + ld_base; *((ulong_t *)roffset) += ld_base + ((Rela *)reladdr)->r_addend; reladdr += relaent; } /* * If an emulation library is being used, use that as the linker's * effective executable name. The real executable is not linked by this * linker. */ if (_emulator != NULL) { _execname = _emulator; rtld_flags2 |= RT_FL2_BRANDED; } /* * Initialize the dyn_plt_ent_size field. It currently contains the * size of the dyn_plt_template. It still needs to be aligned and have * space for the 'dyn_data' area added. */ dyn_plt_ent_size = ROUND(dyn_plt_ent_size, M_WORD_ALIGN) + sizeof (uintptr_t) + sizeof (uintptr_t) + sizeof (ulong_t) + sizeof (ulong_t) + sizeof (Sym); /* * Initialize the amd64 specific PLT relocation constants based on the * FP information that we have. */ _setup_plt_fpu(fpkind, fpsize); /* * Continue with generic startup processing. */ if ((lmp = setup((char **)_envp, (auxv_t *)_auxv, _flags, _platform, _syspagsz, _rt_name, ld_base, interp_base, fd, phdr, _execname, _argv, uid, euid, gid, egid, auxflags, hwcap)) == NULL) { rtldexit(&lml_main, 1); } return (LM_ENTRY_PT(lmp)()); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ /* * amd64 machine dependent and ELF file class dependent functions. * Contains routines for performing function binding and symbol relocations. */ #include #include #include #include #include #include #include #include #include #include #include "_rtld.h" #include "_audit.h" #include "_elf.h" #include "_inline_gen.h" #include "_inline_reloc.h" #include "msg.h" extern void elf_rtbndr(Rt_map *, ulong_t, caddr_t); int elf_mach_flags_check(Rej_desc *rej, Ehdr *ehdr) { /* * Check machine type and flags. */ if (ehdr->e_flags != 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) { /* * There is no need to analyze ld.so because we don't map in any of * its dependencies. However we may map these dependencies in later * (as if ld.so had dlopened them), so initialize the plt and the * permission information. */ if (PLTGOT(lmp)) elf_plt_init((PLTGOT(lmp)), (caddr_t)lmp); } static const uchar_t dyn_plt_template[] = { /* 0x00 */ 0x55, /* pushq %rbp */ /* 0x01 */ 0x48, 0x89, 0xe5, /* movq %rsp, %rbp */ /* 0x04 */ 0x48, 0x83, 0xec, 0x10, /* subq $0x10, %rsp */ /* 0x08 */ 0x4c, 0x8d, 0x1d, 0x00, /* leaq trace_fields(%rip), %r11 */ 0x00, 0x00, 0x00, /* 0x0f */ 0x4c, 0x89, 0x5d, 0xf8, /* movq %r11, -0x8(%rbp) */ /* 0x13 */ 0x49, 0xbb, 0x00, 0x00, /* movq $elf_plt_trace, %r11 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1d */ 0x41, 0xff, 0xe3 /* jmp *%r11 */ /* 0x20 */ }; /* * And the virutal outstanding relocations against the * above block are: * * reloc offset Addend symbol * R_AMD64_PC32 0x0b -4 trace_fields * R_AMD64_64 0x15 0 elf_plt_trace */ #define TRCREL1OFF 0x0b #define TRCREL2OFF 0x15 int dyn_plt_ent_size = sizeof (dyn_plt_template); /* * the dynamic plt entry is: * * pushq %rbp * movq %rsp, %rbp * subq $0x10, %rsp * leaq trace_fields(%rip), %r11 * movq %r11, -0x8(%rbp) * movq $elf_plt_trace, %r11 * jmp *%r11 * dyn_data: * .align 8 * uintptr_t reflmp * uintptr_t deflmp * uint_t symndx * uint_t sb_flags * Sym symdef */ static caddr_t elf_plt_trace_write(ulong_t roffset, Rt_map *rlmp, Rt_map *dlmp, Sym *sym, uint_t symndx, uint_t pltndx, caddr_t to, uint_t sb_flags, int *fail) { extern int elf_plt_trace(); ulong_t got_entry; uchar_t *dyn_plt; uintptr_t *dyndata; /* * We only need to add the glue code if there is an auditing * library that is interested in this binding. */ dyn_plt = (uchar_t *)((uintptr_t)AUDINFO(rlmp)->ai_dynplts + (pltndx * dyn_plt_ent_size)); /* * Have we initialized this dynamic plt entry yet? If we haven't do it * now. Otherwise this function has been called before, but from a * different plt (ie. from another shared object). In that case * we just set the plt to point to the new dyn_plt. */ if (*dyn_plt == 0) { Sym *symp; Xword symvalue; Lm_list *lml = LIST(rlmp); (void) memcpy((void *)dyn_plt, dyn_plt_template, sizeof (dyn_plt_template)); dyndata = (uintptr_t *)((uintptr_t)dyn_plt + ROUND(sizeof (dyn_plt_template), M_WORD_ALIGN)); /* * relocate: * leaq trace_fields(%rip), %r11 * R_AMD64_PC32 0x0b -4 trace_fields */ symvalue = (Xword)((uintptr_t)dyndata - (uintptr_t)(&dyn_plt[TRCREL1OFF]) - 4); if (do_reloc_rtld(R_AMD64_PC32, &dyn_plt[TRCREL1OFF], &symvalue, MSG_ORIG(MSG_SYM_LADYNDATA), MSG_ORIG(MSG_SPECFIL_DYNPLT), lml) == 0) { *fail = 1; return (0); } /* * relocating: * movq $elf_plt_trace, %r11 * R_AMD64_64 0x15 0 elf_plt_trace */ symvalue = (Xword)elf_plt_trace; if (do_reloc_rtld(R_AMD64_64, &dyn_plt[TRCREL2OFF], &symvalue, MSG_ORIG(MSG_SYM_ELFPLTTRACE), MSG_ORIG(MSG_SPECFIL_DYNPLT), lml) == 0) { *fail = 1; return (0); } *dyndata++ = (uintptr_t)rlmp; *dyndata++ = (uintptr_t)dlmp; *dyndata = (uintptr_t)(((uint64_t)sb_flags << 32) | symndx); dyndata++; symp = (Sym *)dyndata; *symp = *sym; symp->st_value = (Addr)to; } got_entry = (ulong_t)roffset; *(ulong_t *)got_entry = (ulong_t)dyn_plt; return ((caddr_t)dyn_plt); } /* * Function binding routine - invoked on the first call to a function through * the procedure linkage table; * passes first through an assembly language interface. * * Takes the offset into the relocation table of the associated * relocation entry and the address of the link map (rt_private_map struct) * for the entry. * * Returns the address of the function referenced after re-writing the PLT * entry to invoke the function directly. * * On error, causes process to terminate with a signal. */ ulong_t elf_bndr(Rt_map *lmp, ulong_t pltndx, caddr_t from) { Rt_map *nlmp, *llmp; ulong_t addr, reloff, symval, rsymndx; char *name; Rela *rptr; Sym *rsym, *nsym; uint_t binfo, sb_flags = 0, dbg_class; Slookup sl; Sresult sr; int entry, lmflags; Lm_list *lml; /* * For compatibility with libthread (TI_VERSION 1) we track the entry * value. A zero value indicates we have recursed into ld.so.1 to * further process a locking request. Under this recursion we disable * tsort and cleanup activities. */ 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; } /* * Perform some basic sanity checks. If we didn't get a load map or * the relocation offset is invalid then its possible someone has walked * over the .got entries or jumped to plt0 out of the blue. */ if ((!lmp) && (pltndx <= (ulong_t)PLTRELSZ(lmp) / (ulong_t)RELENT(lmp))) { Conv_inv_buf_t inv_buf; eprintf(lml, ERR_FATAL, MSG_INTL(MSG_REL_PLTREF), conv_reloc_amd64_type(R_AMD64_JUMP_SLOT, 0, &inv_buf), EC_NATPTR(lmp), EC_XWORD(pltndx), EC_NATPTR(from)); rtldexit(lml, 1); } reloff = pltndx * (ulong_t)RELENT(lmp); /* * Use relocation entry to get symbol table entry and symbol name. */ 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); /* * Determine the last link-map of this list, this'll be the starting * point for any tsort() processing. */ llmp = lml->lm_tail; /* * Find definition for symbol. Initialize the symbol lookup, and * symbol result, data structures. */ 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)) { /* * Record that this new link map is now bound to the caller. */ 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 = (((uintptr_t)nsym - (uintptr_t)SYMTAB(nlmp)) / SYMENT(nlmp)); symval = audit_symbind(lmp, nlmp, nsym, symndx, symval, &sb_flags); } if (!(rtld_flags & RT_FL_NOBIND)) { addr = rptr->r_offset; if (!(FLAGS(lmp) & FLG_RT_FIXED)) addr += ADDR(lmp); if (((lml->lm_tflags | AFLAGS(lmp)) & (LML_TFLG_AUD_PLTENTER | LML_TFLG_AUD_PLTEXIT)) && AUDINFO(lmp)->ai_dynplts) { int fail = 0; uint_t pltndx = reloff / sizeof (Rela); uint_t symndx = (((uintptr_t)nsym - (uintptr_t)SYMTAB(nlmp)) / SYMENT(nlmp)); symval = (ulong_t)elf_plt_trace_write(addr, lmp, nlmp, nsym, symndx, pltndx, (caddr_t)symval, sb_flags, &fail); if (fail) rtldexit(lml, 1); } else { /* * Write standard PLT entry to jump directly * to newly bound function. */ *(ulong_t *)addr = symval; } } /* * Print binding information and rebuild PLT entry. */ DBG_CALL(Dbg_bind_global(lmp, (Addr)from, (Off)(from - ADDR(lmp)), (Xword)(reloff / sizeof (Rela)), PLT_T_FULL, nlmp, (Addr)symval, nsym->st_value, name, binfo)); /* * Complete any processing for newly loaded objects. Note we don't * know exactly where any new objects are loaded (we know the object * that supplied the symbol, but others may have been loaded lazily as * we searched for the symbol), so sorting starts from the last * link-map know on entry to this routine. */ if (entry) load_completion(llmp); /* * Some operations like dldump() or dlopen()'ing a relocatable object * result in objects being loaded on rtld's link-map, make sure these * objects are initialized also. */ if ((LIST(nlmp)->lm_flags & LML_FLG_RTLDLM) && LIST(nlmp)->lm_init) load_completion(nlmp); /* * Make sure the object to which we've bound has had it's .init fired. * Cleanup before return to user code. */ if (entry) { is_dep_init(nlmp, lmp); leave(lml, 0); } if (lmflags & LML_FLG_RTLDLM) dbg_desc->d_class = dbg_class; return (symval); } /* * Read and process the relocations for one link object, we assume all * relocation sections for loadable segments are stored contiguously in * the file. */ int elf_reloc(Rt_map *lmp, uint_t plt, int *in_nfavl, APlist **textrel) { ulong_t relbgn, relend, relsiz, basebgn; ulong_t pltbgn, pltend, _pltbgn, _pltend; ulong_t roffset, rsymndx, psymndx = 0; ulong_t dsymndx; uchar_t rtype; long reladd, value, pvalue; Sym *symref, *psymref, *symdef, *psymdef; Syminfo *sip; char *name, *pname; Rt_map *_lmp, *plmp; int ret = 1, noplt = 0; int relacount = RELACOUNT(lmp), plthint = 0; Rela *rel; uint_t binfo, pbinfo; APlist *bound = NULL; /* * Although only necessary for lazy binding, initialize the first * global offset entry to go to elf_rtbndr(). dbx(1) seems * to find this useful. */ if ((plt == 0) && PLTGOT(lmp)) { mmapobj_result_t *mpp; /* * Make sure the segment is writable. */ 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); elf_plt_init(PLTGOT(lmp), (caddr_t)lmp); } /* * Initialize the plt start and end addresses. */ if ((pltbgn = (ulong_t)JMPREL(lmp)) != 0) pltend = pltbgn + (ulong_t)(PLTRELSZ(lmp)); relsiz = (ulong_t)(RELENT(lmp)); basebgn = ADDR(lmp); if (PLTRELSZ(lmp)) plthint = PLTRELSZ(lmp) / relsiz; /* * If we've been called upon to promote an RTLD_LAZY object to an * RTLD_NOW then we're only interested in scaning the .plt table. * An uninitialized .plt is the case where the associated got entry * points back to the plt itself. Determine the range of the real .plt * entries using the _PROCEDURE_LINKAGE_TABLE_ symbol. */ if (plt) { Slookup sl; Sresult sr; relbgn = pltbgn; relend = pltend; if (!relbgn || (relbgn == relend)) return (1); /* * Initialize the symbol lookup, and symbol result, data * structures. */ SLOOKUP_INIT(sl, MSG_ORIG(MSG_SYM_PLT), lmp, lmp, ld_entry_cnt, elf_hash(MSG_ORIG(MSG_SYM_PLT)), 0, 0, 0, LKUP_DEFT); SRESULT_INIT(sr, MSG_ORIG(MSG_SYM_PLT)); if (elf_find_sym(&sl, &sr, &binfo, NULL) == 0) return (1); symdef = sr.sr_sym; _pltbgn = symdef->st_value; if (!(FLAGS(lmp) & FLG_RT_FIXED) && (symdef->st_shndx != SHN_ABS)) _pltbgn += basebgn; _pltend = _pltbgn + (((PLTRELSZ(lmp) / relsiz)) * M_PLT_ENTSIZE) + M_PLT_RESERVSZ; } else { /* * The relocation sections appear to the run-time linker as a * single table. Determine the address of the beginning and end * of this table. There are two different interpretations of * the ABI at this point: * * o The REL table and its associated RELSZ indicate the * concatenation of *all* relocation sections (this is the * model our link-editor constructs). * * o The REL table and its associated RELSZ indicate the * concatenation of all *but* the .plt relocations. These * relocations are specified individually by the JMPREL and * PLTRELSZ entries. * * Determine from our knowledege of the relocation range and * .plt range, the range of the total relocation table. Note * that one other ABI assumption seems to be that the .plt * relocations always follow any other relocations, the * following range checking drops that assumption. */ 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); } DBG_CALL(Dbg_reloc_run(lmp, M_REL_SHT_TYPE, plt, DBG_REL_START)); /* * If we're processing a dynamic executable in lazy mode there is no * need to scan the .rel.plt table, however if we're processing a shared * object in lazy mode the .got addresses associated to each .plt must * be relocated to reflect the location of the shared object. */ if (pltbgn && ((MODE(lmp) & RTLD_NOW) == 0) && (FLAGS(lmp) & FLG_RT_FIXED)) noplt = 1; sip = SYMINFO(lmp); /* * Loop through relocations. */ while (relbgn < relend) { mmapobj_result_t *mpp; uint_t sb_flags = 0; rtype = ELF_R_TYPE(((Rela *)relbgn)->r_info, M_MACH); /* * If this is a RELATIVE relocation in a shared object (the * common case), and if we are not debugging, then jump into a * tighter relocation loop (elf_reloc_relative). */ if ((rtype == R_AMD64_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; /* * If this is a shared object, add the base address to offset. */ if (!(FLAGS(lmp) & FLG_RT_FIXED)) { /* * If we're processing lazy bindings, we have to step * through the plt entries and add the base address * to the corresponding got entry. */ if (plthint && (plt == 0) && (rtype == R_AMD64_JUMP_SLOT) && ((MODE(lmp) & RTLD_NOW) == 0)) { relbgn = elf_reloc_relative_count(relbgn, plthint, relsiz, basebgn, lmp, textrel, 1); plthint = 0; continue; } roffset += basebgn; } reladd = (long)(((Rela *)relbgn)->r_addend); rsymndx = ELF_R_SYM(((Rela *)relbgn)->r_info); rel = (Rela *)relbgn; relbgn += relsiz; /* * Optimizations. */ if (rtype == R_AMD64_NONE) continue; if (noplt && ((ulong_t)rel >= pltbgn) && ((ulong_t)rel < pltend)) { relbgn = pltend; continue; } /* * If we're promoting plts, determine if this one has already * been written. */ if (plt && ((*(ulong_t *)roffset < _pltbgn) || (*(ulong_t *)roffset > _pltend))) continue; /* * If this relocation is not against part of the image * mapped into memory we skip it. */ if ((mpp = find_segment((caddr_t)roffset, lmp)) == NULL) { elf_reloc_bad(lmp, (void *)rel, rtype, roffset, rsymndx); continue; } binfo = 0; /* * If a symbol index is specified then get the symbol table * entry, locate the symbol definition, and determine its * address. */ if (rsymndx) { /* * If a Syminfo section is provided, determine if this * symbol is deferred, and if so, skip this relocation. */ if (sip && is_sym_deferred((ulong_t)rel, basebgn, lmp, textrel, sip, rsymndx)) continue; /* * Get the local symbol table entry. */ symref = (Sym *)((ulong_t)SYMTAB(lmp) + (rsymndx * SYMENT(lmp))); /* * If this is a local symbol, just use the base address. * (we should have no local relocations in the * executable). */ if (ELF_ST_BIND(symref->st_info) == STB_LOCAL) { value = basebgn; name = NULL; /* * Special case TLS relocations. */ if (rtype == R_AMD64_DTPMOD64) { /* * Use the TLS modid. */ value = TLSMODID(lmp); } else if ((rtype == R_AMD64_TPOFF64) || (rtype == R_AMD64_TPOFF32)) { if ((value = elf_static_tls(lmp, symref, rel, rtype, 0, roffset, 0)) == 0) { ret = 0; break; } } } else { /* * If the symbol index is equal to the previous * symbol index relocation we processed then * reuse the previous values. (Note that there * have been cases where a relocation exists * against a copy relocation symbol, our ld(1) * should optimize this away, but make sure we * don't use the same symbol information should * this case exist). */ if ((rsymndx == psymndx) && (rtype != R_AMD64_COPY)) { /* LINTED */ if (psymdef == 0) { DBG_CALL(Dbg_bind_weak(lmp, (Addr)roffset, (Addr) (roffset - basebgn), name)); continue; } /* LINTED */ value = pvalue; /* LINTED */ name = pname; /* LINTED */ symdef = psymdef; /* LINTED */ symref = psymref; /* LINTED */ _lmp = plmp; /* LINTED */ binfo = pbinfo; if ((LIST(_lmp)->lm_tflags | AFLAGS(_lmp)) & LML_TFLG_AUD_SYMBIND) { value = audit_symbind(lmp, _lmp, /* LINTED */ symdef, dsymndx, value, &sb_flags); } } else { Slookup sl; Sresult sr; /* * Lookup the symbol definition. * Initialize the symbol lookup, and * symbol result, data structure. */ 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 the symbol is not found and the * reference was not to a weak symbol, * report an error. Weak references * may be unresolved. */ /* BEGIN CSTYLED */ 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; } } /* END CSTYLED */ /* * If symbol was found in an object * other than the referencing object * then record the binding. */ if ((lmp != _lmp) && ((FLAGS1(_lmp) & FL1_RT_NOINIFIN) == 0)) { if (aplist_test(&bound, _lmp, AL_CNT_RELBIND) == 0) { ret = 0; break; } } /* * Calculate the location of definition; * symbol value plus base address of * containing shared object. */ 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); /* * Retain this symbol index and the * value in case it can be used for the * subsequent relocations. */ if (rtype != R_AMD64_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 relocation is PC-relative, subtract * offset address. */ if (IS_PC_RELATIVE(rtype)) value -= roffset; /* * Special case TLS relocations. */ if (rtype == R_AMD64_DTPMOD64) { /* * Relocation value is the TLS modid. */ value = TLSMODID(_lmp); } else if ((rtype == R_AMD64_TPOFF64) || (rtype == R_AMD64_TPOFF32)) { if ((value = elf_static_tls(_lmp, symdef, rel, rtype, name, roffset, value)) == 0) { ret = 0; break; } } } } else { /* * Special cases. */ if (rtype == R_AMD64_DTPMOD64) { /* * TLS relocation value is the TLS modid. */ 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)); /* * Make sure the segment is writable. */ if (((mpp->mr_prot & PROT_WRITE) == 0) && ((set_prot(lmp, mpp, 1) == 0) || (aplist_append(textrel, mpp, AL_CNT_TEXTREL) == NULL))) { ret = 0; break; } /* * Call relocation routine to perform required relocation. */ switch (rtype) { case R_AMD64_COPY: if (elf_copy_reloc(name, symref, lmp, (void *)roffset, symdef, _lmp, (const void *)value) == 0) ret = 0; break; case R_AMD64_JUMP_SLOT: if (((LIST(lmp)->lm_tflags | AFLAGS(lmp)) & (LML_TFLG_AUD_PLTENTER | LML_TFLG_AUD_PLTEXIT)) && AUDINFO(lmp)->ai_dynplts) { int fail = 0; int pltndx = (((ulong_t)rel - (uintptr_t)JMPREL(lmp)) / relsiz); int symndx = (((uintptr_t)symdef - (uintptr_t)SYMTAB(_lmp)) / SYMENT(_lmp)); (void) elf_plt_trace_write(roffset, lmp, _lmp, symdef, symndx, pltndx, (caddr_t)value, sb_flags, &fail); if (fail) ret = 0; } else { /* * Write standard PLT entry to jump directly * to newly bound function. */ DBG_CALL(Dbg_reloc_apply_val(LIST(lmp), ELF_DBG_RTLD, (Xword)roffset, (Xword)value)); *(ulong_t *)roffset = value; } break; default: value += reladd; /* * Write the relocation out. */ 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 ((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), (Xword)(-1), PLT_T_FULL, _lmp, (Addr)value, symdef->st_value, name, binfo)); } } return (relocate_finish(lmp, bound, ret)); } /* * Initialize the first few got entries so that function calls go to * elf_rtbndr: * * GOT[GOT_XLINKMAP] = the address of the link map * GOT[GOT_XRTLD] = the address of rtbinder */ void elf_plt_init(void *got, caddr_t l) { uint64_t *_got; /* LINTED */ Rt_map *lmp = (Rt_map *)l; _got = (uint64_t *)got + M_GOT_XLINKMAP; *_got = (uint64_t)lmp; _got = (uint64_t *)got + M_GOT_XRTLD; *_got = (uint64_t)elf_rtbndr; } /* * Plt writing interface to allow debugging initialization to be generic. */ Pltbindtype /* ARGSUSED1 */ elf_plt_write(uintptr_t addr, uintptr_t vaddr, void *rptr, uintptr_t symval, Xword pltndx) { Rela *rel = (Rela*)rptr; uintptr_t pltaddr; pltaddr = addr + rel->r_offset; *(ulong_t *)pltaddr = (ulong_t)symval + rel->r_addend; DBG_CALL(pltcntfull++); return (PLT_T_FULL); } /* * Provide a machine specific interface to the conversion routine. By calling * the machine specific version, rather than the generic version, we insure that * the data tables/strings for all known machine versions aren't dragged into * ld.so.1. */ const char * _conv_reloc_type(uint_t rel) { static Conv_inv_buf_t inv_buf; return (conv_reloc_amd64_type(rel, 0, &inv_buf)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1988 AT&T * All Rights Reserved * * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Bootstrap routine for run-time linker. * We get control from exec which has loaded our text and * data into the process' address space and created the process * stack. * * On entry, the process stack looks like this: * * # # <- %rsp * #_______________________# high addresses * # strings # * #_______________________# * # 0 word # * #_______________________# * # Auxiliary # * # entries # * # ... # * # (size varies) # * #_______________________# * # 0 word # * #_______________________# * # Environment # * # pointers # * # ... # * # (one word each) # * #_______________________# * # 0 word # * #_______________________# * # Argument # low addresses * # pointers # * # Argc words # * #_______________________# * # argc # * #_______________________# <- %rbp * * * We must calculate the address at which ld.so was loaded, * find the addr of the dynamic section of ld.so, of argv[0], and of * the process' environment pointers - and pass the thing to _setup * to handle. We then call _rtld - on return we jump to the entry * point for the executable. */ #if defined(lint) extern unsigned long _setup(); extern void atexit_fini(); void main() { (void) _setup(); atexit_fini(); } #else #include .file "boot.s" .text .globl _rt_boot .globl _setup .globl _GLOBAL_OFFSET_TABLE_ .type _rt_boot,@function .align 4 _rt_alias: / in case we were invoked from libc.so jmp .get_got _rt_boot: / save for referencing args movq %rsp,%rbp / make room for a max sized boot vector subq $EB_MAX_SIZE64,%rsp / use esi as a pointer to &eb[0] movq %rsp,%rsi / set up tag for argv movq $EB_ARGV,0(%rsi) / get address of argv leaq 8(%rbp),%rax / put after tag movq %rax,8(%rsi) / set up tag for envp movq $EB_ENVP,16(%rsi) / get # of args movq (%rbp),%rax / one for the zero & one for argc addq $2,%rax / now points past args & @ envp leaq (%rbp,%rax,8),%rdi / set envp movq %rdi,24(%rsi) / next .L0: addq $8,%rdi / search for 0 at end of env cmpq $0,-8(%rdi) jne .L0 / set up tag for auxv movq $EB_AUXV,32(%rsi) / point to auxv movq %rdi,40(%rsi) / set up NULL tag movq $EB_NULL,48(%rsi) / arg1 - address of &eb[0] movq %rsi, %rdi .get_got: leaq _GLOBAL_OFFSET_TABLE_(%rip), %rbx / addq $_GLOBAL_OFFSET_TABLE_, %rbx movq %rbx,%r9 // addq $[.L2-.L1], %rbx movq %rbx,%r10 movq (%rbx),%rsi / _setup(&eb[0], _DYNAMIC) call _setup@PLT / release stack frame movq %rbp,%rsp movq atexit_fini@GOTPCREL(%rip), %rdx / transfer control to the executable jmp *%rax .size _rt_boot,.-_rt_boot #endif /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2018 Joyent, Inc. All rights reserved. */ /* * Welcome to the magic behind the PLT (procedure linkage table). When rtld * fills out the PLT entries, it will refer initially to the functions in this * file. As such our goal is simple: * * The lie of the function call must be preserved at all costs. * * This means that we need to prepare the system for an arbitrary series of * instructions to be called. For example, as a side effect of resolving a * symbol we may need to open a shared object which will cause any _init * functions to be called. Those functions can use any and all of the ABI state * that they desire (for example, the FPU registers). Therefore we must save and * restore all the ABI mandated registers here. * * For the full information about what we need to save and restore and why, * please see the System V amd64 PS ABI '3.2.3 Parameter Passing'. For general * purpose registers, we need to take care of the following: * * %rax - Used for information about the number of vector arguments * %rdi - arg0 * %rsi - arg1 * %rdx - arg2 * %rcx - arg3 * %r8 - arg4 * %r9 - arg5 * %r10 - static chain pointer * * Unfortunately, the world of the FPU is more complicated. * * The ABI mandates that we must save %xmm0-%xmm7. On newer Intel processors, * %xmm0-%xmm7 shadow %ymm0-%ymm7 and %zmm0-%zmm7. Historically, when saving the * FPU, we only saved and restored these eight registers. Unfortunately, this * process itself ended up having side effects. Because the registers shadow one * another, if we saved a full %zmm register when only a %xmm register was * valid, we would end up causing the processor to think that the full %zmm * register was valid. Once it believed that this was the case, it would then * degrade performance of code that only used the %xmm registers. * * One way to tackle this problem would have been to use xgetbv with ecx=1 to * get information about what was actually in use and only save and restore * that. You can imagine that this logic roughly ends up as something like: * * if (zmm_inuse) * save_zmm() * if (ymm_inuse) * save_ymm() * save_xmm() * * However, this logic leaves us at the mercy of the branch predictor. This * means that all of our efforts can end up still causing the CPU to execute * things to make it think that some of these other FPU registers are in use and * thus defeat the optimizations that it has. * * To deal with this problem, Intel has suggested using the xsave family of * instructions. The kernel provides information about the size required for the * floating point registers as well as which of several methods we need to * employ through the aux vector. This gets us out of trying to look at the * hardware capabilities and make decisions every time. As part of the * amd64-specific portion of rtld, it will process those values and determine * the functions on an as-needed basis. * * There are two different functions that we export. The first is elf_rtbndr(). * This is basically the glue that gets us into the PLT and to perform * relocations. elf_rtbndr() determines the address of the function that we must * call and arranges its stack such that when we return from elf_rtbndr() we * will instead jump to the actual relocated function which will return to the * original caller. Because of this, we must preserve all of the registers that * are used for arguments and restore them before returning. * * The second function we export is elf_plt_trace(). This is used to add support * for audit libraries among other things. elf_plt_trace() may or may not call * the underlying function as a side effect or merely set up its return to it. * This changes how we handle %rax. If we call the function ourself, then we end * up making sure that %rax is the return value versus the initial value. In * addition, because we get %r11 from the surrounding PLT code, we opt to * preserve it in case some of the relocation logic ever ends up calling back * into us again. */ #if defined(lint) #include #include <_rtld.h> #include <_audit.h> #include <_elf.h> #include #include #else #include #include <_audit.h> #include #include #include /* * This macro is used to zero the xsave header. The contents of scratchreg will * be destroyed. locreg should contain the starting address of the xsave header. */ #define XSAVE_HEADER_ZERO(scratch, loc) \ xorq scratch, scratch; \ movq scratch, 0x200(loc); \ movq scratch, 0x208(loc); \ movq scratch, 0x210(loc); \ movq scratch, 0x218(loc); \ movq scratch, 0x220(loc); \ movq scratch, 0x228(loc); \ movq scratch, 0x230(loc); \ movq scratch, 0x238(loc) .file "boot_elf.s" .text /* * This section of the code contains glue functions that are used to take care * of saving and restoring the FPU. We deal with this in a few different ways * based on the hardware support and what exists. Historically we've only saved * and restored the first 8 floating point registers rather than the entire FPU. * That implementation still exists here and is kept around mostly as an * insurance policy. */ ENTRY(_elf_rtbndr_fp_save_orig) movq org_scapset@GOTPCREL(%rip),%r11 movq (%r11),%r11 /* Syscapset_t pointer */ movl 8(%r11),%edx /* sc_hw_2 */ testl $AV_386_2_AVX512F,%edx jne .save_zmm movl (%r11),%edx /* sc_hw_1 */ testl $AV_386_AVX,%edx jne .save_ymm movdqa %xmm0, (%rdi) movdqa %xmm1, 64(%rdi) movdqa %xmm2, 128(%rdi) movdqa %xmm3, 192(%rdi) movdqa %xmm4, 256(%rdi) movdqa %xmm5, 320(%rdi) movdqa %xmm6, 384(%rdi) movdqa %xmm7, 448(%rdi) jmp .save_finish .save_ymm: vmovdqa %ymm0, (%rdi) vmovdqa %ymm1, 64(%rdi) vmovdqa %ymm2, 128(%rdi) vmovdqa %ymm3, 192(%rdi) vmovdqa %ymm4, 256(%rdi) vmovdqa %ymm5, 320(%rdi) vmovdqa %ymm6, 384(%rdi) vmovdqa %ymm7, 448(%rdi) jmp .save_finish .save_zmm: vmovdqa64 %zmm0, (%rdi) vmovdqa64 %zmm1, 64(%rdi) vmovdqa64 %zmm2, 128(%rdi) vmovdqa64 %zmm3, 192(%rdi) vmovdqa64 %zmm4, 256(%rdi) vmovdqa64 %zmm5, 320(%rdi) vmovdqa64 %zmm6, 384(%rdi) vmovdqa64 %zmm7, 448(%rdi) .save_finish: ret SET_SIZE(_elf_rtbndr_fp_save_orig) ENTRY(_elf_rtbndr_fp_restore_orig) movq org_scapset@GOTPCREL(%rip),%r11 movq (%r11),%r11 /* Syscapset_t pointer */ movl 8(%r11),%edx /* sc_hw_2 */ testl $AV_386_2_AVX512F,%edx jne .restore_zmm movl (%r11),%edx /* sc_hw_1 */ testl $AV_386_AVX,%edx jne .restore_ymm movdqa (%rdi), %xmm0 movdqa 64(%rdi), %xmm1 movdqa 128(%rdi), %xmm2 movdqa 192(%rdi), %xmm3 movdqa 256(%rdi), %xmm4 movdqa 320(%rdi), %xmm5 movdqa 384(%rdi), %xmm6 movdqa 448(%rdi), %xmm7 jmp .restore_finish .restore_ymm: vmovdqa (%rdi), %ymm0 vmovdqa 64(%rdi), %ymm1 vmovdqa 128(%rdi), %ymm2 vmovdqa 192(%rdi), %ymm3 vmovdqa 256(%rdi), %ymm4 vmovdqa 320(%rdi), %ymm5 vmovdqa 384(%rdi), %ymm6 vmovdqa 448(%rdi), %ymm7 jmp .restore_finish .restore_zmm: vmovdqa64 (%rdi), %zmm0 vmovdqa64 64(%rdi), %zmm1 vmovdqa64 128(%rdi), %zmm2 vmovdqa64 192(%rdi), %zmm3 vmovdqa64 256(%rdi), %zmm4 vmovdqa64 320(%rdi), %zmm5 vmovdqa64 384(%rdi), %zmm6 vmovdqa64 448(%rdi), %zmm7 .restore_finish: ret SET_SIZE(_elf_rtbndr_fp_restore_orig) ENTRY(_elf_rtbndr_fp_fxsave) fxsaveq (%rdi) ret SET_SIZE(_elf_rtbndr_fp_fxsave) ENTRY(_elf_rtbndr_fp_fxrestore) fxrstor (%rdi) ret SET_SIZE(_elf_rtbndr_fp_fxrestore) ENTRY(_elf_rtbndr_fp_xsave) XSAVE_HEADER_ZERO(%rdx, %rdi) movq $_CONST(XFEATURE_FP_ALL), %rdx movl %edx, %eax shrq $32, %rdx xsave (%rdi) /* save data */ ret SET_SIZE(_elf_rtbndr_fp_xsave) ENTRY(_elf_rtbndr_fp_xrestore) movq $_CONST(XFEATURE_FP_ALL), %rdx movl %edx, %eax shrq $32, %rdx xrstor (%rdi) /* save data */ ret SET_SIZE(_elf_rtbndr_fp_xrestore) #endif #if defined(lint) /* ARGSUSED0 */ int elf_plt_trace() { return (0); } #else /* * On entry the 'glue code' has already done the following: * * pushq %rbp * movq %rsp, %rbp * subq $0x10, %rsp * leaq trace_fields(%rip), %r11 * movq %r11, -0x8(%rbp) * movq $elf_plt_trace, %r11 * jmp *%r11 * * so - -8(%rbp) contains the dyndata ptr * * 0x0 Addr *reflmp * 0x8 Addr *deflmp * 0x10 Word symndx * 0x14 Word sb_flags * 0x18 Sym symdef.st_name * 0x1c symdef.st_info * 0x1d symdef.st_other * 0x1e symdef.st_shndx * 0x20 symdef.st_value * 0x28 symdef.st_size * * Also note - on entry 16 bytes have already been subtracted * from the %rsp. The first 8 bytes is for the dyn_data_ptr, * the second 8 bytes are to align the stack and are available * for use. */ #define REFLMP_OFF 0x0 #define DEFLMP_OFF 0x8 #define SYMNDX_OFF 0x10 #define SBFLAGS_OFF 0x14 #define SYMDEF_OFF 0x18 #define SYMDEF_VALUE_OFF 0x20 /* * Next, we need to create a bunch of local storage. First, we have to preserve * the standard registers per the amd64 ABI. This means we need to deal with: * %rax - Used for information about the number of vector arguments * %rdi - arg0 * %rsi - arg1 * %rdx - arg2 * %rcx - arg3 * %r8 - arg4 * %r9 - arg5 * %r10 - static chain pointer * %r11 - PLT Interwork register, our caller is using this, so it's not * a temporary for us. * * In addition, we need to save the amd64 ABI floating point arguments. Finally, * we need to deal with our local storage. We need a La_amd64_regs and a * uint64_t for the previous stack size. * * To deal with this and the potentially variable size of the FPU regs, we have * to play a few different games. We refer to all of the standard registers, the * previous stack size, and La_amd64_regs structure off of %rbp. These are all * values that are below %rbp. */ #define SPDYNOFF -8 #define SPDESTOFF -16 #define SPPRVSTKOFF -24 #define SPLAREGOFF -88 #define ORIG_RDI -96 #define ORIG_RSI -104 #define ORIG_RDX -112 #define ORIG_RCX -120 #define ORIG_R8 -128 #define ORIG_R9 -136 #define ORIG_R10 -144 #define ORIG_R11 -152 #define ORIG_RAX -160 #define PLT_SAVE_OFF 168 ENTRY(elf_plt_trace) /* * Save our static registers. After that 64-byte align us and subtract * the appropriate amount for the FPU. The frame pointer has already * been pushed for us by the glue code. */ movq %rdi, ORIG_RDI(%rbp) movq %rsi, ORIG_RSI(%rbp) movq %rdx, ORIG_RDX(%rbp) movq %rcx, ORIG_RCX(%rbp) movq %r8, ORIG_R8(%rbp) movq %r9, ORIG_R9(%rbp) movq %r10, ORIG_R10(%rbp) movq %r11, ORIG_R11(%rbp) movq %rax, ORIG_RAX(%rbp) subq $PLT_SAVE_OFF, %rsp movq _plt_save_size@GOTPCREL(%rip),%r9 movq _plt_fp_save@GOTPCREL(%rip),%r10 subq (%r9), %rsp andq $-64, %rsp movq %rsp, %rdi call *(%r10) /* * Now that we've saved all of our registers, figure out what we need to * do next. */ movq SPDYNOFF(%rbp), %rax / %rax = dyndata testb $LA_SYMB_NOPLTENTER, SBFLAGS_OFF(%rax) / je .start_pltenter movq SYMDEF_VALUE_OFF(%rax), %rdi movq %rdi, SPDESTOFF(%rbp) / save destination address jmp .end_pltenter .start_pltenter: /* * save all registers into La_amd64_regs */ leaq SPLAREGOFF(%rbp), %rsi / %rsi = &La_amd64_regs leaq 8(%rbp), %rdi movq %rdi, 0(%rsi) / la_rsp movq 0(%rbp), %rdi movq %rdi, 8(%rsi) / la_rbp movq ORIG_RDI(%rbp), %rdi movq %rdi, 16(%rsi) / la_rdi movq ORIG_RSI(%rbp), %rdi movq %rdi, 24(%rsi) / la_rsi movq ORIG_RDX(%rbp), %rdi movq %rdi, 32(%rsi) / la_rdx movq ORIG_RCX(%rbp), %rdi movq %rdi, 40(%rsi) / la_rcx movq ORIG_R8(%rbp), %rdi movq %rdi, 48(%rsi) / la_r8 movq ORIG_R9(%rbp), %rdi movq %rdi, 56(%rsi) / la_r9 /* * prepare for call to la_pltenter */ movq SPDYNOFF(%rbp), %r11 / %r11 = &dyndata leaq SBFLAGS_OFF(%r11), %r9 / arg6 (&sb_flags) leaq SPLAREGOFF(%rbp), %r8 / arg5 (&La_amd64_regs) movl SYMNDX_OFF(%r11), %ecx / arg4 (symndx) leaq SYMDEF_OFF(%r11), %rdx / arg3 (&Sym) movq DEFLMP_OFF(%r11), %rsi / arg2 (dlmp) movq REFLMP_OFF(%r11), %rdi / arg1 (rlmp) call audit_pltenter@PLT movq %rax, SPDESTOFF(%rbp) / save calling address .end_pltenter: /* * If *no* la_pltexit() routines exist * we do not need to keep the stack frame * before we call the actual routine. Instead we * jump to it and remove our stack from the stack * at the same time. */ movl audit_flags(%rip), %eax andl $AF_PLTEXIT, %eax / value of audit.h:AF_PLTEXIT cmpl $0, %eax je .bypass_pltexit /* * Has the *nopltexit* flag been set for this entry point */ movq SPDYNOFF(%rbp), %r11 / %r11 = &dyndata testb $LA_SYMB_NOPLTEXIT, SBFLAGS_OFF(%r11) je .start_pltexit .bypass_pltexit: /* * No PLTEXIT processing required. */ movq 0(%rbp), %r11 movq %r11, -8(%rbp) / move prev %rbp movq SPDESTOFF(%rbp), %r11 / r11 == calling destination movq %r11, 0(%rbp) / store destination at top /* Restore FPU */ movq _plt_fp_restore@GOTPCREL(%rip),%r10 movq %rsp, %rdi call *(%r10) movq ORIG_RDI(%rbp), %rdi movq ORIG_RSI(%rbp), %rsi movq ORIG_RDX(%rbp), %rdx movq ORIG_RCX(%rbp), %rcx movq ORIG_R8(%rbp), %r8 movq ORIG_R9(%rbp), %r9 movq ORIG_R10(%rbp), %r10 movq ORIG_R11(%rbp), %r11 movq ORIG_RAX(%rbp), %rax subq $8, %rbp / adjust %rbp for 'ret' movq %rbp, %rsp / /* * At this point, after a little doctoring, we should * have the following on the stack: * * 16(%rsp): ret addr * 8(%rsp): dest_addr * 0(%rsp): Previous %rbp * * So - we pop the previous %rbp, and then * ret to our final destination. */ popq %rbp / ret / jmp to final destination / and clean up stack :) .start_pltexit: /* * In order to call the destination procedure and then return * to audit_pltexit() for post analysis we must first grow * our stack frame and then duplicate the original callers * stack state. This duplicates all of the arguements * that were to be passed to the destination procedure. */ movq %rbp, %rdi / addq $16, %rdi / %rdi = src movq (%rbp), %rdx / subq %rdi, %rdx / %rdx == prev frame sz /* * If audit_argcnt > 0 then we limit the number of * arguements that will be duplicated to audit_argcnt. * * If (prev_stack_size > (audit_argcnt * 8)) * prev_stack_size = audit_argcnt * 8; */ movl audit_argcnt(%rip),%eax / %eax = audit_argcnt cmpl $0, %eax jle .grow_stack leaq (,%rax,8), %rax / %eax = %eax * 4 cmpq %rax,%rdx jle .grow_stack movq %rax, %rdx /* * Grow the stack and duplicate the arguements of the * original caller. */ .grow_stack: movq %rsp, %r11 subq %rdx, %rsp / grow the stack movq %rdx, SPPRVSTKOFF(%rbp) / -88(%rbp) == prev frame sz movq %rsp, %rcx / %rcx = dest addq %rcx, %rdx / %rdx == tail of dest .while_base: cmpq %rdx, %rcx / while (base+size >= src++) { jge .end_while / movq (%rdi), %rsi movq %rsi,(%rcx) / *dest = *src addq $8, %rdi / src++ addq $8, %rcx / dest++ jmp .while_base / } /* * The above stack is now an exact duplicate of * the stack of the original calling procedure. */ .end_while: / / Restore registers using %r11 which contains our old %rsp value / before growing the stack. / movq _plt_fp_restore@GOTPCREL(%rip),%r10 movq %r11, %rdi call *(%r10) .trace_r2_finish: movq ORIG_RDI(%rbp), %rdi movq ORIG_RSI(%rbp), %rsi movq ORIG_RDX(%rbp), %rdx movq ORIG_RCX(%rbp), %rcx movq ORIG_R8(%rbp), %r8 movq ORIG_R9(%rbp), %r9 movq ORIG_R10(%rbp), %r10 movq ORIG_RAX(%rbp), %rax movq ORIG_R11(%rbp), %r11 /* * Call to desitnation function - we'll return here * for pltexit monitoring. */ call *SPDESTOFF(%rbp) addq SPPRVSTKOFF(%rbp), %rsp / cleanup dupped stack / / prepare for call to audit_pltenter() / movq SPDYNOFF(%rbp), %r11 / %r11 = &dyndata movq SYMNDX_OFF(%r11), %r8 / arg5 (symndx) leaq SYMDEF_OFF(%r11), %rcx / arg4 (&Sym) movq DEFLMP_OFF(%r11), %rdx / arg3 (dlmp) movq REFLMP_OFF(%r11), %rsi / arg2 (rlmp) movq %rax, %rdi / arg1 (returnval) call audit_pltexit@PLT /* * Clean up after ourselves and return to the * original calling procedure. Make sure to restore * registers. */ movq _plt_fp_restore@GOTPCREL(%rip),%r10 movq %rsp, %rdi movq %rax, SPPRVSTKOFF(%rbp) call *(%r10) movq ORIG_RDI(%rbp), %rdi movq ORIG_RSI(%rbp), %rsi movq ORIG_RDX(%rbp), %rdx movq ORIG_RCX(%rbp), %rcx movq ORIG_R8(%rbp), %r8 movq ORIG_R9(%rbp), %r9 movq ORIG_R10(%rbp), %r10 movq ORIG_R11(%rbp), %r11 movq SPPRVSTKOFF(%rbp), %rax movq %rbp, %rsp / popq %rbp / ret / return to caller SET_SIZE(elf_plt_trace) #endif /* * We got here because a call to a function resolved to a procedure * linkage table entry. That entry did a JMPL to the first PLT entry, which * in turn did a call to elf_rtbndr. * * the code sequence that got us here was: * * .PLT0: * pushq GOT+8(%rip) #GOT[1] * jmp *GOT+16(%rip) #GOT[2] * nop * nop * nop * nop * ... * PLT entry for foo: * jmp *name1@GOTPCREL(%rip) * pushl $rel.plt.foo * jmp PLT0 * * At entry, the stack looks like this: * * return address 16(%rsp) * $rel.plt.foo (plt index) 8(%rsp) * lmp 0(%rsp) * */ #if defined(lint) extern unsigned long elf_bndr(Rt_map *, unsigned long, caddr_t); void elf_rtbndr(Rt_map * lmp, unsigned long reloc, caddr_t pc) { (void) elf_bndr(lmp, reloc, pc); } #else /* * The PLT code that landed us here placed 2 arguments on the stack as * arguments to elf_rtbndr. * Additionally the pc of caller is below these 2 args. * Our stack will look like this after we establish a stack frame with * push %rbp; movq %rsp, %rbp sequence: * * 8(%rbp) arg1 - *lmp * 16(%rbp), %rsi arg2 - reloc index * 24(%rbp), %rdx arg3 - pc of caller */ #define LBPLMPOFF 8 /* arg1 - *lmp */ #define LBPRELOCOFF 16 /* arg2 - reloc index */ #define LBRPCOFF 24 /* arg3 - pc of caller */ /* * With the above in place, we must now proceed to preserve all temporary * registers that are also used for passing arguments. Specifically this * means: * * %rax - Used for information about the number of vector arguments * %rdi - arg0 * %rsi - arg1 * %rdx - arg2 * %rcx - arg3 * %r8 - arg4 * %r9 - arg5 * %r10 - static chain pointer * * While we don't have to preserve %r11, we do have to preserve the FPU * registers. The FPU logic is delegated to a specific function that we'll call. * However, it requires that its stack is 64-byte aligned. We defer the * alignment to that point. This will also take care of the fact that a caller * may not call us with a correctly aligned stack pointer per the amd64 ABI. */ .extern _plt_save_size .extern _plt_fp_save .extern plt_fp_restore .weak _elf_rtbndr _elf_rtbndr = elf_rtbndr ENTRY(elf_rtbndr) pushq %rbp /* Establish stack frame */ movq %rsp, %rbp /* * Save basic regs. */ pushq %rax pushq %rdi pushq %rsi pushq %rdx pushq %rcx pushq %r8 pushq %r9 pushq %r10 pushq %r12 /* * Save the amount of space we need for the FPU registers and call that * function. Save %rsp before we manipulate it to make restore easier. */ movq %rsp, %r12 movq _plt_save_size@GOTPCREL(%rip),%r9 movq _plt_fp_save@GOTPCREL(%rip),%r10 subq (%r9), %rsp andq $-64, %rsp movq %rsp, %rdi call *(%r10) /* * Perform actual PLT logic. Note that the plt related arguments are * located at an offset relative to %rbp. */ movq LBPLMPOFF(%rbp), %rdi /* arg1 - *lmp */ movq LBPRELOCOFF(%rbp), %rsi /* arg2 - reloc index */ movq LBRPCOFF(%rbp), %rdx /* arg3 - pc of caller */ call elf_bndr@PLT /* call elf_rtbndr(lmp, relndx, pc) */ movq %rax, LBPRELOCOFF(%rbp) /* store final destination */ /* Restore FPU */ movq _plt_fp_restore@GOTPCREL(%rip),%r10 movq %rsp, %rdi call *(%r10) movq %r12, %rsp popq %r12 popq %r10 popq %r9 popq %r8 popq %rcx popq %rdx popq %rsi popq %rdi popq %rax movq %rbp, %rsp /* Restore our stack frame */ popq %rbp addq $8, %rsp /* pop 1st plt-pushed args */ /* the second arguement is used */ /* for the 'return' address to our */ /* final destination */ ret /* invoke resolved function */ SET_SIZE(elf_rtbndr) #endif /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Return the pc of the calling routine. */ #if defined(lint) #include caddr_t caller() { return (0); } #else #include .file "caller.s" ENTRY(caller) movq 8(%rbp),%rax ret SET_SIZE(caller) #endif /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include "_rtld.h" #include "_elf.h" #include "_inline_gen.h" #include "msg.h" static Dl_amd64_unwindinfo * getunwind_core(Lm_list *lml, void *pc, Dl_amd64_unwindinfo *unwindinfo) { Rt_map *lmp; /* * Validate the version information. */ if (unwindinfo == NULL) { eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLVAL)); return (0); } if ((unwindinfo->dlui_version < DLUI_VERS_1) || (unwindinfo->dlui_version > DLUI_VERS_CURRENT)) { eprintf(lml, ERR_FATAL, MSG_INTL(MSG_UNW_BADVERS), unwindinfo->dlui_version, DLUI_VERS_CURRENT); return (0); } /* * Clean out the structure. */ unwindinfo->dlui_flags = 0; unwindinfo->dlui_objname = 0; unwindinfo->dlui_unwindstart = 0; unwindinfo->dlui_unwindend = 0; unwindinfo->dlui_segstart = 0; unwindinfo->dlui_segend = 0; /* * Identify the link-map associated with the exception "pc". Note, * the "pc" might not correspond to a link-map (as can happen with a * "pc" fabricated by a debugger such as dbx). In this case, the * unwind data buffer will be filled with flags set to indicate an * unknown caller. */ lmp = _caller(pc, CL_NONE); if (lmp) { mmapobj_result_t *mpp; /* * Determine the associated segment. */ if ((mpp = find_segment(pc, lmp)) == NULL) return (0); unwindinfo->dlui_objname = (char *)PATHNAME(lmp); unwindinfo->dlui_segstart = mpp->mr_addr; unwindinfo->dlui_segend = mpp->mr_addr + mpp->mr_msize; if (PTUNWIND(lmp) && (mpp->mr_addr)) { uintptr_t base; if (FLAGS(lmp) & FLG_RT_FIXED) base = 0; else base = ADDR(lmp); unwindinfo->dlui_unwindstart = (void *)(PTUNWIND(lmp)->p_vaddr + base); unwindinfo->dlui_unwindend = (void *)(PTUNWIND(lmp)->p_vaddr + PTUNWIND(lmp)->p_memsz + base); } else if (mpp->mr_addr) unwindinfo->dlui_flags |= DLUI_FLG_NOUNWIND; else unwindinfo->dlui_flags |= DLUI_FLG_NOUNWIND | DLUI_FLG_NOOBJ; } else { /* * No object found. */ unwindinfo->dlui_flags = DLUI_FLG_NOOBJ | DLUI_FLG_NOUNWIND; } return (unwindinfo); } #pragma weak _dlamd64getunwind = dlamd64getunwind Dl_amd64_unwindinfo * dlamd64getunwind(void *pc, Dl_amd64_unwindinfo *unwindinfo) { Rt_map *lmp; Lm_list *lml; int entry = enter(0); lmp = _caller(caller(), CL_EXECDEF); lml = LIST(lmp); unwindinfo = getunwind_core(lml, pc, unwindinfo); if (entry) leave(lml, 0); return (unwindinfo); }