Index: projects/clang350-import/Makefile.inc1 =================================================================== --- projects/clang350-import/Makefile.inc1 (revision 275386) +++ projects/clang350-import/Makefile.inc1 (revision 275387) @@ -1,2109 +1,2121 @@ # # $FreeBSD$ # # Make command line options: # -DNO_CLEANDIR run ${MAKE} clean, instead of ${MAKE} cleandir # -DNO_CLEAN do not clean at all # -DDB_FROM_SRC use the user/group databases in src/etc instead of # the system database when installing. # -DNO_SHARE do not go into share subdir # -DKERNFAST define NO_KERNEL{CONFIG,CLEAN,DEPEND,OBJ} # -DNO_KERNELCONFIG do not run config in ${MAKE} buildkernel # -DNO_KERNELCLEAN do not run ${MAKE} clean in ${MAKE} buildkernel # -DNO_KERNELDEPEND do not run ${MAKE} depend in ${MAKE} buildkernel # -DNO_KERNELOBJ do not run ${MAKE} obj in ${MAKE} buildkernel # -DNO_PORTSUPDATE do not update ports in ${MAKE} update # -DNO_ROOT install without using root privilege # -DNO_DOCUPDATE do not update doc in ${MAKE} update # -DWITHOUT_CTF do not run the DTrace CTF conversion tools on built objects # LOCAL_DIRS="list of dirs" to add additional dirs to the SUBDIR list # LOCAL_ITOOLS="list of tools" to add additional tools to the ITOOLS list # LOCAL_LIB_DIRS="list of dirs" to add additional dirs to libraries target # LOCAL_MTREE="list of mtree files" to process to allow local directories # to be created before files are installed # LOCAL_TOOL_DIRS="list of dirs" to add additional dirs to the build-tools # list # METALOG="path to metadata log" to write permission and ownership # when NO_ROOT is set. (default: ${DESTDIR}/METALOG) # TARGET="machine" to crossbuild world for a different machine type # TARGET_ARCH= may be required when a TARGET supports multiple endians # BUILDENV_SHELL= shell to launch for the buildenv target (def:/bin/sh) # WORLD_FLAGS= additional flags to pass to make(1) during buildworld # KERNEL_FLAGS= additional flags to pass to make(1) during buildkernel # # The intended user-driven targets are: # buildworld - rebuild *everything*, including glue to help do upgrades # installworld- install everything built by "buildworld" # doxygen - build API documentation of the kernel # update - convenient way to update your source tree (eg: svn/svnup) # # Standard targets (not defined here) are documented in the makefiles in # /usr/share/mk. These include: # obj depend all install clean cleandepend cleanobj .if !defined(TARGET) || !defined(TARGET_ARCH) .error "Both TARGET and TARGET_ARCH must be defined." .endif .include "share/mk/src.opts.mk" .include .include # We must do share/info early so that installation of info `dir' # entries works correctly. Do it first since it is less likely to # grow dependencies on include and lib than vice versa. # # We must do lib/ and libexec/ before bin/, because if installworld # installs a new /bin/sh, the 'make' command will *immediately* # use that new version. And the new (dynamically-linked) /bin/sh # will expect to find appropriate libraries in /lib and /libexec. # SRCDIR?= ${.CURDIR} .if defined(SUBDIR_OVERRIDE) SUBDIR= ${SUBDIR_OVERRIDE} .else SUBDIR= share/info lib libexec SUBDIR+=bin .if ${MK_GAMES} != "no" SUBDIR+=games .endif .if ${MK_CDDL} != "no" SUBDIR+=cddl .endif SUBDIR+=gnu include .if ${MK_KERBEROS} != "no" SUBDIR+=kerberos5 .endif .if ${MK_RESCUE} != "no" SUBDIR+=rescue .endif SUBDIR+=sbin .if ${MK_CRYPT} != "no" SUBDIR+=secure .endif .if !defined(NO_SHARE) SUBDIR+=share .endif SUBDIR+=sys usr.bin usr.sbin .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .if ${MK_OFED} != "no" SUBDIR+=contrib/ofed .endif # # We must do etc/ last for install/distribute to work. # SUBDIR+=etc # Local directories are last, since it is nice to at least get the base # system rebuilt before you do them. .for _DIR in ${LOCAL_DIRS} .if exists(${.CURDIR}/${_DIR}/Makefile) SUBDIR+= ${_DIR} .endif .endfor # Add LOCAL_LIB_DIRS, but only if they will not be picked up as a SUBDIR # of a LOCAL_DIRS directory. This allows LOCAL_DIRS=foo and # LOCAL_LIB_DIRS=foo/lib to behave as expected. .for _DIR in ${LOCAL_DIRS:M*/} ${LOCAL_DIRS:N*/:S|$|/|} _REDUNDENT_LIB_DIRS+= ${LOCAL_LIB_DIRS:M${_DIR}*} .endfor .for _DIR in ${LOCAL_LIB_DIRS} .if empty(_REDUNDENT_LIB_DIRS:M${_DIR}) && exists(${.CURDIR}/${_DIR}/Makefile) SUBDIR+= ${_DIR} .endif .endfor .endif .if defined(NOCLEAN) NO_CLEAN= ${NOCLEAN} .endif .if defined(NO_CLEANDIR) CLEANDIR= clean cleandepend .else CLEANDIR= cleandir .endif LOCAL_TOOL_DIRS?= BUILDENV_SHELL?=/bin/sh SVN?= /usr/local/bin/svn SVNFLAGS?= -r HEAD MAKEOBJDIRPREFIX?= /usr/obj .if !defined(OSRELDATE) .if exists(/usr/include/osreldate.h) OSRELDATE!= awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ /usr/include/osreldate.h .else OSRELDATE= 0 .endif .endif .if !defined(VERSION) REVISION!= ${MAKE} -C ${SRCDIR}/release -V REVISION BRANCH!= ${MAKE} -C ${SRCDIR}/release -V BRANCH SRCRELDATE!= awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ ${SRCDIR}/sys/sys/param.h VERSION= FreeBSD ${REVISION}-${BRANCH:C/-p[0-9]+$//} ${TARGET_ARCH} ${SRCRELDATE} .endif KNOWN_ARCHES?= amd64 arm armeb/arm armv6/arm armv6hf/arm i386 i386/pc98 mips mipsel/mips mips64el/mips mips64/mips mipsn32el/mips mipsn32/mips powerpc powerpc64/powerpc sparc64 .if ${TARGET} == ${TARGET_ARCH} _t= ${TARGET} .else _t= ${TARGET_ARCH}/${TARGET} .endif .for _t in ${_t} .if empty(KNOWN_ARCHES:M${_t}) .error Unknown target ${TARGET_ARCH}:${TARGET}. .endif .endfor .if ${TARGET} == ${MACHINE} TARGET_CPUTYPE?=${CPUTYPE} .else TARGET_CPUTYPE?= .endif .if !empty(TARGET_CPUTYPE) _TARGET_CPUTYPE=${TARGET_CPUTYPE} .else _TARGET_CPUTYPE=dummy .endif _CPUTYPE!= MAKEFLAGS= CPUTYPE=${_TARGET_CPUTYPE} ${MAKE} \ -f /dev/null -m ${.CURDIR}/share/mk -V CPUTYPE .if ${_CPUTYPE} != ${_TARGET_CPUTYPE} .error CPUTYPE global should be set with ?=. .endif .if make(buildworld) BUILD_ARCH!= uname -p .if ${MACHINE_ARCH} != ${BUILD_ARCH} .error To cross-build, set TARGET_ARCH. .endif .endif .if ${MACHINE} == ${TARGET} && ${MACHINE_ARCH} == ${TARGET_ARCH} && !defined(CROSS_BUILD_TESTING) OBJTREE= ${MAKEOBJDIRPREFIX} .else OBJTREE= ${MAKEOBJDIRPREFIX}/${TARGET}.${TARGET_ARCH} .endif WORLDTMP= ${OBJTREE}${.CURDIR}/tmp # /usr/games added for fortune which depend on strfile BPATH= ${WORLDTMP}/legacy/usr/sbin:${WORLDTMP}/legacy/usr/bin:${WORLDTMP}/legacy/usr/games:${WORLDTMP}/legacy/bin XPATH= ${WORLDTMP}/usr/sbin:${WORLDTMP}/usr/bin:${WORLDTMP}/usr/games STRICTTMPPATH= ${BPATH}:${XPATH} TMPPATH= ${STRICTTMPPATH}:${PATH} # # Avoid running mktemp(1) unless actually needed. # It may not be functional, e.g., due to new ABI # when in the middle of installing over this system. # .if make(distributeworld) || make(installworld) INSTALLTMP!= /usr/bin/mktemp -d -u -t install .endif # # Building a world goes through the following stages # # 1. legacy stage [BMAKE] # This stage is responsible for creating compatibility # shims that are needed by the bootstrap-tools, # build-tools and cross-tools stages. # 1. bootstrap-tools stage [BMAKE] # This stage is responsible for creating programs that # are needed for backward compatibility reasons. They # are not built as cross-tools. # 2. build-tools stage [TMAKE] # This stage is responsible for creating the object # tree and building any tools that are needed during # the build process. # 3. cross-tools stage [XMAKE] # This stage is responsible for creating any tools that # are needed for cross-builds. A cross-compiler is one # of them. # 4. world stage [WMAKE] # This stage actually builds the world. # 5. install stage (optional) [IMAKE] # This stage installs a previously built world. # BOOTSTRAPPING?= 0 # Common environment for world related stages CROSSENV= MAKEOBJDIRPREFIX=${OBJTREE} \ MACHINE_ARCH=${TARGET_ARCH} \ MACHINE=${TARGET} \ CPUTYPE=${TARGET_CPUTYPE} .if ${MK_GROFF} != "no" CROSSENV+= GROFF_BIN_PATH=${WORLDTMP}/legacy/usr/bin \ GROFF_FONT_PATH=${WORLDTMP}/legacy/usr/share/groff_font \ GROFF_TMAC_PATH=${WORLDTMP}/legacy/usr/share/tmac .endif .if defined(TARGET_CFLAGS) CROSSENV+= ${TARGET_CFLAGS} .endif # bootstrap-tools stage BMAKEENV= INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${BPATH}:${PATH} \ WORLDTMP=${WORLDTMP} \ VERSION="${VERSION}" \ MAKEFLAGS="-m ${.CURDIR}/tools/build/mk ${.MAKEFLAGS}" BMAKE= MAKEOBJDIRPREFIX=${WORLDTMP} \ ${BMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ DESTDIR= \ BOOTSTRAPPING=${OSRELDATE} \ SSP_CFLAGS= \ MK_HTML=no MK_INFO=no NO_LINT=yes MK_MAN=no \ -DNO_PIC MK_PROFILE=no -DNO_SHARED \ -DNO_CPU_CFLAGS MK_WARNS=no MK_CTF=no \ MK_CLANG_FULL=no MK_LLDB=no MK_TESTS=no # build-tools stage TMAKE= MAKEOBJDIRPREFIX=${OBJTREE} \ ${BMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ DESTDIR= \ BOOTSTRAPPING=${OSRELDATE} \ SSP_CFLAGS= \ -DNO_LINT \ -DNO_CPU_CFLAGS MK_WARNS=no MK_CTF=no MK_CLANG_FULL=no MK_LLDB=no MK_TESTS=no # cross-tools stage XMAKE= TOOLS_PREFIX=${WORLDTMP} ${BMAKE} \ TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ MK_GDB=no MK_TESTS=no # kernel-tools stage KTMAKEENV= INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${BPATH}:${PATH} \ WORLDTMP=${WORLDTMP} \ VERSION="${VERSION}" KTMAKE= TOOLS_PREFIX=${WORLDTMP} MAKEOBJDIRPREFIX=${WORLDTMP} \ ${KTMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ DESTDIR= \ BOOTSTRAPPING=${OSRELDATE} \ SSP_CFLAGS= \ MK_HTML=no MK_INFO=no -DNO_LINT MK_MAN=no \ -DNO_PIC MK_PROFILE=no -DNO_SHARED \ -DNO_CPU_CFLAGS MK_WARNS=no MK_CTF=no # world stage WMAKEENV= ${CROSSENV} \ _SHLIBDIRPREFIX=${WORLDTMP} \ _LDSCRIPTROOT= \ VERSION="${VERSION}" \ INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${TMPPATH} # make hierarchy HMAKE= PATH=${TMPPATH} ${MAKE} LOCAL_MTREE=${LOCAL_MTREE:Q} .if defined(NO_ROOT) HMAKE+= PATH=${TMPPATH} METALOG=${METALOG} -DNO_ROOT .endif .if ${MK_CDDL} == "no" WMAKEENV+= MK_CTF=no .endif .if defined(CROSS_TOOLCHAIN) LOCALBASE?= /usr/local .include "${LOCALBASE}/share/toolchains/${CROSS_TOOLCHAIN}.mk" .endif .if defined(CROSS_TOOLCHAIN_PREFIX) CROSS_COMPILER_PREFIX?=${CROSS_TOOLCHAIN_PREFIX} CROSS_BINUTILS_PREFIX?=${CROSS_TOOLCHAIN_PREFIX} .endif XCOMPILERS= CC CXX CPP .for COMPILER in ${XCOMPILERS} .if defined(CROSS_COMPILER_PREFIX) X${COMPILER}?= ${CROSS_COMPILER_PREFIX}${${COMPILER}} .else X${COMPILER}?= ${${COMPILER}} .endif .endfor XBINUTILS= AS AR LD NM OBJCOPY OBJDUMP RANLIB SIZE STRINGS .for BINUTIL in ${XBINUTILS} .if defined(CROSS_BINUTILS_PREFIX) X${BINUTIL}?= ${CROSS_BINUTILS_PREFIX}${${BINUTIL}} .else X${BINUTIL}?= ${${BINUTIL}} .endif .endfor WMAKEENV+= CC="${XCC} ${XCFLAGS}" CXX="${XCXX} ${XCFLAGS} ${XCXXFLAGS}" \ DEPFLAGS="${DEPFLAGS}" \ CPP="${XCPP} ${XCFLAGS}" \ AS="${XAS}" AR="${XAR}" LD="${XLD}" NM=${XNM} \ OBJDUMP=${XOBJDUMP} OBJCOPY="${XOBJCOPY}" \ RANLIB=${XRANLIB} STRINGS=${XSTRINGS} \ SIZE="${XSIZE}" .if ${XCC:M/*} XFLAGS= --sysroot=${WORLDTMP} .if defined(CROSS_BINUTILS_PREFIX) # In the case of xdev-build tools, CROSS_BINUTILS_PREFIX won't be a # directory, but the compiler will look in the right place for it's # tools so we don't need to tell it where to look. .if exists(${CROSS_BINUTILS_PREFIX}) XFLAGS+= -B${CROSS_BINUTILS_PREFIX} .endif .else XFLAGS+= -B${WORLDTMP}/usr/bin .endif .if ${TARGET} == "arm" .if ${TARGET_ARCH:M*hf*} != "" TARGET_ABI= gnueabihf .else TARGET_ABI= gnueabi .endif .endif .if defined(X_COMPILER_TYPE) && ${X_COMPILER_TYPE} == gcc XCFLAGS+= -isystem ${WORLDTMP}/usr/include -L${WORLDTMP}/usr/lib XCXXFLAGS+= -I${WORLDTMP}/usr/include/c++/v1 -std=gnu++11 -L${WORLDTMP}/../lib/libc++ DEPFLAGS+= -I${WORLDTMP}/usr/include/c++/v1 .else TARGET_ABI?= unknown TARGET_TRIPLE?= ${TARGET_ARCH:C/amd64/x86_64/}-${TARGET_ABI}-freebsd11.0 XCFLAGS+= -target ${TARGET_TRIPLE} .endif .endif WMAKE= ${WMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 DESTDIR=${WORLDTMP} .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" # 32 bit world LIB32_OBJTREE= ${OBJTREE}${.CURDIR}/world32 LIB32TMP= ${OBJTREE}${.CURDIR}/lib32 .if ${TARGET_ARCH} == "amd64" .if empty(TARGET_CPUTYPE) LIB32CPUFLAGS= -march=i686 -mmmx -msse -msse2 .else LIB32CPUFLAGS= -march=${TARGET_CPUTYPE} .endif LIB32WMAKEENV= MACHINE=i386 MACHINE_ARCH=i386 \ MACHINE_CPU="i686 mmx sse sse2" LIB32WMAKEFLAGS= \ AS="${AS} --32" \ LD="${LD} -m elf_i386_fbsd -Y P,${LIB32TMP}/usr/lib32" .elif ${TARGET_ARCH} == "powerpc64" .if empty(TARGET_CPUTYPE) LIB32CPUFLAGS= -mcpu=powerpc .else LIB32CPUFLAGS= -mcpu=${TARGET_CPUTYPE} .endif LIB32WMAKEENV= MACHINE=powerpc MACHINE_ARCH=powerpc LIB32WMAKEFLAGS= \ LD="${LD} -m elf32ppc_fbsd" .endif LIB32FLAGS= -m32 ${LIB32CPUFLAGS} -DCOMPAT_32BIT \ -isystem ${LIB32TMP}/usr/include/ \ -L${LIB32TMP}/usr/lib32 \ -B${LIB32TMP}/usr/lib32 .if ${XCC:M/*} LIB32FLAGS+= --sysroot=${WORLDTMP} .endif # Yes, the flags are redundant. LIB32WMAKEENV+= MAKEOBJDIRPREFIX=${LIB32_OBJTREE} \ _SHLIBDIRPREFIX=${LIB32TMP} \ _LDSCRIPTROOT=${LIB32TMP} \ VERSION="${VERSION}" \ INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${TMPPATH} \ LIBDIR=/usr/lib32 \ SHLIBDIR=/usr/lib32 \ LIBPRIVATEDIR=/usr/lib32/private \ DTRACE="${DTRACE} -32" LIB32WMAKEFLAGS+= CC="${XCC} ${LIB32FLAGS}" \ CXX="${XCXX} ${LIB32FLAGS}" \ DESTDIR=${LIB32TMP} \ -DCOMPAT_32BIT \ -DLIBRARIES_ONLY \ -DNO_CPU_CFLAGS \ MK_CTF=no \ -DNO_LINT \ MK_TESTS=no LIB32WMAKE= ${LIB32WMAKEENV} ${MAKE} ${LIB32WMAKEFLAGS} \ MK_MAN=no MK_INFO=no MK_HTML=no LIB32IMAKE= ${LIB32WMAKE:NINSTALL=*:NDESTDIR=*:N_LDSCRIPTROOT=*} \ MK_TOOLCHAIN=no ${IMAKE_INSTALL} .endif IMAKEENV= ${CROSSENV:N_LDSCRIPTROOT=*} IMAKE= ${IMAKEENV} ${MAKE} -f Makefile.inc1 \ ${IMAKE_INSTALL} ${IMAKE_MTREE} .if empty(.MAKEFLAGS:M-n) IMAKEENV+= PATH=${STRICTTMPPATH}:${INSTALLTMP} \ LD_LIBRARY_PATH=${INSTALLTMP} \ PATH_LOCALE=${INSTALLTMP}/locale IMAKE+= __MAKE_SHELL=${INSTALLTMP}/sh .else IMAKEENV+= PATH=${TMPPATH}:${INSTALLTMP} .endif .if defined(DB_FROM_SRC) INSTALLFLAGS+= -N ${.CURDIR}/etc MTREEFLAGS+= -N ${.CURDIR}/etc .endif _INSTALL_DDIR= ${DESTDIR}/${DISTDIR} INSTALL_DDIR= ${_INSTALL_DDIR:S://:/:g:C:/$::} .if defined(NO_ROOT) METALOG?= ${DESTDIR}/${DISTDIR}/METALOG IMAKE+= -DNO_ROOT METALOG=${METALOG} INSTALLFLAGS+= -U -M ${METALOG} -D ${INSTALL_DDIR} MTREEFLAGS+= -W .endif .if defined(DB_FROM_SRC) || defined(NO_ROOT) IMAKE_INSTALL= INSTALL="install ${INSTALLFLAGS}" IMAKE_MTREE= MTREE_CMD="mtree ${MTREEFLAGS}" .endif # kernel stage KMAKEENV= ${WMAKEENV} KMAKE= ${KMAKEENV} ${MAKE} ${.MAKEFLAGS} ${KERNEL_FLAGS} KERNEL=${INSTKERNNAME} # # buildworld # # Attempt to rebuild the entire system, with reasonable chance of # success, regardless of how old your existing system is. # _worldtmp: .if ${.CURDIR:C/[^,]//g} != "" # The m4 build of sendmail files doesn't like it if ',' is used # anywhere in the path of it's files. @echo @echo "*** Error: path to source tree contains a comma ','" @echo false .endif @echo @echo "--------------------------------------------------------------" @echo ">>> Rebuilding the temporary build tree" @echo "--------------------------------------------------------------" .if !defined(NO_CLEAN) rm -rf ${WORLDTMP} .if defined(LIB32TMP) rm -rf ${LIB32TMP} .endif .else rm -rf ${WORLDTMP}/legacy/usr/include # XXX - These three can depend on any header file. rm -f ${OBJTREE}${.CURDIR}/usr.bin/kdump/ioctl.c rm -f ${OBJTREE}${.CURDIR}/usr.bin/kdump/kdump_subr.c rm -f ${OBJTREE}${.CURDIR}/usr.bin/truss/ioctl.c .endif .for _dir in \ lib usr legacy/bin legacy/usr mkdir -p ${WORLDTMP}/${_dir} .endfor mtree -deU -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${WORLDTMP}/legacy/usr >/dev/null .if ${MK_GROFF} != "no" mtree -deU -f ${.CURDIR}/etc/mtree/BSD.groff.dist \ -p ${WORLDTMP}/legacy/usr >/dev/null .endif mtree -deU -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${WORLDTMP}/usr >/dev/null mtree -deU -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${WORLDTMP}/usr/include >/dev/null ln -sf ${.CURDIR}/sys ${WORLDTMP} .if ${MK_DEBUG_FILES} != "no" # We could instead disable debug files for these build stages mtree -deU -f ${.CURDIR}/etc/mtree/BSD.debug.dist \ -p ${WORLDTMP}/legacy/usr/lib >/dev/null mtree -deU -f ${.CURDIR}/etc/mtree/BSD.debug.dist \ -p ${WORLDTMP}/usr/lib >/dev/null .endif .if ${MK_TESTS} != "no" mtree -deU -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${WORLDTMP}/usr >/dev/null .endif .for _mtree in ${LOCAL_MTREE} mtree -deU -f ${.CURDIR}/${_mtree} -p ${WORLDTMP} > /dev/null .endfor _legacy: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 1.1: legacy release compatibility shims" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${BMAKE} legacy _bootstrap-tools: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 1.2: bootstrap tools" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${BMAKE} bootstrap-tools _cleanobj: .if !defined(NO_CLEAN) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.1: cleaning up the object tree" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${WMAKE} ${CLEANDIR:S/^/par-/} .if defined(LIB32TMP) ${_+_}cd ${.CURDIR}; ${LIB32WMAKE} -f Makefile.inc1 ${CLEANDIR:S/^/par-/} .endif .endif _obj: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.2: rebuilding the object tree" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${WMAKE} par-obj _build-tools: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.3: build tools" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${TMAKE} build-tools _cross-tools: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 3: cross tools" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${XMAKE} cross-tools ${_+_}cd ${.CURDIR}; ${XMAKE} kernel-tools _includes: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.1: building includes" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${WMAKE} SHARED=symlinks par-includes _libraries: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.2: building libraries" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; \ ${WMAKE} -DNO_FSCHG MK_HTML=no MK_INFO=no -DNO_LINT MK_MAN=no \ MK_PROFILE=no MK_TESTS=no MK_TESTS_SUPPORT=${MK_TESTS} libraries _depend: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.3: make dependencies" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${WMAKE} par-depend everything: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.4: building everything" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${WMAKE} par-all .if defined(LIB32TMP) build32: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 5.1: building 32 bit shim libraries" @echo "--------------------------------------------------------------" mkdir -p ${LIB32TMP}/usr/include mtree -deU -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${LIB32TMP}/usr >/dev/null mtree -deU -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${LIB32TMP}/usr/include >/dev/null .if ${MK_DEBUG_FILES} != "no" mtree -deU -f ${.CURDIR}/etc/mtree/BSD.debug.dist \ -p ${LIB32TMP}/usr/lib >/dev/null .endif mkdir -p ${WORLDTMP} ln -sf ${.CURDIR}/sys ${WORLDTMP} .for _t in obj includes cd ${.CURDIR}/include; ${LIB32WMAKE} DIRPRFX=include/ ${_t} cd ${.CURDIR}/lib; ${LIB32WMAKE} DIRPRFX=lib/ ${_t} .if ${MK_CDDL} != "no" cd ${.CURDIR}/cddl/lib; ${LIB32WMAKE} DIRPRFX=cddl/lib/ ${_t} .endif cd ${.CURDIR}/gnu/lib; ${LIB32WMAKE} DIRPRFX=gnu/lib/ ${_t} .if ${MK_CRYPT} != "no" cd ${.CURDIR}/secure/lib; ${LIB32WMAKE} DIRPRFX=secure/lib/ ${_t} .endif .if ${MK_KERBEROS} != "no" cd ${.CURDIR}/kerberos5/lib; ${LIB32WMAKE} DIRPRFX=kerberos5/lib ${_t} .endif .endfor .for _dir in usr.bin/lex/lib cd ${.CURDIR}/${_dir}; ${LIB32WMAKE} DIRPRFX=${_dir}/ obj .endfor .for _dir in lib/ncurses/ncurses lib/ncurses/ncursesw lib/libmagic cd ${.CURDIR}/${_dir}; \ WORLDTMP=${WORLDTMP} \ MAKEFLAGS="-m ${.CURDIR}/tools/build/mk ${.MAKEFLAGS}" \ MAKEOBJDIRPREFIX=${LIB32_OBJTREE} ${MAKE} SSP_CFLAGS= DESTDIR= \ DIRPRFX=${_dir}/ -DNO_LINT -DNO_CPU_CFLAGS MK_WARNS=no MK_CTF=no \ build-tools .endfor cd ${.CURDIR}; \ ${LIB32WMAKE} -f Makefile.inc1 libraries .for _t in obj depend all cd ${.CURDIR}/libexec/rtld-elf; PROG=ld-elf32.so.1 ${LIB32WMAKE} \ DIRPRFX=libexec/rtld-elf/ ${_t} cd ${.CURDIR}/usr.bin/ldd; PROG=ldd32 ${LIB32WMAKE} \ DIRPRFX=usr.bin/ldd ${_t} .endfor distribute32 install32: cd ${.CURDIR}/lib; ${LIB32IMAKE} ${.TARGET:S/32$//} .if ${MK_CDDL} != "no" cd ${.CURDIR}/cddl/lib; ${LIB32IMAKE} ${.TARGET:S/32$//} .endif cd ${.CURDIR}/gnu/lib; ${LIB32IMAKE} ${.TARGET:S/32$//} .if ${MK_CRYPT} != "no" cd ${.CURDIR}/secure/lib; ${LIB32IMAKE} ${.TARGET:S/32$//} .endif .if ${MK_KERBEROS} != "no" cd ${.CURDIR}/kerberos5/lib; ${LIB32IMAKE} ${.TARGET:S/32$//} .endif cd ${.CURDIR}/libexec/rtld-elf; \ PROG=ld-elf32.so.1 ${LIB32IMAKE} ${.TARGET:S/32$//} cd ${.CURDIR}/usr.bin/ldd; PROG=ldd32 ${LIB32IMAKE} ${.TARGET:S/32$//} .endif WMAKE_TGTS= .if !defined(SUBDIR_OVERRIDE) WMAKE_TGTS+= _worldtmp _legacy _bootstrap-tools .endif WMAKE_TGTS+= _cleanobj _obj _build-tools .if !defined(SUBDIR_OVERRIDE) WMAKE_TGTS+= _cross-tools .endif WMAKE_TGTS+= _includes _libraries _depend everything .if defined(LIB32TMP) && ${MK_LIB32} != "no" WMAKE_TGTS+= build32 .endif buildworld: buildworld_prologue ${WMAKE_TGTS} buildworld_epilogue .ORDER: buildworld_prologue ${WMAKE_TGTS} buildworld_epilogue buildworld_prologue: @echo "--------------------------------------------------------------" @echo ">>> World build started on `LC_ALL=C date`" @echo "--------------------------------------------------------------" buildworld_epilogue: @echo @echo "--------------------------------------------------------------" @echo ">>> World build completed on `LC_ALL=C date`" @echo "--------------------------------------------------------------" # # We need to have this as a target because the indirection between Makefile # and Makefile.inc1 causes the correct PATH to be used, rather than a # modification of the current environment's PATH. In addition, we need # to quote multiword values. # buildenvvars: @echo ${WMAKEENV:Q} .if ${.TARGETS:Mbuildenv} .if ${.MAKEFLAGS:M-j} .error The buildenv target is incompatible with -j .endif .endif buildenv: @echo Entering world for ${TARGET_ARCH}:${TARGET} @cd ${.CURDIR} && env ${WMAKEENV} ${BUILDENV_SHELL} || true TOOLCHAIN_TGTS= ${WMAKE_TGTS:N_depend:Neverything:Nbuild32} toolchain: ${TOOLCHAIN_TGTS} kernel-toolchain: ${TOOLCHAIN_TGTS:N_includes:N_libraries} # # installcheck # # Checks to be sure system is ready for installworld/installkernel. # installcheck: _installcheck_world _installcheck_kernel _installcheck_world: _installcheck_kernel: # # Require DESTDIR to be set if installing for a different architecture or # using the user/group database in the source tree. # .if ${TARGET_ARCH} != ${MACHINE_ARCH} || ${TARGET} != ${MACHINE} || \ defined(DB_FROM_SRC) .if !make(distributeworld) _installcheck_world: __installcheck_DESTDIR _installcheck_kernel: __installcheck_DESTDIR __installcheck_DESTDIR: .if !defined(DESTDIR) || empty(DESTDIR) @echo "ERROR: Please set DESTDIR!"; \ false .endif .endif .endif .if !defined(DB_FROM_SRC) # # Check for missing UIDs/GIDs. # CHECK_UIDS= auditdistd CHECK_GIDS= audit .if ${MK_SENDMAIL} != "no" CHECK_UIDS+= smmsp CHECK_GIDS+= smmsp .endif .if ${MK_PF} != "no" CHECK_UIDS+= proxy CHECK_GIDS+= proxy authpf .endif .if ${MK_UNBOUND} != "no" CHECK_UIDS+= unbound CHECK_GIDS+= unbound .endif _installcheck_world: __installcheck_UGID __installcheck_UGID: .for uid in ${CHECK_UIDS} @if ! `id -u ${uid} >/dev/null 2>&1`; then \ echo "ERROR: Required ${uid} user is missing, see /usr/src/UPDATING."; \ false; \ fi .endfor .for gid in ${CHECK_GIDS} @if ! `find / -prune -group ${gid} >/dev/null 2>&1`; then \ echo "ERROR: Required ${gid} group is missing, see /usr/src/UPDATING."; \ false; \ fi .endfor .endif # # Required install tools to be saved in a scratch dir for safety. # .if ${MK_INFO} != "no" _install-info= install-info .endif .if ${MK_ZONEINFO} != "no" _zoneinfo= zic tzsetup .endif ITOOLS= [ awk cap_mkdb cat chflags chmod chown \ date echo egrep find grep id install ${_install-info} \ ln lockf make mkdir mtree mv pwd_mkdb \ rm sed services_mkdb sh sysctl test true uname wc ${_zoneinfo} \ ${LOCAL_ITOOLS} # # distributeworld # # Distributes everything compiled by a `buildworld'. # # installworld # # Installs everything compiled by a 'buildworld'. # # Non-base distributions produced by the base system EXTRA_DISTRIBUTIONS= doc .if ${MK_GAMES} != "no" EXTRA_DISTRIBUTIONS+= games .endif .if defined(LIB32TMP) && ${MK_LIB32} != "no" EXTRA_DISTRIBUTIONS+= lib32 .endif .if ${MK_TESTS} != "no" EXTRA_DISTRIBUTIONS+= tests .endif DEBUG_DISTRIBUTIONS= .if ${MK_DEBUG_FILES} != "no" DEBUG_DISTRIBUTIONS+= base ${EXTRA_DISTRIBUTIONS:S,doc,,} .endif MTREE_MAGIC?= mtree 2.0 distributeworld installworld: _installcheck_world mkdir -p ${INSTALLTMP} progs=$$(for prog in ${ITOOLS}; do \ if progpath=`which $$prog`; then \ echo $$progpath; \ else \ echo "Required tool $$prog not found in PATH." >&2; \ exit 1; \ fi; \ done); \ libs=$$(ldd -f "%o %p\n" -f "%o %p\n" $$progs 2>/dev/null | sort -u | \ while read line; do \ set -- $$line; \ if [ "$$2 $$3" != "not found" ]; then \ echo $$2; \ else \ echo "Required library $$1 not found." >&2; \ exit 1; \ fi; \ done); \ cp $$libs $$progs ${INSTALLTMP} cp -R $${PATH_LOCALE:-"/usr/share/locale"} ${INSTALLTMP}/locale .if defined(NO_ROOT) echo "#${MTREE_MAGIC}" > ${METALOG} .endif .if make(distributeworld) .for dist in ${EXTRA_DISTRIBUTIONS} -mkdir ${DESTDIR}/${DISTDIR}/${dist} mtree -deU -f ${.CURDIR}/etc/mtree/BSD.root.dist \ -p ${DESTDIR}/${DISTDIR}/${dist} >/dev/null mtree -deU -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr >/dev/null mtree -deU -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/include >/dev/null .if ${MK_DEBUG_FILES} != "no" mtree -deU -f ${.CURDIR}/etc/mtree/BSD.debug.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/lib >/dev/null .endif .if ${MK_TESTS} != "no" && ${dist} == "tests" mtree -deU -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr >/dev/null .endif .if defined(NO_ROOT) ${IMAKEENV} mtree -C -f ${.CURDIR}/etc/mtree/BSD.root.dist | \ sed -e 's#^\./#./${dist}/#' >> ${METALOG} ${IMAKEENV} mtree -C -f ${.CURDIR}/etc/mtree/BSD.usr.dist | \ sed -e 's#^\./#./${dist}/usr/#' >> ${METALOG} ${IMAKEENV} mtree -C -f ${.CURDIR}/etc/mtree/BSD.include.dist | \ sed -e 's#^\./#./${dist}/usr/include/#' >> ${METALOG} .endif .endfor -mkdir ${DESTDIR}/${DISTDIR}/base cd ${.CURDIR}/etc; ${CROSSENV} PATH=${TMPPATH} ${MAKE} \ METALOG=${METALOG} ${IMAKE_INSTALL} ${IMAKE_MTREE} \ DISTBASE=/base DESTDIR=${DESTDIR}/${DISTDIR}/base \ LOCAL_MTREE=${LOCAL_MTREE:Q} distrib-dirs .endif ${_+_}cd ${.CURDIR}; ${IMAKE} re${.TARGET:S/world$//}; \ ${IMAKEENV} rm -rf ${INSTALLTMP} .if make(distributeworld) .for dist in ${EXTRA_DISTRIBUTIONS} find ${DESTDIR}/${DISTDIR}/${dist} -mindepth 1 -empty -delete .endfor .if defined(NO_ROOT) .for dist in base ${EXTRA_DISTRIBUTIONS} @# For each file that exists in this dist, print the corresponding @# line from the METALOG. This relies on the fact that @# a line containing only the filename will sort immediatly before @# the relevant mtree line. cd ${DESTDIR}/${DISTDIR}; \ find ./${dist} | sort -u ${METALOG} - | \ awk 'BEGIN { print "#${MTREE_MAGIC}" } !/ type=/ { file = $$1 } / type=/ { if ($$1 == file) { sub(/^\.\/${dist}\//, "./"); print } }' > \ ${DESTDIR}/${DISTDIR}/${dist}.meta .endfor .for dist in ${DEBUG_DISTRIBUTIONS} @# For each file that exists in this dist, print the corresponding @# line from the METALOG. This relies on the fact that @# a line containing only the filename will sort immediatly before @# the relevant mtree line. cd ${DESTDIR}/${DISTDIR}; \ find ./${dist}/usr/lib/debug | sort -u ${METALOG} - | \ awk 'BEGIN { print "#${MTREE_MAGIC}" } !/ type=/ { file = $$1 } / type=/ { if ($$1 == file) { sub(/^\.\/${dist}\//, "./"); print } }' > \ ${DESTDIR}/${DISTDIR}/${dist}.debug.meta .endfor .endif .endif packageworld: .for dist in base ${EXTRA_DISTRIBUTIONS} .if defined(NO_ROOT) ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ tar cvJf ${DESTDIR}/${DISTDIR}/${dist}.txz \ --exclude usr/lib/debug \ @${DESTDIR}/${DISTDIR}/${dist}.meta .else ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ tar cvJf ${DESTDIR}/${DISTDIR}/${dist}.txz \ --exclude usr/lib/debug . .endif .endfor .for dist in ${DEBUG_DISTRIBUTIONS} . if defined(NO_ROOT) ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ tar cvJf ${DESTDIR}/${DISTDIR}/${dist}-dbg.txz \ @${DESTDIR}/${DISTDIR}/${dist}.debug.meta . else ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ tar cvJfL ${DESTDIR}/${DISTDIR}/${dist}-dbg.txz \ usr/lib/debug . endif .endfor # # reinstall # # If you have a build server, you can NFS mount the source and obj directories # and do a 'make reinstall' on the *client* to install new binaries from the # most recent server build. # reinstall: .MAKE @echo "--------------------------------------------------------------" @echo ">>> Making hierarchy" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 \ LOCAL_MTREE=${LOCAL_MTREE:Q} hierarchy @echo @echo "--------------------------------------------------------------" @echo ">>> Installing everything" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 install .if defined(LIB32TMP) && ${MK_LIB32} != "no" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 install32 .endif redistribute: .MAKE @echo "--------------------------------------------------------------" @echo ">>> Distributing everything" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 distribute .if defined(LIB32TMP) && ${MK_LIB32} != "no" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 distribute32 \ DISTRIBUTION=lib32 .endif distrib-dirs distribution: .MAKE cd ${.CURDIR}/etc; ${CROSSENV} PATH=${TMPPATH} ${MAKE} \ ${IMAKE_INSTALL} ${IMAKE_MTREE} METALOG=${METALOG} ${.TARGET} # # buildkernel and installkernel # # Which kernels to build and/or install is specified by setting # KERNCONF. If not defined a GENERIC kernel is built/installed. # Only the existing (depending TARGET) config files are used # for building kernels and only the first of these is designated # as the one being installed. # # Note that we have to use TARGET instead of TARGET_ARCH when # we're in kernel-land. Since only TARGET_ARCH is (expected) to # be set to cross-build, we have to make sure TARGET is set # properly. .if defined(KERNFAST) NO_KERNELCLEAN= t NO_KERNELCONFIG= t NO_KERNELDEPEND= t NO_KERNELOBJ= t # Shortcut for KERNCONF=Blah -DKERNFAST is now KERNFAST=Blah .if !defined(KERNCONF) && ${KERNFAST} != "1" KERNCONF=${KERNFAST} .endif .endif .if ${TARGET_ARCH} == "powerpc64" KERNCONF?= GENERIC64 .else KERNCONF?= GENERIC .endif INSTKERNNAME?= kernel KERNSRCDIR?= ${.CURDIR}/sys KRNLCONFDIR= ${KERNSRCDIR}/${TARGET}/conf KRNLOBJDIR= ${OBJTREE}${KERNSRCDIR} KERNCONFDIR?= ${KRNLCONFDIR} BUILDKERNELS= INSTALLKERNEL= .for _kernel in ${KERNCONF} .if exists(${KERNCONFDIR}/${_kernel}) BUILDKERNELS+= ${_kernel} .if empty(INSTALLKERNEL) INSTALLKERNEL= ${_kernel} .endif .endif .endfor buildkernel ${WMAKE_TGTS} ${.ALLTARGETS:M_*}: .MAKE # # buildkernel # # Builds all kernels defined by BUILDKERNELS. # buildkernel: .if empty(BUILDKERNELS) @echo "ERROR: Missing kernel configuration file(s) (${KERNCONF})."; \ false .endif @echo .for _kernel in ${BUILDKERNELS} @echo "--------------------------------------------------------------" @echo ">>> Kernel build for ${_kernel} started on `LC_ALL=C date`" @echo "--------------------------------------------------------------" @echo "===> ${_kernel}" mkdir -p ${KRNLOBJDIR} .if !defined(NO_KERNELCONFIG) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 1: configuring the kernel" @echo "--------------------------------------------------------------" cd ${KRNLCONFDIR}; \ PATH=${TMPPATH} \ config ${CONFIGARGS} -d ${KRNLOBJDIR}/${_kernel} \ -I '${KERNCONFDIR}' '${KERNCONFDIR}/${_kernel}' .endif .if !defined(NO_CLEAN) && !defined(NO_KERNELCLEAN) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.1: cleaning up the object tree" @echo "--------------------------------------------------------------" cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} ${CLEANDIR} .endif .if !defined(NO_KERNELOBJ) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.2: rebuilding the object tree" @echo "--------------------------------------------------------------" cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} obj .endif @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.3: build tools" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${KTMAKE} kernel-tools .if !defined(NO_KERNELDEPEND) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 3.1: making dependencies" @echo "--------------------------------------------------------------" cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} depend -DNO_MODULES_OBJ .endif @echo @echo "--------------------------------------------------------------" @echo ">>> stage 3.2: building everything" @echo "--------------------------------------------------------------" cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} all -DNO_MODULES_OBJ @echo "--------------------------------------------------------------" @echo ">>> Kernel build for ${_kernel} completed on `LC_ALL=C date`" @echo "--------------------------------------------------------------" .endfor # # installkernel, etc. # # Install the kernel defined by INSTALLKERNEL # installkernel installkernel.debug \ reinstallkernel reinstallkernel.debug: _installcheck_kernel .if empty(INSTALLKERNEL) @echo "ERROR: No kernel \"${KERNCONF}\" to install."; \ false .endif @echo "--------------------------------------------------------------" @echo ">>> Installing kernel ${INSTALLKERNEL}" @echo "--------------------------------------------------------------" cd ${KRNLOBJDIR}/${INSTALLKERNEL}; \ ${CROSSENV} PATH=${TMPPATH} \ ${MAKE} ${IMAKE_INSTALL} KERNEL=${INSTKERNNAME} ${.TARGET:S/kernel//} distributekernel distributekernel.debug: .if empty(INSTALLKERNEL) @echo "ERROR: No kernel \"${KERNCONF}\" to install."; \ false .endif mkdir -p ${DESTDIR}/${DISTDIR} .if defined(NO_ROOT) echo "#${MTREE_MAGIC}" > ${DESTDIR}/${DISTDIR}/kernel.premeta .endif cd ${KRNLOBJDIR}/${INSTALLKERNEL}; \ ${IMAKEENV} ${IMAKE_INSTALL:S/METALOG/kernel.premeta/} \ ${IMAKE_MTREE} PATH=${TMPPATH} ${MAKE} KERNEL=${INSTKERNNAME} \ DESTDIR=${INSTALL_DDIR}/kernel \ ${.TARGET:S/distributekernel/install/} .if defined(NO_ROOT) sed -e 's|^./kernel|.|' ${DESTDIR}/${DISTDIR}/kernel.premeta > \ ${DESTDIR}/${DISTDIR}/kernel.meta .endif .for _kernel in ${BUILDKERNELS:S/${INSTALLKERNEL}//} .if defined(NO_ROOT) echo "#${MTREE_MAGIC}" > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.premeta .endif cd ${KRNLOBJDIR}/${_kernel}; \ ${IMAKEENV} ${IMAKE_INSTALL:S/METALOG/kernel.${_kernel}.premeta/} \ ${IMAKE_MTREE} PATH=${TMPPATH} ${MAKE} \ KERNEL=${INSTKERNNAME}.${_kernel} \ DESTDIR=${INSTALL_DDIR}/kernel.${_kernel} \ ${.TARGET:S/distributekernel/install/} .if defined(NO_ROOT) sed -e 's|^./kernel|.|' \ ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.premeta > \ ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta .endif .endfor packagekernel: .if defined(NO_ROOT) cd ${DESTDIR}/${DISTDIR}/kernel; \ tar cvJf ${DESTDIR}/${DISTDIR}/kernel.txz \ @${DESTDIR}/${DISTDIR}/kernel.meta .for _kernel in ${BUILDKERNELS:S/${INSTALLKERNEL}//} cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ tar cvJf ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.txz \ @${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta .endfor .else cd ${DESTDIR}/${DISTDIR}/kernel; \ tar cvJf ${DESTDIR}/${DISTDIR}/kernel.txz . .for _kernel in ${BUILDKERNELS:S/${INSTALLKERNEL}//} cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ tar cvJf ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.txz . .endfor .endif # # doxygen # # Build the API documentation with doxygen # doxygen: @if [ ! -x `/usr/bin/which doxygen` ]; then \ echo "You need doxygen (devel/doxygen) to generate the API documentation of the kernel." | /usr/bin/fmt; \ exit 1; \ fi cd ${.CURDIR}/tools/kerneldoc/subsys && ${MAKE} obj all # # update # # Update the source tree(s), by running svn/svnup to update to the # latest copy. # update: .if (defined(CVS_UPDATE) || defined(SUP_UPDATE)) && !defined(SVN_UPDATE) @echo "--------------------------------------------------------------" @echo "CVS_UPDATE and SUP_UPDATE are no longer supported." @echo "Please see: https://wiki.freebsd.org/CvsIsDeprecated" @echo "--------------------------------------------------------------" @exit 1 .endif .if defined(SVN_UPDATE) @echo "--------------------------------------------------------------" @echo ">>> Updating ${.CURDIR} using Subversion" @echo "--------------------------------------------------------------" @(cd ${.CURDIR} && ${SVN} update ${SVNFLAGS}) .endif # # ------------------------------------------------------------------------ # # From here onwards are utility targets used by the 'make world' and # related targets. If your 'world' breaks, you may like to try to fix # the problem and manually run the following targets to attempt to # complete the build. Beware, this is *not* guaranteed to work, you # need to have a pretty good grip on the current state of the system # to attempt to manually finish it. If in doubt, 'make world' again. # # # legacy: Build compatibility shims for the next three targets # legacy: .if ${BOOTSTRAPPING} < 800107 && ${BOOTSTRAPPING} != 0 @echo "ERROR: Source upgrades from versions prior to 8.0 not supported."; \ false .endif .for _tool in tools/build ${_+_}@${ECHODIR} "===> ${_tool} (obj,includes,depend,all,install)"; \ cd ${.CURDIR}/${_tool} && \ ${MAKE} DIRPRFX=${_tool}/ obj && \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${MAKEOBJDIRPREFIX}/legacy includes && \ ${MAKE} DIRPRFX=${_tool}/ depend && \ ${MAKE} DIRPRFX=${_tool}/ all && \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${MAKEOBJDIRPREFIX}/legacy install .endfor # # bootstrap-tools: Build tools needed for compatibility # .if ${MK_GAMES} != "no" _strfile= games/fortune/strfile .endif .if ${MK_CXX} != "no" _gperf= gnu/usr.bin/gperf .endif .if ${MK_GROFF} != "no" _groff= gnu/usr.bin/groff .endif .if ${MK_VT} != "no" _vtfontcvt= usr.bin/vtfontcvt .endif .if ${BOOTSTRAPPING} < 900002 _sed= usr.bin/sed .endif .if ${BOOTSTRAPPING} < 1000002 _m4= lib/libohash \ usr.bin/m4 .endif .if ${BOOTSTRAPPING} < 1000013 _yacc= lib/liby \ usr.bin/yacc .endif .if ${BOOTSTRAPPING} < 1000014 _crunch= usr.sbin/crunch .endif .if ${BOOTSTRAPPING} < 1000026 _nmtree= lib/libnetbsd \ usr.sbin/nmtree .endif .if ${BOOTSTRAPPING} < 1000027 _cat= bin/cat .endif .if ${BOOTSTRAPPING} < 1000033 _lex= usr.bin/lex .endif .if ${BOOTSTRAPPING} >= 900040 && ${BOOTSTRAPPING} < 900041 _awk= usr.bin/awk .endif .if ${MK_BSNMP} != "no" _gensnmptree= usr.sbin/bsnmpd/gensnmptree .endif # We need to build tblgen when we're building clang either as # the bootstrap compiler, or as the part of the normal build. .if ${MK_CLANG_BOOTSTRAP} != "no" || ${MK_CLANG} != "no" _clang_tblgen= \ lib/clang/libllvmsupport \ lib/clang/libllvmtablegen \ usr.bin/clang/tblgen \ usr.bin/clang/clang-tblgen .endif # dtrace tools are required for older bootstrap env and cross-build # pre libdwarf .if ${MK_CDDL} != "no" && (${BOOTSTRAPPING} < 1100006 \ || (${MACHINE} != ${TARGET} || ${MACHINE_ARCH} != ${TARGET_ARCH})) _dtrace_tools= cddl/usr.bin/sgsmsg cddl/lib/libctf lib/libelf \ lib/libdwarf cddl/usr.bin/ctfconvert cddl/usr.bin/ctfmerge .endif # Default to building the GPL DTC, but build the BSDL one if users explicitly # request it. _dtc= usr.bin/dtc .if ${MK_GPL_DTC} != "no" _dtc= gnu/usr.bin/dtc .endif .if ${MK_KERBEROS} != "no" _kerberos5_bootstrap_tools= \ kerberos5/tools/make-roken \ kerberos5/lib/libroken \ kerberos5/lib/libvers \ kerberos5/tools/asn1_compile \ kerberos5/tools/slc \ usr.bin/compile_et .endif # Please document (add comment) why something is in 'bootstrap-tools'. # Try to bound the building of the bootstrap-tool to just the # FreeBSD versions that need the tool built at this stage of the build. bootstrap-tools: .MAKE .for _tool in \ ${_clang_tblgen} \ ${_kerberos5_bootstrap_tools} \ ${_dtrace_tools} \ ${_strfile} \ ${_gperf} \ ${_groff} \ ${_dtc} \ ${_awk} \ ${_cat} \ usr.bin/lorder \ usr.bin/makewhatis \ usr.bin/rpcgen \ ${_sed} \ ${_yacc} \ ${_m4} \ ${_lex} \ lib/libmd \ usr.bin/xinstall \ ${_gensnmptree} \ usr.sbin/config \ ${_crunch} \ ${_nmtree} \ ${_vtfontcvt} ${_+_}@${ECHODIR} "===> ${_tool} (obj,depend,all,install)"; \ cd ${.CURDIR}/${_tool} && \ ${MAKE} DIRPRFX=${_tool}/ obj && \ ${MAKE} DIRPRFX=${_tool}/ depend && \ ${MAKE} DIRPRFX=${_tool}/ all && \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${MAKEOBJDIRPREFIX}/legacy install .endfor # # build-tools: Build special purpose build tools # .if !defined(NO_SHARE) _share= share/syscons/scrnmaps .endif .if ${MK_GCC} != "no" _gcc_tools= gnu/usr.bin/cc/cc_tools .endif .if ${MK_RESCUE} != "no" _rescue= rescue/rescue .endif build-tools: .MAKE .for _tool in \ bin/csh \ bin/sh \ ${_rescue} \ ${LOCAL_TOOL_DIRS} \ lib/ncurses/ncurses \ lib/ncurses/ncursesw \ ${_share} \ usr.bin/awk \ lib/libmagic \ usr.bin/mkesdb_static \ usr.bin/mkcsmapper_static \ usr.bin/vi/catalog ${_+_}@${ECHODIR} "===> ${_tool} (obj,build-tools)"; \ cd ${.CURDIR}/${_tool} && \ ${MAKE} DIRPRFX=${_tool}/ obj && \ ${MAKE} DIRPRFX=${_tool}/ build-tools .endfor .for _tool in \ ${_gcc_tools} ${_+_}@${ECHODIR} "===> ${_tool} (obj,depend,all)"; \ cd ${.CURDIR}/${_tool} && \ ${MAKE} DIRPRFX=${_tool}/ obj && \ ${MAKE} DIRPRFX=${_tool}/ depend && \ ${MAKE} DIRPRFX=${_tool}/ all .endfor # # kernel-tools: Build kernel-building tools # kernel-tools: .MAKE mkdir -p ${MAKEOBJDIRPREFIX}/usr mtree -deU -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${MAKEOBJDIRPREFIX}/usr >/dev/null # # cross-tools: Build cross-building tools # .if ${TARGET_ARCH} != ${MACHINE_ARCH} .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" _btxld= usr.sbin/btxld .endif .endif .if ${TARGET_ARCH} != ${MACHINE_ARCH} .if ${MK_RESCUE} != "no" || defined(RELEASEDIR) _crunchide= usr.sbin/crunch/crunchide .endif .if ${TARGET_ARCH} == "i386" && defined(RELEASEDIR) _kgzip= usr.sbin/kgzip .endif .endif # If we're given an XAS, don't build binutils. .if ${XAS:M/*} == "" && ${MK_BINUTILS_BOOTSTRAP} != "no" _binutils= gnu/usr.bin/binutils +.if ${MK_ELFTOOLCHAIN_TOOLS} != "no" +_elftctools= lib/libelftc \ + usr.bin/addr2line \ + usr.bin/elfcopy \ + usr.bin/nm \ + usr.bin/size \ + usr.bin/strings .endif +.endif # If an full path to an external cross compiler is given, don't build # a cross compiler. .if ${XCC:M/*} == "" && ${MK_CROSS_COMPILER} != "no" .if ${MK_CLANG_BOOTSTRAP} != "no" _clang= usr.bin/clang _clang_libs= lib/clang .endif .if ${MK_GCC_BOOTSTRAP} != "no" _cc= gnu/usr.bin/cc .endif .endif cross-tools: .MAKE .for _tool in \ ${_clang_libs} \ ${_clang} \ ${_binutils} \ + ${_elftctools} \ ${_cc} \ usr.bin/xlint/lint1 usr.bin/xlint/lint2 usr.bin/xlint/xlint \ ${_btxld} \ ${_crunchide} \ ${_kgzip} \ sys/boot/usb/tools ${_+_}@${ECHODIR} "===> ${_tool} (obj,depend,all,install)"; \ cd ${.CURDIR}/${_tool} && \ ${MAKE} DIRPRFX=${_tool}/ obj && \ ${MAKE} DIRPRFX=${_tool}/ depend && \ ${MAKE} DIRPRFX=${_tool}/ all && \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${MAKEOBJDIRPREFIX} install .endfor NXBENV= MAKEOBJDIRPREFIX=${OBJTREE}/nxb \ INSTALL="sh ${.CURDIR}/tools/install.sh" \ VERSION="${VERSION}" NXBMAKE= ${NXBENV} ${MAKE} \ TBLGEN=${OBJTREE}/nxb-bin/usr/bin/tblgen \ CLANG_TBLGEN=${OBJTREE}/nxb-bin/usr/bin/clang-tblgen \ MACHINE=${TARGET} MACHINE_ARCH=${TARGET_ARCH} \ MK_GDB=no MK_TESTS=no \ SSP_CFLAGS= \ MK_HTML=no MK_INFO=no NO_LINT=yes MK_MAN=no \ -DNO_PIC MK_PROFILE=no -DNO_SHARED \ -DNO_CPU_CFLAGS MK_WARNS=no MK_CTF=no \ MK_CLANG_FULL=no MK_LLDB=no native-xtools: .MAKE mkdir -p ${OBJTREE}/nxb-bin/bin mkdir -p ${OBJTREE}/nxb-bin/sbin mkdir -p ${OBJTREE}/nxb-bin/usr mtree -deU -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${OBJTREE}/nxb-bin/usr >/dev/null mtree -deU -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${OBJTREE}/nxb-bin/usr/include >/dev/null .for _tool in \ bin/cat \ bin/chmod \ bin/cp \ bin/csh \ bin/echo \ bin/expr \ bin/hostname \ bin/ln \ bin/ls \ bin/mkdir \ bin/mv \ bin/ps \ bin/realpath \ bin/rm \ bin/rmdir \ bin/sh \ bin/sleep \ ${_clang_tblgen} \ usr.bin/ar \ ${_binutils} \ + ${_elftctools} \ ${_cc} \ ${_gcc_tools} \ ${_clang_libs} \ ${_clang} \ sbin/md5 \ sbin/sysctl \ gnu/usr.bin/diff \ usr.bin/awk \ usr.bin/basename \ usr.bin/bmake \ usr.bin/bzip2 \ usr.bin/cmp \ usr.bin/dirname \ usr.bin/env \ usr.bin/fetch \ usr.bin/find \ usr.bin/grep \ usr.bin/gzip \ usr.bin/id \ usr.bin/lex \ usr.bin/lorder \ usr.bin/mktemp \ usr.bin/mt \ usr.bin/patch \ usr.bin/sed \ usr.bin/sort \ usr.bin/tar \ usr.bin/touch \ usr.bin/tr \ usr.bin/true \ usr.bin/uniq \ usr.bin/unzip \ usr.bin/xargs \ usr.bin/xinstall \ usr.bin/xz \ usr.bin/yacc \ usr.sbin/chown ${_+_}@${ECHODIR} "===> ${_tool} (obj,depend,all,install)"; \ cd ${.CURDIR}/${_tool} && \ ${NXBMAKE} DIRPRFX=${_tool}/ obj && \ ${NXBMAKE} DIRPRFX=${_tool}/ depend && \ ${NXBMAKE} DIRPRFX=${_tool}/ all && \ ${NXBMAKE} DIRPRFX=${_tool}/ DESTDIR=${OBJTREE}/nxb-bin install .endfor # # hierarchy - ensure that all the needed directories are present # hierarchy hier: .MAKE cd ${.CURDIR}/etc && ${HMAKE} distrib-dirs # # libraries - build all libraries, and install them under ${DESTDIR}. # # The list of libraries with dependents (${_prebuild_libs}) and their # interdependencies (__L) are built automatically by the # ${.CURDIR}/tools/make_libdeps.sh script. # libraries: .MAKE cd ${.CURDIR} && \ ${MAKE} -f Makefile.inc1 _prereq_libs && \ ${MAKE} -f Makefile.inc1 _startup_libs && \ ${MAKE} -f Makefile.inc1 _prebuild_libs && \ ${MAKE} -f Makefile.inc1 _generic_libs # # static libgcc.a prerequisite for shared libc # _prereq_libs= gnu/lib/libssp/libssp_nonshared gnu/lib/libgcc lib/libcompiler_rt # These dependencies are not automatically generated: # # gnu/lib/csu, gnu/lib/libgcc, lib/csu and lib/libc must be built before # all shared libraries for ELF. # _startup_libs= gnu/lib/csu .if exists(${.CURDIR}/lib/csu/${MACHINE_ARCH}-elf) _startup_libs+= lib/csu/${MACHINE_ARCH}-elf .elif exists(${.CURDIR}/lib/csu/${MACHINE_ARCH}) _startup_libs+= lib/csu/${MACHINE_ARCH} .else _startup_libs+= lib/csu/${MACHINE_CPUARCH} .endif _startup_libs+= gnu/lib/libgcc _startup_libs+= lib/libcompiler_rt _startup_libs+= lib/libc _startup_libs+= lib/libc_nonshared .if ${MK_LIBCPLUSPLUS} != "no" _startup_libs+= lib/libcxxrt .endif gnu/lib/libgcc__L: lib/libc__L gnu/lib/libgcc__L: lib/libc_nonshared__L .if ${MK_LIBCPLUSPLUS} != "no" lib/libcxxrt__L: gnu/lib/libgcc__L .endif _prebuild_libs= ${_kerberos5_lib_libasn1} \ ${_kerberos5_lib_libhdb} \ ${_kerberos5_lib_libheimbase} \ ${_kerberos5_lib_libheimntlm} \ ${_kerberos5_lib_libheimsqlite} \ ${_kerberos5_lib_libheimipcc} \ ${_kerberos5_lib_libhx509} ${_kerberos5_lib_libkrb5} \ ${_kerberos5_lib_libroken} \ ${_kerberos5_lib_libwind} \ lib/libbz2 ${_libcom_err} lib/libcrypt \ lib/libelf lib/libexpat \ lib/libfigpar \ ${_lib_libgssapi} \ lib/libkiconv lib/libkvm lib/liblzma lib/libmd lib/libnv \ ${_lib_libcapsicum} \ lib/ncurses/ncurses lib/ncurses/ncursesw \ lib/libopie lib/libpam ${_lib_libthr} \ lib/libradius lib/libsbuf lib/libtacplus \ lib/libgeom \ ${_cddl_lib_libumem} ${_cddl_lib_libnvpair} \ ${_cddl_lib_libuutil} \ ${_cddl_lib_libavl} \ ${_cddl_lib_libzfs_core} \ ${_cddl_lib_libctf} \ lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \ ${_secure_lib_libcrypto} ${_lib_libldns} \ ${_secure_lib_libssh} ${_secure_lib_libssl} \ gnu/lib/libdialog .if ${MK_GNUCXX} != "no" _prebuild_libs+= gnu/lib/libstdc++ gnu/lib/libsupc++ gnu/lib/libstdc++__L: lib/msun__L gnu/lib/libsupc++__L: gnu/lib/libstdc++__L .endif lib/libgeom__L: lib/libexpat__L .if ${MK_LIBTHR} != "no" _lib_libthr= lib/libthr .endif .if ${MK_OFED} != "no" _ofed_lib= contrib/ofed/usr.lib/ .endif .if ${MK_CASPER} != "no" _lib_libcapsicum=lib/libcapsicum .endif lib/libcapsicum__L: lib/libnv__L lib/libpjdlog__L: lib/libutil__L _generic_libs= ${_cddl_lib} gnu/lib ${_kerberos5_lib} lib ${_secure_lib} usr.bin/lex/lib ${_ofed_lib} .for _DIR in ${LOCAL_LIB_DIRS} .if exists(${.CURDIR}/${_DIR}/Makefile) _generic_libs+= ${_DIR} .endif .endfor lib/libopie__L lib/libtacplus__L: lib/libmd__L .if ${MK_CDDL} != "no" _cddl_lib_libumem= cddl/lib/libumem _cddl_lib_libnvpair= cddl/lib/libnvpair _cddl_lib_libavl= cddl/lib/libavl _cddl_lib_libuutil= cddl/lib/libuutil _cddl_lib_libzfs_core= cddl/lib/libzfs_core _cddl_lib_libctf= cddl/lib/libctf _cddl_lib= cddl/lib cddl/lib/libzfs_core__L: cddl/lib/libnvpair__L cddl/lib/libzfs__L: lib/libgeom__L cddl/lib/libctf__L: lib/libz__L .endif .if ${MK_CRYPT} != "no" .if ${MK_OPENSSL} != "no" _secure_lib_libcrypto= secure/lib/libcrypto _secure_lib_libssl= secure/lib/libssl lib/libradius__L secure/lib/libssl__L: secure/lib/libcrypto__L .if ${MK_LDNS} != "no" _lib_libldns= lib/libldns lib/libldns__L: secure/lib/libcrypto__L .endif .if ${MK_OPENSSH} != "no" _secure_lib_libssh= secure/lib/libssh secure/lib/libssh__L: lib/libz__L secure/lib/libcrypto__L lib/libcrypt__L .if ${MK_LDNS} != "no" secure/lib/libssh__L: lib/libldns__L .endif .if ${MK_KERBEROS_SUPPORT} != "no" secure/lib/libssh__L: lib/libgssapi__L kerberos5/lib/libkrb5__L \ kerberos5/lib/libhx509__L kerberos5/lib/libasn1__L lib/libcom_err__L \ lib/libmd__L kerberos5/lib/libroken__L .endif .endif .endif _secure_lib= secure/lib .endif .if ${MK_KERBEROS} != "no" kerberos5/lib/libasn1__L: lib/libcom_err__L kerberos5/lib/libroken__L kerberos5/lib/libhdb__L: kerberos5/lib/libasn1__L lib/libcom_err__L \ kerberos5/lib/libkrb5__L kerberos5/lib/libroken__L \ kerberos5/lib/libwind__L kerberos5/lib/libheimsqlite__L kerberos5/lib/libheimntlm__L: secure/lib/libcrypto__L kerberos5/lib/libkrb5__L \ kerberos5/lib/libroken__L lib/libcom_err__L kerberos5/lib/libhx509__L: kerberos5/lib/libasn1__L lib/libcom_err__L \ secure/lib/libcrypto__L kerberos5/lib/libroken__L kerberos5/lib/libwind__L kerberos5/lib/libkrb5__L: kerberos5/lib/libasn1__L lib/libcom_err__L \ lib/libcrypt__L secure/lib/libcrypto__L kerberos5/lib/libhx509__L \ kerberos5/lib/libroken__L kerberos5/lib/libwind__L \ kerberos5/lib/libheimbase__L kerberos5/lib/libheimipcc__L kerberos5/lib/libroken__L: lib/libcrypt__L kerberos5/lib/libwind__L: kerberos5/lib/libroken__L lib/libcom_err__L kerberos5/lib/libheimbase__L: lib/libthr__L kerberos5/lib/libheimipcc__L: kerberos5/lib/libroken__L kerberos5/lib/libheimbase__L lib/libthr__L kerberos5/lib/libheimsqlite__L: lib/libthr__L .endif .if ${MK_GSSAPI} != "no" _lib_libgssapi= lib/libgssapi .endif .if ${MK_KERBEROS} != "no" _kerberos5_lib= kerberos5/lib _kerberos5_lib_libasn1= kerberos5/lib/libasn1 _kerberos5_lib_libhdb= kerberos5/lib/libhdb _kerberos5_lib_libheimbase= kerberos5/lib/libheimbase _kerberos5_lib_libkrb5= kerberos5/lib/libkrb5 _kerberos5_lib_libhx509= kerberos5/lib/libhx509 _kerberos5_lib_libroken= kerberos5/lib/libroken _kerberos5_lib_libheimntlm= kerberos5/lib/libheimntlm _kerberos5_lib_libheimsqlite= kerberos5/lib/libheimsqlite _kerberos5_lib_libheimipcc= kerberos5/lib/libheimipcc _kerberos5_lib_libwind= kerberos5/lib/libwind _libcom_err= lib/libcom_err .endif .if ${MK_NIS} != "no" _lib_libypclnt= lib/libypclnt .endif .if ${MK_OPENSSL} == "no" lib/libradius__L: lib/libmd__L .endif gnu/lib/libdialog__L: lib/msun__L lib/ncurses/ncursesw__L .for _lib in ${_prereq_libs} ${_lib}__PL: .PHONY .MAKE .if exists(${.CURDIR}/${_lib}) ${_+_}@${ECHODIR} "===> ${_lib} (obj,depend,all,install)"; \ cd ${.CURDIR}/${_lib} && \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ obj && \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ depend && \ ${MAKE} MK_TESTS=no MK_PROFILE=no -DNO_PIC \ DIRPRFX=${_lib}/ all && \ ${MAKE} MK_TESTS=no MK_PROFILE=no -DNO_PIC \ DIRPRFX=${_lib}/ install .endif .endfor .for _lib in ${_startup_libs} ${_prebuild_libs:Nlib/libpam} ${_generic_libs} ${_lib}__L: .PHONY .MAKE .if exists(${.CURDIR}/${_lib}) ${_+_}@${ECHODIR} "===> ${_lib} (obj,depend,all,install)"; \ cd ${.CURDIR}/${_lib} && \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ obj && \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ depend && \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ all && \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ install .endif .endfor # libpam is special: we need to build static PAM modules before # static PAM library, and dynamic PAM library before dynamic PAM # modules. lib/libpam__L: .PHONY .MAKE ${_+_}@${ECHODIR} "===> lib/libpam (obj,depend,all,install)"; \ cd ${.CURDIR}/lib/libpam && \ ${MAKE} MK_TESTS=no DIRPRFX=lib/libpam/ obj && \ ${MAKE} MK_TESTS=no DIRPRFX=lib/libpam/ depend && \ ${MAKE} MK_TESTS=no DIRPRFX=lib/libpam/ \ -D_NO_LIBPAM_SO_YET all && \ ${MAKE} MK_TESTS=no DIRPRFX=lib/libpam/ \ -D_NO_LIBPAM_SO_YET install _prereq_libs: ${_prereq_libs:S/$/__PL/} _startup_libs: ${_startup_libs:S/$/__L/} _prebuild_libs: ${_prebuild_libs:S/$/__L/} _generic_libs: ${_generic_libs:S/$/__L/} .for __target in all clean cleandepend cleandir depend includes obj .for entry in ${SUBDIR} ${entry}.${__target}__D: .PHONY .MAKE ${_+_}@set -e; if test -d ${.CURDIR}/${entry}.${MACHINE_ARCH}; then \ ${ECHODIR} "===> ${DIRPRFX}${entry}.${MACHINE_ARCH} (${__target})"; \ edir=${entry}.${MACHINE_ARCH}; \ cd ${.CURDIR}/$${edir}; \ else \ ${ECHODIR} "===> ${DIRPRFX}${entry} (${__target})"; \ edir=${entry}; \ cd ${.CURDIR}/$${edir}; \ fi; \ ${MAKE} ${__target} DIRPRFX=${DIRPRFX}$${edir}/ .endfor par-${__target}: ${SUBDIR:S/$/.${__target}__D/} .endfor .include .if make(check-old) || make(check-old-dirs) || \ make(check-old-files) || make(check-old-libs) || \ make(delete-old) || make(delete-old-dirs) || \ make(delete-old-files) || make(delete-old-libs) # # check for / delete old files section # .include "ObsoleteFiles.inc" OLD_LIBS_MESSAGE="Please be sure no application still uses those libraries, \ else you can not start such an application. Consult UPDATING for more \ information regarding how to cope with the removal/revision bump of a \ specific library." .if !defined(BATCH_DELETE_OLD_FILES) RM_I=-i .else RM_I=-v .endif delete-old-files: @echo ">>> Removing old files (only deletes safe to delete libs)" # Ask for every old file if the user really wants to remove it. # It's annoying, but better safe than sorry. # NB: We cannot pass the list of OLD_FILES as a parameter because the # argument list will get too long. Using .for/.endfor make "loops" will make # the Makefile parser segfault. @exec 3<&0; \ cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_FILES -V "OLD_FILES:Musr/share/*.gz:R" | xargs -n1 | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ chflags noschg "${DESTDIR}/$${file}" 2>/dev/null || true; \ rm ${RM_I} "${DESTDIR}/$${file}" <&3; \ fi; \ done # Remove catpages without corresponding manpages. @exec 3<&0; \ find ${DESTDIR}/usr/share/man/cat* ! -type d | \ sed -ep -e's:${DESTDIR}/usr/share/man/cat:${DESTDIR}/usr/share/man/man:' | \ while read catpage; do \ read manpage; \ if [ ! -e "$${manpage}" ]; then \ rm ${RM_I} $${catpage} <&3; \ fi; \ done @echo ">>> Old files removed" check-old-files: @echo ">>> Checking for old files" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_FILES -V "OLD_FILES:Musr/share/*.gz:R" | xargs -n1 | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ echo "${DESTDIR}/$${file}"; \ fi; \ done # Check for catpages without corresponding manpages. @find ${DESTDIR}/usr/share/man/cat* ! -type d | \ sed -ep -e's:${DESTDIR}/usr/share/man/cat:${DESTDIR}/usr/share/man/man:' | \ while read catpage; do \ read manpage; \ if [ ! -e "$${manpage}" ]; then \ echo $${catpage}; \ fi; \ done delete-old-libs: @echo ">>> Removing old libraries" @echo "${OLD_LIBS_MESSAGE}" | fmt @exec 3<&0; \ cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_LIBS | xargs -n1 | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ chflags noschg "${DESTDIR}/$${file}" 2>/dev/null || true; \ rm ${RM_I} "${DESTDIR}/$${file}" <&3; \ fi; \ for ext in debug symbols; do \ if ! [ -e "${DESTDIR}/$${file}" ] && [ -f \ "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ rm ${RM_I} "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" \ <&3; \ fi; \ done; \ done @echo ">>> Old libraries removed" check-old-libs: @echo ">>> Checking for old libraries" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_LIBS | xargs -n1 | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ echo "${DESTDIR}/$${file}"; \ fi; \ for ext in debug symbols; do \ if [ -f "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}"; \ fi; \ done; \ done delete-old-dirs: @echo ">>> Removing old directories" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_DIRS | xargs -n1 | sort -r | \ while read dir; do \ if [ -d "${DESTDIR}/$${dir}" ]; then \ rmdir -v "${DESTDIR}/$${dir}" || true; \ elif [ -L "${DESTDIR}/$${dir}" ]; then \ echo "${DESTDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ done @echo ">>> Old directories removed" check-old-dirs: @echo ">>> Checking for old directories" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_DIRS | xargs -n1 | \ while read dir; do \ if [ -d "${DESTDIR}/$${dir}" ]; then \ echo "${DESTDIR}/$${dir}"; \ elif [ -L "${DESTDIR}/$${dir}" ]; then \ echo "${DESTDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ done delete-old: delete-old-files delete-old-dirs @echo "To remove old libraries run '${MAKE} delete-old-libs'." check-old: check-old-files check-old-libs check-old-dirs @echo "To remove old files and directories run '${MAKE} delete-old'." @echo "To remove old libraries run '${MAKE} delete-old-libs'." .endif # # showconfig - show build configuration. # showconfig: @(${MAKE} -n -f ${.CURDIR}/sys/conf/kern.opts.mk -V dummy -dg1; \ ${MAKE} -n -f ${.CURDIR}/share/mk/src.opts.mk -V dummy -dg1) 2>&1 | grep ^MK_ | sort -u .if !empty(KRNLOBJDIR) && !empty(KERNCONF) DTBOUTPUTPATH= ${KRNLOBJDIR}/${KERNCONF}/ .if !defined(FDT_DTS_FILE) || empty(FDT_DTS_FILE) .if exists(${KERNCONFDIR}/${KERNCONF}) FDT_DTS_FILE!= awk 'BEGIN {FS="="} /^makeoptions[[:space:]]+FDT_DTS_FILE/ {print $$2}' \ '${KERNCONFDIR}/${KERNCONF}' ; echo .endif .endif .endif .if !defined(DTBOUTPUTPATH) || !exists(${DTBOUTPUTPATH}) DTBOUTPUTPATH= ${.CURDIR} .endif # # Build 'standalone' Device Tree Blob # builddtb: @PATH=${TMPPATH} MACHINE=${TARGET} \ ${.CURDIR}/sys/tools/fdt/make_dtb.sh ${.CURDIR}/sys \ "${FDT_DTS_FILE}" ${DTBOUTPUTPATH} ############### .if defined(TARGET) && defined(TARGET_ARCH) .if ${TARGET} == ${MACHINE} && ${TARGET_ARCH} == ${MACHINE_ARCH} XDEV_CPUTYPE?=${CPUTYPE} .else XDEV_CPUTYPE?=${TARGET_CPUTYPE} .endif NOFUN=-DNO_FSCHG MK_HTML=no MK_INFO=no -DNO_LINT \ MK_MAN=no MK_NLS=no MK_PROFILE=no \ MK_KERBEROS=no MK_RESCUE=no MK_TESTS=no MK_WARNS=no \ TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ CPUTYPE=${XDEV_CPUTYPE} XDDIR=${TARGET_ARCH}-freebsd XDTP?=/usr/${XDDIR} .if ${XDTP:N/*} .error XDTP variable should be an absolute path .endif CDBENV=MAKEOBJDIRPREFIX=${MAKEOBJDIRPREFIX}/${XDDIR} \ INSTALL="sh ${.CURDIR}/tools/install.sh" CDENV= ${CDBENV} \ _SHLIBDIRPREFIX=${XDDESTDIR} \ TOOLS_PREFIX=${XDTP} CD2CFLAGS=-isystem ${XDDESTDIR}/usr/include -L${XDDESTDIR}/usr/lib \ --sysroot=${XDDESTDIR}/ -B${XDDESTDIR}/usr/libexec \ -B${XDDESTDIR}/usr/bin -B${XDDESTDIR}/usr/lib CD2ENV=${CDENV} CC="${CC} ${CD2CFLAGS}" CXX="${CXX} ${CD2CFLAGS}" \ CPP="${CPP} ${CD2CFLAGS}" \ MACHINE=${TARGET} MACHINE_ARCH=${TARGET_ARCH} CDTMP= ${MAKEOBJDIRPREFIX}/${XDDIR}/${.CURDIR}/tmp CDMAKE=${CDENV} PATH=${CDTMP}/usr/bin:${PATH} ${MAKE} ${NOFUN} CD2MAKE=${CD2ENV} PATH=${CDTMP}/usr/bin:${XDDESTDIR}/usr/bin:${PATH} ${MAKE} ${NOFUN} XDDESTDIR=${DESTDIR}/${XDTP} .if !defined(OSREL) OSREL!= uname -r | sed -e 's/[-(].*//' .endif .ORDER: xdev-build xdev-install xdev-links xdev: xdev-build xdev-install .ORDER: _xb-worldtmp _xb-bootstrap-tools _xb-build-tools _xb-cross-tools xdev-build: _xb-worldtmp _xb-bootstrap-tools _xb-build-tools _xb-cross-tools _xb-worldtmp: mkdir -p ${CDTMP}/usr mtree -deU -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${CDTMP}/usr >/dev/null _xb-bootstrap-tools: .for _tool in \ ${_clang_tblgen} ${_+_}@${ECHODIR} "===> ${_tool} (obj,depend,all,install)"; \ cd ${.CURDIR}/${_tool} && \ ${CDMAKE} DIRPRFX=${_tool}/ obj && \ ${CDMAKE} DIRPRFX=${_tool}/ depend && \ ${CDMAKE} DIRPRFX=${_tool}/ all && \ ${CDMAKE} DIRPRFX=${_tool}/ DESTDIR=${CDTMP} install .endfor _xb-build-tools: ${_+_}@cd ${.CURDIR}; \ ${CDBENV} ${MAKE} -f Makefile.inc1 ${NOFUN} build-tools _xb-cross-tools: .for _tool in \ ${_binutils} \ + ${_elftctools} \ usr.bin/ar \ ${_clang_libs} \ ${_clang} \ ${_cc} ${_+_}@${ECHODIR} "===> xdev ${_tool} (obj,depend,all)"; \ cd ${.CURDIR}/${_tool} && \ ${CDMAKE} DIRPRFX=${_tool}/ obj && \ ${CDMAKE} DIRPRFX=${_tool}/ depend && \ ${CDMAKE} DIRPRFX=${_tool}/ all .endfor _xi-mtree: ${_+_}@${ECHODIR} "mtree populating ${XDDESTDIR}" mkdir -p ${XDDESTDIR} mtree -deU -f ${.CURDIR}/etc/mtree/BSD.root.dist \ -p ${XDDESTDIR} >/dev/null mtree -deU -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${XDDESTDIR}/usr >/dev/null mtree -deU -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${XDDESTDIR}/usr/include >/dev/null .if ${MK_TESTS} != "no" mtree -deU -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${XDDESTDIR}/usr >/dev/null .endif .ORDER: xdev-build _xi-mtree _xi-cross-tools _xi-includes _xi-libraries xdev-install: xdev-build _xi-mtree _xi-cross-tools _xi-includes _xi-libraries _xi-cross-tools: @echo "_xi-cross-tools" .for _tool in \ ${_binutils} \ + ${_elftctools} \ usr.bin/ar \ ${_clang_libs} \ ${_clang} \ ${_cc} ${_+_}@${ECHODIR} "===> xdev ${_tool} (install)"; \ cd ${.CURDIR}/${_tool}; \ ${CDMAKE} DIRPRFX=${_tool}/ install DESTDIR=${XDDESTDIR} .endfor _xi-includes: ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 par-includes \ DESTDIR=${XDDESTDIR} _xi-libraries: ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 libraries \ DESTDIR=${XDDESTDIR} xdev-links: ${_+_}cd ${XDDESTDIR}/usr/bin; \ mkdir -p ../../../../usr/bin; \ for i in *; do \ ln -sf ../../${XDTP}/usr/bin/$$i \ ../../../../usr/bin/${XDDIR}-$$i; \ ln -sf ../../${XDTP}/usr/bin/$$i \ ../../../../usr/bin/${XDDIR}${OSREL}-$$i; \ done .else xdev xdev-build xdev-install xdev-links: @echo "*** Error: Both TARGET and TARGET_ARCH must be defined for \"${.TARGET}\" target" .endif Index: projects/clang350-import/contrib/binutils/bfd/elflink.c =================================================================== --- projects/clang350-import/contrib/binutils/bfd/elflink.c (revision 275386) +++ projects/clang350-import/contrib/binutils/bfd/elflink.c (revision 275387) @@ -1,11571 +1,11600 @@ /* ELF linking support for BFD. Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of BFD, the Binary File Descriptor library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sysdep.h" #include "bfd.h" #include "bfdlink.h" #include "libbfd.h" #define ARCH_SIZE 0 #include "elf-bfd.h" #include "safe-ctype.h" #include "libiberty.h" #include "objalloc.h" /* Define a symbol in a dynamic linkage section. */ struct elf_link_hash_entry * _bfd_elf_define_linkage_sym (bfd *abfd, struct bfd_link_info *info, asection *sec, const char *name) { struct elf_link_hash_entry *h; struct bfd_link_hash_entry *bh; const struct elf_backend_data *bed; h = elf_link_hash_lookup (elf_hash_table (info), name, FALSE, FALSE, FALSE); if (h != NULL) { /* Zap symbol defined in an as-needed lib that wasn't linked. This is a symptom of a larger problem: Absolute symbols defined in shared libraries can't be overridden, because we lose the link to the bfd which is via the symbol section. */ h->root.type = bfd_link_hash_new; } bh = &h->root; if (!_bfd_generic_link_add_one_symbol (info, abfd, name, BSF_GLOBAL, sec, 0, NULL, FALSE, get_elf_backend_data (abfd)->collect, &bh)) return NULL; h = (struct elf_link_hash_entry *) bh; h->def_regular = 1; h->type = STT_OBJECT; h->other = (h->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN; bed = get_elf_backend_data (abfd); (*bed->elf_backend_hide_symbol) (info, h, TRUE); return h; } bfd_boolean _bfd_elf_create_got_section (bfd *abfd, struct bfd_link_info *info) { flagword flags; asection *s; struct elf_link_hash_entry *h; const struct elf_backend_data *bed = get_elf_backend_data (abfd); int ptralign; /* This function may be called more than once. */ s = bfd_get_section_by_name (abfd, ".got"); if (s != NULL && (s->flags & SEC_LINKER_CREATED) != 0) return TRUE; switch (bed->s->arch_size) { case 32: ptralign = 2; break; case 64: ptralign = 3; break; default: bfd_set_error (bfd_error_bad_value); return FALSE; } flags = bed->dynamic_sec_flags; s = bfd_make_section_with_flags (abfd, ".got", flags); if (s == NULL || !bfd_set_section_alignment (abfd, s, ptralign)) return FALSE; if (bed->want_got_plt) { s = bfd_make_section_with_flags (abfd, ".got.plt", flags); if (s == NULL || !bfd_set_section_alignment (abfd, s, ptralign)) return FALSE; } if (bed->want_got_sym) { /* Define the symbol _GLOBAL_OFFSET_TABLE_ at the start of the .got (or .got.plt) section. We don't do this in the linker script because we don't want to define the symbol if we are not creating a global offset table. */ h = _bfd_elf_define_linkage_sym (abfd, info, s, "_GLOBAL_OFFSET_TABLE_"); elf_hash_table (info)->hgot = h; if (h == NULL) return FALSE; } /* The first bit of the global offset table is the header. */ s->size += bed->got_header_size; return TRUE; } /* Create a strtab to hold the dynamic symbol names. */ static bfd_boolean _bfd_elf_link_create_dynstrtab (bfd *abfd, struct bfd_link_info *info) { struct elf_link_hash_table *hash_table; hash_table = elf_hash_table (info); if (hash_table->dynobj == NULL) hash_table->dynobj = abfd; if (hash_table->dynstr == NULL) { hash_table->dynstr = _bfd_elf_strtab_init (); if (hash_table->dynstr == NULL) return FALSE; } return TRUE; } /* Create some sections which will be filled in with dynamic linking information. ABFD is an input file which requires dynamic sections to be created. The dynamic sections take up virtual memory space when the final executable is run, so we need to create them before addresses are assigned to the output sections. We work out the actual contents and size of these sections later. */ bfd_boolean _bfd_elf_link_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) { flagword flags; register asection *s; const struct elf_backend_data *bed; if (! is_elf_hash_table (info->hash)) return FALSE; if (elf_hash_table (info)->dynamic_sections_created) return TRUE; if (!_bfd_elf_link_create_dynstrtab (abfd, info)) return FALSE; abfd = elf_hash_table (info)->dynobj; bed = get_elf_backend_data (abfd); flags = bed->dynamic_sec_flags; /* A dynamically linked executable has a .interp section, but a shared library does not. */ if (info->executable) { s = bfd_make_section_with_flags (abfd, ".interp", flags | SEC_READONLY); if (s == NULL) return FALSE; } /* Create sections to hold version informations. These are removed if they are not needed. */ s = bfd_make_section_with_flags (abfd, ".gnu.version_d", flags | SEC_READONLY); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) return FALSE; s = bfd_make_section_with_flags (abfd, ".gnu.version", flags | SEC_READONLY); if (s == NULL || ! bfd_set_section_alignment (abfd, s, 1)) return FALSE; s = bfd_make_section_with_flags (abfd, ".gnu.version_r", flags | SEC_READONLY); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) return FALSE; s = bfd_make_section_with_flags (abfd, ".dynsym", flags | SEC_READONLY); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) return FALSE; s = bfd_make_section_with_flags (abfd, ".dynstr", flags | SEC_READONLY); if (s == NULL) return FALSE; s = bfd_make_section_with_flags (abfd, ".dynamic", flags); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) return FALSE; /* The special symbol _DYNAMIC is always set to the start of the .dynamic section. We could set _DYNAMIC in a linker script, but we only want to define it if we are, in fact, creating a .dynamic section. We don't want to define it if there is no .dynamic section, since on some ELF platforms the start up code examines it to decide how to initialize the process. */ if (!_bfd_elf_define_linkage_sym (abfd, info, s, "_DYNAMIC")) return FALSE; if (info->emit_hash) { s = bfd_make_section_with_flags (abfd, ".hash", flags | SEC_READONLY); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) return FALSE; elf_section_data (s)->this_hdr.sh_entsize = bed->s->sizeof_hash_entry; } if (info->emit_gnu_hash) { s = bfd_make_section_with_flags (abfd, ".gnu.hash", flags | SEC_READONLY); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) return FALSE; /* For 64-bit ELF, .gnu.hash is a non-uniform entity size section: 4 32-bit words followed by variable count of 64-bit words, then variable count of 32-bit words. */ if (bed->s->arch_size == 64) elf_section_data (s)->this_hdr.sh_entsize = 0; else elf_section_data (s)->this_hdr.sh_entsize = 4; } /* Let the backend create the rest of the sections. This lets the backend set the right flags. The backend will normally create the .got and .plt sections. */ if (! (*bed->elf_backend_create_dynamic_sections) (abfd, info)) return FALSE; elf_hash_table (info)->dynamic_sections_created = TRUE; return TRUE; } /* Create dynamic sections when linking against a dynamic object. */ bfd_boolean _bfd_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) { flagword flags, pltflags; struct elf_link_hash_entry *h; asection *s; const struct elf_backend_data *bed = get_elf_backend_data (abfd); /* We need to create .plt, .rel[a].plt, .got, .got.plt, .dynbss, and .rel[a].bss sections. */ flags = bed->dynamic_sec_flags; pltflags = flags; if (bed->plt_not_loaded) /* We do not clear SEC_ALLOC here because we still want the OS to allocate space for the section; it's just that there's nothing to read in from the object file. */ pltflags &= ~ (SEC_CODE | SEC_LOAD | SEC_HAS_CONTENTS); else pltflags |= SEC_ALLOC | SEC_CODE | SEC_LOAD; if (bed->plt_readonly) pltflags |= SEC_READONLY; s = bfd_make_section_with_flags (abfd, ".plt", pltflags); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment)) return FALSE; /* Define the symbol _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt section. */ if (bed->want_plt_sym) { h = _bfd_elf_define_linkage_sym (abfd, info, s, "_PROCEDURE_LINKAGE_TABLE_"); elf_hash_table (info)->hplt = h; if (h == NULL) return FALSE; } s = bfd_make_section_with_flags (abfd, (bed->default_use_rela_p ? ".rela.plt" : ".rel.plt"), flags | SEC_READONLY); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) return FALSE; if (! _bfd_elf_create_got_section (abfd, info)) return FALSE; if (bed->want_dynbss) { /* The .dynbss section is a place to put symbols which are defined by dynamic objects, are referenced by regular objects, and are not functions. We must allocate space for them in the process image and use a R_*_COPY reloc to tell the dynamic linker to initialize them at run time. The linker script puts the .dynbss section into the .bss section of the final image. */ s = bfd_make_section_with_flags (abfd, ".dynbss", (SEC_ALLOC | SEC_LINKER_CREATED)); if (s == NULL) return FALSE; /* The .rel[a].bss section holds copy relocs. This section is not normally needed. We need to create it here, though, so that the linker will map it to an output section. We can't just create it only if we need it, because we will not know whether we need it until we have seen all the input files, and the first time the main linker code calls BFD after examining all the input files (size_dynamic_sections) the input sections have already been mapped to the output sections. If the section turns out not to be needed, we can discard it later. We will never need this section when generating a shared object, since they do not use copy relocs. */ if (! info->shared) { s = bfd_make_section_with_flags (abfd, (bed->default_use_rela_p ? ".rela.bss" : ".rel.bss"), flags | SEC_READONLY); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) return FALSE; } } return TRUE; } /* Record a new dynamic symbol. We record the dynamic symbols as we read the input files, since we need to have a list of all of them before we can determine the final sizes of the output sections. Note that we may actually call this function even though we are not going to output any dynamic symbols; in some cases we know that a symbol should be in the dynamic symbol table, but only if there is one. */ bfd_boolean bfd_elf_link_record_dynamic_symbol (struct bfd_link_info *info, struct elf_link_hash_entry *h) { if (h->dynindx == -1) { struct elf_strtab_hash *dynstr; char *p; const char *name; bfd_size_type indx; /* XXX: The ABI draft says the linker must turn hidden and internal symbols into STB_LOCAL symbols when producing the DSO. However, if ld.so honors st_other in the dynamic table, this would not be necessary. */ switch (ELF_ST_VISIBILITY (h->other)) { case STV_INTERNAL: case STV_HIDDEN: if (h->root.type != bfd_link_hash_undefined && h->root.type != bfd_link_hash_undefweak) { h->forced_local = 1; if (!elf_hash_table (info)->is_relocatable_executable) return TRUE; } default: break; } h->dynindx = elf_hash_table (info)->dynsymcount; ++elf_hash_table (info)->dynsymcount; dynstr = elf_hash_table (info)->dynstr; if (dynstr == NULL) { /* Create a strtab to hold the dynamic symbol names. */ elf_hash_table (info)->dynstr = dynstr = _bfd_elf_strtab_init (); if (dynstr == NULL) return FALSE; } /* We don't put any version information in the dynamic string table. */ name = h->root.root.string; p = strchr (name, ELF_VER_CHR); if (p != NULL) /* We know that the p points into writable memory. In fact, there are only a few symbols that have read-only names, being those like _GLOBAL_OFFSET_TABLE_ that are created specially by the backends. Most symbols will have names pointing into an ELF string table read from a file, or to objalloc memory. */ *p = 0; indx = _bfd_elf_strtab_add (dynstr, name, p != NULL); if (p != NULL) *p = ELF_VER_CHR; if (indx == (bfd_size_type) -1) return FALSE; h->dynstr_index = indx; } return TRUE; } /* Mark a symbol dynamic. */ void bfd_elf_link_mark_dynamic_symbol (struct bfd_link_info *info, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) { struct bfd_elf_dynamic_list *d = info->dynamic_list; /* It may be called more than once on the same H. */ if(h->dynamic || info->relocatable) return; if ((info->dynamic_data && (h->type == STT_OBJECT || (sym != NULL && ELF_ST_TYPE (sym->st_info) == STT_OBJECT))) || (d != NULL && h->root.type == bfd_link_hash_new && (*d->match) (&d->head, NULL, h->root.root.string))) h->dynamic = 1; } /* Record an assignment to a symbol made by a linker script. We need this in case some dynamic object refers to this symbol. */ bfd_boolean bfd_elf_record_link_assignment (bfd *output_bfd, struct bfd_link_info *info, const char *name, bfd_boolean provide, bfd_boolean hidden) { struct elf_link_hash_entry *h; struct elf_link_hash_table *htab; if (!is_elf_hash_table (info->hash)) return TRUE; htab = elf_hash_table (info); h = elf_link_hash_lookup (htab, name, !provide, TRUE, FALSE); if (h == NULL) return provide; /* Since we're defining the symbol, don't let it seem to have not been defined. record_dynamic_symbol and size_dynamic_sections may depend on this. */ if (h->root.type == bfd_link_hash_undefweak || h->root.type == bfd_link_hash_undefined) { h->root.type = bfd_link_hash_new; if (h->root.u.undef.next != NULL || htab->root.undefs_tail == &h->root) bfd_link_repair_undef_list (&htab->root); } else if (h->root.type == bfd_link_hash_new) { bfd_elf_link_mark_dynamic_symbol (info, h, NULL); h->non_elf = 0; } else if (h->root.type == bfd_link_hash_indirect) { const struct elf_backend_data *bed = get_elf_backend_data (output_bfd); struct elf_link_hash_entry *hv = h; do hv = (struct elf_link_hash_entry *) hv->root.u.i.link; while (hv->root.type == bfd_link_hash_indirect || hv->root.type == bfd_link_hash_warning); h->root.type = bfd_link_hash_undefined; hv->root.type = bfd_link_hash_indirect; hv->root.u.i.link = (struct bfd_link_hash_entry *) h; (*bed->elf_backend_copy_indirect_symbol) (info, h, hv); } else if (h->root.type == bfd_link_hash_warning) { abort (); } /* If this symbol is being provided by the linker script, and it is currently defined by a dynamic object, but not by a regular object, then mark it as undefined so that the generic linker will force the correct value. */ if (provide && h->def_dynamic && !h->def_regular) h->root.type = bfd_link_hash_undefined; /* If this symbol is not being provided by the linker script, and it is currently defined by a dynamic object, but not by a regular object, then clear out any version information because the symbol will not be associated with the dynamic object any more. */ if (!provide && h->def_dynamic && !h->def_regular) h->verinfo.verdef = NULL; h->def_regular = 1; if (provide && hidden) { const struct elf_backend_data *bed = get_elf_backend_data (output_bfd); h->other = (h->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN; (*bed->elf_backend_hide_symbol) (info, h, TRUE); } /* STV_HIDDEN and STV_INTERNAL symbols must be STB_LOCAL in shared objects and executables. */ if (!info->relocatable && h->dynindx != -1 && (ELF_ST_VISIBILITY (h->other) == STV_HIDDEN || ELF_ST_VISIBILITY (h->other) == STV_INTERNAL)) h->forced_local = 1; if ((h->def_dynamic || h->ref_dynamic || info->shared || (info->executable && elf_hash_table (info)->is_relocatable_executable)) && h->dynindx == -1) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; /* If this is a weak defined symbol, and we know a corresponding real symbol from the same dynamic object, make sure the real symbol is also made into a dynamic symbol. */ if (h->u.weakdef != NULL && h->u.weakdef->dynindx == -1) { if (! bfd_elf_link_record_dynamic_symbol (info, h->u.weakdef)) return FALSE; } } return TRUE; } /* Record a new local dynamic symbol. Returns 0 on failure, 1 on success, and 2 on a failure caused by attempting to record a symbol in a discarded section, eg. a discarded link-once section symbol. */ int bfd_elf_link_record_local_dynamic_symbol (struct bfd_link_info *info, bfd *input_bfd, long input_indx) { bfd_size_type amt; struct elf_link_local_dynamic_entry *entry; struct elf_link_hash_table *eht; struct elf_strtab_hash *dynstr; unsigned long dynstr_index; char *name; Elf_External_Sym_Shndx eshndx; char esym[sizeof (Elf64_External_Sym)]; if (! is_elf_hash_table (info->hash)) return 0; /* See if the entry exists already. */ for (entry = elf_hash_table (info)->dynlocal; entry ; entry = entry->next) if (entry->input_bfd == input_bfd && entry->input_indx == input_indx) return 1; amt = sizeof (*entry); entry = bfd_alloc (input_bfd, amt); if (entry == NULL) return 0; /* Go find the symbol, so that we can find it's name. */ if (!bfd_elf_get_elf_syms (input_bfd, &elf_tdata (input_bfd)->symtab_hdr, 1, input_indx, &entry->isym, esym, &eshndx)) { bfd_release (input_bfd, entry); return 0; } if (entry->isym.st_shndx != SHN_UNDEF && (entry->isym.st_shndx < SHN_LORESERVE || entry->isym.st_shndx > SHN_HIRESERVE)) { asection *s; s = bfd_section_from_elf_index (input_bfd, entry->isym.st_shndx); if (s == NULL || bfd_is_abs_section (s->output_section)) { /* We can still bfd_release here as nothing has done another bfd_alloc. We can't do this later in this function. */ bfd_release (input_bfd, entry); return 2; } } name = (bfd_elf_string_from_elf_section (input_bfd, elf_tdata (input_bfd)->symtab_hdr.sh_link, entry->isym.st_name)); dynstr = elf_hash_table (info)->dynstr; if (dynstr == NULL) { /* Create a strtab to hold the dynamic symbol names. */ elf_hash_table (info)->dynstr = dynstr = _bfd_elf_strtab_init (); if (dynstr == NULL) return 0; } dynstr_index = _bfd_elf_strtab_add (dynstr, name, FALSE); if (dynstr_index == (unsigned long) -1) return 0; entry->isym.st_name = dynstr_index; eht = elf_hash_table (info); entry->next = eht->dynlocal; eht->dynlocal = entry; entry->input_bfd = input_bfd; entry->input_indx = input_indx; eht->dynsymcount++; /* Whatever binding the symbol had before, it's now local. */ entry->isym.st_info = ELF_ST_INFO (STB_LOCAL, ELF_ST_TYPE (entry->isym.st_info)); /* The dynindx will be set at the end of size_dynamic_sections. */ return 1; } /* Return the dynindex of a local dynamic symbol. */ long _bfd_elf_link_lookup_local_dynindx (struct bfd_link_info *info, bfd *input_bfd, long input_indx) { struct elf_link_local_dynamic_entry *e; for (e = elf_hash_table (info)->dynlocal; e ; e = e->next) if (e->input_bfd == input_bfd && e->input_indx == input_indx) return e->dynindx; return -1; } /* This function is used to renumber the dynamic symbols, if some of them are removed because they are marked as local. This is called via elf_link_hash_traverse. */ static bfd_boolean elf_link_renumber_hash_table_dynsyms (struct elf_link_hash_entry *h, void *data) { size_t *count = data; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->forced_local) return TRUE; if (h->dynindx != -1) h->dynindx = ++(*count); return TRUE; } /* Like elf_link_renumber_hash_table_dynsyms, but just number symbols with STB_LOCAL binding. */ static bfd_boolean elf_link_renumber_local_hash_table_dynsyms (struct elf_link_hash_entry *h, void *data) { size_t *count = data; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (!h->forced_local) return TRUE; if (h->dynindx != -1) h->dynindx = ++(*count); return TRUE; } /* Return true if the dynamic symbol for a given section should be omitted when creating a shared library. */ bfd_boolean _bfd_elf_link_omit_section_dynsym (bfd *output_bfd ATTRIBUTE_UNUSED, struct bfd_link_info *info, asection *p) { struct elf_link_hash_table *htab; switch (elf_section_data (p)->this_hdr.sh_type) { case SHT_PROGBITS: case SHT_NOBITS: /* If sh_type is yet undecided, assume it could be SHT_PROGBITS/SHT_NOBITS. */ case SHT_NULL: htab = elf_hash_table (info); if (p == htab->tls_sec) return FALSE; if (htab->text_index_section != NULL) return p != htab->text_index_section && p != htab->data_index_section; if (strcmp (p->name, ".got") == 0 || strcmp (p->name, ".got.plt") == 0 || strcmp (p->name, ".plt") == 0) { asection *ip; if (htab->dynobj != NULL && (ip = bfd_get_section_by_name (htab->dynobj, p->name)) != NULL && (ip->flags & SEC_LINKER_CREATED) && ip->output_section == p) return TRUE; } return FALSE; /* There shouldn't be section relative relocations against any other section. */ default: return TRUE; } } /* Assign dynsym indices. In a shared library we generate a section symbol for each output section, which come first. Next come symbols which have been forced to local binding. Then all of the back-end allocated local dynamic syms, followed by the rest of the global symbols. */ static unsigned long _bfd_elf_link_renumber_dynsyms (bfd *output_bfd, struct bfd_link_info *info, unsigned long *section_sym_count) { unsigned long dynsymcount = 0; if (info->shared || elf_hash_table (info)->is_relocatable_executable) { const struct elf_backend_data *bed = get_elf_backend_data (output_bfd); asection *p; for (p = output_bfd->sections; p ; p = p->next) if ((p->flags & SEC_EXCLUDE) == 0 && (p->flags & SEC_ALLOC) != 0 && !(*bed->elf_backend_omit_section_dynsym) (output_bfd, info, p)) elf_section_data (p)->dynindx = ++dynsymcount; else elf_section_data (p)->dynindx = 0; } *section_sym_count = dynsymcount; elf_link_hash_traverse (elf_hash_table (info), elf_link_renumber_local_hash_table_dynsyms, &dynsymcount); if (elf_hash_table (info)->dynlocal) { struct elf_link_local_dynamic_entry *p; for (p = elf_hash_table (info)->dynlocal; p ; p = p->next) p->dynindx = ++dynsymcount; } elf_link_hash_traverse (elf_hash_table (info), elf_link_renumber_hash_table_dynsyms, &dynsymcount); /* There is an unused NULL entry at the head of the table which we must account for in our count. Unless there weren't any symbols, which means we'll have no table at all. */ if (dynsymcount != 0) ++dynsymcount; elf_hash_table (info)->dynsymcount = dynsymcount; return dynsymcount; } /* This function is called when we want to define a new symbol. It handles the various cases which arise when we find a definition in a dynamic object, or when there is already a definition in a dynamic object. The new symbol is described by NAME, SYM, PSEC, and PVALUE. We set SYM_HASH to the hash table entry. We set OVERRIDE if the old symbol is overriding a new definition. We set TYPE_CHANGE_OK if it is OK for the type to change. We set SIZE_CHANGE_OK if it is OK for the size to change. By OK to change, we mean that we shouldn't warn if the type or size does change. We set POLD_ALIGNMENT if an old common symbol in a dynamic object is overridden by a regular object. */ bfd_boolean _bfd_elf_merge_symbol (bfd *abfd, struct bfd_link_info *info, const char *name, Elf_Internal_Sym *sym, asection **psec, bfd_vma *pvalue, unsigned int *pold_alignment, struct elf_link_hash_entry **sym_hash, bfd_boolean *skip, bfd_boolean *override, bfd_boolean *type_change_ok, bfd_boolean *size_change_ok) { asection *sec, *oldsec; struct elf_link_hash_entry *h; struct elf_link_hash_entry *flip; int bind; bfd *oldbfd; bfd_boolean newdyn, olddyn, olddef, newdef, newdyncommon, olddyncommon; bfd_boolean newweak, oldweak; const struct elf_backend_data *bed; *skip = FALSE; *override = FALSE; sec = *psec; bind = ELF_ST_BIND (sym->st_info); /* Silently discard TLS symbols from --just-syms. There's no way to combine a static TLS block with a new TLS block for this executable. */ if (ELF_ST_TYPE (sym->st_info) == STT_TLS && sec->sec_info_type == ELF_INFO_TYPE_JUST_SYMS) { *skip = TRUE; return TRUE; } if (! bfd_is_und_section (sec)) h = elf_link_hash_lookup (elf_hash_table (info), name, TRUE, FALSE, FALSE); else h = ((struct elf_link_hash_entry *) bfd_wrapped_link_hash_lookup (abfd, info, name, TRUE, FALSE, FALSE)); if (h == NULL) return FALSE; *sym_hash = h; bed = get_elf_backend_data (abfd); /* This code is for coping with dynamic objects, and is only useful if we are doing an ELF link. */ if (!(*bed->relocs_compatible) (abfd->xvec, info->hash->creator)) return TRUE; /* For merging, we only care about real symbols. */ while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* We have to check it for every instance since the first few may be refereences and not all compilers emit symbol type for undefined symbols. */ bfd_elf_link_mark_dynamic_symbol (info, h, sym); /* If we just created the symbol, mark it as being an ELF symbol. Other than that, there is nothing to do--there is no merge issue with a newly defined symbol--so we just return. */ if (h->root.type == bfd_link_hash_new) { h->non_elf = 0; return TRUE; } /* OLDBFD and OLDSEC are a BFD and an ASECTION associated with the existing symbol. */ switch (h->root.type) { default: oldbfd = NULL; oldsec = NULL; break; case bfd_link_hash_undefined: case bfd_link_hash_undefweak: oldbfd = h->root.u.undef.abfd; oldsec = NULL; break; case bfd_link_hash_defined: case bfd_link_hash_defweak: oldbfd = h->root.u.def.section->owner; oldsec = h->root.u.def.section; break; case bfd_link_hash_common: oldbfd = h->root.u.c.p->section->owner; oldsec = h->root.u.c.p->section; break; } /* In cases involving weak versioned symbols, we may wind up trying to merge a symbol with itself. Catch that here, to avoid the confusion that results if we try to override a symbol with itself. The additional tests catch cases like _GLOBAL_OFFSET_TABLE_, which are regular symbols defined in a dynamic object, which we do want to handle here. */ if (abfd == oldbfd && ((abfd->flags & DYNAMIC) == 0 || !h->def_regular)) return TRUE; /* NEWDYN and OLDDYN indicate whether the new or old symbol, respectively, is from a dynamic object. */ newdyn = (abfd->flags & DYNAMIC) != 0; olddyn = FALSE; if (oldbfd != NULL) olddyn = (oldbfd->flags & DYNAMIC) != 0; else if (oldsec != NULL) { /* This handles the special SHN_MIPS_{TEXT,DATA} section indices used by MIPS ELF. */ olddyn = (oldsec->symbol->flags & BSF_DYNAMIC) != 0; } /* NEWDEF and OLDDEF indicate whether the new or old symbol, respectively, appear to be a definition rather than reference. */ newdef = !bfd_is_und_section (sec) && !bfd_is_com_section (sec); olddef = (h->root.type != bfd_link_hash_undefined && h->root.type != bfd_link_hash_undefweak && h->root.type != bfd_link_hash_common); /* When we try to create a default indirect symbol from the dynamic definition with the default version, we skip it if its type and the type of existing regular definition mismatch. We only do it if the existing regular definition won't be dynamic. */ if (pold_alignment == NULL && !info->shared && !info->export_dynamic && !h->ref_dynamic && newdyn && newdef && !olddyn && (olddef || h->root.type == bfd_link_hash_common) && ELF_ST_TYPE (sym->st_info) != h->type && ELF_ST_TYPE (sym->st_info) != STT_NOTYPE && h->type != STT_NOTYPE && !(bed->is_function_type (ELF_ST_TYPE (sym->st_info)) && bed->is_function_type (h->type))) { *skip = TRUE; return TRUE; } /* Check TLS symbol. We don't check undefined symbol introduced by "ld -u". */ if ((ELF_ST_TYPE (sym->st_info) == STT_TLS || h->type == STT_TLS) && ELF_ST_TYPE (sym->st_info) != h->type && oldbfd != NULL) { bfd *ntbfd, *tbfd; bfd_boolean ntdef, tdef; asection *ntsec, *tsec; if (h->type == STT_TLS) { ntbfd = abfd; ntsec = sec; ntdef = newdef; tbfd = oldbfd; tsec = oldsec; tdef = olddef; } else { ntbfd = oldbfd; ntsec = oldsec; ntdef = olddef; tbfd = abfd; tsec = sec; tdef = newdef; } if (tdef && ntdef) (*_bfd_error_handler) (_("%s: TLS definition in %B section %A mismatches non-TLS definition in %B section %A"), tbfd, tsec, ntbfd, ntsec, h->root.root.string); else if (!tdef && !ntdef) (*_bfd_error_handler) (_("%s: TLS reference in %B mismatches non-TLS reference in %B"), tbfd, ntbfd, h->root.root.string); else if (tdef) (*_bfd_error_handler) (_("%s: TLS definition in %B section %A mismatches non-TLS reference in %B"), tbfd, tsec, ntbfd, h->root.root.string); else (*_bfd_error_handler) (_("%s: TLS reference in %B mismatches non-TLS definition in %B section %A"), tbfd, ntbfd, ntsec, h->root.root.string); bfd_set_error (bfd_error_bad_value); return FALSE; } /* We need to remember if a symbol has a definition in a dynamic object or is weak in all dynamic objects. Internal and hidden visibility will make it unavailable to dynamic objects. */ if (newdyn && !h->dynamic_def) { if (!bfd_is_und_section (sec)) h->dynamic_def = 1; else { /* Check if this symbol is weak in all dynamic objects. If it is the first time we see it in a dynamic object, we mark if it is weak. Otherwise, we clear it. */ if (!h->ref_dynamic) { if (bind == STB_WEAK) h->dynamic_weak = 1; } else if (bind != STB_WEAK) h->dynamic_weak = 0; } } /* If the old symbol has non-default visibility, we ignore the new definition from a dynamic object. */ if (newdyn && ELF_ST_VISIBILITY (h->other) != STV_DEFAULT && !bfd_is_und_section (sec)) { *skip = TRUE; /* Make sure this symbol is dynamic. */ h->ref_dynamic = 1; /* A protected symbol has external availability. Make sure it is recorded as dynamic. FIXME: Should we check type and size for protected symbol? */ if (ELF_ST_VISIBILITY (h->other) == STV_PROTECTED) return bfd_elf_link_record_dynamic_symbol (info, h); else return TRUE; } else if (!newdyn && ELF_ST_VISIBILITY (sym->st_other) != STV_DEFAULT && h->def_dynamic) { /* If the new symbol with non-default visibility comes from a relocatable file and the old definition comes from a dynamic object, we remove the old definition. */ if ((*sym_hash)->root.type == bfd_link_hash_indirect) { /* Handle the case where the old dynamic definition is default versioned. We need to copy the symbol info from the symbol with default version to the normal one if it was referenced before. */ if (h->ref_regular) { const struct elf_backend_data *bed = get_elf_backend_data (abfd); struct elf_link_hash_entry *vh = *sym_hash; vh->root.type = h->root.type; h->root.type = bfd_link_hash_indirect; (*bed->elf_backend_copy_indirect_symbol) (info, vh, h); /* Protected symbols will override the dynamic definition with default version. */ if (ELF_ST_VISIBILITY (sym->st_other) == STV_PROTECTED) { h->root.u.i.link = (struct bfd_link_hash_entry *) vh; vh->dynamic_def = 1; vh->ref_dynamic = 1; } else { h->root.type = vh->root.type; vh->ref_dynamic = 0; /* We have to hide it here since it was made dynamic global with extra bits when the symbol info was copied from the old dynamic definition. */ (*bed->elf_backend_hide_symbol) (info, vh, TRUE); } h = vh; } else h = *sym_hash; } if ((h->root.u.undef.next || info->hash->undefs_tail == &h->root) && bfd_is_und_section (sec)) { /* If the new symbol is undefined and the old symbol was also undefined before, we need to make sure _bfd_generic_link_add_one_symbol doesn't mess up the linker hash table undefs list. Since the old definition came from a dynamic object, it is still on the undefs list. */ h->root.type = bfd_link_hash_undefined; h->root.u.undef.abfd = abfd; } else { h->root.type = bfd_link_hash_new; h->root.u.undef.abfd = NULL; } if (h->def_dynamic) { h->def_dynamic = 0; h->ref_dynamic = 1; h->dynamic_def = 1; } /* FIXME: Should we check type and size for protected symbol? */ h->size = 0; h->type = 0; return TRUE; } /* Differentiate strong and weak symbols. */ newweak = bind == STB_WEAK; oldweak = (h->root.type == bfd_link_hash_defweak || h->root.type == bfd_link_hash_undefweak); /* If a new weak symbol definition comes from a regular file and the old symbol comes from a dynamic library, we treat the new one as strong. Similarly, an old weak symbol definition from a regular file is treated as strong when the new symbol comes from a dynamic library. Further, an old weak symbol from a dynamic library is treated as strong if the new symbol is from a dynamic library. This reflects the way glibc's ld.so works. Do this before setting *type_change_ok or *size_change_ok so that we warn properly when dynamic library symbols are overridden. */ if (newdef && !newdyn && olddyn) newweak = FALSE; if (olddef && newdyn) oldweak = FALSE; /* Allow changes between different types of funciton symbol. */ if (bed->is_function_type (ELF_ST_TYPE (sym->st_info)) && bed->is_function_type (h->type)) *type_change_ok = TRUE; /* It's OK to change the type if either the existing symbol or the new symbol is weak. A type change is also OK if the old symbol is undefined and the new symbol is defined. */ if (oldweak || newweak || (newdef && h->root.type == bfd_link_hash_undefined)) *type_change_ok = TRUE; /* It's OK to change the size if either the existing symbol or the new symbol is weak, or if the old symbol is undefined. */ if (*type_change_ok || h->root.type == bfd_link_hash_undefined) *size_change_ok = TRUE; /* NEWDYNCOMMON and OLDDYNCOMMON indicate whether the new or old symbol, respectively, appears to be a common symbol in a dynamic object. If a symbol appears in an uninitialized section, and is not weak, and is not a function, then it may be a common symbol which was resolved when the dynamic object was created. We want to treat such symbols specially, because they raise special considerations when setting the symbol size: if the symbol appears as a common symbol in a regular object, and the size in the regular object is larger, we must make sure that we use the larger size. This problematic case can always be avoided in C, but it must be handled correctly when using Fortran shared libraries. Note that if NEWDYNCOMMON is set, NEWDEF will be set, and likewise for OLDDYNCOMMON and OLDDEF. Note that this test is just a heuristic, and that it is quite possible to have an uninitialized symbol in a shared object which is really a definition, rather than a common symbol. This could lead to some minor confusion when the symbol really is a common symbol in some regular object. However, I think it will be harmless. */ if (newdyn && newdef && !newweak && (sec->flags & SEC_ALLOC) != 0 && (sec->flags & SEC_LOAD) == 0 && sym->st_size > 0 && !bed->is_function_type (ELF_ST_TYPE (sym->st_info))) newdyncommon = TRUE; else newdyncommon = FALSE; if (olddyn && olddef && h->root.type == bfd_link_hash_defined && h->def_dynamic && (h->root.u.def.section->flags & SEC_ALLOC) != 0 && (h->root.u.def.section->flags & SEC_LOAD) == 0 && h->size > 0 && !bed->is_function_type (h->type)) olddyncommon = TRUE; else olddyncommon = FALSE; /* We now know everything about the old and new symbols. We ask the backend to check if we can merge them. */ if (bed->merge_symbol && !bed->merge_symbol (info, sym_hash, h, sym, psec, pvalue, pold_alignment, skip, override, type_change_ok, size_change_ok, &newdyn, &newdef, &newdyncommon, &newweak, abfd, &sec, &olddyn, &olddef, &olddyncommon, &oldweak, oldbfd, &oldsec)) return FALSE; /* If both the old and the new symbols look like common symbols in a dynamic object, set the size of the symbol to the larger of the two. */ if (olddyncommon && newdyncommon && sym->st_size != h->size) { /* Since we think we have two common symbols, issue a multiple common warning if desired. Note that we only warn if the size is different. If the size is the same, we simply let the old symbol override the new one as normally happens with symbols defined in dynamic objects. */ if (! ((*info->callbacks->multiple_common) (info, h->root.root.string, oldbfd, bfd_link_hash_common, h->size, abfd, bfd_link_hash_common, sym->st_size))) return FALSE; if (sym->st_size > h->size) h->size = sym->st_size; *size_change_ok = TRUE; } /* If we are looking at a dynamic object, and we have found a definition, we need to see if the symbol was already defined by some other object. If so, we want to use the existing definition, and we do not want to report a multiple symbol definition error; we do this by clobbering *PSEC to be bfd_und_section_ptr. We treat a common symbol as a definition if the symbol in the shared library is a function, since common symbols always represent variables; this can cause confusion in principle, but any such confusion would seem to indicate an erroneous program or shared library. We also permit a common symbol in a regular object to override a weak symbol in a shared object. */ if (newdyn && newdef && (olddef || (h->root.type == bfd_link_hash_common && (newweak || bed->is_function_type (ELF_ST_TYPE (sym->st_info)))))) { *override = TRUE; newdef = FALSE; newdyncommon = FALSE; *psec = sec = bfd_und_section_ptr; *size_change_ok = TRUE; /* If we get here when the old symbol is a common symbol, then we are explicitly letting it override a weak symbol or function in a dynamic object, and we don't want to warn about a type change. If the old symbol is a defined symbol, a type change warning may still be appropriate. */ if (h->root.type == bfd_link_hash_common) *type_change_ok = TRUE; } /* Handle the special case of an old common symbol merging with a new symbol which looks like a common symbol in a shared object. We change *PSEC and *PVALUE to make the new symbol look like a common symbol, and let _bfd_generic_link_add_one_symbol do the right thing. */ if (newdyncommon && h->root.type == bfd_link_hash_common) { *override = TRUE; newdef = FALSE; newdyncommon = FALSE; *pvalue = sym->st_size; *psec = sec = bed->common_section (oldsec); *size_change_ok = TRUE; } /* Skip weak definitions of symbols that are already defined. */ if (newdef && olddef && newweak) *skip = TRUE; /* If the old symbol is from a dynamic object, and the new symbol is a definition which is not from a dynamic object, then the new symbol overrides the old symbol. Symbols from regular files always take precedence over symbols from dynamic objects, even if they are defined after the dynamic object in the link. As above, we again permit a common symbol in a regular object to override a definition in a shared object if the shared object symbol is a function or is weak. */ flip = NULL; if (!newdyn && (newdef || (bfd_is_com_section (sec) && (oldweak || bed->is_function_type (h->type)))) && olddyn && olddef && h->def_dynamic) { /* Change the hash table entry to undefined, and let _bfd_generic_link_add_one_symbol do the right thing with the new definition. */ h->root.type = bfd_link_hash_undefined; h->root.u.undef.abfd = h->root.u.def.section->owner; *size_change_ok = TRUE; olddef = FALSE; olddyncommon = FALSE; /* We again permit a type change when a common symbol may be overriding a function. */ if (bfd_is_com_section (sec)) *type_change_ok = TRUE; if ((*sym_hash)->root.type == bfd_link_hash_indirect) flip = *sym_hash; else /* This union may have been set to be non-NULL when this symbol was seen in a dynamic object. We must force the union to be NULL, so that it is correct for a regular symbol. */ h->verinfo.vertree = NULL; } /* Handle the special case of a new common symbol merging with an old symbol that looks like it might be a common symbol defined in a shared object. Note that we have already handled the case in which a new common symbol should simply override the definition in the shared library. */ if (! newdyn && bfd_is_com_section (sec) && olddyncommon) { /* It would be best if we could set the hash table entry to a common symbol, but we don't know what to use for the section or the alignment. */ if (! ((*info->callbacks->multiple_common) (info, h->root.root.string, oldbfd, bfd_link_hash_common, h->size, abfd, bfd_link_hash_common, sym->st_size))) return FALSE; /* If the presumed common symbol in the dynamic object is larger, pretend that the new symbol has its size. */ if (h->size > *pvalue) *pvalue = h->size; /* We need to remember the alignment required by the symbol in the dynamic object. */ BFD_ASSERT (pold_alignment); *pold_alignment = h->root.u.def.section->alignment_power; olddef = FALSE; olddyncommon = FALSE; h->root.type = bfd_link_hash_undefined; h->root.u.undef.abfd = h->root.u.def.section->owner; *size_change_ok = TRUE; *type_change_ok = TRUE; if ((*sym_hash)->root.type == bfd_link_hash_indirect) flip = *sym_hash; else h->verinfo.vertree = NULL; } if (flip != NULL) { /* Handle the case where we had a versioned symbol in a dynamic library and now find a definition in a normal object. In this case, we make the versioned symbol point to the normal one. */ const struct elf_backend_data *bed = get_elf_backend_data (abfd); flip->root.type = h->root.type; flip->root.u.undef.abfd = h->root.u.undef.abfd; h->root.type = bfd_link_hash_indirect; h->root.u.i.link = (struct bfd_link_hash_entry *) flip; (*bed->elf_backend_copy_indirect_symbol) (info, flip, h); if (h->def_dynamic) { h->def_dynamic = 0; flip->ref_dynamic = 1; } } return TRUE; } /* This function is called to create an indirect symbol from the default for the symbol with the default version if needed. The symbol is described by H, NAME, SYM, PSEC, VALUE, and OVERRIDE. We set DYNSYM if the new indirect symbol is dynamic. */ bfd_boolean _bfd_elf_add_default_symbol (bfd *abfd, struct bfd_link_info *info, struct elf_link_hash_entry *h, const char *name, Elf_Internal_Sym *sym, asection **psec, bfd_vma *value, bfd_boolean *dynsym, bfd_boolean override) { bfd_boolean type_change_ok; bfd_boolean size_change_ok; bfd_boolean skip; char *shortname; struct elf_link_hash_entry *hi; struct bfd_link_hash_entry *bh; const struct elf_backend_data *bed; bfd_boolean collect; bfd_boolean dynamic; char *p; size_t len, shortlen; asection *sec; /* If this symbol has a version, and it is the default version, we create an indirect symbol from the default name to the fully decorated name. This will cause external references which do not specify a version to be bound to this version of the symbol. */ p = strchr (name, ELF_VER_CHR); if (p == NULL || p[1] != ELF_VER_CHR) return TRUE; if (override) { /* We are overridden by an old definition. We need to check if we need to create the indirect symbol from the default name. */ hi = elf_link_hash_lookup (elf_hash_table (info), name, TRUE, FALSE, FALSE); BFD_ASSERT (hi != NULL); if (hi == h) return TRUE; while (hi->root.type == bfd_link_hash_indirect || hi->root.type == bfd_link_hash_warning) { hi = (struct elf_link_hash_entry *) hi->root.u.i.link; if (hi == h) return TRUE; } } bed = get_elf_backend_data (abfd); collect = bed->collect; dynamic = (abfd->flags & DYNAMIC) != 0; shortlen = p - name; shortname = bfd_hash_allocate (&info->hash->table, shortlen + 1); if (shortname == NULL) return FALSE; memcpy (shortname, name, shortlen); shortname[shortlen] = '\0'; /* We are going to create a new symbol. Merge it with any existing symbol with this name. For the purposes of the merge, act as though we were defining the symbol we just defined, although we actually going to define an indirect symbol. */ type_change_ok = FALSE; size_change_ok = FALSE; sec = *psec; if (!_bfd_elf_merge_symbol (abfd, info, shortname, sym, &sec, value, NULL, &hi, &skip, &override, &type_change_ok, &size_change_ok)) return FALSE; if (skip) goto nondefault; if (! override) { bh = &hi->root; if (! (_bfd_generic_link_add_one_symbol (info, abfd, shortname, BSF_INDIRECT, bfd_ind_section_ptr, 0, name, FALSE, collect, &bh))) return FALSE; hi = (struct elf_link_hash_entry *) bh; } else { /* In this case the symbol named SHORTNAME is overriding the indirect symbol we want to add. We were planning on making SHORTNAME an indirect symbol referring to NAME. SHORTNAME is the name without a version. NAME is the fully versioned name, and it is the default version. Overriding means that we already saw a definition for the symbol SHORTNAME in a regular object, and it is overriding the symbol defined in the dynamic object. When this happens, we actually want to change NAME, the symbol we just added, to refer to SHORTNAME. This will cause references to NAME in the shared object to become references to SHORTNAME in the regular object. This is what we expect when we override a function in a shared object: that the references in the shared object will be mapped to the definition in the regular object. */ while (hi->root.type == bfd_link_hash_indirect || hi->root.type == bfd_link_hash_warning) hi = (struct elf_link_hash_entry *) hi->root.u.i.link; h->root.type = bfd_link_hash_indirect; h->root.u.i.link = (struct bfd_link_hash_entry *) hi; if (h->def_dynamic) { h->def_dynamic = 0; hi->ref_dynamic = 1; if (hi->ref_regular || hi->def_regular) { if (! bfd_elf_link_record_dynamic_symbol (info, hi)) return FALSE; } } /* Now set HI to H, so that the following code will set the other fields correctly. */ hi = h; } /* Check if HI is a warning symbol. */ if (hi->root.type == bfd_link_hash_warning) hi = (struct elf_link_hash_entry *) hi->root.u.i.link; /* If there is a duplicate definition somewhere, then HI may not point to an indirect symbol. We will have reported an error to the user in that case. */ if (hi->root.type == bfd_link_hash_indirect) { struct elf_link_hash_entry *ht; ht = (struct elf_link_hash_entry *) hi->root.u.i.link; (*bed->elf_backend_copy_indirect_symbol) (info, ht, hi); /* See if the new flags lead us to realize that the symbol must be dynamic. */ if (! *dynsym) { if (! dynamic) { if (info->shared || hi->ref_dynamic) *dynsym = TRUE; } else { if (hi->ref_regular) *dynsym = TRUE; } } } /* We also need to define an indirection from the nondefault version of the symbol. */ nondefault: len = strlen (name); shortname = bfd_hash_allocate (&info->hash->table, len); if (shortname == NULL) return FALSE; memcpy (shortname, name, shortlen); memcpy (shortname + shortlen, p + 1, len - shortlen); /* Once again, merge with any existing symbol. */ type_change_ok = FALSE; size_change_ok = FALSE; sec = *psec; if (!_bfd_elf_merge_symbol (abfd, info, shortname, sym, &sec, value, NULL, &hi, &skip, &override, &type_change_ok, &size_change_ok)) return FALSE; if (skip) return TRUE; if (override) { /* Here SHORTNAME is a versioned name, so we don't expect to see the type of override we do in the case above unless it is overridden by a versioned definition. */ if (hi->root.type != bfd_link_hash_defined && hi->root.type != bfd_link_hash_defweak) (*_bfd_error_handler) (_("%B: unexpected redefinition of indirect versioned symbol `%s'"), abfd, shortname); } else { bh = &hi->root; if (! (_bfd_generic_link_add_one_symbol (info, abfd, shortname, BSF_INDIRECT, bfd_ind_section_ptr, 0, name, FALSE, collect, &bh))) return FALSE; hi = (struct elf_link_hash_entry *) bh; /* If there is a duplicate definition somewhere, then HI may not point to an indirect symbol. We will have reported an error to the user in that case. */ if (hi->root.type == bfd_link_hash_indirect) { (*bed->elf_backend_copy_indirect_symbol) (info, h, hi); /* See if the new flags lead us to realize that the symbol must be dynamic. */ if (! *dynsym) { if (! dynamic) { if (info->shared || hi->ref_dynamic) *dynsym = TRUE; } else { if (hi->ref_regular) *dynsym = TRUE; } } } } return TRUE; } /* This routine is used to export all defined symbols into the dynamic symbol table. It is called via elf_link_hash_traverse. */ bfd_boolean _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data) { struct elf_info_failed *eif = data; /* Ignore this if we won't export it. */ if (!eif->info->export_dynamic && !h->dynamic) return TRUE; /* Ignore indirect symbols. These are added by the versioning code. */ if (h->root.type == bfd_link_hash_indirect) return TRUE; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->dynindx == -1 && (h->def_regular || h->ref_regular)) { struct bfd_elf_version_tree *t; struct bfd_elf_version_expr *d; for (t = eif->verdefs; t != NULL; t = t->next) { if (t->globals.list != NULL) { d = (*t->match) (&t->globals, NULL, h->root.root.string); if (d != NULL) goto doit; } if (t->locals.list != NULL) { d = (*t->match) (&t->locals, NULL, h->root.root.string); if (d != NULL) return TRUE; } } if (!eif->verdefs) { doit: if (! bfd_elf_link_record_dynamic_symbol (eif->info, h)) { eif->failed = TRUE; return FALSE; } } } return TRUE; } /* Look through the symbols which are defined in other shared libraries and referenced here. Update the list of version dependencies. This will be put into the .gnu.version_r section. This function is called via elf_link_hash_traverse. */ bfd_boolean _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h, void *data) { struct elf_find_verdep_info *rinfo = data; Elf_Internal_Verneed *t; Elf_Internal_Vernaux *a; bfd_size_type amt; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* We only care about symbols defined in shared objects with version information. */ if (!h->def_dynamic || h->def_regular || h->dynindx == -1 || h->verinfo.verdef == NULL) return TRUE; /* See if we already know about this version. */ for (t = elf_tdata (rinfo->output_bfd)->verref; t != NULL; t = t->vn_nextref) { if (t->vn_bfd != h->verinfo.verdef->vd_bfd) continue; for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) if (a->vna_nodename == h->verinfo.verdef->vd_nodename) return TRUE; break; } /* This is a new version. Add it to tree we are building. */ if (t == NULL) { amt = sizeof *t; t = bfd_zalloc (rinfo->output_bfd, amt); if (t == NULL) { rinfo->failed = TRUE; return FALSE; } t->vn_bfd = h->verinfo.verdef->vd_bfd; t->vn_nextref = elf_tdata (rinfo->output_bfd)->verref; elf_tdata (rinfo->output_bfd)->verref = t; } amt = sizeof *a; a = bfd_zalloc (rinfo->output_bfd, amt); /* Note that we are copying a string pointer here, and testing it above. If bfd_elf_string_from_elf_section is ever changed to discard the string data when low in memory, this will have to be fixed. */ a->vna_nodename = h->verinfo.verdef->vd_nodename; a->vna_flags = h->verinfo.verdef->vd_flags; a->vna_nextptr = t->vn_auxptr; h->verinfo.verdef->vd_exp_refno = rinfo->vers; ++rinfo->vers; a->vna_other = h->verinfo.verdef->vd_exp_refno + 1; t->vn_auxptr = a; return TRUE; } /* Figure out appropriate versions for all the symbols. We may not have the version number script until we have read all of the input files, so until that point we don't know which symbols should be local. This function is called via elf_link_hash_traverse. */ bfd_boolean _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data) { struct elf_assign_sym_version_info *sinfo; struct bfd_link_info *info; const struct elf_backend_data *bed; struct elf_info_failed eif; char *p; bfd_size_type amt; sinfo = data; info = sinfo->info; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* Fix the symbol flags. */ eif.failed = FALSE; eif.info = info; if (! _bfd_elf_fix_symbol_flags (h, &eif)) { if (eif.failed) sinfo->failed = TRUE; return FALSE; } /* We only need version numbers for symbols defined in regular objects. */ if (!h->def_regular) return TRUE; bed = get_elf_backend_data (sinfo->output_bfd); p = strchr (h->root.root.string, ELF_VER_CHR); if (p != NULL && h->verinfo.vertree == NULL) { struct bfd_elf_version_tree *t; bfd_boolean hidden; hidden = TRUE; /* There are two consecutive ELF_VER_CHR characters if this is not a hidden symbol. */ ++p; if (*p == ELF_VER_CHR) { hidden = FALSE; ++p; } /* If there is no version string, we can just return out. */ if (*p == '\0') { if (hidden) h->hidden = 1; return TRUE; } /* Look for the version. If we find it, it is no longer weak. */ for (t = sinfo->verdefs; t != NULL; t = t->next) { if (strcmp (t->name, p) == 0) { size_t len; char *alc; struct bfd_elf_version_expr *d; len = p - h->root.root.string; alc = bfd_malloc (len); if (alc == NULL) return FALSE; memcpy (alc, h->root.root.string, len - 1); alc[len - 1] = '\0'; if (alc[len - 2] == ELF_VER_CHR) alc[len - 2] = '\0'; h->verinfo.vertree = t; t->used = TRUE; d = NULL; if (t->globals.list != NULL) d = (*t->match) (&t->globals, NULL, alc); /* See if there is anything to force this symbol to local scope. */ if (d == NULL && t->locals.list != NULL) { d = (*t->match) (&t->locals, NULL, alc); if (d != NULL && h->dynindx != -1 && ! info->export_dynamic) (*bed->elf_backend_hide_symbol) (info, h, TRUE); } free (alc); break; } } /* If we are building an application, we need to create a version node for this version. */ if (t == NULL && info->executable) { struct bfd_elf_version_tree **pp; int version_index; /* If we aren't going to export this symbol, we don't need to worry about it. */ if (h->dynindx == -1) return TRUE; amt = sizeof *t; t = bfd_zalloc (sinfo->output_bfd, amt); if (t == NULL) { sinfo->failed = TRUE; return FALSE; } t->name = p; t->name_indx = (unsigned int) -1; t->used = TRUE; version_index = 1; /* Don't count anonymous version tag. */ if (sinfo->verdefs != NULL && sinfo->verdefs->vernum == 0) version_index = 0; for (pp = &sinfo->verdefs; *pp != NULL; pp = &(*pp)->next) ++version_index; t->vernum = version_index; *pp = t; h->verinfo.vertree = t; } else if (t == NULL) { /* We could not find the version for a symbol when generating a shared archive. Return an error. */ (*_bfd_error_handler) (_("%B: version node not found for symbol %s"), sinfo->output_bfd, h->root.root.string); bfd_set_error (bfd_error_bad_value); sinfo->failed = TRUE; return FALSE; } if (hidden) h->hidden = 1; } /* If we don't have a version for this symbol, see if we can find something. */ if (h->verinfo.vertree == NULL && sinfo->verdefs != NULL) { struct bfd_elf_version_tree *t; struct bfd_elf_version_tree *local_ver; struct bfd_elf_version_expr *d; /* See if can find what version this symbol is in. If the symbol is supposed to be local, then don't actually register it. */ local_ver = NULL; for (t = sinfo->verdefs; t != NULL; t = t->next) { if (t->globals.list != NULL) { bfd_boolean matched; matched = FALSE; d = NULL; while ((d = (*t->match) (&t->globals, d, h->root.root.string)) != NULL) if (d->symver) matched = TRUE; else { /* There is a version without definition. Make the symbol the default definition for this version. */ h->verinfo.vertree = t; local_ver = NULL; d->script = 1; break; } if (d != NULL) break; else if (matched) /* There is no undefined version for this symbol. Hide the default one. */ (*bed->elf_backend_hide_symbol) (info, h, TRUE); } if (t->locals.list != NULL) { d = NULL; while ((d = (*t->match) (&t->locals, d, h->root.root.string)) != NULL) { local_ver = t; /* If the match is "*", keep looking for a more explicit, perhaps even global, match. XXX: Shouldn't this be !d->wildcard instead? */ if (d->pattern[0] != '*' || d->pattern[1] != '\0') break; } if (d != NULL) break; } } if (local_ver != NULL) { h->verinfo.vertree = local_ver; if (h->dynindx != -1 && ! info->export_dynamic) { (*bed->elf_backend_hide_symbol) (info, h, TRUE); } } } return TRUE; } /* Read and swap the relocs from the section indicated by SHDR. This may be either a REL or a RELA section. The relocations are translated into RELA relocations and stored in INTERNAL_RELOCS, which should have already been allocated to contain enough space. The EXTERNAL_RELOCS are a buffer where the external form of the relocations should be stored. Returns FALSE if something goes wrong. */ static bfd_boolean elf_link_read_relocs_from_section (bfd *abfd, asection *sec, Elf_Internal_Shdr *shdr, void *external_relocs, Elf_Internal_Rela *internal_relocs) { const struct elf_backend_data *bed; void (*swap_in) (bfd *, const bfd_byte *, Elf_Internal_Rela *); const bfd_byte *erela; const bfd_byte *erelaend; Elf_Internal_Rela *irela; Elf_Internal_Shdr *symtab_hdr; size_t nsyms; /* Position ourselves at the start of the section. */ if (bfd_seek (abfd, shdr->sh_offset, SEEK_SET) != 0) return FALSE; /* Read the relocations. */ if (bfd_bread (external_relocs, shdr->sh_size, abfd) != shdr->sh_size) return FALSE; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; nsyms = symtab_hdr->sh_size / symtab_hdr->sh_entsize; bed = get_elf_backend_data (abfd); /* Convert the external relocations to the internal format. */ if (shdr->sh_entsize == bed->s->sizeof_rel) swap_in = bed->s->swap_reloc_in; else if (shdr->sh_entsize == bed->s->sizeof_rela) swap_in = bed->s->swap_reloca_in; else { bfd_set_error (bfd_error_wrong_format); return FALSE; } erela = external_relocs; erelaend = erela + shdr->sh_size; irela = internal_relocs; while (erela < erelaend) { bfd_vma r_symndx; (*swap_in) (abfd, erela, irela); r_symndx = ELF32_R_SYM (irela->r_info); if (bed->s->arch_size == 64) r_symndx >>= 24; if ((size_t) r_symndx >= nsyms) { (*_bfd_error_handler) (_("%B: bad reloc symbol index (0x%lx >= 0x%lx)" " for offset 0x%lx in section `%A'"), abfd, sec, (unsigned long) r_symndx, (unsigned long) nsyms, irela->r_offset); bfd_set_error (bfd_error_bad_value); return FALSE; } irela += bed->s->int_rels_per_ext_rel; erela += shdr->sh_entsize; } return TRUE; } /* Read and swap the relocs for a section O. They may have been cached. If the EXTERNAL_RELOCS and INTERNAL_RELOCS arguments are not NULL, they are used as buffers to read into. They are known to be large enough. If the INTERNAL_RELOCS relocs argument is NULL, the return value is allocated using either malloc or bfd_alloc, according to the KEEP_MEMORY argument. If O has two relocation sections (both REL and RELA relocations), then the REL_HDR relocations will appear first in INTERNAL_RELOCS, followed by the REL_HDR2 relocations. */ Elf_Internal_Rela * _bfd_elf_link_read_relocs (bfd *abfd, asection *o, void *external_relocs, Elf_Internal_Rela *internal_relocs, bfd_boolean keep_memory) { Elf_Internal_Shdr *rel_hdr; void *alloc1 = NULL; Elf_Internal_Rela *alloc2 = NULL; const struct elf_backend_data *bed = get_elf_backend_data (abfd); if (elf_section_data (o)->relocs != NULL) return elf_section_data (o)->relocs; if (o->reloc_count == 0) return NULL; rel_hdr = &elf_section_data (o)->rel_hdr; if (internal_relocs == NULL) { bfd_size_type size; size = o->reloc_count; size *= bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rela); if (keep_memory) internal_relocs = bfd_alloc (abfd, size); else internal_relocs = alloc2 = bfd_malloc (size); if (internal_relocs == NULL) goto error_return; } if (external_relocs == NULL) { bfd_size_type size = rel_hdr->sh_size; if (elf_section_data (o)->rel_hdr2) size += elf_section_data (o)->rel_hdr2->sh_size; alloc1 = bfd_malloc (size); if (alloc1 == NULL) goto error_return; external_relocs = alloc1; } if (!elf_link_read_relocs_from_section (abfd, o, rel_hdr, external_relocs, internal_relocs)) goto error_return; if (elf_section_data (o)->rel_hdr2 && (!elf_link_read_relocs_from_section (abfd, o, elf_section_data (o)->rel_hdr2, ((bfd_byte *) external_relocs) + rel_hdr->sh_size, internal_relocs + (NUM_SHDR_ENTRIES (rel_hdr) * bed->s->int_rels_per_ext_rel)))) goto error_return; /* Cache the results for next time, if we can. */ if (keep_memory) elf_section_data (o)->relocs = internal_relocs; if (alloc1 != NULL) free (alloc1); /* Don't free alloc2, since if it was allocated we are passing it back (under the name of internal_relocs). */ return internal_relocs; error_return: if (alloc1 != NULL) free (alloc1); if (alloc2 != NULL) free (alloc2); return NULL; } /* Compute the size of, and allocate space for, REL_HDR which is the section header for a section containing relocations for O. */ bfd_boolean _bfd_elf_link_size_reloc_section (bfd *abfd, Elf_Internal_Shdr *rel_hdr, asection *o) { bfd_size_type reloc_count; bfd_size_type num_rel_hashes; /* Figure out how many relocations there will be. */ if (rel_hdr == &elf_section_data (o)->rel_hdr) reloc_count = elf_section_data (o)->rel_count; else reloc_count = elf_section_data (o)->rel_count2; num_rel_hashes = o->reloc_count; if (num_rel_hashes < reloc_count) num_rel_hashes = reloc_count; /* That allows us to calculate the size of the section. */ rel_hdr->sh_size = rel_hdr->sh_entsize * reloc_count; /* The contents field must last into write_object_contents, so we allocate it with bfd_alloc rather than malloc. Also since we cannot be sure that the contents will actually be filled in, we zero the allocated space. */ rel_hdr->contents = bfd_zalloc (abfd, rel_hdr->sh_size); if (rel_hdr->contents == NULL && rel_hdr->sh_size != 0) return FALSE; /* We only allocate one set of hash entries, so we only do it the first time we are called. */ if (elf_section_data (o)->rel_hashes == NULL && num_rel_hashes) { struct elf_link_hash_entry **p; p = bfd_zmalloc (num_rel_hashes * sizeof (struct elf_link_hash_entry *)); if (p == NULL) return FALSE; elf_section_data (o)->rel_hashes = p; } return TRUE; } /* Copy the relocations indicated by the INTERNAL_RELOCS (which originated from the section given by INPUT_REL_HDR) to the OUTPUT_BFD. */ bfd_boolean _bfd_elf_link_output_relocs (bfd *output_bfd, asection *input_section, Elf_Internal_Shdr *input_rel_hdr, Elf_Internal_Rela *internal_relocs, struct elf_link_hash_entry **rel_hash ATTRIBUTE_UNUSED) { Elf_Internal_Rela *irela; Elf_Internal_Rela *irelaend; bfd_byte *erel; Elf_Internal_Shdr *output_rel_hdr; asection *output_section; unsigned int *rel_countp = NULL; const struct elf_backend_data *bed; void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *); output_section = input_section->output_section; output_rel_hdr = NULL; if (elf_section_data (output_section)->rel_hdr.sh_entsize == input_rel_hdr->sh_entsize) { output_rel_hdr = &elf_section_data (output_section)->rel_hdr; rel_countp = &elf_section_data (output_section)->rel_count; } else if (elf_section_data (output_section)->rel_hdr2 && (elf_section_data (output_section)->rel_hdr2->sh_entsize == input_rel_hdr->sh_entsize)) { output_rel_hdr = elf_section_data (output_section)->rel_hdr2; rel_countp = &elf_section_data (output_section)->rel_count2; } else { (*_bfd_error_handler) (_("%B: relocation size mismatch in %B section %A"), output_bfd, input_section->owner, input_section); bfd_set_error (bfd_error_wrong_object_format); return FALSE; } bed = get_elf_backend_data (output_bfd); if (input_rel_hdr->sh_entsize == bed->s->sizeof_rel) swap_out = bed->s->swap_reloc_out; else if (input_rel_hdr->sh_entsize == bed->s->sizeof_rela) swap_out = bed->s->swap_reloca_out; else abort (); erel = output_rel_hdr->contents; erel += *rel_countp * input_rel_hdr->sh_entsize; irela = internal_relocs; irelaend = irela + (NUM_SHDR_ENTRIES (input_rel_hdr) * bed->s->int_rels_per_ext_rel); while (irela < irelaend) { (*swap_out) (output_bfd, irela, erel); irela += bed->s->int_rels_per_ext_rel; erel += input_rel_hdr->sh_entsize; } /* Bump the counter, so that we know where to add the next set of relocations. */ *rel_countp += NUM_SHDR_ENTRIES (input_rel_hdr); return TRUE; } /* Make weak undefined symbols in PIE dynamic. */ bfd_boolean _bfd_elf_link_hash_fixup_symbol (struct bfd_link_info *info, struct elf_link_hash_entry *h) { if (info->pie && h->dynindx == -1 && h->root.type == bfd_link_hash_undefweak) return bfd_elf_link_record_dynamic_symbol (info, h); return TRUE; } /* Fix up the flags for a symbol. This handles various cases which can only be fixed after all the input files are seen. This is currently called by both adjust_dynamic_symbol and assign_sym_version, which is unnecessary but perhaps more robust in the face of future changes. */ bfd_boolean _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h, struct elf_info_failed *eif) { const struct elf_backend_data *bed = NULL; /* If this symbol was mentioned in a non-ELF file, try to set DEF_REGULAR and REF_REGULAR correctly. This is the only way to permit a non-ELF file to correctly refer to a symbol defined in an ELF dynamic object. */ if (h->non_elf) { while (h->root.type == bfd_link_hash_indirect) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak) { h->ref_regular = 1; h->ref_regular_nonweak = 1; } else { if (h->root.u.def.section->owner != NULL && (bfd_get_flavour (h->root.u.def.section->owner) == bfd_target_elf_flavour)) { h->ref_regular = 1; h->ref_regular_nonweak = 1; } else h->def_regular = 1; } if (h->dynindx == -1 && (h->def_dynamic || h->ref_dynamic)) { if (! bfd_elf_link_record_dynamic_symbol (eif->info, h)) { eif->failed = TRUE; return FALSE; } } } else { /* Unfortunately, NON_ELF is only correct if the symbol was first seen in a non-ELF file. Fortunately, if the symbol was first seen in an ELF file, we're probably OK unless the symbol was defined in a non-ELF file. Catch that case here. FIXME: We're still in trouble if the symbol was first seen in a dynamic object, and then later in a non-ELF regular object. */ if ((h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && !h->def_regular && (h->root.u.def.section->owner != NULL ? (bfd_get_flavour (h->root.u.def.section->owner) != bfd_target_elf_flavour) : (bfd_is_abs_section (h->root.u.def.section) && !h->def_dynamic))) h->def_regular = 1; } /* Backend specific symbol fixup. */ if (elf_hash_table (eif->info)->dynobj) { bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj); if (bed->elf_backend_fixup_symbol && !(*bed->elf_backend_fixup_symbol) (eif->info, h)) return FALSE; } /* If this is a final link, and the symbol was defined as a common symbol in a regular object file, and there was no definition in any dynamic object, then the linker will have allocated space for the symbol in a common section but the DEF_REGULAR flag will not have been set. */ if (h->root.type == bfd_link_hash_defined && !h->def_regular && h->ref_regular && !h->def_dynamic && (h->root.u.def.section->owner->flags & DYNAMIC) == 0) h->def_regular = 1; /* If -Bsymbolic was used (which means to bind references to global symbols to the definition within the shared object), and this symbol was defined in a regular object, then it actually doesn't need a PLT entry. Likewise, if the symbol has non-default visibility. If the symbol has hidden or internal visibility, we will force it local. */ if (h->needs_plt && eif->info->shared && is_elf_hash_table (eif->info->hash) && (SYMBOLIC_BIND (eif->info, h) || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) && h->def_regular) { bfd_boolean force_local; force_local = (ELF_ST_VISIBILITY (h->other) == STV_INTERNAL || ELF_ST_VISIBILITY (h->other) == STV_HIDDEN); (*bed->elf_backend_hide_symbol) (eif->info, h, force_local); } /* If a weak undefined symbol has non-default visibility, we also hide it from the dynamic linker. */ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT && h->root.type == bfd_link_hash_undefweak) { const struct elf_backend_data *bed; bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj); (*bed->elf_backend_hide_symbol) (eif->info, h, TRUE); } /* If this is a weak defined symbol in a dynamic object, and we know the real definition in the dynamic object, copy interesting flags over to the real definition. */ if (h->u.weakdef != NULL) { struct elf_link_hash_entry *weakdef; weakdef = h->u.weakdef; if (h->root.type == bfd_link_hash_indirect) h = (struct elf_link_hash_entry *) h->root.u.i.link; BFD_ASSERT (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak); BFD_ASSERT (weakdef->root.type == bfd_link_hash_defined || weakdef->root.type == bfd_link_hash_defweak); BFD_ASSERT (weakdef->def_dynamic); /* If the real definition is defined by a regular object file, don't do anything special. See the longer description in _bfd_elf_adjust_dynamic_symbol, below. */ if (weakdef->def_regular) h->u.weakdef = NULL; else (*bed->elf_backend_copy_indirect_symbol) (eif->info, weakdef, h); } return TRUE; } /* Make the backend pick a good value for a dynamic symbol. This is called via elf_link_hash_traverse, and also calls itself recursively. */ bfd_boolean _bfd_elf_adjust_dynamic_symbol (struct elf_link_hash_entry *h, void *data) { struct elf_info_failed *eif = data; bfd *dynobj; const struct elf_backend_data *bed; if (! is_elf_hash_table (eif->info->hash)) return FALSE; if (h->root.type == bfd_link_hash_warning) { h->got = elf_hash_table (eif->info)->init_got_offset; h->plt = elf_hash_table (eif->info)->init_plt_offset; /* When warning symbols are created, they **replace** the "real" entry in the hash table, thus we never get to see the real symbol in a hash traversal. So look at it now. */ h = (struct elf_link_hash_entry *) h->root.u.i.link; } /* Ignore indirect symbols. These are added by the versioning code. */ if (h->root.type == bfd_link_hash_indirect) return TRUE; /* Fix the symbol flags. */ if (! _bfd_elf_fix_symbol_flags (h, eif)) return FALSE; /* If this symbol does not require a PLT entry, and it is not defined by a dynamic object, or is not referenced by a regular object, ignore it. We do have to handle a weak defined symbol, even if no regular object refers to it, if we decided to add it to the dynamic symbol table. FIXME: Do we normally need to worry about symbols which are defined by one dynamic object and referenced by another one? */ if (!h->needs_plt && (h->def_regular || !h->def_dynamic || (!h->ref_regular && (h->u.weakdef == NULL || h->u.weakdef->dynindx == -1)))) { h->plt = elf_hash_table (eif->info)->init_plt_offset; return TRUE; } /* If we've already adjusted this symbol, don't do it again. This can happen via a recursive call. */ if (h->dynamic_adjusted) return TRUE; /* Don't look at this symbol again. Note that we must set this after checking the above conditions, because we may look at a symbol once, decide not to do anything, and then get called recursively later after REF_REGULAR is set below. */ h->dynamic_adjusted = 1; /* If this is a weak definition, and we know a real definition, and the real symbol is not itself defined by a regular object file, then get a good value for the real definition. We handle the real symbol first, for the convenience of the backend routine. Note that there is a confusing case here. If the real definition is defined by a regular object file, we don't get the real symbol from the dynamic object, but we do get the weak symbol. If the processor backend uses a COPY reloc, then if some routine in the dynamic object changes the real symbol, we will not see that change in the corresponding weak symbol. This is the way other ELF linkers work as well, and seems to be a result of the shared library model. I will clarify this issue. Most SVR4 shared libraries define the variable _timezone and define timezone as a weak synonym. The tzset call changes _timezone. If you write extern int timezone; int _timezone = 5; int main () { tzset (); printf ("%d %d\n", timezone, _timezone); } you might expect that, since timezone is a synonym for _timezone, the same number will print both times. However, if the processor backend uses a COPY reloc, then actually timezone will be copied into your process image, and, since you define _timezone yourself, _timezone will not. Thus timezone and _timezone will wind up at different memory locations. The tzset call will set _timezone, leaving timezone unchanged. */ if (h->u.weakdef != NULL) { /* If we get to this point, we know there is an implicit reference by a regular object file via the weak symbol H. FIXME: Is this really true? What if the traversal finds H->U.WEAKDEF before it finds H? */ h->u.weakdef->ref_regular = 1; if (! _bfd_elf_adjust_dynamic_symbol (h->u.weakdef, eif)) return FALSE; } /* If a symbol has no type and no size and does not require a PLT entry, then we are probably about to do the wrong thing here: we are probably going to create a COPY reloc for an empty object. This case can arise when a shared object is built with assembly code, and the assembly code fails to set the symbol type. */ if (h->size == 0 && h->type == STT_NOTYPE && !h->needs_plt) (*_bfd_error_handler) (_("warning: type and size of dynamic symbol `%s' are not defined"), h->root.root.string); dynobj = elf_hash_table (eif->info)->dynobj; bed = get_elf_backend_data (dynobj); if (! (*bed->elf_backend_adjust_dynamic_symbol) (eif->info, h)) { eif->failed = TRUE; return FALSE; } return TRUE; } /* Adjust the dynamic symbol, H, for copy in the dynamic bss section, DYNBSS. */ bfd_boolean _bfd_elf_adjust_dynamic_copy (struct elf_link_hash_entry *h, asection *dynbss) { unsigned int power_of_two; bfd_vma mask; asection *sec = h->root.u.def.section; /* The section aligment of definition is the maximum alignment requirement of symbols defined in the section. Since we don't know the symbol alignment requirement, we start with the maximum alignment and check low bits of the symbol address for the minimum alignment. */ power_of_two = bfd_get_section_alignment (sec->owner, sec); mask = ((bfd_vma) 1 << power_of_two) - 1; while ((h->root.u.def.value & mask) != 0) { mask >>= 1; --power_of_two; } if (power_of_two > bfd_get_section_alignment (dynbss->owner, dynbss)) { /* Adjust the section alignment if needed. */ if (! bfd_set_section_alignment (dynbss->owner, dynbss, power_of_two)) return FALSE; } /* We make sure that the symbol will be aligned properly. */ dynbss->size = BFD_ALIGN (dynbss->size, mask + 1); /* Define the symbol as being at this point in DYNBSS. */ h->root.u.def.section = dynbss; h->root.u.def.value = dynbss->size; /* Increment the size of DYNBSS to make room for the symbol. */ dynbss->size += h->size; return TRUE; } /* Adjust all external symbols pointing into SEC_MERGE sections to reflect the object merging within the sections. */ bfd_boolean _bfd_elf_link_sec_merge_syms (struct elf_link_hash_entry *h, void *data) { asection *sec; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if ((h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && ((sec = h->root.u.def.section)->flags & SEC_MERGE) && sec->sec_info_type == ELF_INFO_TYPE_MERGE) { bfd *output_bfd = data; h->root.u.def.value = _bfd_merged_section_offset (output_bfd, &h->root.u.def.section, elf_section_data (sec)->sec_info, h->root.u.def.value); } return TRUE; } /* Returns false if the symbol referred to by H should be considered to resolve local to the current module, and true if it should be considered to bind dynamically. */ bfd_boolean _bfd_elf_dynamic_symbol_p (struct elf_link_hash_entry *h, struct bfd_link_info *info, bfd_boolean ignore_protected) { bfd_boolean binding_stays_local_p; const struct elf_backend_data *bed; struct elf_link_hash_table *hash_table; if (h == NULL) return FALSE; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* If it was forced local, then clearly it's not dynamic. */ if (h->dynindx == -1) return FALSE; if (h->forced_local) return FALSE; /* Identify the cases where name binding rules say that a visible symbol resolves locally. */ binding_stays_local_p = info->executable || SYMBOLIC_BIND (info, h); switch (ELF_ST_VISIBILITY (h->other)) { case STV_INTERNAL: case STV_HIDDEN: return FALSE; case STV_PROTECTED: hash_table = elf_hash_table (info); if (!is_elf_hash_table (hash_table)) return FALSE; bed = get_elf_backend_data (hash_table->dynobj); /* Proper resolution for function pointer equality may require that these symbols perhaps be resolved dynamically, even though we should be resolving them to the current module. */ if (!ignore_protected || !bed->is_function_type (h->type)) binding_stays_local_p = TRUE; break; default: break; } /* If it isn't defined locally, then clearly it's dynamic. */ if (!h->def_regular) return TRUE; /* Otherwise, the symbol is dynamic if binding rules don't tell us that it remains local. */ return !binding_stays_local_p; } /* Return true if the symbol referred to by H should be considered to resolve local to the current module, and false otherwise. Differs from (the inverse of) _bfd_elf_dynamic_symbol_p in the treatment of undefined symbols and weak symbols. */ bfd_boolean _bfd_elf_symbol_refs_local_p (struct elf_link_hash_entry *h, struct bfd_link_info *info, bfd_boolean local_protected) { const struct elf_backend_data *bed; struct elf_link_hash_table *hash_table; /* If it's a local sym, of course we resolve locally. */ if (h == NULL) return TRUE; /* Common symbols that become definitions don't get the DEF_REGULAR flag set, so test it first, and don't bail out. */ if (ELF_COMMON_DEF_P (h)) /* Do nothing. */; /* If we don't have a definition in a regular file, then we can't resolve locally. The sym is either undefined or dynamic. */ else if (!h->def_regular) return FALSE; /* Forced local symbols resolve locally. */ if (h->forced_local) return TRUE; /* As do non-dynamic symbols. */ if (h->dynindx == -1) return TRUE; /* At this point, we know the symbol is defined and dynamic. In an executable it must resolve locally, likewise when building symbolic shared libraries. */ if (info->executable || SYMBOLIC_BIND (info, h)) return TRUE; /* Now deal with defined dynamic symbols in shared libraries. Ones with default visibility might not resolve locally. */ if (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT) return FALSE; /* However, STV_HIDDEN or STV_INTERNAL ones must be local. */ if (ELF_ST_VISIBILITY (h->other) != STV_PROTECTED) return TRUE; hash_table = elf_hash_table (info); if (!is_elf_hash_table (hash_table)) return TRUE; bed = get_elf_backend_data (hash_table->dynobj); /* STV_PROTECTED non-function symbols are local. */ if (!bed->is_function_type (h->type)) return TRUE; /* Function pointer equality tests may require that STV_PROTECTED symbols be treated as dynamic symbols, even when we know that the dynamic linker will resolve them locally. */ return local_protected; } /* Caches some TLS segment info, and ensures that the TLS segment vma is aligned. Returns the first TLS output section. */ struct bfd_section * _bfd_elf_tls_setup (bfd *obfd, struct bfd_link_info *info) { struct bfd_section *sec, *tls; unsigned int align = 0; for (sec = obfd->sections; sec != NULL; sec = sec->next) if ((sec->flags & SEC_THREAD_LOCAL) != 0) break; tls = sec; for (; sec != NULL && (sec->flags & SEC_THREAD_LOCAL) != 0; sec = sec->next) if (sec->alignment_power > align) align = sec->alignment_power; elf_hash_table (info)->tls_sec = tls; /* Ensure the alignment of the first section is the largest alignment, so that the tls segment starts aligned. */ if (tls != NULL) tls->alignment_power = align; return tls; } /* Return TRUE iff this is a non-common, definition of a non-function symbol. */ static bfd_boolean is_global_data_symbol_definition (bfd *abfd ATTRIBUTE_UNUSED, Elf_Internal_Sym *sym) { const struct elf_backend_data *bed; /* Local symbols do not count, but target specific ones might. */ if (ELF_ST_BIND (sym->st_info) != STB_GLOBAL && ELF_ST_BIND (sym->st_info) < STB_LOOS) return FALSE; bed = get_elf_backend_data (abfd); /* Function symbols do not count. */ if (bed->is_function_type (ELF_ST_TYPE (sym->st_info))) return FALSE; /* If the section is undefined, then so is the symbol. */ if (sym->st_shndx == SHN_UNDEF) return FALSE; /* If the symbol is defined in the common section, then it is a common definition and so does not count. */ if (bed->common_definition (sym)) return FALSE; /* If the symbol is in a target specific section then we must rely upon the backend to tell us what it is. */ if (sym->st_shndx >= SHN_LORESERVE && sym->st_shndx < SHN_ABS) /* FIXME - this function is not coded yet: return _bfd_is_global_symbol_definition (abfd, sym); Instead for now assume that the definition is not global, Even if this is wrong, at least the linker will behave in the same way that it used to do. */ return FALSE; return TRUE; } /* Search the symbol table of the archive element of the archive ABFD whose archive map contains a mention of SYMDEF, and determine if the symbol is defined in this element. */ static bfd_boolean elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef) { Elf_Internal_Shdr * hdr; bfd_size_type symcount; bfd_size_type extsymcount; bfd_size_type extsymoff; Elf_Internal_Sym *isymbuf; Elf_Internal_Sym *isym; Elf_Internal_Sym *isymend; bfd_boolean result; abfd = _bfd_get_elt_at_filepos (abfd, symdef->file_offset); if (abfd == NULL) return FALSE; if (! bfd_check_format (abfd, bfd_object)) return FALSE; /* If we have already included the element containing this symbol in the link then we do not need to include it again. Just claim that any symbol it contains is not a definition, so that our caller will not decide to (re)include this element. */ if (abfd->archive_pass) return FALSE; /* Select the appropriate symbol table. */ if ((abfd->flags & DYNAMIC) == 0 || elf_dynsymtab (abfd) == 0) hdr = &elf_tdata (abfd)->symtab_hdr; else hdr = &elf_tdata (abfd)->dynsymtab_hdr; symcount = hdr->sh_size / get_elf_backend_data (abfd)->s->sizeof_sym; /* The sh_info field of the symtab header tells us where the external symbols start. We don't care about the local symbols. */ if (elf_bad_symtab (abfd)) { extsymcount = symcount; extsymoff = 0; } else { extsymcount = symcount - hdr->sh_info; extsymoff = hdr->sh_info; } if (extsymcount == 0) return FALSE; /* Read in the symbol table. */ isymbuf = bfd_elf_get_elf_syms (abfd, hdr, extsymcount, extsymoff, NULL, NULL, NULL); if (isymbuf == NULL) return FALSE; /* Scan the symbol table looking for SYMDEF. */ result = FALSE; for (isym = isymbuf, isymend = isymbuf + extsymcount; isym < isymend; isym++) { const char *name; name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link, isym->st_name); if (name == NULL) break; if (strcmp (name, symdef->name) == 0) { result = is_global_data_symbol_definition (abfd, isym); break; } } free (isymbuf); return result; } /* Add an entry to the .dynamic table. */ bfd_boolean _bfd_elf_add_dynamic_entry (struct bfd_link_info *info, bfd_vma tag, bfd_vma val) { struct elf_link_hash_table *hash_table; const struct elf_backend_data *bed; asection *s; bfd_size_type newsize; bfd_byte *newcontents; Elf_Internal_Dyn dyn; hash_table = elf_hash_table (info); if (! is_elf_hash_table (hash_table)) return FALSE; bed = get_elf_backend_data (hash_table->dynobj); s = bfd_get_section_by_name (hash_table->dynobj, ".dynamic"); BFD_ASSERT (s != NULL); newsize = s->size + bed->s->sizeof_dyn; newcontents = bfd_realloc (s->contents, newsize); if (newcontents == NULL) return FALSE; dyn.d_tag = tag; dyn.d_un.d_val = val; bed->s->swap_dyn_out (hash_table->dynobj, &dyn, newcontents + s->size); s->size = newsize; s->contents = newcontents; return TRUE; } /* Add a DT_NEEDED entry for this dynamic object if DO_IT is true, otherwise just check whether one already exists. Returns -1 on error, 1 if a DT_NEEDED tag already exists, and 0 on success. */ static int elf_add_dt_needed_tag (bfd *abfd, struct bfd_link_info *info, const char *soname, bfd_boolean do_it) { struct elf_link_hash_table *hash_table; bfd_size_type oldsize; bfd_size_type strindex; if (!_bfd_elf_link_create_dynstrtab (abfd, info)) return -1; hash_table = elf_hash_table (info); oldsize = _bfd_elf_strtab_size (hash_table->dynstr); strindex = _bfd_elf_strtab_add (hash_table->dynstr, soname, FALSE); if (strindex == (bfd_size_type) -1) return -1; if (oldsize == _bfd_elf_strtab_size (hash_table->dynstr)) { asection *sdyn; const struct elf_backend_data *bed; bfd_byte *extdyn; bed = get_elf_backend_data (hash_table->dynobj); sdyn = bfd_get_section_by_name (hash_table->dynobj, ".dynamic"); if (sdyn != NULL) for (extdyn = sdyn->contents; extdyn < sdyn->contents + sdyn->size; extdyn += bed->s->sizeof_dyn) { Elf_Internal_Dyn dyn; bed->s->swap_dyn_in (hash_table->dynobj, extdyn, &dyn); if (dyn.d_tag == DT_NEEDED && dyn.d_un.d_val == strindex) { _bfd_elf_strtab_delref (hash_table->dynstr, strindex); return 1; } } } if (do_it) { if (!_bfd_elf_link_create_dynamic_sections (hash_table->dynobj, info)) return -1; if (!_bfd_elf_add_dynamic_entry (info, DT_NEEDED, strindex)) return -1; } else /* We were just checking for existence of the tag. */ _bfd_elf_strtab_delref (hash_table->dynstr, strindex); return 0; } /* Sort symbol by value and section. */ static int elf_sort_symbol (const void *arg1, const void *arg2) { const struct elf_link_hash_entry *h1; const struct elf_link_hash_entry *h2; bfd_signed_vma vdiff; h1 = *(const struct elf_link_hash_entry **) arg1; h2 = *(const struct elf_link_hash_entry **) arg2; vdiff = h1->root.u.def.value - h2->root.u.def.value; if (vdiff != 0) return vdiff > 0 ? 1 : -1; else { long sdiff = h1->root.u.def.section->id - h2->root.u.def.section->id; if (sdiff != 0) return sdiff > 0 ? 1 : -1; } return 0; } /* This function is used to adjust offsets into .dynstr for dynamic symbols. This is called via elf_link_hash_traverse. */ static bfd_boolean elf_adjust_dynstr_offsets (struct elf_link_hash_entry *h, void *data) { struct elf_strtab_hash *dynstr = data; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->dynindx != -1) h->dynstr_index = _bfd_elf_strtab_offset (dynstr, h->dynstr_index); return TRUE; } /* Assign string offsets in .dynstr, update all structures referencing them. */ static bfd_boolean elf_finalize_dynstr (bfd *output_bfd, struct bfd_link_info *info) { struct elf_link_hash_table *hash_table = elf_hash_table (info); struct elf_link_local_dynamic_entry *entry; struct elf_strtab_hash *dynstr = hash_table->dynstr; bfd *dynobj = hash_table->dynobj; asection *sdyn; bfd_size_type size; const struct elf_backend_data *bed; bfd_byte *extdyn; _bfd_elf_strtab_finalize (dynstr); size = _bfd_elf_strtab_size (dynstr); bed = get_elf_backend_data (dynobj); sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); BFD_ASSERT (sdyn != NULL); /* Update all .dynamic entries referencing .dynstr strings. */ for (extdyn = sdyn->contents; extdyn < sdyn->contents + sdyn->size; extdyn += bed->s->sizeof_dyn) { Elf_Internal_Dyn dyn; bed->s->swap_dyn_in (dynobj, extdyn, &dyn); switch (dyn.d_tag) { case DT_STRSZ: dyn.d_un.d_val = size; break; case DT_NEEDED: case DT_SONAME: case DT_RPATH: case DT_RUNPATH: case DT_FILTER: case DT_AUXILIARY: dyn.d_un.d_val = _bfd_elf_strtab_offset (dynstr, dyn.d_un.d_val); break; default: continue; } bed->s->swap_dyn_out (dynobj, &dyn, extdyn); } /* Now update local dynamic symbols. */ for (entry = hash_table->dynlocal; entry ; entry = entry->next) entry->isym.st_name = _bfd_elf_strtab_offset (dynstr, entry->isym.st_name); /* And the rest of dynamic symbols. */ elf_link_hash_traverse (hash_table, elf_adjust_dynstr_offsets, dynstr); /* Adjust version definitions. */ if (elf_tdata (output_bfd)->cverdefs) { asection *s; bfd_byte *p; bfd_size_type i; Elf_Internal_Verdef def; Elf_Internal_Verdaux defaux; s = bfd_get_section_by_name (dynobj, ".gnu.version_d"); p = s->contents; do { _bfd_elf_swap_verdef_in (output_bfd, (Elf_External_Verdef *) p, &def); p += sizeof (Elf_External_Verdef); if (def.vd_aux != sizeof (Elf_External_Verdef)) continue; for (i = 0; i < def.vd_cnt; ++i) { _bfd_elf_swap_verdaux_in (output_bfd, (Elf_External_Verdaux *) p, &defaux); defaux.vda_name = _bfd_elf_strtab_offset (dynstr, defaux.vda_name); _bfd_elf_swap_verdaux_out (output_bfd, &defaux, (Elf_External_Verdaux *) p); p += sizeof (Elf_External_Verdaux); } } while (def.vd_next); } /* Adjust version references. */ if (elf_tdata (output_bfd)->verref) { asection *s; bfd_byte *p; bfd_size_type i; Elf_Internal_Verneed need; Elf_Internal_Vernaux needaux; s = bfd_get_section_by_name (dynobj, ".gnu.version_r"); p = s->contents; do { _bfd_elf_swap_verneed_in (output_bfd, (Elf_External_Verneed *) p, &need); need.vn_file = _bfd_elf_strtab_offset (dynstr, need.vn_file); _bfd_elf_swap_verneed_out (output_bfd, &need, (Elf_External_Verneed *) p); p += sizeof (Elf_External_Verneed); for (i = 0; i < need.vn_cnt; ++i) { _bfd_elf_swap_vernaux_in (output_bfd, (Elf_External_Vernaux *) p, &needaux); needaux.vna_name = _bfd_elf_strtab_offset (dynstr, needaux.vna_name); _bfd_elf_swap_vernaux_out (output_bfd, &needaux, (Elf_External_Vernaux *) p); p += sizeof (Elf_External_Vernaux); } } while (need.vn_next); } return TRUE; } /* Return TRUE iff relocations for INPUT are compatible with OUTPUT. The default is to only match when the INPUT and OUTPUT are exactly the same target. */ bfd_boolean _bfd_elf_default_relocs_compatible (const bfd_target *input, const bfd_target *output) { return input == output; } /* Return TRUE iff relocations for INPUT are compatible with OUTPUT. This version is used when different targets for the same architecture are virtually identical. */ bfd_boolean _bfd_elf_relocs_compatible (const bfd_target *input, const bfd_target *output) { const struct elf_backend_data *obed, *ibed; if (input == output) return TRUE; ibed = xvec_get_elf_backend_data (input); obed = xvec_get_elf_backend_data (output); if (ibed->arch != obed->arch) return FALSE; /* If both backends are using this function, deem them compatible. */ return ibed->relocs_compatible == obed->relocs_compatible; } /* Add symbols from an ELF object file to the linker hash table. */ static bfd_boolean elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info) { Elf_Internal_Shdr *hdr; bfd_size_type symcount; bfd_size_type extsymcount; bfd_size_type extsymoff; struct elf_link_hash_entry **sym_hash; bfd_boolean dynamic; Elf_External_Versym *extversym = NULL; Elf_External_Versym *ever; struct elf_link_hash_entry *weaks; struct elf_link_hash_entry **nondeflt_vers = NULL; bfd_size_type nondeflt_vers_cnt = 0; Elf_Internal_Sym *isymbuf = NULL; Elf_Internal_Sym *isym; Elf_Internal_Sym *isymend; const struct elf_backend_data *bed; bfd_boolean add_needed; struct elf_link_hash_table *htab; bfd_size_type amt; void *alloc_mark = NULL; struct bfd_hash_entry **old_table = NULL; unsigned int old_size = 0; unsigned int old_count = 0; void *old_tab = NULL; void *old_hash; void *old_ent; struct bfd_link_hash_entry *old_undefs = NULL; struct bfd_link_hash_entry *old_undefs_tail = NULL; long old_dynsymcount = 0; size_t tabsize = 0; size_t hashsize = 0; htab = elf_hash_table (info); bed = get_elf_backend_data (abfd); if ((abfd->flags & DYNAMIC) == 0) dynamic = FALSE; else { dynamic = TRUE; /* You can't use -r against a dynamic object. Also, there's no hope of using a dynamic object which does not exactly match the format of the output file. */ if (info->relocatable || !is_elf_hash_table (htab) || htab->root.creator != abfd->xvec) { if (info->relocatable) bfd_set_error (bfd_error_invalid_operation); else bfd_set_error (bfd_error_wrong_format); goto error_return; } } /* As a GNU extension, any input sections which are named .gnu.warning.SYMBOL are treated as warning symbols for the given symbol. This differs from .gnu.warning sections, which generate warnings when they are included in an output file. */ if (info->executable) { asection *s; for (s = abfd->sections; s != NULL; s = s->next) { const char *name; name = bfd_get_section_name (abfd, s); if (CONST_STRNEQ (name, ".gnu.warning.")) { char *msg; bfd_size_type sz; name += sizeof ".gnu.warning." - 1; /* If this is a shared object, then look up the symbol in the hash table. If it is there, and it is already been defined, then we will not be using the entry from this shared object, so we don't need to warn. FIXME: If we see the definition in a regular object later on, we will warn, but we shouldn't. The only fix is to keep track of what warnings we are supposed to emit, and then handle them all at the end of the link. */ if (dynamic) { struct elf_link_hash_entry *h; h = elf_link_hash_lookup (htab, name, FALSE, FALSE, TRUE); /* FIXME: What about bfd_link_hash_common? */ if (h != NULL && (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak)) { /* We don't want to issue this warning. Clobber the section size so that the warning does not get copied into the output file. */ s->size = 0; continue; } } sz = s->size; msg = bfd_alloc (abfd, sz + 1); if (msg == NULL) goto error_return; if (! bfd_get_section_contents (abfd, s, msg, 0, sz)) goto error_return; msg[sz] = '\0'; if (! (_bfd_generic_link_add_one_symbol (info, abfd, name, BSF_WARNING, s, 0, msg, FALSE, bed->collect, NULL))) goto error_return; if (! info->relocatable) { /* Clobber the section size so that the warning does not get copied into the output file. */ s->size = 0; /* Also set SEC_EXCLUDE, so that symbols defined in the warning section don't get copied to the output. */ s->flags |= SEC_EXCLUDE; } } } } add_needed = TRUE; if (! dynamic) { /* If we are creating a shared library, create all the dynamic sections immediately. We need to attach them to something, so we attach them to this BFD, provided it is the right format. FIXME: If there are no input BFD's of the same format as the output, we can't make a shared library. */ if (info->shared && is_elf_hash_table (htab) && htab->root.creator == abfd->xvec && !htab->dynamic_sections_created) { if (! _bfd_elf_link_create_dynamic_sections (abfd, info)) goto error_return; } } else if (!is_elf_hash_table (htab)) goto error_return; else { asection *s; const char *soname = NULL; struct bfd_link_needed_list *rpath = NULL, *runpath = NULL; int ret; /* ld --just-symbols and dynamic objects don't mix very well. ld shouldn't allow it. */ if ((s = abfd->sections) != NULL && s->sec_info_type == ELF_INFO_TYPE_JUST_SYMS) abort (); /* If this dynamic lib was specified on the command line with --as-needed in effect, then we don't want to add a DT_NEEDED tag unless the lib is actually used. Similary for libs brought in by another lib's DT_NEEDED. When --no-add-needed is used on a dynamic lib, we don't want to add a DT_NEEDED entry for any dynamic library in DT_NEEDED tags in the dynamic lib at all. */ add_needed = (elf_dyn_lib_class (abfd) & (DYN_AS_NEEDED | DYN_DT_NEEDED | DYN_NO_NEEDED)) == 0; s = bfd_get_section_by_name (abfd, ".dynamic"); if (s != NULL) { bfd_byte *dynbuf; bfd_byte *extdyn; int elfsec; unsigned long shlink; if (!bfd_malloc_and_get_section (abfd, s, &dynbuf)) goto error_free_dyn; elfsec = _bfd_elf_section_from_bfd_section (abfd, s); if (elfsec == -1) goto error_free_dyn; shlink = elf_elfsections (abfd)[elfsec]->sh_link; for (extdyn = dynbuf; extdyn < dynbuf + s->size; extdyn += bed->s->sizeof_dyn) { Elf_Internal_Dyn dyn; bed->s->swap_dyn_in (abfd, extdyn, &dyn); if (dyn.d_tag == DT_SONAME) { unsigned int tagv = dyn.d_un.d_val; soname = bfd_elf_string_from_elf_section (abfd, shlink, tagv); if (soname == NULL) goto error_free_dyn; } if (dyn.d_tag == DT_NEEDED) { struct bfd_link_needed_list *n, **pn; char *fnm, *anm; unsigned int tagv = dyn.d_un.d_val; amt = sizeof (struct bfd_link_needed_list); n = bfd_alloc (abfd, amt); fnm = bfd_elf_string_from_elf_section (abfd, shlink, tagv); if (n == NULL || fnm == NULL) goto error_free_dyn; amt = strlen (fnm) + 1; anm = bfd_alloc (abfd, amt); if (anm == NULL) goto error_free_dyn; memcpy (anm, fnm, amt); n->name = anm; n->by = abfd; n->next = NULL; for (pn = &htab->needed; *pn != NULL; pn = &(*pn)->next) ; *pn = n; } if (dyn.d_tag == DT_RUNPATH) { struct bfd_link_needed_list *n, **pn; char *fnm, *anm; unsigned int tagv = dyn.d_un.d_val; amt = sizeof (struct bfd_link_needed_list); n = bfd_alloc (abfd, amt); fnm = bfd_elf_string_from_elf_section (abfd, shlink, tagv); if (n == NULL || fnm == NULL) goto error_free_dyn; amt = strlen (fnm) + 1; anm = bfd_alloc (abfd, amt); if (anm == NULL) goto error_free_dyn; memcpy (anm, fnm, amt); n->name = anm; n->by = abfd; n->next = NULL; for (pn = & runpath; *pn != NULL; pn = &(*pn)->next) ; *pn = n; } /* Ignore DT_RPATH if we have seen DT_RUNPATH. */ if (!runpath && dyn.d_tag == DT_RPATH) { struct bfd_link_needed_list *n, **pn; char *fnm, *anm; unsigned int tagv = dyn.d_un.d_val; amt = sizeof (struct bfd_link_needed_list); n = bfd_alloc (abfd, amt); fnm = bfd_elf_string_from_elf_section (abfd, shlink, tagv); if (n == NULL || fnm == NULL) goto error_free_dyn; amt = strlen (fnm) + 1; anm = bfd_alloc (abfd, amt); if (anm == NULL) { error_free_dyn: free (dynbuf); goto error_return; } memcpy (anm, fnm, amt); n->name = anm; n->by = abfd; n->next = NULL; for (pn = & rpath; *pn != NULL; pn = &(*pn)->next) ; *pn = n; } } free (dynbuf); } /* DT_RUNPATH overrides DT_RPATH. Do _NOT_ bfd_release, as that frees all more recently bfd_alloc'd blocks as well. */ if (runpath) rpath = runpath; if (rpath) { struct bfd_link_needed_list **pn; for (pn = &htab->runpath; *pn != NULL; pn = &(*pn)->next) ; *pn = rpath; } /* We do not want to include any of the sections in a dynamic object in the output file. We hack by simply clobbering the list of sections in the BFD. This could be handled more cleanly by, say, a new section flag; the existing SEC_NEVER_LOAD flag is not the one we want, because that one still implies that the section takes up space in the output file. */ bfd_section_list_clear (abfd); /* Find the name to use in a DT_NEEDED entry that refers to this object. If the object has a DT_SONAME entry, we use it. Otherwise, if the generic linker stuck something in elf_dt_name, we use that. Otherwise, we just use the file name. */ if (soname == NULL || *soname == '\0') { soname = elf_dt_name (abfd); if (soname == NULL || *soname == '\0') soname = bfd_get_filename (abfd); } /* Save the SONAME because sometimes the linker emulation code will need to know it. */ elf_dt_name (abfd) = soname; ret = elf_add_dt_needed_tag (abfd, info, soname, add_needed); if (ret < 0) goto error_return; /* If we have already included this dynamic object in the link, just ignore it. There is no reason to include a particular dynamic object more than once. */ if (ret > 0) return TRUE; } /* If this is a dynamic object, we always link against the .dynsym symbol table, not the .symtab symbol table. The dynamic linker will only see the .dynsym symbol table, so there is no reason to look at .symtab for a dynamic object. */ if (! dynamic || elf_dynsymtab (abfd) == 0) hdr = &elf_tdata (abfd)->symtab_hdr; else hdr = &elf_tdata (abfd)->dynsymtab_hdr; symcount = hdr->sh_size / bed->s->sizeof_sym; /* The sh_info field of the symtab header tells us where the external symbols start. We don't care about the local symbols at this point. */ if (elf_bad_symtab (abfd)) { extsymcount = symcount; extsymoff = 0; } else { extsymcount = symcount - hdr->sh_info; extsymoff = hdr->sh_info; } sym_hash = NULL; if (extsymcount != 0) { isymbuf = bfd_elf_get_elf_syms (abfd, hdr, extsymcount, extsymoff, NULL, NULL, NULL); if (isymbuf == NULL) goto error_return; /* We store a pointer to the hash table entry for each external symbol. */ amt = extsymcount * sizeof (struct elf_link_hash_entry *); sym_hash = bfd_alloc (abfd, amt); if (sym_hash == NULL) goto error_free_sym; elf_sym_hashes (abfd) = sym_hash; } if (dynamic) { /* Read in any version definitions. */ if (!_bfd_elf_slurp_version_tables (abfd, info->default_imported_symver)) goto error_free_sym; /* Read in the symbol versions, but don't bother to convert them to internal format. */ if (elf_dynversym (abfd) != 0) { Elf_Internal_Shdr *versymhdr; versymhdr = &elf_tdata (abfd)->dynversym_hdr; extversym = bfd_malloc (versymhdr->sh_size); if (extversym == NULL) goto error_free_sym; amt = versymhdr->sh_size; if (bfd_seek (abfd, versymhdr->sh_offset, SEEK_SET) != 0 || bfd_bread (extversym, amt, abfd) != amt) goto error_free_vers; } } /* If we are loading an as-needed shared lib, save the symbol table state before we start adding symbols. If the lib turns out to be unneeded, restore the state. */ if ((elf_dyn_lib_class (abfd) & DYN_AS_NEEDED) != 0) { unsigned int i; size_t entsize; for (entsize = 0, i = 0; i < htab->root.table.size; i++) { struct bfd_hash_entry *p; struct elf_link_hash_entry *h; for (p = htab->root.table.table[i]; p != NULL; p = p->next) { h = (struct elf_link_hash_entry *) p; entsize += htab->root.table.entsize; if (h->root.type == bfd_link_hash_warning) entsize += htab->root.table.entsize; } } tabsize = htab->root.table.size * sizeof (struct bfd_hash_entry *); hashsize = extsymcount * sizeof (struct elf_link_hash_entry *); old_tab = bfd_malloc (tabsize + entsize + hashsize); if (old_tab == NULL) goto error_free_vers; /* Remember the current objalloc pointer, so that all mem for symbols added can later be reclaimed. */ alloc_mark = bfd_hash_allocate (&htab->root.table, 1); if (alloc_mark == NULL) goto error_free_vers; /* Make a special call to the linker "notice" function to tell it that we are about to handle an as-needed lib. */ if (!(*info->callbacks->notice) (info, NULL, abfd, NULL, notice_as_needed)) return FALSE; /* Clone the symbol table and sym hashes. Remember some pointers into the symbol table, and dynamic symbol count. */ old_hash = (char *) old_tab + tabsize; old_ent = (char *) old_hash + hashsize; memcpy (old_tab, htab->root.table.table, tabsize); memcpy (old_hash, sym_hash, hashsize); old_undefs = htab->root.undefs; old_undefs_tail = htab->root.undefs_tail; old_table = htab->root.table.table; old_size = htab->root.table.size; old_count = htab->root.table.count; old_dynsymcount = htab->dynsymcount; for (i = 0; i < htab->root.table.size; i++) { struct bfd_hash_entry *p; struct elf_link_hash_entry *h; for (p = htab->root.table.table[i]; p != NULL; p = p->next) { memcpy (old_ent, p, htab->root.table.entsize); old_ent = (char *) old_ent + htab->root.table.entsize; h = (struct elf_link_hash_entry *) p; if (h->root.type == bfd_link_hash_warning) { memcpy (old_ent, h->root.u.i.link, htab->root.table.entsize); old_ent = (char *) old_ent + htab->root.table.entsize; } } } } weaks = NULL; ever = extversym != NULL ? extversym + extsymoff : NULL; for (isym = isymbuf, isymend = isymbuf + extsymcount; isym < isymend; isym++, sym_hash++, ever = (ever != NULL ? ever + 1 : NULL)) { int bind; bfd_vma value; asection *sec, *new_sec; flagword flags; const char *name; struct elf_link_hash_entry *h; bfd_boolean definition; bfd_boolean size_change_ok; bfd_boolean type_change_ok; bfd_boolean new_weakdef; bfd_boolean override; bfd_boolean common; unsigned int old_alignment; bfd *old_bfd; override = FALSE; flags = BSF_NO_FLAGS; sec = NULL; value = isym->st_value; *sym_hash = NULL; common = bed->common_definition (isym); bind = ELF_ST_BIND (isym->st_info); if (bind == STB_LOCAL) { /* This should be impossible, since ELF requires that all global symbols follow all local symbols, and that sh_info point to the first global symbol. Unfortunately, Irix 5 screws this up. */ continue; } else if (bind == STB_GLOBAL) { if (isym->st_shndx != SHN_UNDEF && !common) flags = BSF_GLOBAL; } else if (bind == STB_WEAK) flags = BSF_WEAK; else { /* Leave it up to the processor backend. */ } if (isym->st_shndx == SHN_UNDEF) sec = bfd_und_section_ptr; else if (isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE) { sec = bfd_section_from_elf_index (abfd, isym->st_shndx); if (sec == NULL) sec = bfd_abs_section_ptr; else if (sec->kept_section) { /* Symbols from discarded section are undefined. We keep its visibility. */ sec = bfd_und_section_ptr; isym->st_shndx = SHN_UNDEF; } else if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0) value -= sec->vma; } else if (isym->st_shndx == SHN_ABS) sec = bfd_abs_section_ptr; else if (isym->st_shndx == SHN_COMMON) { sec = bfd_com_section_ptr; /* What ELF calls the size we call the value. What ELF calls the value we call the alignment. */ value = isym->st_size; } else { /* Leave it up to the processor backend. */ } name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link, isym->st_name); if (name == NULL) goto error_free_vers; if (isym->st_shndx == SHN_COMMON && ELF_ST_TYPE (isym->st_info) == STT_TLS && !info->relocatable) { asection *tcomm = bfd_get_section_by_name (abfd, ".tcommon"); if (tcomm == NULL) { tcomm = bfd_make_section_with_flags (abfd, ".tcommon", (SEC_ALLOC | SEC_IS_COMMON | SEC_LINKER_CREATED | SEC_THREAD_LOCAL)); if (tcomm == NULL) goto error_free_vers; } sec = tcomm; } else if (bed->elf_add_symbol_hook) { if (! (*bed->elf_add_symbol_hook) (abfd, info, isym, &name, &flags, &sec, &value)) goto error_free_vers; /* The hook function sets the name to NULL if this symbol should be skipped for some reason. */ if (name == NULL) continue; } /* Sanity check that all possibilities were handled. */ if (sec == NULL) { bfd_set_error (bfd_error_bad_value); goto error_free_vers; } if (bfd_is_und_section (sec) || bfd_is_com_section (sec)) definition = FALSE; else definition = TRUE; size_change_ok = FALSE; type_change_ok = bed->type_change_ok; old_alignment = 0; old_bfd = NULL; new_sec = sec; if (is_elf_hash_table (htab)) { Elf_Internal_Versym iver; unsigned int vernum = 0; bfd_boolean skip; if (ever == NULL) { if (info->default_imported_symver) /* Use the default symbol version created earlier. */ iver.vs_vers = elf_tdata (abfd)->cverdefs; else iver.vs_vers = 0; } else _bfd_elf_swap_versym_in (abfd, ever, &iver); vernum = iver.vs_vers & VERSYM_VERSION; /* If this is a hidden symbol, or if it is not version 1, we append the version name to the symbol name. However, we do not modify a non-hidden absolute symbol if it is not a function, because it might be the version symbol itself. FIXME: What if it isn't? */ if ((iver.vs_vers & VERSYM_HIDDEN) != 0 || (vernum > 1 && (!bfd_is_abs_section (sec) || bed->is_function_type (ELF_ST_TYPE (isym->st_info))))) { const char *verstr; size_t namelen, verlen, newlen; char *newname, *p; if (isym->st_shndx != SHN_UNDEF) { if (vernum > elf_tdata (abfd)->cverdefs) verstr = NULL; else if (vernum > 1) verstr = elf_tdata (abfd)->verdef[vernum - 1].vd_nodename; else verstr = ""; if (verstr == NULL) { (*_bfd_error_handler) (_("%B: %s: invalid version %u (max %d)"), abfd, name, vernum, elf_tdata (abfd)->cverdefs); bfd_set_error (bfd_error_bad_value); goto error_free_vers; } } else { /* We cannot simply test for the number of entries in the VERNEED section since the numbers for the needed versions do not start at 0. */ Elf_Internal_Verneed *t; verstr = NULL; for (t = elf_tdata (abfd)->verref; t != NULL; t = t->vn_nextref) { Elf_Internal_Vernaux *a; for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) { if (a->vna_other == vernum) { verstr = a->vna_nodename; break; } } if (a != NULL) break; } if (verstr == NULL) { (*_bfd_error_handler) (_("%B: %s: invalid needed version %d"), abfd, name, vernum); bfd_set_error (bfd_error_bad_value); goto error_free_vers; } } namelen = strlen (name); verlen = strlen (verstr); newlen = namelen + verlen + 2; if ((iver.vs_vers & VERSYM_HIDDEN) == 0 && isym->st_shndx != SHN_UNDEF) ++newlen; newname = bfd_hash_allocate (&htab->root.table, newlen); if (newname == NULL) goto error_free_vers; memcpy (newname, name, namelen); p = newname + namelen; *p++ = ELF_VER_CHR; /* If this is a defined non-hidden version symbol, we add another @ to the name. This indicates the default version of the symbol. */ if ((iver.vs_vers & VERSYM_HIDDEN) == 0 && isym->st_shndx != SHN_UNDEF) *p++ = ELF_VER_CHR; memcpy (p, verstr, verlen + 1); name = newname; } if (!_bfd_elf_merge_symbol (abfd, info, name, isym, &sec, &value, &old_alignment, sym_hash, &skip, &override, &type_change_ok, &size_change_ok)) goto error_free_vers; if (skip) continue; if (override) definition = FALSE; h = *sym_hash; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* Remember the old alignment if this is a common symbol, so that we don't reduce the alignment later on. We can't check later, because _bfd_generic_link_add_one_symbol will set a default for the alignment which we want to override. We also remember the old bfd where the existing definition comes from. */ switch (h->root.type) { default: break; case bfd_link_hash_defined: case bfd_link_hash_defweak: old_bfd = h->root.u.def.section->owner; break; case bfd_link_hash_common: old_bfd = h->root.u.c.p->section->owner; old_alignment = h->root.u.c.p->alignment_power; break; } if (elf_tdata (abfd)->verdef != NULL && ! override && vernum > 1 && definition) h->verinfo.verdef = &elf_tdata (abfd)->verdef[vernum - 1]; } if (! (_bfd_generic_link_add_one_symbol (info, abfd, name, flags, sec, value, NULL, FALSE, bed->collect, (struct bfd_link_hash_entry **) sym_hash))) goto error_free_vers; h = *sym_hash; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; *sym_hash = h; new_weakdef = FALSE; if (dynamic && definition && (flags & BSF_WEAK) != 0 && !bed->is_function_type (ELF_ST_TYPE (isym->st_info)) && is_elf_hash_table (htab) && h->u.weakdef == NULL) { /* Keep a list of all weak defined non function symbols from a dynamic object, using the weakdef field. Later in this function we will set the weakdef field to the correct value. We only put non-function symbols from dynamic objects on this list, because that happens to be the only time we need to know the normal symbol corresponding to a weak symbol, and the information is time consuming to figure out. If the weakdef field is not already NULL, then this symbol was already defined by some previous dynamic object, and we will be using that previous definition anyhow. */ h->u.weakdef = weaks; weaks = h; new_weakdef = TRUE; } /* Set the alignment of a common symbol. */ if ((common || bfd_is_com_section (sec)) && h->root.type == bfd_link_hash_common) { unsigned int align; if (common) align = bfd_log2 (isym->st_value); else { /* The new symbol is a common symbol in a shared object. We need to get the alignment from the section. */ align = new_sec->alignment_power; } if (align > old_alignment /* Permit an alignment power of zero if an alignment of one is specified and no other alignments have been specified. */ || (isym->st_value == 1 && old_alignment == 0)) h->root.u.c.p->alignment_power = align; else h->root.u.c.p->alignment_power = old_alignment; } if (is_elf_hash_table (htab)) { bfd_boolean dynsym; /* Check the alignment when a common symbol is involved. This can change when a common symbol is overridden by a normal definition or a common symbol is ignored due to the old normal definition. We need to make sure the maximum alignment is maintained. */ if ((old_alignment || common) && h->root.type != bfd_link_hash_common) { unsigned int common_align; unsigned int normal_align; unsigned int symbol_align; bfd *normal_bfd; bfd *common_bfd; symbol_align = ffs (h->root.u.def.value) - 1; if (h->root.u.def.section->owner != NULL && (h->root.u.def.section->owner->flags & DYNAMIC) == 0) { normal_align = h->root.u.def.section->alignment_power; if (normal_align > symbol_align) normal_align = symbol_align; } else normal_align = symbol_align; if (old_alignment) { common_align = old_alignment; common_bfd = old_bfd; normal_bfd = abfd; } else { common_align = bfd_log2 (isym->st_value); common_bfd = abfd; normal_bfd = old_bfd; } if (normal_align < common_align) { /* PR binutils/2735 */ if (normal_bfd == NULL) (*_bfd_error_handler) (_("Warning: alignment %u of common symbol `%s' in %B" " is greater than the alignment (%u) of its section %A"), common_bfd, h->root.u.def.section, 1 << common_align, name, 1 << normal_align); else (*_bfd_error_handler) (_("Warning: alignment %u of symbol `%s' in %B" " is smaller than %u in %B"), normal_bfd, common_bfd, 1 << normal_align, name, 1 << common_align); } } /* Remember the symbol size if it isn't undefined. */ if ((isym->st_size != 0 && isym->st_shndx != SHN_UNDEF) && (definition || h->size == 0)) { if (h->size != 0 && h->size != isym->st_size && ! size_change_ok) (*_bfd_error_handler) (_("Warning: size of symbol `%s' changed" " from %lu in %B to %lu in %B"), old_bfd, abfd, name, (unsigned long) h->size, (unsigned long) isym->st_size); h->size = isym->st_size; } /* If this is a common symbol, then we always want H->SIZE to be the size of the common symbol. The code just above won't fix the size if a common symbol becomes larger. We don't warn about a size change here, because that is covered by --warn-common. Allow changed between different function types. */ if (h->root.type == bfd_link_hash_common) h->size = h->root.u.c.size; if (ELF_ST_TYPE (isym->st_info) != STT_NOTYPE && (definition || h->type == STT_NOTYPE)) { if (h->type != STT_NOTYPE && h->type != ELF_ST_TYPE (isym->st_info) && ! type_change_ok) (*_bfd_error_handler) (_("Warning: type of symbol `%s' changed" " from %d to %d in %B"), abfd, name, h->type, ELF_ST_TYPE (isym->st_info)); h->type = ELF_ST_TYPE (isym->st_info); } /* If st_other has a processor-specific meaning, specific code might be needed here. We never merge the visibility attribute with the one from a dynamic object. */ if (bed->elf_backend_merge_symbol_attribute) (*bed->elf_backend_merge_symbol_attribute) (h, isym, definition, dynamic); /* If this symbol has default visibility and the user has requested we not re-export it, then mark it as hidden. */ if (definition && !dynamic && (abfd->no_export || (abfd->my_archive && abfd->my_archive->no_export)) && ELF_ST_VISIBILITY (isym->st_other) != STV_INTERNAL) isym->st_other = (STV_HIDDEN | (isym->st_other & ~ELF_ST_VISIBILITY (-1))); if (ELF_ST_VISIBILITY (isym->st_other) != 0 && !dynamic) { unsigned char hvis, symvis, other, nvis; /* Only merge the visibility. Leave the remainder of the st_other field to elf_backend_merge_symbol_attribute. */ other = h->other & ~ELF_ST_VISIBILITY (-1); /* Combine visibilities, using the most constraining one. */ hvis = ELF_ST_VISIBILITY (h->other); symvis = ELF_ST_VISIBILITY (isym->st_other); if (! hvis) nvis = symvis; else if (! symvis) nvis = hvis; else nvis = hvis < symvis ? hvis : symvis; h->other = other | nvis; } /* Set a flag in the hash table entry indicating the type of reference or definition we just found. Keep a count of the number of dynamic symbols we find. A dynamic symbol is one which is referenced or defined by both a regular object and a shared object. */ dynsym = FALSE; if (! dynamic) { if (! definition) { h->ref_regular = 1; if (bind != STB_WEAK) h->ref_regular_nonweak = 1; } else h->def_regular = 1; if (! info->executable || h->def_dynamic || h->ref_dynamic) dynsym = TRUE; } else { if (! definition) h->ref_dynamic = 1; else h->def_dynamic = 1; if (h->def_regular || h->ref_regular || (h->u.weakdef != NULL && ! new_weakdef && h->u.weakdef->dynindx != -1)) dynsym = TRUE; } if (definition && (sec->flags & SEC_DEBUGGING)) { /* We don't want to make debug symbol dynamic. */ (*bed->elf_backend_hide_symbol) (info, h, TRUE); dynsym = FALSE; } /* Check to see if we need to add an indirect symbol for the default name. */ if (definition || h->root.type == bfd_link_hash_common) if (!_bfd_elf_add_default_symbol (abfd, info, h, name, isym, &sec, &value, &dynsym, override)) goto error_free_vers; if (definition && !dynamic) { char *p = strchr (name, ELF_VER_CHR); if (p != NULL && p[1] != ELF_VER_CHR) { /* Queue non-default versions so that .symver x, x@FOO aliases can be checked. */ if (!nondeflt_vers) { amt = ((isymend - isym + 1) * sizeof (struct elf_link_hash_entry *)); nondeflt_vers = bfd_malloc (amt); } nondeflt_vers[nondeflt_vers_cnt++] = h; } } if (dynsym && h->dynindx == -1) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) goto error_free_vers; if (h->u.weakdef != NULL && ! new_weakdef && h->u.weakdef->dynindx == -1) { if (!bfd_elf_link_record_dynamic_symbol (info, h->u.weakdef)) goto error_free_vers; } } else if (dynsym && h->dynindx != -1) /* If the symbol already has a dynamic index, but visibility says it should not be visible, turn it into a local symbol. */ switch (ELF_ST_VISIBILITY (h->other)) { case STV_INTERNAL: case STV_HIDDEN: (*bed->elf_backend_hide_symbol) (info, h, TRUE); dynsym = FALSE; break; } if (!add_needed && definition && dynsym && h->ref_regular) { int ret; const char *soname = elf_dt_name (abfd); /* A symbol from a library loaded via DT_NEEDED of some other library is referenced by a regular object. Add a DT_NEEDED entry for it. Issue an error if --no-add-needed is used. */ if ((elf_dyn_lib_class (abfd) & DYN_NO_NEEDED) != 0) { + bfd_boolean looks_soish; + const char *print_name; + int print_len; + size_t len, lend = 0; + + looks_soish = FALSE; + print_name = soname; + print_len = strlen(soname); + if (strncmp(soname, "lib", 3) == 0) + { + len = print_len; + if (len > 5 && strcmp(soname + len - 2, ".a") == 0) + lend = len - 5; + else + { + while (len > 6 && (ISDIGIT(soname[len - 1]) || + soname[len - 1] == '.')) + len--; + if (strncmp(soname + len - 3, ".so", 3) == 0) + lend = len - 6; + } + if (lend != 0) + { + print_name = soname + 3; + print_len = lend; + looks_soish = TRUE; + } + } + (*_bfd_error_handler) - (_("%B: invalid DSO for symbol `%s' definition"), - abfd, name); + (_("undefined reference to symbol `%s' (try adding -l%s%.*s)"), + name, looks_soish? "" : ":", print_len, print_name); bfd_set_error (bfd_error_bad_value); goto error_free_vers; } elf_dyn_lib_class (abfd) &= ~DYN_AS_NEEDED; add_needed = TRUE; ret = elf_add_dt_needed_tag (abfd, info, soname, add_needed); if (ret < 0) goto error_free_vers; BFD_ASSERT (ret == 0); } } } if (extversym != NULL) { free (extversym); extversym = NULL; } if (isymbuf != NULL) { free (isymbuf); isymbuf = NULL; } if ((elf_dyn_lib_class (abfd) & DYN_AS_NEEDED) != 0) { unsigned int i; /* Restore the symbol table. */ if (bed->as_needed_cleanup) (*bed->as_needed_cleanup) (abfd, info); old_hash = (char *) old_tab + tabsize; old_ent = (char *) old_hash + hashsize; sym_hash = elf_sym_hashes (abfd); htab->root.table.table = old_table; htab->root.table.size = old_size; htab->root.table.count = old_count; memcpy (htab->root.table.table, old_tab, tabsize); memcpy (sym_hash, old_hash, hashsize); htab->root.undefs = old_undefs; htab->root.undefs_tail = old_undefs_tail; for (i = 0; i < htab->root.table.size; i++) { struct bfd_hash_entry *p; struct elf_link_hash_entry *h; for (p = htab->root.table.table[i]; p != NULL; p = p->next) { h = (struct elf_link_hash_entry *) p; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->dynindx >= old_dynsymcount) _bfd_elf_strtab_delref (htab->dynstr, h->dynstr_index); memcpy (p, old_ent, htab->root.table.entsize); old_ent = (char *) old_ent + htab->root.table.entsize; h = (struct elf_link_hash_entry *) p; if (h->root.type == bfd_link_hash_warning) { memcpy (h->root.u.i.link, old_ent, htab->root.table.entsize); old_ent = (char *) old_ent + htab->root.table.entsize; } } } /* Make a special call to the linker "notice" function to tell it that symbols added for crefs may need to be removed. */ if (!(*info->callbacks->notice) (info, NULL, abfd, NULL, notice_not_needed)) return FALSE; free (old_tab); objalloc_free_block ((struct objalloc *) htab->root.table.memory, alloc_mark); if (nondeflt_vers != NULL) free (nondeflt_vers); return TRUE; } if (old_tab != NULL) { if (!(*info->callbacks->notice) (info, NULL, abfd, NULL, notice_needed)) return FALSE; free (old_tab); old_tab = NULL; } /* Now that all the symbols from this input file are created, handle .symver foo, foo@BAR such that any relocs against foo become foo@BAR. */ if (nondeflt_vers != NULL) { bfd_size_type cnt, symidx; for (cnt = 0; cnt < nondeflt_vers_cnt; ++cnt) { struct elf_link_hash_entry *h = nondeflt_vers[cnt], *hi; char *shortname, *p; p = strchr (h->root.root.string, ELF_VER_CHR); if (p == NULL || (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak)) continue; amt = p - h->root.root.string; shortname = bfd_malloc (amt + 1); memcpy (shortname, h->root.root.string, amt); shortname[amt] = '\0'; hi = (struct elf_link_hash_entry *) bfd_link_hash_lookup (&htab->root, shortname, FALSE, FALSE, FALSE); if (hi != NULL && hi->root.type == h->root.type && hi->root.u.def.value == h->root.u.def.value && hi->root.u.def.section == h->root.u.def.section) { (*bed->elf_backend_hide_symbol) (info, hi, TRUE); hi->root.type = bfd_link_hash_indirect; hi->root.u.i.link = (struct bfd_link_hash_entry *) h; (*bed->elf_backend_copy_indirect_symbol) (info, h, hi); sym_hash = elf_sym_hashes (abfd); if (sym_hash) for (symidx = 0; symidx < extsymcount; ++symidx) if (sym_hash[symidx] == hi) { sym_hash[symidx] = h; break; } } free (shortname); } free (nondeflt_vers); nondeflt_vers = NULL; } /* Now set the weakdefs field correctly for all the weak defined symbols we found. The only way to do this is to search all the symbols. Since we only need the information for non functions in dynamic objects, that's the only time we actually put anything on the list WEAKS. We need this information so that if a regular object refers to a symbol defined weakly in a dynamic object, the real symbol in the dynamic object is also put in the dynamic symbols; we also must arrange for both symbols to point to the same memory location. We could handle the general case of symbol aliasing, but a general symbol alias can only be generated in assembler code, handling it correctly would be very time consuming, and other ELF linkers don't handle general aliasing either. */ if (weaks != NULL) { struct elf_link_hash_entry **hpp; struct elf_link_hash_entry **hppend; struct elf_link_hash_entry **sorted_sym_hash; struct elf_link_hash_entry *h; size_t sym_count; /* Since we have to search the whole symbol list for each weak defined symbol, search time for N weak defined symbols will be O(N^2). Binary search will cut it down to O(NlogN). */ amt = extsymcount * sizeof (struct elf_link_hash_entry *); sorted_sym_hash = bfd_malloc (amt); if (sorted_sym_hash == NULL) goto error_return; sym_hash = sorted_sym_hash; hpp = elf_sym_hashes (abfd); hppend = hpp + extsymcount; sym_count = 0; for (; hpp < hppend; hpp++) { h = *hpp; if (h != NULL && h->root.type == bfd_link_hash_defined && !bed->is_function_type (h->type)) { *sym_hash = h; sym_hash++; sym_count++; } } qsort (sorted_sym_hash, sym_count, sizeof (struct elf_link_hash_entry *), elf_sort_symbol); while (weaks != NULL) { struct elf_link_hash_entry *hlook; asection *slook; bfd_vma vlook; long ilook; size_t i, j, idx; hlook = weaks; weaks = hlook->u.weakdef; hlook->u.weakdef = NULL; BFD_ASSERT (hlook->root.type == bfd_link_hash_defined || hlook->root.type == bfd_link_hash_defweak || hlook->root.type == bfd_link_hash_common || hlook->root.type == bfd_link_hash_indirect); slook = hlook->root.u.def.section; vlook = hlook->root.u.def.value; ilook = -1; i = 0; j = sym_count; while (i < j) { bfd_signed_vma vdiff; idx = (i + j) / 2; h = sorted_sym_hash [idx]; vdiff = vlook - h->root.u.def.value; if (vdiff < 0) j = idx; else if (vdiff > 0) i = idx + 1; else { long sdiff = slook->id - h->root.u.def.section->id; if (sdiff < 0) j = idx; else if (sdiff > 0) i = idx + 1; else { ilook = idx; break; } } } /* We didn't find a value/section match. */ if (ilook == -1) continue; for (i = ilook; i < sym_count; i++) { h = sorted_sym_hash [i]; /* Stop if value or section doesn't match. */ if (h->root.u.def.value != vlook || h->root.u.def.section != slook) break; else if (h != hlook) { hlook->u.weakdef = h; /* If the weak definition is in the list of dynamic symbols, make sure the real definition is put there as well. */ if (hlook->dynindx != -1 && h->dynindx == -1) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) goto error_return; } /* If the real definition is in the list of dynamic symbols, make sure the weak definition is put there as well. If we don't do this, then the dynamic loader might not merge the entries for the real definition and the weak definition. */ if (h->dynindx != -1 && hlook->dynindx == -1) { if (! bfd_elf_link_record_dynamic_symbol (info, hlook)) goto error_return; } break; } } } free (sorted_sym_hash); } if (bed->check_directives) (*bed->check_directives) (abfd, info); /* If this object is the same format as the output object, and it is not a shared library, then let the backend look through the relocs. This is required to build global offset table entries and to arrange for dynamic relocs. It is not required for the particular common case of linking non PIC code, even when linking against shared libraries, but unfortunately there is no way of knowing whether an object file has been compiled PIC or not. Looking through the relocs is not particularly time consuming. The problem is that we must either (1) keep the relocs in memory, which causes the linker to require additional runtime memory or (2) read the relocs twice from the input file, which wastes time. This would be a good case for using mmap. I have no idea how to handle linking PIC code into a file of a different format. It probably can't be done. */ if (! dynamic && is_elf_hash_table (htab) && bed->check_relocs != NULL && (*bed->relocs_compatible) (abfd->xvec, htab->root.creator)) { asection *o; for (o = abfd->sections; o != NULL; o = o->next) { Elf_Internal_Rela *internal_relocs; bfd_boolean ok; if ((o->flags & SEC_RELOC) == 0 || o->reloc_count == 0 || ((info->strip == strip_all || info->strip == strip_debugger) && (o->flags & SEC_DEBUGGING) != 0) || bfd_is_abs_section (o->output_section)) continue; internal_relocs = _bfd_elf_link_read_relocs (abfd, o, NULL, NULL, info->keep_memory); if (internal_relocs == NULL) goto error_return; ok = (*bed->check_relocs) (abfd, info, o, internal_relocs); if (elf_section_data (o)->relocs != internal_relocs) free (internal_relocs); if (! ok) goto error_return; } } /* If this is a non-traditional link, try to optimize the handling of the .stab/.stabstr sections. */ if (! dynamic && ! info->traditional_format && is_elf_hash_table (htab) && (info->strip != strip_all && info->strip != strip_debugger)) { asection *stabstr; stabstr = bfd_get_section_by_name (abfd, ".stabstr"); if (stabstr != NULL) { bfd_size_type string_offset = 0; asection *stab; for (stab = abfd->sections; stab; stab = stab->next) if (CONST_STRNEQ (stab->name, ".stab") && (!stab->name[5] || (stab->name[5] == '.' && ISDIGIT (stab->name[6]))) && (stab->flags & SEC_MERGE) == 0 && !bfd_is_abs_section (stab->output_section)) { struct bfd_elf_section_data *secdata; secdata = elf_section_data (stab); if (! _bfd_link_section_stabs (abfd, &htab->stab_info, stab, stabstr, &secdata->sec_info, &string_offset)) goto error_return; if (secdata->sec_info) stab->sec_info_type = ELF_INFO_TYPE_STABS; } } } if (is_elf_hash_table (htab) && add_needed) { /* Add this bfd to the loaded list. */ struct elf_link_loaded_list *n; n = bfd_alloc (abfd, sizeof (struct elf_link_loaded_list)); if (n == NULL) goto error_return; n->abfd = abfd; n->next = htab->loaded; htab->loaded = n; } return TRUE; error_free_vers: if (old_tab != NULL) free (old_tab); if (nondeflt_vers != NULL) free (nondeflt_vers); if (extversym != NULL) free (extversym); error_free_sym: if (isymbuf != NULL) free (isymbuf); error_return: return FALSE; } /* Return the linker hash table entry of a symbol that might be satisfied by an archive symbol. Return -1 on error. */ struct elf_link_hash_entry * _bfd_elf_archive_symbol_lookup (bfd *abfd, struct bfd_link_info *info, const char *name) { struct elf_link_hash_entry *h; char *p, *copy; size_t len, first; h = elf_link_hash_lookup (elf_hash_table (info), name, FALSE, FALSE, FALSE); if (h != NULL) return h; /* If this is a default version (the name contains @@), look up the symbol again with only one `@' as well as without the version. The effect is that references to the symbol with and without the version will be matched by the default symbol in the archive. */ p = strchr (name, ELF_VER_CHR); if (p == NULL || p[1] != ELF_VER_CHR) return h; /* First check with only one `@'. */ len = strlen (name); copy = bfd_alloc (abfd, len); if (copy == NULL) return (struct elf_link_hash_entry *) 0 - 1; first = p - name + 1; memcpy (copy, name, first); memcpy (copy + first, name + first + 1, len - first); h = elf_link_hash_lookup (elf_hash_table (info), copy, FALSE, FALSE, FALSE); if (h == NULL) { /* We also need to check references to the symbol without the version. */ copy[first - 1] = '\0'; h = elf_link_hash_lookup (elf_hash_table (info), copy, FALSE, FALSE, FALSE); } bfd_release (abfd, copy); return h; } /* Add symbols from an ELF archive file to the linker hash table. We don't use _bfd_generic_link_add_archive_symbols because of a problem which arises on UnixWare. The UnixWare libc.so is an archive which includes an entry libc.so.1 which defines a bunch of symbols. The libc.so archive also includes a number of other object files, which also define symbols, some of which are the same as those defined in libc.so.1. Correct linking requires that we consider each object file in turn, and include it if it defines any symbols we need. _bfd_generic_link_add_archive_symbols does not do this; it looks through the list of undefined symbols, and includes any object file which defines them. When this algorithm is used on UnixWare, it winds up pulling in libc.so.1 early and defining a bunch of symbols. This means that some of the other objects in the archive are not included in the link, which is incorrect since they precede libc.so.1 in the archive. Fortunately, ELF archive handling is simpler than that done by _bfd_generic_link_add_archive_symbols, which has to allow for a.out oddities. In ELF, if we find a symbol in the archive map, and the symbol is currently undefined, we know that we must pull in that object file. Unfortunately, we do have to make multiple passes over the symbol table until nothing further is resolved. */ static bfd_boolean elf_link_add_archive_symbols (bfd *abfd, struct bfd_link_info *info) { symindex c; bfd_boolean *defined = NULL; bfd_boolean *included = NULL; carsym *symdefs; bfd_boolean loop; bfd_size_type amt; const struct elf_backend_data *bed; struct elf_link_hash_entry * (*archive_symbol_lookup) (bfd *, struct bfd_link_info *, const char *); if (! bfd_has_map (abfd)) { /* An empty archive is a special case. */ if (bfd_openr_next_archived_file (abfd, NULL) == NULL) return TRUE; bfd_set_error (bfd_error_no_armap); return FALSE; } /* Keep track of all symbols we know to be already defined, and all files we know to be already included. This is to speed up the second and subsequent passes. */ c = bfd_ardata (abfd)->symdef_count; if (c == 0) return TRUE; amt = c; amt *= sizeof (bfd_boolean); defined = bfd_zmalloc (amt); included = bfd_zmalloc (amt); if (defined == NULL || included == NULL) goto error_return; symdefs = bfd_ardata (abfd)->symdefs; bed = get_elf_backend_data (abfd); archive_symbol_lookup = bed->elf_backend_archive_symbol_lookup; do { file_ptr last; symindex i; carsym *symdef; carsym *symdefend; loop = FALSE; last = -1; symdef = symdefs; symdefend = symdef + c; for (i = 0; symdef < symdefend; symdef++, i++) { struct elf_link_hash_entry *h; bfd *element; struct bfd_link_hash_entry *undefs_tail; symindex mark; if (defined[i] || included[i]) continue; if (symdef->file_offset == last) { included[i] = TRUE; continue; } h = archive_symbol_lookup (abfd, info, symdef->name); if (h == (struct elf_link_hash_entry *) 0 - 1) goto error_return; if (h == NULL) continue; if (h->root.type == bfd_link_hash_common) { /* We currently have a common symbol. The archive map contains a reference to this symbol, so we may want to include it. We only want to include it however, if this archive element contains a definition of the symbol, not just another common declaration of it. Unfortunately some archivers (including GNU ar) will put declarations of common symbols into their archive maps, as well as real definitions, so we cannot just go by the archive map alone. Instead we must read in the element's symbol table and check that to see what kind of symbol definition this is. */ if (! elf_link_is_defined_archive_symbol (abfd, symdef)) continue; } else if (h->root.type != bfd_link_hash_undefined) { if (h->root.type != bfd_link_hash_undefweak) defined[i] = TRUE; continue; } /* We need to include this archive member. */ element = _bfd_get_elt_at_filepos (abfd, symdef->file_offset); if (element == NULL) goto error_return; if (! bfd_check_format (element, bfd_object)) goto error_return; /* Doublecheck that we have not included this object already--it should be impossible, but there may be something wrong with the archive. */ if (element->archive_pass != 0) { bfd_set_error (bfd_error_bad_value); goto error_return; } element->archive_pass = 1; undefs_tail = info->hash->undefs_tail; if (! (*info->callbacks->add_archive_element) (info, element, symdef->name)) goto error_return; if (! bfd_link_add_symbols (element, info)) goto error_return; /* If there are any new undefined symbols, we need to make another pass through the archive in order to see whether they can be defined. FIXME: This isn't perfect, because common symbols wind up on undefs_tail and because an undefined symbol which is defined later on in this pass does not require another pass. This isn't a bug, but it does make the code less efficient than it could be. */ if (undefs_tail != info->hash->undefs_tail) loop = TRUE; /* Look backward to mark all symbols from this object file which we have already seen in this pass. */ mark = i; do { included[mark] = TRUE; if (mark == 0) break; --mark; } while (symdefs[mark].file_offset == symdef->file_offset); /* We mark subsequent symbols from this object file as we go on through the loop. */ last = symdef->file_offset; } } while (loop); free (defined); free (included); return TRUE; error_return: if (defined != NULL) free (defined); if (included != NULL) free (included); return FALSE; } /* Given an ELF BFD, add symbols to the global hash table as appropriate. */ bfd_boolean bfd_elf_link_add_symbols (bfd *abfd, struct bfd_link_info *info) { switch (bfd_get_format (abfd)) { case bfd_object: return elf_link_add_object_symbols (abfd, info); case bfd_archive: return elf_link_add_archive_symbols (abfd, info); default: bfd_set_error (bfd_error_wrong_format); return FALSE; } } /* This function will be called though elf_link_hash_traverse to store all hash value of the exported symbols in an array. */ static bfd_boolean elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data) { unsigned long **valuep = data; const char *name; char *p; unsigned long ha; char *alc = NULL; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* Ignore indirect symbols. These are added by the versioning code. */ if (h->dynindx == -1) return TRUE; name = h->root.root.string; p = strchr (name, ELF_VER_CHR); if (p != NULL) { alc = bfd_malloc (p - name + 1); memcpy (alc, name, p - name); alc[p - name] = '\0'; name = alc; } /* Compute the hash value. */ ha = bfd_elf_hash (name); /* Store the found hash value in the array given as the argument. */ *(*valuep)++ = ha; /* And store it in the struct so that we can put it in the hash table later. */ h->u.elf_hash_value = ha; if (alc != NULL) free (alc); return TRUE; } struct collect_gnu_hash_codes { bfd *output_bfd; const struct elf_backend_data *bed; unsigned long int nsyms; unsigned long int maskbits; unsigned long int *hashcodes; unsigned long int *hashval; unsigned long int *indx; unsigned long int *counts; bfd_vma *bitmask; bfd_byte *contents; long int min_dynindx; unsigned long int bucketcount; unsigned long int symindx; long int local_indx; long int shift1, shift2; unsigned long int mask; }; /* This function will be called though elf_link_hash_traverse to store all hash value of the exported symbols in an array. */ static bfd_boolean elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data) { struct collect_gnu_hash_codes *s = data; const char *name; char *p; unsigned long ha; char *alc = NULL; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* Ignore indirect symbols. These are added by the versioning code. */ if (h->dynindx == -1) return TRUE; /* Ignore also local symbols and undefined symbols. */ if (! (*s->bed->elf_hash_symbol) (h)) return TRUE; name = h->root.root.string; p = strchr (name, ELF_VER_CHR); if (p != NULL) { alc = bfd_malloc (p - name + 1); memcpy (alc, name, p - name); alc[p - name] = '\0'; name = alc; } /* Compute the hash value. */ ha = bfd_elf_gnu_hash (name); /* Store the found hash value in the array for compute_bucket_count, and also for .dynsym reordering purposes. */ s->hashcodes[s->nsyms] = ha; s->hashval[h->dynindx] = ha; ++s->nsyms; if (s->min_dynindx < 0 || s->min_dynindx > h->dynindx) s->min_dynindx = h->dynindx; if (alc != NULL) free (alc); return TRUE; } /* This function will be called though elf_link_hash_traverse to do final dynaminc symbol renumbering. */ static bfd_boolean elf_renumber_gnu_hash_syms (struct elf_link_hash_entry *h, void *data) { struct collect_gnu_hash_codes *s = data; unsigned long int bucket; unsigned long int val; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* Ignore indirect symbols. */ if (h->dynindx == -1) return TRUE; /* Ignore also local symbols and undefined symbols. */ if (! (*s->bed->elf_hash_symbol) (h)) { if (h->dynindx >= s->min_dynindx) h->dynindx = s->local_indx++; return TRUE; } bucket = s->hashval[h->dynindx] % s->bucketcount; val = (s->hashval[h->dynindx] >> s->shift1) & ((s->maskbits >> s->shift1) - 1); s->bitmask[val] |= ((bfd_vma) 1) << (s->hashval[h->dynindx] & s->mask); s->bitmask[val] |= ((bfd_vma) 1) << ((s->hashval[h->dynindx] >> s->shift2) & s->mask); val = s->hashval[h->dynindx] & ~(unsigned long int) 1; if (s->counts[bucket] == 1) /* Last element terminates the chain. */ val |= 1; bfd_put_32 (s->output_bfd, val, s->contents + (s->indx[bucket] - s->symindx) * 4); --s->counts[bucket]; h->dynindx = s->indx[bucket]++; return TRUE; } /* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */ bfd_boolean _bfd_elf_hash_symbol (struct elf_link_hash_entry *h) { return !(h->forced_local || h->root.type == bfd_link_hash_undefined || h->root.type == bfd_link_hash_undefweak || ((h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && h->root.u.def.section->output_section == NULL)); } /* Array used to determine the number of hash table buckets to use based on the number of symbols there are. If there are fewer than 3 symbols we use 1 bucket, fewer than 17 symbols we use 3 buckets, fewer than 37 we use 17 buckets, and so forth. We never use more than 32771 buckets. */ static const size_t elf_buckets[] = { 1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209, 16411, 32771, 0 }; /* Compute bucket count for hashing table. We do not use a static set of possible tables sizes anymore. Instead we determine for all possible reasonable sizes of the table the outcome (i.e., the number of collisions etc) and choose the best solution. The weighting functions are not too simple to allow the table to grow without bounds. Instead one of the weighting factors is the size. Therefore the result is always a good payoff between few collisions (= short chain lengths) and table size. */ static size_t compute_bucket_count (struct bfd_link_info *info, unsigned long int *hashcodes, unsigned long int nsyms, int gnu_hash) { size_t dynsymcount = elf_hash_table (info)->dynsymcount; size_t best_size = 0; unsigned long int i; bfd_size_type amt; /* We have a problem here. The following code to optimize the table size requires an integer type with more the 32 bits. If BFD_HOST_U_64_BIT is set we know about such a type. */ #ifdef BFD_HOST_U_64_BIT if (info->optimize) { size_t minsize; size_t maxsize; BFD_HOST_U_64_BIT best_chlen = ~((BFD_HOST_U_64_BIT) 0); bfd *dynobj = elf_hash_table (info)->dynobj; const struct elf_backend_data *bed = get_elf_backend_data (dynobj); unsigned long int *counts; /* Possible optimization parameters: if we have NSYMS symbols we say that the hashing table must at least have NSYMS/4 and at most 2*NSYMS buckets. */ minsize = nsyms / 4; if (minsize == 0) minsize = 1; best_size = maxsize = nsyms * 2; if (gnu_hash) { if (minsize < 2) minsize = 2; if ((best_size & 31) == 0) ++best_size; } /* Create array where we count the collisions in. We must use bfd_malloc since the size could be large. */ amt = maxsize; amt *= sizeof (unsigned long int); counts = bfd_malloc (amt); if (counts == NULL) return 0; /* Compute the "optimal" size for the hash table. The criteria is a minimal chain length. The minor criteria is (of course) the size of the table. */ for (i = minsize; i < maxsize; ++i) { /* Walk through the array of hashcodes and count the collisions. */ BFD_HOST_U_64_BIT max; unsigned long int j; unsigned long int fact; if (gnu_hash && (i & 31) == 0) continue; memset (counts, '\0', i * sizeof (unsigned long int)); /* Determine how often each hash bucket is used. */ for (j = 0; j < nsyms; ++j) ++counts[hashcodes[j] % i]; /* For the weight function we need some information about the pagesize on the target. This is information need not be 100% accurate. Since this information is not available (so far) we define it here to a reasonable default value. If it is crucial to have a better value some day simply define this value. */ # ifndef BFD_TARGET_PAGESIZE # define BFD_TARGET_PAGESIZE (4096) # endif /* We in any case need 2 + DYNSYMCOUNT entries for the size values and the chains. */ max = (2 + dynsymcount) * bed->s->sizeof_hash_entry; # if 1 /* Variant 1: optimize for short chains. We add the squares of all the chain lengths (which favors many small chain over a few long chains). */ for (j = 0; j < i; ++j) max += counts[j] * counts[j]; /* This adds penalties for the overall size of the table. */ fact = i / (BFD_TARGET_PAGESIZE / bed->s->sizeof_hash_entry) + 1; max *= fact * fact; # else /* Variant 2: Optimize a lot more for small table. Here we also add squares of the size but we also add penalties for empty slots (the +1 term). */ for (j = 0; j < i; ++j) max += (1 + counts[j]) * (1 + counts[j]); /* The overall size of the table is considered, but not as strong as in variant 1, where it is squared. */ fact = i / (BFD_TARGET_PAGESIZE / bed->s->sizeof_hash_entry) + 1; max *= fact; # endif /* Compare with current best results. */ if (max < best_chlen) { best_chlen = max; best_size = i; } } free (counts); } else #endif /* defined (BFD_HOST_U_64_BIT) */ { /* This is the fallback solution if no 64bit type is available or if we are not supposed to spend much time on optimizations. We select the bucket count using a fixed set of numbers. */ for (i = 0; elf_buckets[i] != 0; i++) { best_size = elf_buckets[i]; if (nsyms < elf_buckets[i + 1]) break; } if (gnu_hash && best_size < 2) best_size = 2; } return best_size; } /* Set up the sizes and contents of the ELF dynamic sections. This is called by the ELF linker emulation before_allocation routine. We must set the sizes of the sections before the linker sets the addresses of the various sections. */ bfd_boolean bfd_elf_size_dynamic_sections (bfd *output_bfd, const char *soname, const char *rpath, const char *filter_shlib, const char * const *auxiliary_filters, struct bfd_link_info *info, asection **sinterpptr, struct bfd_elf_version_tree *verdefs) { bfd_size_type soname_indx; bfd *dynobj; const struct elf_backend_data *bed; struct elf_assign_sym_version_info asvinfo; *sinterpptr = NULL; soname_indx = (bfd_size_type) -1; if (!is_elf_hash_table (info->hash)) return TRUE; bed = get_elf_backend_data (output_bfd); elf_tdata (output_bfd)->relro = info->relro; if (info->execstack) elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X; else if (info->noexecstack) elf_tdata (output_bfd)->stack_flags = PF_R | PF_W; else { bfd *inputobj; asection *notesec = NULL; int exec = 0; for (inputobj = info->input_bfds; inputobj; inputobj = inputobj->link_next) { asection *s; if (inputobj->flags & (DYNAMIC | BFD_LINKER_CREATED)) continue; s = bfd_get_section_by_name (inputobj, ".note.GNU-stack"); if (s) { if (s->flags & SEC_CODE) exec = PF_X; notesec = s; } else if (bed->default_execstack) exec = PF_X; } if (notesec) { elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | exec; if (exec && info->relocatable && notesec->output_section != bfd_abs_section_ptr) notesec->output_section->flags |= SEC_CODE; } } /* Any syms created from now on start with -1 in got.refcount/offset and plt.refcount/offset. */ elf_hash_table (info)->init_got_refcount = elf_hash_table (info)->init_got_offset; elf_hash_table (info)->init_plt_refcount = elf_hash_table (info)->init_plt_offset; /* The backend may have to create some sections regardless of whether we're dynamic or not. */ if (bed->elf_backend_always_size_sections && ! (*bed->elf_backend_always_size_sections) (output_bfd, info)) return FALSE; if (! _bfd_elf_maybe_strip_eh_frame_hdr (info)) return FALSE; dynobj = elf_hash_table (info)->dynobj; /* If there were no dynamic objects in the link, there is nothing to do here. */ if (dynobj == NULL) return TRUE; if (elf_hash_table (info)->dynamic_sections_created) { struct elf_info_failed eif; struct elf_link_hash_entry *h; asection *dynstr; struct bfd_elf_version_tree *t; struct bfd_elf_version_expr *d; asection *s; bfd_boolean all_defined; *sinterpptr = bfd_get_section_by_name (dynobj, ".interp"); BFD_ASSERT (*sinterpptr != NULL || !info->executable); if (soname != NULL) { soname_indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, soname, TRUE); if (soname_indx == (bfd_size_type) -1 || !_bfd_elf_add_dynamic_entry (info, DT_SONAME, soname_indx)) return FALSE; } if (info->symbolic) { if (!_bfd_elf_add_dynamic_entry (info, DT_SYMBOLIC, 0)) return FALSE; info->flags |= DF_SYMBOLIC; } if (rpath != NULL) { bfd_size_type indx; indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, rpath, TRUE); if (indx == (bfd_size_type) -1 || !_bfd_elf_add_dynamic_entry (info, DT_RPATH, indx)) return FALSE; if (info->new_dtags) { _bfd_elf_strtab_addref (elf_hash_table (info)->dynstr, indx); if (!_bfd_elf_add_dynamic_entry (info, DT_RUNPATH, indx)) return FALSE; } } if (filter_shlib != NULL) { bfd_size_type indx; indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, filter_shlib, TRUE); if (indx == (bfd_size_type) -1 || !_bfd_elf_add_dynamic_entry (info, DT_FILTER, indx)) return FALSE; } if (auxiliary_filters != NULL) { const char * const *p; for (p = auxiliary_filters; *p != NULL; p++) { bfd_size_type indx; indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, *p, TRUE); if (indx == (bfd_size_type) -1 || !_bfd_elf_add_dynamic_entry (info, DT_AUXILIARY, indx)) return FALSE; } } eif.info = info; eif.verdefs = verdefs; eif.failed = FALSE; /* If we are supposed to export all symbols into the dynamic symbol table (this is not the normal case), then do so. */ if (info->export_dynamic || (info->executable && info->dynamic)) { elf_link_hash_traverse (elf_hash_table (info), _bfd_elf_export_symbol, &eif); if (eif.failed) return FALSE; } /* Make all global versions with definition. */ for (t = verdefs; t != NULL; t = t->next) for (d = t->globals.list; d != NULL; d = d->next) if (!d->symver && d->symbol) { const char *verstr, *name; size_t namelen, verlen, newlen; char *newname, *p; struct elf_link_hash_entry *newh; name = d->symbol; namelen = strlen (name); verstr = t->name; verlen = strlen (verstr); newlen = namelen + verlen + 3; newname = bfd_malloc (newlen); if (newname == NULL) return FALSE; memcpy (newname, name, namelen); /* Check the hidden versioned definition. */ p = newname + namelen; *p++ = ELF_VER_CHR; memcpy (p, verstr, verlen + 1); newh = elf_link_hash_lookup (elf_hash_table (info), newname, FALSE, FALSE, FALSE); if (newh == NULL || (newh->root.type != bfd_link_hash_defined && newh->root.type != bfd_link_hash_defweak)) { /* Check the default versioned definition. */ *p++ = ELF_VER_CHR; memcpy (p, verstr, verlen + 1); newh = elf_link_hash_lookup (elf_hash_table (info), newname, FALSE, FALSE, FALSE); } free (newname); /* Mark this version if there is a definition and it is not defined in a shared object. */ if (newh != NULL && !newh->def_dynamic && (newh->root.type == bfd_link_hash_defined || newh->root.type == bfd_link_hash_defweak)) d->symver = 1; } /* Attach all the symbols to their version information. */ asvinfo.output_bfd = output_bfd; asvinfo.info = info; asvinfo.verdefs = verdefs; asvinfo.failed = FALSE; elf_link_hash_traverse (elf_hash_table (info), _bfd_elf_link_assign_sym_version, &asvinfo); if (asvinfo.failed) return FALSE; if (!info->allow_undefined_version) { /* Check if all global versions have a definition. */ all_defined = TRUE; for (t = verdefs; t != NULL; t = t->next) for (d = t->globals.list; d != NULL; d = d->next) if (!d->symver && !d->script) { (*_bfd_error_handler) (_("%s: undefined version: %s"), d->pattern, t->name); all_defined = FALSE; } if (!all_defined) { bfd_set_error (bfd_error_bad_value); return FALSE; } } /* Find all symbols which were defined in a dynamic object and make the backend pick a reasonable value for them. */ elf_link_hash_traverse (elf_hash_table (info), _bfd_elf_adjust_dynamic_symbol, &eif); if (eif.failed) return FALSE; /* Add some entries to the .dynamic section. We fill in some of the values later, in bfd_elf_final_link, but we must add the entries now so that we know the final size of the .dynamic section. */ /* If there are initialization and/or finalization functions to call then add the corresponding DT_INIT/DT_FINI entries. */ h = (info->init_function ? elf_link_hash_lookup (elf_hash_table (info), info->init_function, FALSE, FALSE, FALSE) : NULL); if (h != NULL && (h->ref_regular || h->def_regular)) { if (!_bfd_elf_add_dynamic_entry (info, DT_INIT, 0)) return FALSE; } h = (info->fini_function ? elf_link_hash_lookup (elf_hash_table (info), info->fini_function, FALSE, FALSE, FALSE) : NULL); if (h != NULL && (h->ref_regular || h->def_regular)) { if (!_bfd_elf_add_dynamic_entry (info, DT_FINI, 0)) return FALSE; } s = bfd_get_section_by_name (output_bfd, ".preinit_array"); if (s != NULL && s->linker_has_input) { /* DT_PREINIT_ARRAY is not allowed in shared library. */ if (! info->executable) { bfd *sub; asection *o; for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) if (bfd_get_flavour (sub) == bfd_target_elf_flavour) for (o = sub->sections; o != NULL; o = o->next) if (elf_section_data (o)->this_hdr.sh_type == SHT_PREINIT_ARRAY) { (*_bfd_error_handler) (_("%B: .preinit_array section is not allowed in DSO"), sub); break; } bfd_set_error (bfd_error_nonrepresentable_section); return FALSE; } if (!_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAY, 0) || !_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAYSZ, 0)) return FALSE; } s = bfd_get_section_by_name (output_bfd, ".init_array"); if (s != NULL && s->linker_has_input) { if (!_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAY, 0) || !_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAYSZ, 0)) return FALSE; } s = bfd_get_section_by_name (output_bfd, ".fini_array"); if (s != NULL && s->linker_has_input) { if (!_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAY, 0) || !_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAYSZ, 0)) return FALSE; } dynstr = bfd_get_section_by_name (dynobj, ".dynstr"); /* If .dynstr is excluded from the link, we don't want any of these tags. Strictly, we should be checking each section individually; This quick check covers for the case where someone does a /DISCARD/ : { *(*) }. */ if (dynstr != NULL && dynstr->output_section != bfd_abs_section_ptr) { bfd_size_type strsize; strsize = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr); if ((info->emit_hash && !_bfd_elf_add_dynamic_entry (info, DT_HASH, 0)) || (info->emit_gnu_hash && !_bfd_elf_add_dynamic_entry (info, DT_GNU_HASH, 0)) || !_bfd_elf_add_dynamic_entry (info, DT_STRTAB, 0) || !_bfd_elf_add_dynamic_entry (info, DT_SYMTAB, 0) || !_bfd_elf_add_dynamic_entry (info, DT_STRSZ, strsize) || !_bfd_elf_add_dynamic_entry (info, DT_SYMENT, bed->s->sizeof_sym)) return FALSE; } } /* The backend must work out the sizes of all the other dynamic sections. */ if (bed->elf_backend_size_dynamic_sections && ! (*bed->elf_backend_size_dynamic_sections) (output_bfd, info)) return FALSE; if (elf_hash_table (info)->dynamic_sections_created) { unsigned long section_sym_count; asection *s; /* Set up the version definition section. */ s = bfd_get_section_by_name (dynobj, ".gnu.version_d"); BFD_ASSERT (s != NULL); /* We may have created additional version definitions if we are just linking a regular application. */ verdefs = asvinfo.verdefs; /* Skip anonymous version tag. */ if (verdefs != NULL && verdefs->vernum == 0) verdefs = verdefs->next; if (verdefs == NULL && !info->create_default_symver) s->flags |= SEC_EXCLUDE; else { unsigned int cdefs; bfd_size_type size; struct bfd_elf_version_tree *t; bfd_byte *p; Elf_Internal_Verdef def; Elf_Internal_Verdaux defaux; struct bfd_link_hash_entry *bh; struct elf_link_hash_entry *h; const char *name; cdefs = 0; size = 0; /* Make space for the base version. */ size += sizeof (Elf_External_Verdef); size += sizeof (Elf_External_Verdaux); ++cdefs; /* Make space for the default version. */ if (info->create_default_symver) { size += sizeof (Elf_External_Verdef); ++cdefs; } for (t = verdefs; t != NULL; t = t->next) { struct bfd_elf_version_deps *n; size += sizeof (Elf_External_Verdef); size += sizeof (Elf_External_Verdaux); ++cdefs; for (n = t->deps; n != NULL; n = n->next) size += sizeof (Elf_External_Verdaux); } s->size = size; s->contents = bfd_alloc (output_bfd, s->size); if (s->contents == NULL && s->size != 0) return FALSE; /* Fill in the version definition section. */ p = s->contents; def.vd_version = VER_DEF_CURRENT; def.vd_flags = VER_FLG_BASE; def.vd_ndx = 1; def.vd_cnt = 1; if (info->create_default_symver) { def.vd_aux = 2 * sizeof (Elf_External_Verdef); def.vd_next = sizeof (Elf_External_Verdef); } else { def.vd_aux = sizeof (Elf_External_Verdef); def.vd_next = (sizeof (Elf_External_Verdef) + sizeof (Elf_External_Verdaux)); } if (soname_indx != (bfd_size_type) -1) { _bfd_elf_strtab_addref (elf_hash_table (info)->dynstr, soname_indx); def.vd_hash = bfd_elf_hash (soname); defaux.vda_name = soname_indx; name = soname; } else { bfd_size_type indx; name = lbasename (output_bfd->filename); def.vd_hash = bfd_elf_hash (name); indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, name, FALSE); if (indx == (bfd_size_type) -1) return FALSE; defaux.vda_name = indx; } defaux.vda_next = 0; _bfd_elf_swap_verdef_out (output_bfd, &def, (Elf_External_Verdef *) p); p += sizeof (Elf_External_Verdef); if (info->create_default_symver) { /* Add a symbol representing this version. */ bh = NULL; if (! (_bfd_generic_link_add_one_symbol (info, dynobj, name, BSF_GLOBAL, bfd_abs_section_ptr, 0, NULL, FALSE, get_elf_backend_data (dynobj)->collect, &bh))) return FALSE; h = (struct elf_link_hash_entry *) bh; h->non_elf = 0; h->def_regular = 1; h->type = STT_OBJECT; h->verinfo.vertree = NULL; if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; /* Create a duplicate of the base version with the same aux block, but different flags. */ def.vd_flags = 0; def.vd_ndx = 2; def.vd_aux = sizeof (Elf_External_Verdef); if (verdefs) def.vd_next = (sizeof (Elf_External_Verdef) + sizeof (Elf_External_Verdaux)); else def.vd_next = 0; _bfd_elf_swap_verdef_out (output_bfd, &def, (Elf_External_Verdef *) p); p += sizeof (Elf_External_Verdef); } _bfd_elf_swap_verdaux_out (output_bfd, &defaux, (Elf_External_Verdaux *) p); p += sizeof (Elf_External_Verdaux); for (t = verdefs; t != NULL; t = t->next) { unsigned int cdeps; struct bfd_elf_version_deps *n; cdeps = 0; for (n = t->deps; n != NULL; n = n->next) ++cdeps; /* Add a symbol representing this version. */ bh = NULL; if (! (_bfd_generic_link_add_one_symbol (info, dynobj, t->name, BSF_GLOBAL, bfd_abs_section_ptr, 0, NULL, FALSE, get_elf_backend_data (dynobj)->collect, &bh))) return FALSE; h = (struct elf_link_hash_entry *) bh; h->non_elf = 0; h->def_regular = 1; h->type = STT_OBJECT; h->verinfo.vertree = t; if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; def.vd_version = VER_DEF_CURRENT; def.vd_flags = 0; if (t->globals.list == NULL && t->locals.list == NULL && ! t->used) def.vd_flags |= VER_FLG_WEAK; def.vd_ndx = t->vernum + (info->create_default_symver ? 2 : 1); def.vd_cnt = cdeps + 1; def.vd_hash = bfd_elf_hash (t->name); def.vd_aux = sizeof (Elf_External_Verdef); def.vd_next = 0; if (t->next != NULL) def.vd_next = (sizeof (Elf_External_Verdef) + (cdeps + 1) * sizeof (Elf_External_Verdaux)); _bfd_elf_swap_verdef_out (output_bfd, &def, (Elf_External_Verdef *) p); p += sizeof (Elf_External_Verdef); defaux.vda_name = h->dynstr_index; _bfd_elf_strtab_addref (elf_hash_table (info)->dynstr, h->dynstr_index); defaux.vda_next = 0; if (t->deps != NULL) defaux.vda_next = sizeof (Elf_External_Verdaux); t->name_indx = defaux.vda_name; _bfd_elf_swap_verdaux_out (output_bfd, &defaux, (Elf_External_Verdaux *) p); p += sizeof (Elf_External_Verdaux); for (n = t->deps; n != NULL; n = n->next) { if (n->version_needed == NULL) { /* This can happen if there was an error in the version script. */ defaux.vda_name = 0; } else { defaux.vda_name = n->version_needed->name_indx; _bfd_elf_strtab_addref (elf_hash_table (info)->dynstr, defaux.vda_name); } if (n->next == NULL) defaux.vda_next = 0; else defaux.vda_next = sizeof (Elf_External_Verdaux); _bfd_elf_swap_verdaux_out (output_bfd, &defaux, (Elf_External_Verdaux *) p); p += sizeof (Elf_External_Verdaux); } } if (!_bfd_elf_add_dynamic_entry (info, DT_VERDEF, 0) || !_bfd_elf_add_dynamic_entry (info, DT_VERDEFNUM, cdefs)) return FALSE; elf_tdata (output_bfd)->cverdefs = cdefs; } if ((info->new_dtags && info->flags) || (info->flags & DF_STATIC_TLS)) { if (!_bfd_elf_add_dynamic_entry (info, DT_FLAGS, info->flags)) return FALSE; } else if (info->flags & DF_BIND_NOW) { if (!_bfd_elf_add_dynamic_entry (info, DT_BIND_NOW, 0)) return FALSE; } if (info->flags_1) { if (info->executable) info->flags_1 &= ~ (DF_1_INITFIRST | DF_1_NODELETE | DF_1_NOOPEN); if (!_bfd_elf_add_dynamic_entry (info, DT_FLAGS_1, info->flags_1)) return FALSE; } /* Work out the size of the version reference section. */ s = bfd_get_section_by_name (dynobj, ".gnu.version_r"); BFD_ASSERT (s != NULL); { struct elf_find_verdep_info sinfo; sinfo.output_bfd = output_bfd; sinfo.info = info; sinfo.vers = elf_tdata (output_bfd)->cverdefs; if (sinfo.vers == 0) sinfo.vers = 1; sinfo.failed = FALSE; elf_link_hash_traverse (elf_hash_table (info), _bfd_elf_link_find_version_dependencies, &sinfo); if (elf_tdata (output_bfd)->verref == NULL) s->flags |= SEC_EXCLUDE; else { Elf_Internal_Verneed *t; unsigned int size; unsigned int crefs; bfd_byte *p; /* Build the version definition section. */ size = 0; crefs = 0; for (t = elf_tdata (output_bfd)->verref; t != NULL; t = t->vn_nextref) { Elf_Internal_Vernaux *a; size += sizeof (Elf_External_Verneed); ++crefs; for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) size += sizeof (Elf_External_Vernaux); } s->size = size; s->contents = bfd_alloc (output_bfd, s->size); if (s->contents == NULL) return FALSE; p = s->contents; for (t = elf_tdata (output_bfd)->verref; t != NULL; t = t->vn_nextref) { unsigned int caux; Elf_Internal_Vernaux *a; bfd_size_type indx; caux = 0; for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) ++caux; t->vn_version = VER_NEED_CURRENT; t->vn_cnt = caux; indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, elf_dt_name (t->vn_bfd) != NULL ? elf_dt_name (t->vn_bfd) : lbasename (t->vn_bfd->filename), FALSE); if (indx == (bfd_size_type) -1) return FALSE; t->vn_file = indx; t->vn_aux = sizeof (Elf_External_Verneed); if (t->vn_nextref == NULL) t->vn_next = 0; else t->vn_next = (sizeof (Elf_External_Verneed) + caux * sizeof (Elf_External_Vernaux)); _bfd_elf_swap_verneed_out (output_bfd, t, (Elf_External_Verneed *) p); p += sizeof (Elf_External_Verneed); for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) { a->vna_hash = bfd_elf_hash (a->vna_nodename); indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, a->vna_nodename, FALSE); if (indx == (bfd_size_type) -1) return FALSE; a->vna_name = indx; if (a->vna_nextptr == NULL) a->vna_next = 0; else a->vna_next = sizeof (Elf_External_Vernaux); _bfd_elf_swap_vernaux_out (output_bfd, a, (Elf_External_Vernaux *) p); p += sizeof (Elf_External_Vernaux); } } if (!_bfd_elf_add_dynamic_entry (info, DT_VERNEED, 0) || !_bfd_elf_add_dynamic_entry (info, DT_VERNEEDNUM, crefs)) return FALSE; elf_tdata (output_bfd)->cverrefs = crefs; } } if ((elf_tdata (output_bfd)->cverrefs == 0 && elf_tdata (output_bfd)->cverdefs == 0) || _bfd_elf_link_renumber_dynsyms (output_bfd, info, §ion_sym_count) == 0) { s = bfd_get_section_by_name (dynobj, ".gnu.version"); s->flags |= SEC_EXCLUDE; } } return TRUE; } /* Find the first non-excluded output section. We'll use its section symbol for some emitted relocs. */ void _bfd_elf_init_1_index_section (bfd *output_bfd, struct bfd_link_info *info) { asection *s; for (s = output_bfd->sections; s != NULL; s = s->next) if ((s->flags & (SEC_EXCLUDE | SEC_ALLOC)) == SEC_ALLOC && !_bfd_elf_link_omit_section_dynsym (output_bfd, info, s)) { elf_hash_table (info)->text_index_section = s; break; } } /* Find two non-excluded output sections, one for code, one for data. We'll use their section symbols for some emitted relocs. */ void _bfd_elf_init_2_index_sections (bfd *output_bfd, struct bfd_link_info *info) { asection *s; for (s = output_bfd->sections; s != NULL; s = s->next) if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY)) == (SEC_ALLOC | SEC_READONLY)) && !_bfd_elf_link_omit_section_dynsym (output_bfd, info, s)) { elf_hash_table (info)->text_index_section = s; break; } for (s = output_bfd->sections; s != NULL; s = s->next) if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC) && !_bfd_elf_link_omit_section_dynsym (output_bfd, info, s)) { elf_hash_table (info)->data_index_section = s; break; } if (elf_hash_table (info)->text_index_section == NULL) elf_hash_table (info)->text_index_section = elf_hash_table (info)->data_index_section; } bfd_boolean bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info) { const struct elf_backend_data *bed; if (!is_elf_hash_table (info->hash)) return TRUE; bed = get_elf_backend_data (output_bfd); (*bed->elf_backend_init_index_section) (output_bfd, info); if (elf_hash_table (info)->dynamic_sections_created) { bfd *dynobj; asection *s; bfd_size_type dynsymcount; unsigned long section_sym_count; unsigned int dtagcount; dynobj = elf_hash_table (info)->dynobj; /* Assign dynsym indicies. In a shared library we generate a section symbol for each output section, which come first. Next come all of the back-end allocated local dynamic syms, followed by the rest of the global symbols. */ dynsymcount = _bfd_elf_link_renumber_dynsyms (output_bfd, info, §ion_sym_count); /* Work out the size of the symbol version section. */ s = bfd_get_section_by_name (dynobj, ".gnu.version"); BFD_ASSERT (s != NULL); if (dynsymcount != 0 && (s->flags & SEC_EXCLUDE) == 0) { s->size = dynsymcount * sizeof (Elf_External_Versym); s->contents = bfd_zalloc (output_bfd, s->size); if (s->contents == NULL) return FALSE; if (!_bfd_elf_add_dynamic_entry (info, DT_VERSYM, 0)) return FALSE; } /* Set the size of the .dynsym and .hash sections. We counted the number of dynamic symbols in elf_link_add_object_symbols. We will build the contents of .dynsym and .hash when we build the final symbol table, because until then we do not know the correct value to give the symbols. We built the .dynstr section as we went along in elf_link_add_object_symbols. */ s = bfd_get_section_by_name (dynobj, ".dynsym"); BFD_ASSERT (s != NULL); s->size = dynsymcount * bed->s->sizeof_sym; if (dynsymcount != 0) { s->contents = bfd_alloc (output_bfd, s->size); if (s->contents == NULL) return FALSE; /* The first entry in .dynsym is a dummy symbol. Clear all the section syms, in case we don't output them all. */ ++section_sym_count; memset (s->contents, 0, section_sym_count * bed->s->sizeof_sym); } elf_hash_table (info)->bucketcount = 0; /* Compute the size of the hashing table. As a side effect this computes the hash values for all the names we export. */ if (info->emit_hash) { unsigned long int *hashcodes; unsigned long int *hashcodesp; bfd_size_type amt; unsigned long int nsyms; size_t bucketcount; size_t hash_entry_size; /* Compute the hash values for all exported symbols. At the same time store the values in an array so that we could use them for optimizations. */ amt = dynsymcount * sizeof (unsigned long int); hashcodes = bfd_malloc (amt); if (hashcodes == NULL) return FALSE; hashcodesp = hashcodes; /* Put all hash values in HASHCODES. */ elf_link_hash_traverse (elf_hash_table (info), elf_collect_hash_codes, &hashcodesp); nsyms = hashcodesp - hashcodes; bucketcount = compute_bucket_count (info, hashcodes, nsyms, 0); free (hashcodes); if (bucketcount == 0) return FALSE; elf_hash_table (info)->bucketcount = bucketcount; s = bfd_get_section_by_name (dynobj, ".hash"); BFD_ASSERT (s != NULL); hash_entry_size = elf_section_data (s)->this_hdr.sh_entsize; s->size = ((2 + bucketcount + dynsymcount) * hash_entry_size); s->contents = bfd_zalloc (output_bfd, s->size); if (s->contents == NULL) return FALSE; bfd_put (8 * hash_entry_size, output_bfd, bucketcount, s->contents); bfd_put (8 * hash_entry_size, output_bfd, dynsymcount, s->contents + hash_entry_size); } if (info->emit_gnu_hash) { size_t i, cnt; unsigned char *contents; struct collect_gnu_hash_codes cinfo; bfd_size_type amt; size_t bucketcount; memset (&cinfo, 0, sizeof (cinfo)); /* Compute the hash values for all exported symbols. At the same time store the values in an array so that we could use them for optimizations. */ amt = dynsymcount * 2 * sizeof (unsigned long int); cinfo.hashcodes = bfd_malloc (amt); if (cinfo.hashcodes == NULL) return FALSE; cinfo.hashval = cinfo.hashcodes + dynsymcount; cinfo.min_dynindx = -1; cinfo.output_bfd = output_bfd; cinfo.bed = bed; /* Put all hash values in HASHCODES. */ elf_link_hash_traverse (elf_hash_table (info), elf_collect_gnu_hash_codes, &cinfo); bucketcount = compute_bucket_count (info, cinfo.hashcodes, cinfo.nsyms, 1); if (bucketcount == 0) { free (cinfo.hashcodes); return FALSE; } s = bfd_get_section_by_name (dynobj, ".gnu.hash"); BFD_ASSERT (s != NULL); if (cinfo.nsyms == 0) { /* Empty .gnu.hash section is special. */ BFD_ASSERT (cinfo.min_dynindx == -1); free (cinfo.hashcodes); s->size = 5 * 4 + bed->s->arch_size / 8; contents = bfd_zalloc (output_bfd, s->size); if (contents == NULL) return FALSE; s->contents = contents; /* 1 empty bucket. */ bfd_put_32 (output_bfd, 1, contents); /* SYMIDX above the special symbol 0. */ bfd_put_32 (output_bfd, 1, contents + 4); /* Just one word for bitmask. */ bfd_put_32 (output_bfd, 1, contents + 8); /* Only hash fn bloom filter. */ bfd_put_32 (output_bfd, 0, contents + 12); /* No hashes are valid - empty bitmask. */ bfd_put (bed->s->arch_size, output_bfd, 0, contents + 16); /* No hashes in the only bucket. */ bfd_put_32 (output_bfd, 0, contents + 16 + bed->s->arch_size / 8); } else { unsigned long int maskwords, maskbitslog2; BFD_ASSERT (cinfo.min_dynindx != -1); maskbitslog2 = bfd_log2 (cinfo.nsyms) + 1; if (maskbitslog2 < 3) maskbitslog2 = 5; else if ((1 << (maskbitslog2 - 2)) & cinfo.nsyms) maskbitslog2 = maskbitslog2 + 3; else maskbitslog2 = maskbitslog2 + 2; if (bed->s->arch_size == 64) { if (maskbitslog2 == 5) maskbitslog2 = 6; cinfo.shift1 = 6; } else cinfo.shift1 = 5; cinfo.mask = (1 << cinfo.shift1) - 1; cinfo.shift2 = maskbitslog2; cinfo.maskbits = 1 << maskbitslog2; maskwords = 1 << (maskbitslog2 - cinfo.shift1); amt = bucketcount * sizeof (unsigned long int) * 2; amt += maskwords * sizeof (bfd_vma); cinfo.bitmask = bfd_malloc (amt); if (cinfo.bitmask == NULL) { free (cinfo.hashcodes); return FALSE; } cinfo.counts = (void *) (cinfo.bitmask + maskwords); cinfo.indx = cinfo.counts + bucketcount; cinfo.symindx = dynsymcount - cinfo.nsyms; memset (cinfo.bitmask, 0, maskwords * sizeof (bfd_vma)); /* Determine how often each hash bucket is used. */ memset (cinfo.counts, 0, bucketcount * sizeof (cinfo.counts[0])); for (i = 0; i < cinfo.nsyms; ++i) ++cinfo.counts[cinfo.hashcodes[i] % bucketcount]; for (i = 0, cnt = cinfo.symindx; i < bucketcount; ++i) if (cinfo.counts[i] != 0) { cinfo.indx[i] = cnt; cnt += cinfo.counts[i]; } BFD_ASSERT (cnt == dynsymcount); cinfo.bucketcount = bucketcount; cinfo.local_indx = cinfo.min_dynindx; s->size = (4 + bucketcount + cinfo.nsyms) * 4; s->size += cinfo.maskbits / 8; contents = bfd_zalloc (output_bfd, s->size); if (contents == NULL) { free (cinfo.bitmask); free (cinfo.hashcodes); return FALSE; } s->contents = contents; bfd_put_32 (output_bfd, bucketcount, contents); bfd_put_32 (output_bfd, cinfo.symindx, contents + 4); bfd_put_32 (output_bfd, maskwords, contents + 8); bfd_put_32 (output_bfd, cinfo.shift2, contents + 12); contents += 16 + cinfo.maskbits / 8; for (i = 0; i < bucketcount; ++i) { if (cinfo.counts[i] == 0) bfd_put_32 (output_bfd, 0, contents); else bfd_put_32 (output_bfd, cinfo.indx[i], contents); contents += 4; } cinfo.contents = contents; /* Renumber dynamic symbols, populate .gnu.hash section. */ elf_link_hash_traverse (elf_hash_table (info), elf_renumber_gnu_hash_syms, &cinfo); contents = s->contents + 16; for (i = 0; i < maskwords; ++i) { bfd_put (bed->s->arch_size, output_bfd, cinfo.bitmask[i], contents); contents += bed->s->arch_size / 8; } free (cinfo.bitmask); free (cinfo.hashcodes); } } s = bfd_get_section_by_name (dynobj, ".dynstr"); BFD_ASSERT (s != NULL); elf_finalize_dynstr (output_bfd, info); s->size = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr); for (dtagcount = 0; dtagcount <= info->spare_dynamic_tags; ++dtagcount) if (!_bfd_elf_add_dynamic_entry (info, DT_NULL, 0)) return FALSE; } return TRUE; } /* Final phase of ELF linker. */ /* A structure we use to avoid passing large numbers of arguments. */ struct elf_final_link_info { /* General link information. */ struct bfd_link_info *info; /* Output BFD. */ bfd *output_bfd; /* Symbol string table. */ struct bfd_strtab_hash *symstrtab; /* .dynsym section. */ asection *dynsym_sec; /* .hash section. */ asection *hash_sec; /* symbol version section (.gnu.version). */ asection *symver_sec; /* Buffer large enough to hold contents of any section. */ bfd_byte *contents; /* Buffer large enough to hold external relocs of any section. */ void *external_relocs; /* Buffer large enough to hold internal relocs of any section. */ Elf_Internal_Rela *internal_relocs; /* Buffer large enough to hold external local symbols of any input BFD. */ bfd_byte *external_syms; /* And a buffer for symbol section indices. */ Elf_External_Sym_Shndx *locsym_shndx; /* Buffer large enough to hold internal local symbols of any input BFD. */ Elf_Internal_Sym *internal_syms; /* Array large enough to hold a symbol index for each local symbol of any input BFD. */ long *indices; /* Array large enough to hold a section pointer for each local symbol of any input BFD. */ asection **sections; /* Buffer to hold swapped out symbols. */ bfd_byte *symbuf; /* And one for symbol section indices. */ Elf_External_Sym_Shndx *symshndxbuf; /* Number of swapped out symbols in buffer. */ size_t symbuf_count; /* Number of symbols which fit in symbuf. */ size_t symbuf_size; /* And same for symshndxbuf. */ size_t shndxbuf_size; }; /* This struct is used to pass information to elf_link_output_extsym. */ struct elf_outext_info { bfd_boolean failed; bfd_boolean localsyms; struct elf_final_link_info *finfo; }; /* Support for evaluating a complex relocation. Complex relocations are generalized, self-describing relocations. The implementation of them consists of two parts: complex symbols, and the relocations themselves. The relocations are use a reserved elf-wide relocation type code (R_RELC external / BFD_RELOC_RELC internal) and an encoding of relocation field information (start bit, end bit, word width, etc) into the addend. This information is extracted from CGEN-generated operand tables within gas. Complex symbols are mangled symbols (BSF_RELC external / STT_RELC internal) representing prefix-notation expressions, including but not limited to those sorts of expressions normally encoded as addends in the addend field. The symbol mangling format is: := | ':' | ':' ':' ; := 's' ':' | 'S' ':' | '#' ; := as in C := as in C, plus "0-" for unambiguous negation. */ static void set_symbol_value (bfd * bfd_with_globals, struct elf_final_link_info * finfo, int symidx, bfd_vma val) { bfd_boolean is_local; Elf_Internal_Sym * sym; struct elf_link_hash_entry ** sym_hashes; struct elf_link_hash_entry * h; sym_hashes = elf_sym_hashes (bfd_with_globals); sym = finfo->internal_syms + symidx; is_local = ELF_ST_BIND(sym->st_info) == STB_LOCAL; if (is_local) { /* It is a local symbol: move it to the "absolute" section and give it a value. */ sym->st_shndx = SHN_ABS; sym->st_value = val; } else { /* It is a global symbol: set its link type to "defined" and give it a value. */ h = sym_hashes [symidx]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; h->root.type = bfd_link_hash_defined; h->root.u.def.value = val; h->root.u.def.section = bfd_abs_section_ptr; } } static bfd_boolean resolve_symbol (const char * name, bfd * input_bfd, struct elf_final_link_info * finfo, bfd_vma * result, size_t locsymcount) { Elf_Internal_Sym * sym; struct bfd_link_hash_entry * global_entry; const char * candidate = NULL; Elf_Internal_Shdr * symtab_hdr; asection * sec = NULL; size_t i; symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; for (i = 0; i < locsymcount; ++ i) { sym = finfo->internal_syms + i; sec = finfo->sections [i]; if (ELF_ST_BIND (sym->st_info) != STB_LOCAL) continue; candidate = bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name); #ifdef DEBUG printf ("Comparing string: '%s' vs. '%s' = 0x%x\n", name, candidate, (unsigned int)sym->st_value); #endif if (candidate && strcmp (candidate, name) == 0) { * result = sym->st_value; if (sym->st_shndx > SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) { #ifdef DEBUG printf ("adjusting for sec '%s' @ 0x%x + 0x%x\n", sec->output_section->name, (unsigned int)sec->output_section->vma, (unsigned int)sec->output_offset); #endif * result += sec->output_offset + sec->output_section->vma; } #ifdef DEBUG printf ("Found symbol with effective value %8.8x\n", (unsigned int)* result); #endif return TRUE; } } /* Hmm, haven't found it yet. perhaps it is a global. */ global_entry = bfd_link_hash_lookup (finfo->info->hash, name, FALSE, FALSE, TRUE); if (!global_entry) return FALSE; if (global_entry->type == bfd_link_hash_defined || global_entry->type == bfd_link_hash_defweak) { * result = global_entry->u.def.value + global_entry->u.def.section->output_section->vma + global_entry->u.def.section->output_offset; #ifdef DEBUG printf ("Found GLOBAL symbol '%s' with value %8.8x\n", global_entry->root.string, (unsigned int)*result); #endif return TRUE; } if (global_entry->type == bfd_link_hash_common) { *result = global_entry->u.def.value + bfd_com_section_ptr->output_section->vma + bfd_com_section_ptr->output_offset; #ifdef DEBUG printf ("Found COMMON symbol '%s' with value %8.8x\n", global_entry->root.string, (unsigned int)*result); #endif return TRUE; } return FALSE; } static bfd_boolean resolve_section (const char * name, asection * sections, bfd_vma * result) { asection * curr; unsigned int len; for (curr = sections; curr; curr = curr->next) if (strcmp (curr->name, name) == 0) { *result = curr->vma; return TRUE; } /* Hmm. still haven't found it. try pseudo-section names. */ for (curr = sections; curr; curr = curr->next) { len = strlen (curr->name); if (len > strlen (name)) continue; if (strncmp (curr->name, name, len) == 0) { if (strncmp (".end", name + len, 4) == 0) { *result = curr->vma + curr->size; return TRUE; } /* Insert more pseudo-section names here, if you like. */ } } return FALSE; } static void undefined_reference (const char * reftype, const char * name) { _bfd_error_handler (_("undefined %s reference in complex symbol: %s"), reftype, name); } static bfd_boolean eval_symbol (bfd_vma * result, char * sym, char ** advanced, bfd * input_bfd, struct elf_final_link_info * finfo, bfd_vma addr, bfd_vma section_offset, size_t locsymcount, int signed_p) { int len; int symlen; bfd_vma a; bfd_vma b; const int bufsz = 4096; char symbuf [bufsz]; const char * symend; bfd_boolean symbol_is_section = FALSE; len = strlen (sym); symend = sym + len; if (len < 1 || len > bufsz) { bfd_set_error (bfd_error_invalid_operation); return FALSE; } switch (* sym) { case '.': * result = addr + section_offset; * advanced = sym + 1; return TRUE; case '#': ++ sym; * result = strtoul (sym, advanced, 16); return TRUE; case 'S': symbol_is_section = TRUE; case 's': ++ sym; symlen = strtol (sym, &sym, 10); ++ sym; /* Skip the trailing ':'. */ if ((symend < sym) || ((symlen + 1) > bufsz)) { bfd_set_error (bfd_error_invalid_operation); return FALSE; } memcpy (symbuf, sym, symlen); symbuf [symlen] = '\0'; * advanced = sym + symlen; /* Is it always possible, with complex symbols, that gas "mis-guessed" the symbol as a section, or vice-versa. so we're pretty liberal in our interpretation here; section means "try section first", not "must be a section", and likewise with symbol. */ if (symbol_is_section) { if ((resolve_section (symbuf, finfo->output_bfd->sections, result) != TRUE) && (resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE)) { undefined_reference ("section", symbuf); return FALSE; } } else { if ((resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE) && (resolve_section (symbuf, finfo->output_bfd->sections, result) != TRUE)) { undefined_reference ("symbol", symbuf); return FALSE; } } return TRUE; /* All that remains are operators. */ #define UNARY_OP(op) \ if (strncmp (sym, #op, strlen (#op)) == 0) \ { \ sym += strlen (#op); \ if (* sym == ':') \ ++ sym; \ if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \ section_offset, locsymcount, signed_p) \ != TRUE) \ return FALSE; \ if (signed_p) \ * result = op ((signed)a); \ else \ * result = op a; \ * advanced = sym; \ return TRUE; \ } #define BINARY_OP(op) \ if (strncmp (sym, #op, strlen (#op)) == 0) \ { \ sym += strlen (#op); \ if (* sym == ':') \ ++ sym; \ if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \ section_offset, locsymcount, signed_p) \ != TRUE) \ return FALSE; \ ++ sym; \ if (eval_symbol (& b, sym, & sym, input_bfd, finfo, addr, \ section_offset, locsymcount, signed_p) \ != TRUE) \ return FALSE; \ if (signed_p) \ * result = ((signed) a) op ((signed) b); \ else \ * result = a op b; \ * advanced = sym; \ return TRUE; \ } default: UNARY_OP (0-); BINARY_OP (<<); BINARY_OP (>>); BINARY_OP (==); BINARY_OP (!=); BINARY_OP (<=); BINARY_OP (>=); BINARY_OP (&&); BINARY_OP (||); UNARY_OP (~); UNARY_OP (!); BINARY_OP (*); BINARY_OP (/); BINARY_OP (%); BINARY_OP (^); BINARY_OP (|); BINARY_OP (&); BINARY_OP (+); BINARY_OP (-); BINARY_OP (<); BINARY_OP (>); #undef UNARY_OP #undef BINARY_OP _bfd_error_handler (_("unknown operator '%c' in complex symbol"), * sym); bfd_set_error (bfd_error_invalid_operation); return FALSE; } } /* Entry point to evaluator, called from elf_link_input_bfd. */ static bfd_boolean evaluate_complex_relocation_symbols (bfd * input_bfd, struct elf_final_link_info * finfo, size_t locsymcount) { const struct elf_backend_data * bed; Elf_Internal_Shdr * symtab_hdr; struct elf_link_hash_entry ** sym_hashes; asection * reloc_sec; bfd_boolean result = TRUE; /* For each section, we're going to check and see if it has any complex relocations, and we're going to evaluate any of them we can. */ if (finfo->info->relocatable) return TRUE; symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; sym_hashes = elf_sym_hashes (input_bfd); bed = get_elf_backend_data (input_bfd); for (reloc_sec = input_bfd->sections; reloc_sec; reloc_sec = reloc_sec->next) { Elf_Internal_Rela * internal_relocs; unsigned long i; /* This section was omitted from the link. */ if (! reloc_sec->linker_mark) continue; /* Only process sections containing relocs. */ if ((reloc_sec->flags & SEC_RELOC) == 0) continue; if (reloc_sec->reloc_count == 0) continue; /* Read in the relocs for this section. */ internal_relocs = _bfd_elf_link_read_relocs (input_bfd, reloc_sec, NULL, (Elf_Internal_Rela *) NULL, FALSE); if (internal_relocs == NULL) continue; for (i = reloc_sec->reloc_count; i--;) { Elf_Internal_Rela * rel; char * sym_name; bfd_vma index; Elf_Internal_Sym * sym; bfd_vma result; bfd_vma section_offset; bfd_vma addr; int signed_p = 0; rel = internal_relocs + i; section_offset = reloc_sec->output_section->vma + reloc_sec->output_offset; addr = rel->r_offset; index = ELF32_R_SYM (rel->r_info); if (bed->s->arch_size == 64) index >>= 24; if (index == STN_UNDEF) continue; if (index < locsymcount) { /* The symbol is local. */ sym = finfo->internal_syms + index; /* We're only processing STT_RELC or STT_SRELC type symbols. */ if ((ELF_ST_TYPE (sym->st_info) != STT_RELC) && (ELF_ST_TYPE (sym->st_info) != STT_SRELC)) continue; sym_name = bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name); signed_p = (ELF_ST_TYPE (sym->st_info) == STT_SRELC); } else { /* The symbol is global. */ struct elf_link_hash_entry * h; if (elf_bad_symtab (input_bfd)) continue; h = sym_hashes [index - locsymcount]; while ( h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->type != STT_RELC && h->type != STT_SRELC) continue; signed_p = (h->type == STT_SRELC); sym_name = (char *) h->root.root.string; } #ifdef DEBUG printf ("Encountered a complex symbol!"); printf (" (input_bfd %s, section %s, reloc %ld\n", input_bfd->filename, reloc_sec->name, i); printf (" symbol: idx %8.8lx, name %s\n", index, sym_name); printf (" reloc : info %8.8lx, addr %8.8lx\n", rel->r_info, addr); printf (" Evaluating '%s' ...\n ", sym_name); #endif if (eval_symbol (& result, sym_name, & sym_name, input_bfd, finfo, addr, section_offset, locsymcount, signed_p)) /* Symbol evaluated OK. Update to absolute value. */ set_symbol_value (input_bfd, finfo, index, result); else result = FALSE; } if (internal_relocs != elf_section_data (reloc_sec)->relocs) free (internal_relocs); } /* If nothing went wrong, then we adjusted everything we wanted to adjust. */ return result; } static void put_value (bfd_vma size, unsigned long chunksz, bfd * input_bfd, bfd_vma x, bfd_byte * location) { location += (size - chunksz); for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8)) { switch (chunksz) { default: case 0: abort (); case 1: bfd_put_8 (input_bfd, x, location); break; case 2: bfd_put_16 (input_bfd, x, location); break; case 4: bfd_put_32 (input_bfd, x, location); break; case 8: #ifdef BFD64 bfd_put_64 (input_bfd, x, location); #else abort (); #endif break; } } } static bfd_vma get_value (bfd_vma size, unsigned long chunksz, bfd * input_bfd, bfd_byte * location) { bfd_vma x = 0; for (; size; size -= chunksz, location += chunksz) { switch (chunksz) { default: case 0: abort (); case 1: x = (x << (8 * chunksz)) | bfd_get_8 (input_bfd, location); break; case 2: x = (x << (8 * chunksz)) | bfd_get_16 (input_bfd, location); break; case 4: x = (x << (8 * chunksz)) | bfd_get_32 (input_bfd, location); break; case 8: #ifdef BFD64 x = (x << (8 * chunksz)) | bfd_get_64 (input_bfd, location); #else abort (); #endif break; } } return x; } static void decode_complex_addend (unsigned long * start, /* in bits */ unsigned long * oplen, /* in bits */ unsigned long * len, /* in bits */ unsigned long * wordsz, /* in bytes */ unsigned long * chunksz, /* in bytes */ unsigned long * lsb0_p, unsigned long * signed_p, unsigned long * trunc_p, unsigned long encoded) { * start = encoded & 0x3F; * len = (encoded >> 6) & 0x3F; * oplen = (encoded >> 12) & 0x3F; * wordsz = (encoded >> 18) & 0xF; * chunksz = (encoded >> 22) & 0xF; * lsb0_p = (encoded >> 27) & 1; * signed_p = (encoded >> 28) & 1; * trunc_p = (encoded >> 29) & 1; } void bfd_elf_perform_complex_relocation (bfd * output_bfd ATTRIBUTE_UNUSED, struct bfd_link_info * info, bfd * input_bfd, asection * input_section, bfd_byte * contents, Elf_Internal_Rela * rel, Elf_Internal_Sym * local_syms, asection ** local_sections) { const struct elf_backend_data * bed; Elf_Internal_Shdr * symtab_hdr; asection * sec; bfd_vma relocation = 0, shift, x; bfd_vma r_symndx; bfd_vma mask; unsigned long start, oplen, len, wordsz, chunksz, lsb0_p, signed_p, trunc_p; /* Perform this reloc, since it is complex. (this is not to say that it necessarily refers to a complex symbol; merely that it is a self-describing CGEN based reloc. i.e. the addend has the complete reloc information (bit start, end, word size, etc) encoded within it.). */ r_symndx = ELF32_R_SYM (rel->r_info); bed = get_elf_backend_data (input_bfd); if (bed->s->arch_size == 64) r_symndx >>= 24; #ifdef DEBUG printf ("Performing complex relocation %ld...\n", r_symndx); #endif symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; if (r_symndx < symtab_hdr->sh_info) { /* The symbol is local. */ Elf_Internal_Sym * sym; sym = local_syms + r_symndx; sec = local_sections [r_symndx]; relocation = sym->st_value; if (sym->st_shndx > SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) relocation += (sec->output_offset + sec->output_section->vma); } else { /* The symbol is global. */ struct elf_link_hash_entry **sym_hashes; struct elf_link_hash_entry * h; sym_hashes = elf_sym_hashes (input_bfd); h = sym_hashes [r_symndx]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) { sec = h->root.u.def.section; relocation = h->root.u.def.value; if (! bfd_is_abs_section (sec)) relocation += (sec->output_section->vma + sec->output_offset); } if (h->root.type == bfd_link_hash_undefined && !((*info->callbacks->undefined_symbol) (info, h->root.root.string, input_bfd, input_section, rel->r_offset, info->unresolved_syms_in_objects == RM_GENERATE_ERROR || ELF_ST_VISIBILITY (h->other)))) return; } decode_complex_addend (& start, & oplen, & len, & wordsz, & chunksz, & lsb0_p, & signed_p, & trunc_p, rel->r_addend); mask = (((1L << (len - 1)) - 1) << 1) | 1; if (lsb0_p) shift = (start + 1) - len; else shift = (8 * wordsz) - (start + len); x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset); #ifdef DEBUG printf ("Doing complex reloc: " "lsb0? %ld, signed? %ld, trunc? %ld, wordsz %ld, " "chunksz %ld, start %ld, len %ld, oplen %ld\n" " dest: %8.8lx, mask: %8.8lx, reloc: %8.8lx\n", lsb0_p, signed_p, trunc_p, wordsz, chunksz, start, len, oplen, x, mask, relocation); #endif if (! trunc_p) { /* Now do an overflow check. */ if (bfd_check_overflow ((signed_p ? complain_overflow_signed : complain_overflow_unsigned), len, 0, (8 * wordsz), relocation) == bfd_reloc_overflow) (*_bfd_error_handler) ("%s (%s + 0x%lx): relocation overflow: 0x%lx %sdoes not fit " "within 0x%lx", input_bfd->filename, input_section->name, rel->r_offset, relocation, (signed_p ? "(signed) " : ""), mask); } /* Do the deed. */ x = (x & ~(mask << shift)) | ((relocation & mask) << shift); #ifdef DEBUG printf (" relocation: %8.8lx\n" " shifted mask: %8.8lx\n" " shifted/masked reloc: %8.8lx\n" " result: %8.8lx\n", relocation, (mask << shift), ((relocation & mask) << shift), x); #endif put_value (wordsz, chunksz, input_bfd, x, contents + rel->r_offset); } /* When performing a relocatable link, the input relocations are preserved. But, if they reference global symbols, the indices referenced must be updated. Update all the relocations in REL_HDR (there are COUNT of them), using the data in REL_HASH. */ static void elf_link_adjust_relocs (bfd *abfd, Elf_Internal_Shdr *rel_hdr, unsigned int count, struct elf_link_hash_entry **rel_hash) { unsigned int i; const struct elf_backend_data *bed = get_elf_backend_data (abfd); bfd_byte *erela; void (*swap_in) (bfd *, const bfd_byte *, Elf_Internal_Rela *); void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *); bfd_vma r_type_mask; int r_sym_shift; if (rel_hdr->sh_entsize == bed->s->sizeof_rel) { swap_in = bed->s->swap_reloc_in; swap_out = bed->s->swap_reloc_out; } else if (rel_hdr->sh_entsize == bed->s->sizeof_rela) { swap_in = bed->s->swap_reloca_in; swap_out = bed->s->swap_reloca_out; } else abort (); if (bed->s->int_rels_per_ext_rel > MAX_INT_RELS_PER_EXT_REL) abort (); if (bed->s->arch_size == 32) { r_type_mask = 0xff; r_sym_shift = 8; } else { r_type_mask = 0xffffffff; r_sym_shift = 32; } erela = rel_hdr->contents; for (i = 0; i < count; i++, rel_hash++, erela += rel_hdr->sh_entsize) { Elf_Internal_Rela irela[MAX_INT_RELS_PER_EXT_REL]; unsigned int j; if (*rel_hash == NULL) continue; BFD_ASSERT ((*rel_hash)->indx >= 0); (*swap_in) (abfd, erela, irela); for (j = 0; j < bed->s->int_rels_per_ext_rel; j++) irela[j].r_info = ((bfd_vma) (*rel_hash)->indx << r_sym_shift | (irela[j].r_info & r_type_mask)); (*swap_out) (abfd, irela, erela); } } struct elf_link_sort_rela { union { bfd_vma offset; bfd_vma sym_mask; } u; enum elf_reloc_type_class type; /* We use this as an array of size int_rels_per_ext_rel. */ Elf_Internal_Rela rela[1]; }; static int elf_link_sort_cmp1 (const void *A, const void *B) { const struct elf_link_sort_rela *a = A; const struct elf_link_sort_rela *b = B; int relativea, relativeb; relativea = a->type == reloc_class_relative; relativeb = b->type == reloc_class_relative; if (relativea < relativeb) return 1; if (relativea > relativeb) return -1; if ((a->rela->r_info & a->u.sym_mask) < (b->rela->r_info & b->u.sym_mask)) return -1; if ((a->rela->r_info & a->u.sym_mask) > (b->rela->r_info & b->u.sym_mask)) return 1; if (a->rela->r_offset < b->rela->r_offset) return -1; if (a->rela->r_offset > b->rela->r_offset) return 1; return 0; } static int elf_link_sort_cmp2 (const void *A, const void *B) { const struct elf_link_sort_rela *a = A; const struct elf_link_sort_rela *b = B; int copya, copyb; if (a->u.offset < b->u.offset) return -1; if (a->u.offset > b->u.offset) return 1; copya = (a->type == reloc_class_copy) * 2 + (a->type == reloc_class_plt); copyb = (b->type == reloc_class_copy) * 2 + (b->type == reloc_class_plt); if (copya < copyb) return -1; if (copya > copyb) return 1; if (a->rela->r_offset < b->rela->r_offset) return -1; if (a->rela->r_offset > b->rela->r_offset) return 1; return 0; } static size_t elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec) { asection *dynamic_relocs; asection *rela_dyn; asection *rel_dyn; bfd_size_type count, size; size_t i, ret, sort_elt, ext_size; bfd_byte *sort, *s_non_relative, *p; struct elf_link_sort_rela *sq; const struct elf_backend_data *bed = get_elf_backend_data (abfd); int i2e = bed->s->int_rels_per_ext_rel; void (*swap_in) (bfd *, const bfd_byte *, Elf_Internal_Rela *); void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *); struct bfd_link_order *lo; bfd_vma r_sym_mask; bfd_boolean use_rela; /* Find a dynamic reloc section. */ rela_dyn = bfd_get_section_by_name (abfd, ".rela.dyn"); rel_dyn = bfd_get_section_by_name (abfd, ".rel.dyn"); if (rela_dyn != NULL && rela_dyn->size > 0 && rel_dyn != NULL && rel_dyn->size > 0) { bfd_boolean use_rela_initialised = FALSE; /* This is just here to stop gcc from complaining. It's initialization checking code is not perfect. */ use_rela = TRUE; /* Both sections are present. Examine the sizes of the indirect sections to help us choose. */ for (lo = rela_dyn->map_head.link_order; lo != NULL; lo = lo->next) if (lo->type == bfd_indirect_link_order) { asection *o = lo->u.indirect.section; if ((o->size % bed->s->sizeof_rela) == 0) { if ((o->size % bed->s->sizeof_rel) == 0) /* Section size is divisible by both rel and rela sizes. It is of no help to us. */ ; else { /* Section size is only divisible by rela. */ if (use_rela_initialised && (use_rela == FALSE)) { _bfd_error_handler (_("%B: Unable to sort relocs - they are in more than one size"), abfd); bfd_set_error (bfd_error_invalid_operation); return 0; } else { use_rela = TRUE; use_rela_initialised = TRUE; } } } else if ((o->size % bed->s->sizeof_rel) == 0) { /* Section size is only divisible by rel. */ if (use_rela_initialised && (use_rela == TRUE)) { _bfd_error_handler (_("%B: Unable to sort relocs - they are in more than one size"), abfd); bfd_set_error (bfd_error_invalid_operation); return 0; } else { use_rela = FALSE; use_rela_initialised = TRUE; } } else { /* The section size is not divisible by either - something is wrong. */ _bfd_error_handler (_("%B: Unable to sort relocs - they are of an unknown size"), abfd); bfd_set_error (bfd_error_invalid_operation); return 0; } } for (lo = rel_dyn->map_head.link_order; lo != NULL; lo = lo->next) if (lo->type == bfd_indirect_link_order) { asection *o = lo->u.indirect.section; if ((o->size % bed->s->sizeof_rela) == 0) { if ((o->size % bed->s->sizeof_rel) == 0) /* Section size is divisible by both rel and rela sizes. It is of no help to us. */ ; else { /* Section size is only divisible by rela. */ if (use_rela_initialised && (use_rela == FALSE)) { _bfd_error_handler (_("%B: Unable to sort relocs - they are in more than one size"), abfd); bfd_set_error (bfd_error_invalid_operation); return 0; } else { use_rela = TRUE; use_rela_initialised = TRUE; } } } else if ((o->size % bed->s->sizeof_rel) == 0) { /* Section size is only divisible by rel. */ if (use_rela_initialised && (use_rela == TRUE)) { _bfd_error_handler (_("%B: Unable to sort relocs - they are in more than one size"), abfd); bfd_set_error (bfd_error_invalid_operation); return 0; } else { use_rela = FALSE; use_rela_initialised = TRUE; } } else { /* The section size is not divisible by either - something is wrong. */ _bfd_error_handler (_("%B: Unable to sort relocs - they are of an unknown size"), abfd); bfd_set_error (bfd_error_invalid_operation); return 0; } } if (! use_rela_initialised) /* Make a guess. */ use_rela = TRUE; } else if (rela_dyn != NULL && rela_dyn->size > 0) use_rela = TRUE; else if (rel_dyn != NULL && rel_dyn->size > 0) use_rela = FALSE; else return 0; if (use_rela) { dynamic_relocs = rela_dyn; ext_size = bed->s->sizeof_rela; swap_in = bed->s->swap_reloca_in; swap_out = bed->s->swap_reloca_out; } else { dynamic_relocs = rel_dyn; ext_size = bed->s->sizeof_rel; swap_in = bed->s->swap_reloc_in; swap_out = bed->s->swap_reloc_out; } size = 0; for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next) if (lo->type == bfd_indirect_link_order) size += lo->u.indirect.section->size; if (size != dynamic_relocs->size) return 0; sort_elt = (sizeof (struct elf_link_sort_rela) + (i2e - 1) * sizeof (Elf_Internal_Rela)); count = dynamic_relocs->size / ext_size; sort = bfd_zmalloc (sort_elt * count); if (sort == NULL) { (*info->callbacks->warning) (info, _("Not enough memory to sort relocations"), 0, abfd, 0, 0); return 0; } if (bed->s->arch_size == 32) r_sym_mask = ~(bfd_vma) 0xff; else r_sym_mask = ~(bfd_vma) 0xffffffff; for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next) if (lo->type == bfd_indirect_link_order) { bfd_byte *erel, *erelend; asection *o = lo->u.indirect.section; if (o->contents == NULL && o->size != 0) { /* This is a reloc section that is being handled as a normal section. See bfd_section_from_shdr. We can't combine relocs in this case. */ free (sort); return 0; } erel = o->contents; erelend = o->contents + o->size; p = sort + o->output_offset / ext_size * sort_elt; while (erel < erelend) { struct elf_link_sort_rela *s = (struct elf_link_sort_rela *) p; (*swap_in) (abfd, erel, s->rela); s->type = (*bed->elf_backend_reloc_type_class) (s->rela); s->u.sym_mask = r_sym_mask; p += sort_elt; erel += ext_size; } } qsort (sort, count, sort_elt, elf_link_sort_cmp1); for (i = 0, p = sort; i < count; i++, p += sort_elt) { struct elf_link_sort_rela *s = (struct elf_link_sort_rela *) p; if (s->type != reloc_class_relative) break; } ret = i; s_non_relative = p; sq = (struct elf_link_sort_rela *) s_non_relative; for (; i < count; i++, p += sort_elt) { struct elf_link_sort_rela *sp = (struct elf_link_sort_rela *) p; if (((sp->rela->r_info ^ sq->rela->r_info) & r_sym_mask) != 0) sq = sp; sp->u.offset = sq->rela->r_offset; } qsort (s_non_relative, count - ret, sort_elt, elf_link_sort_cmp2); for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next) if (lo->type == bfd_indirect_link_order) { bfd_byte *erel, *erelend; asection *o = lo->u.indirect.section; erel = o->contents; erelend = o->contents + o->size; p = sort + o->output_offset / ext_size * sort_elt; while (erel < erelend) { struct elf_link_sort_rela *s = (struct elf_link_sort_rela *) p; (*swap_out) (abfd, s->rela, erel); p += sort_elt; erel += ext_size; } } free (sort); *psec = dynamic_relocs; return ret; } /* Flush the output symbols to the file. */ static bfd_boolean elf_link_flush_output_syms (struct elf_final_link_info *finfo, const struct elf_backend_data *bed) { if (finfo->symbuf_count > 0) { Elf_Internal_Shdr *hdr; file_ptr pos; bfd_size_type amt; hdr = &elf_tdata (finfo->output_bfd)->symtab_hdr; pos = hdr->sh_offset + hdr->sh_size; amt = finfo->symbuf_count * bed->s->sizeof_sym; if (bfd_seek (finfo->output_bfd, pos, SEEK_SET) != 0 || bfd_bwrite (finfo->symbuf, amt, finfo->output_bfd) != amt) return FALSE; hdr->sh_size += amt; finfo->symbuf_count = 0; } return TRUE; } /* Add a symbol to the output symbol table. */ static bfd_boolean elf_link_output_sym (struct elf_final_link_info *finfo, const char *name, Elf_Internal_Sym *elfsym, asection *input_sec, struct elf_link_hash_entry *h) { bfd_byte *dest; Elf_External_Sym_Shndx *destshndx; bfd_boolean (*output_symbol_hook) (struct bfd_link_info *, const char *, Elf_Internal_Sym *, asection *, struct elf_link_hash_entry *); const struct elf_backend_data *bed; bed = get_elf_backend_data (finfo->output_bfd); output_symbol_hook = bed->elf_backend_link_output_symbol_hook; if (output_symbol_hook != NULL) { if (! (*output_symbol_hook) (finfo->info, name, elfsym, input_sec, h)) return FALSE; } if (name == NULL || *name == '\0') elfsym->st_name = 0; else if (input_sec->flags & SEC_EXCLUDE) elfsym->st_name = 0; else { elfsym->st_name = (unsigned long) _bfd_stringtab_add (finfo->symstrtab, name, TRUE, FALSE); if (elfsym->st_name == (unsigned long) -1) return FALSE; } if (finfo->symbuf_count >= finfo->symbuf_size) { if (! elf_link_flush_output_syms (finfo, bed)) return FALSE; } dest = finfo->symbuf + finfo->symbuf_count * bed->s->sizeof_sym; destshndx = finfo->symshndxbuf; if (destshndx != NULL) { if (bfd_get_symcount (finfo->output_bfd) >= finfo->shndxbuf_size) { bfd_size_type amt; amt = finfo->shndxbuf_size * sizeof (Elf_External_Sym_Shndx); finfo->symshndxbuf = destshndx = bfd_realloc (destshndx, amt * 2); if (destshndx == NULL) return FALSE; memset ((char *) destshndx + amt, 0, amt); finfo->shndxbuf_size *= 2; } destshndx += bfd_get_symcount (finfo->output_bfd); } bed->s->swap_symbol_out (finfo->output_bfd, elfsym, dest, destshndx); finfo->symbuf_count += 1; bfd_get_symcount (finfo->output_bfd) += 1; return TRUE; } /* Return TRUE if the dynamic symbol SYM in ABFD is supported. */ static bfd_boolean check_dynsym (bfd *abfd, Elf_Internal_Sym *sym) { if (sym->st_shndx > SHN_HIRESERVE) { /* The gABI doesn't support dynamic symbols in output sections beyond 64k. */ (*_bfd_error_handler) (_("%B: Too many sections: %d (>= %d)"), abfd, bfd_count_sections (abfd), SHN_LORESERVE); bfd_set_error (bfd_error_nonrepresentable_section); return FALSE; } return TRUE; } /* For DSOs loaded in via a DT_NEEDED entry, emulate ld.so in allowing an unsatisfied unversioned symbol in the DSO to match a versioned symbol that would normally require an explicit version. We also handle the case that a DSO references a hidden symbol which may be satisfied by a versioned symbol in another DSO. */ static bfd_boolean elf_link_check_versioned_symbol (struct bfd_link_info *info, const struct elf_backend_data *bed, struct elf_link_hash_entry *h) { bfd *abfd; struct elf_link_loaded_list *loaded; if (!is_elf_hash_table (info->hash)) return FALSE; switch (h->root.type) { default: abfd = NULL; break; case bfd_link_hash_undefined: case bfd_link_hash_undefweak: abfd = h->root.u.undef.abfd; if ((abfd->flags & DYNAMIC) == 0 || (elf_dyn_lib_class (abfd) & DYN_DT_NEEDED) == 0) return FALSE; break; case bfd_link_hash_defined: case bfd_link_hash_defweak: abfd = h->root.u.def.section->owner; break; case bfd_link_hash_common: abfd = h->root.u.c.p->section->owner; break; } BFD_ASSERT (abfd != NULL); for (loaded = elf_hash_table (info)->loaded; loaded != NULL; loaded = loaded->next) { bfd *input; Elf_Internal_Shdr *hdr; bfd_size_type symcount; bfd_size_type extsymcount; bfd_size_type extsymoff; Elf_Internal_Shdr *versymhdr; Elf_Internal_Sym *isym; Elf_Internal_Sym *isymend; Elf_Internal_Sym *isymbuf; Elf_External_Versym *ever; Elf_External_Versym *extversym; input = loaded->abfd; /* We check each DSO for a possible hidden versioned definition. */ if (input == abfd || (input->flags & DYNAMIC) == 0 || elf_dynversym (input) == 0) continue; hdr = &elf_tdata (input)->dynsymtab_hdr; symcount = hdr->sh_size / bed->s->sizeof_sym; if (elf_bad_symtab (input)) { extsymcount = symcount; extsymoff = 0; } else { extsymcount = symcount - hdr->sh_info; extsymoff = hdr->sh_info; } if (extsymcount == 0) continue; isymbuf = bfd_elf_get_elf_syms (input, hdr, extsymcount, extsymoff, NULL, NULL, NULL); if (isymbuf == NULL) return FALSE; /* Read in any version definitions. */ versymhdr = &elf_tdata (input)->dynversym_hdr; extversym = bfd_malloc (versymhdr->sh_size); if (extversym == NULL) goto error_ret; if (bfd_seek (input, versymhdr->sh_offset, SEEK_SET) != 0 || (bfd_bread (extversym, versymhdr->sh_size, input) != versymhdr->sh_size)) { free (extversym); error_ret: free (isymbuf); return FALSE; } ever = extversym + extsymoff; isymend = isymbuf + extsymcount; for (isym = isymbuf; isym < isymend; isym++, ever++) { const char *name; Elf_Internal_Versym iver; unsigned short version_index; if (ELF_ST_BIND (isym->st_info) == STB_LOCAL || isym->st_shndx == SHN_UNDEF) continue; name = bfd_elf_string_from_elf_section (input, hdr->sh_link, isym->st_name); if (strcmp (name, h->root.root.string) != 0) continue; _bfd_elf_swap_versym_in (input, ever, &iver); if ((iver.vs_vers & VERSYM_HIDDEN) == 0) { /* If we have a non-hidden versioned sym, then it should have provided a definition for the undefined sym. */ abort (); } version_index = iver.vs_vers & VERSYM_VERSION; if (version_index == 1 || version_index == 2) { /* This is the base or first version. We can use it. */ free (extversym); free (isymbuf); return TRUE; } } free (extversym); free (isymbuf); } return FALSE; } /* Add an external symbol to the symbol table. This is called from the hash table traversal routine. When generating a shared object, we go through the symbol table twice. The first time we output anything that might have been forced to local scope in a version script. The second time we output the symbols that are still global symbols. */ static bfd_boolean elf_link_output_extsym (struct elf_link_hash_entry *h, void *data) { struct elf_outext_info *eoinfo = data; struct elf_final_link_info *finfo = eoinfo->finfo; bfd_boolean strip; Elf_Internal_Sym sym; asection *input_sec; const struct elf_backend_data *bed; if (h->root.type == bfd_link_hash_warning) { h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->root.type == bfd_link_hash_new) return TRUE; } /* Decide whether to output this symbol in this pass. */ if (eoinfo->localsyms) { if (!h->forced_local) return TRUE; } else { if (h->forced_local) return TRUE; } bed = get_elf_backend_data (finfo->output_bfd); if (h->root.type == bfd_link_hash_undefined) { /* If we have an undefined symbol reference here then it must have come from a shared library that is being linked in. (Undefined references in regular files have already been handled). */ bfd_boolean ignore_undef = FALSE; /* Some symbols may be special in that the fact that they're undefined can be safely ignored - let backend determine that. */ if (bed->elf_backend_ignore_undef_symbol) ignore_undef = bed->elf_backend_ignore_undef_symbol (h); /* If we are reporting errors for this situation then do so now. */ if (ignore_undef == FALSE && h->ref_dynamic && ! h->ref_regular && ! elf_link_check_versioned_symbol (finfo->info, bed, h) && finfo->info->unresolved_syms_in_shared_libs != RM_IGNORE) { if (! (finfo->info->callbacks->undefined_symbol (finfo->info, h->root.root.string, h->root.u.undef.abfd, NULL, 0, finfo->info->unresolved_syms_in_shared_libs == RM_GENERATE_ERROR))) { eoinfo->failed = TRUE; return FALSE; } } } /* We should also warn if a forced local symbol is referenced from shared libraries. */ if (! finfo->info->relocatable && (! finfo->info->shared) && h->forced_local && h->ref_dynamic && !h->dynamic_def && !h->dynamic_weak && ! elf_link_check_versioned_symbol (finfo->info, bed, h)) { (*_bfd_error_handler) (_("%B: %s symbol `%s' in %B is referenced by DSO"), finfo->output_bfd, h->root.u.def.section == bfd_abs_section_ptr ? finfo->output_bfd : h->root.u.def.section->owner, ELF_ST_VISIBILITY (h->other) == STV_INTERNAL ? "internal" : ELF_ST_VISIBILITY (h->other) == STV_HIDDEN ? "hidden" : "local", h->root.root.string); eoinfo->failed = TRUE; return FALSE; } /* We don't want to output symbols that have never been mentioned by a regular file, or that we have been told to strip. However, if h->indx is set to -2, the symbol is used by a reloc and we must output it. */ if (h->indx == -2) strip = FALSE; else if ((h->def_dynamic || h->ref_dynamic || h->root.type == bfd_link_hash_new) && !h->def_regular && !h->ref_regular) strip = TRUE; else if (finfo->info->strip == strip_all) strip = TRUE; else if (finfo->info->strip == strip_some && bfd_hash_lookup (finfo->info->keep_hash, h->root.root.string, FALSE, FALSE) == NULL) strip = TRUE; else if (finfo->info->strip_discarded && (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && elf_discarded_section (h->root.u.def.section)) strip = TRUE; else strip = FALSE; /* If we're stripping it, and it's not a dynamic symbol, there's nothing else to do unless it is a forced local symbol. */ if (strip && h->dynindx == -1 && !h->forced_local) return TRUE; sym.st_value = 0; sym.st_size = h->size; sym.st_other = h->other; if (h->forced_local) sym.st_info = ELF_ST_INFO (STB_LOCAL, h->type); else if (h->root.type == bfd_link_hash_undefweak || h->root.type == bfd_link_hash_defweak) sym.st_info = ELF_ST_INFO (STB_WEAK, h->type); else sym.st_info = ELF_ST_INFO (STB_GLOBAL, h->type); switch (h->root.type) { default: case bfd_link_hash_new: case bfd_link_hash_warning: abort (); return FALSE; case bfd_link_hash_undefined: case bfd_link_hash_undefweak: input_sec = bfd_und_section_ptr; sym.st_shndx = SHN_UNDEF; break; case bfd_link_hash_defined: case bfd_link_hash_defweak: { input_sec = h->root.u.def.section; if (input_sec->output_section != NULL) { sym.st_shndx = _bfd_elf_section_from_bfd_section (finfo->output_bfd, input_sec->output_section); if (sym.st_shndx == SHN_BAD) { (*_bfd_error_handler) (_("%B: could not find output section %A for input section %A"), finfo->output_bfd, input_sec->output_section, input_sec); eoinfo->failed = TRUE; return FALSE; } /* ELF symbols in relocatable files are section relative, but in nonrelocatable files they are virtual addresses. */ sym.st_value = h->root.u.def.value + input_sec->output_offset; if (! finfo->info->relocatable) { sym.st_value += input_sec->output_section->vma; if (h->type == STT_TLS) { /* STT_TLS symbols are relative to PT_TLS segment base. */ BFD_ASSERT (elf_hash_table (finfo->info)->tls_sec != NULL); sym.st_value -= elf_hash_table (finfo->info)->tls_sec->vma; } } } else { BFD_ASSERT (input_sec->owner == NULL || (input_sec->owner->flags & DYNAMIC) != 0); sym.st_shndx = SHN_UNDEF; input_sec = bfd_und_section_ptr; } } break; case bfd_link_hash_common: input_sec = h->root.u.c.p->section; sym.st_shndx = bed->common_section_index (input_sec); sym.st_value = 1 << h->root.u.c.p->alignment_power; break; case bfd_link_hash_indirect: /* These symbols are created by symbol versioning. They point to the decorated version of the name. For example, if the symbol foo@@GNU_1.2 is the default, which should be used when foo is used with no version, then we add an indirect symbol foo which points to foo@@GNU_1.2. We ignore these symbols, since the indirected symbol is already in the hash table. */ return TRUE; } /* Give the processor backend a chance to tweak the symbol value, and also to finish up anything that needs to be done for this symbol. FIXME: Not calling elf_backend_finish_dynamic_symbol for forced local syms when non-shared is due to a historical quirk. */ if ((h->dynindx != -1 || h->forced_local) && ((finfo->info->shared && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak)) || !h->forced_local) && elf_hash_table (finfo->info)->dynamic_sections_created) { if (! ((*bed->elf_backend_finish_dynamic_symbol) (finfo->output_bfd, finfo->info, h, &sym))) { eoinfo->failed = TRUE; return FALSE; } } /* If we are marking the symbol as undefined, and there are no non-weak references to this symbol from a regular object, then mark the symbol as weak undefined; if there are non-weak references, mark the symbol as strong. We can't do this earlier, because it might not be marked as undefined until the finish_dynamic_symbol routine gets through with it. */ if (sym.st_shndx == SHN_UNDEF && h->ref_regular && (ELF_ST_BIND (sym.st_info) == STB_GLOBAL || ELF_ST_BIND (sym.st_info) == STB_WEAK)) { int bindtype; if (h->ref_regular_nonweak) bindtype = STB_GLOBAL; else bindtype = STB_WEAK; sym.st_info = ELF_ST_INFO (bindtype, ELF_ST_TYPE (sym.st_info)); } /* If a non-weak symbol with non-default visibility is not defined locally, it is a fatal error. */ if (! finfo->info->relocatable && ELF_ST_VISIBILITY (sym.st_other) != STV_DEFAULT && ELF_ST_BIND (sym.st_info) != STB_WEAK && h->root.type == bfd_link_hash_undefined && !h->def_regular) { (*_bfd_error_handler) (_("%B: %s symbol `%s' isn't defined"), finfo->output_bfd, ELF_ST_VISIBILITY (sym.st_other) == STV_PROTECTED ? "protected" : ELF_ST_VISIBILITY (sym.st_other) == STV_INTERNAL ? "internal" : "hidden", h->root.root.string); eoinfo->failed = TRUE; return FALSE; } /* If this symbol should be put in the .dynsym section, then put it there now. We already know the symbol index. We also fill in the entry in the .hash section. */ if (h->dynindx != -1 && elf_hash_table (finfo->info)->dynamic_sections_created) { bfd_byte *esym; sym.st_name = h->dynstr_index; esym = finfo->dynsym_sec->contents + h->dynindx * bed->s->sizeof_sym; if (! check_dynsym (finfo->output_bfd, &sym)) { eoinfo->failed = TRUE; return FALSE; } bed->s->swap_symbol_out (finfo->output_bfd, &sym, esym, 0); if (finfo->hash_sec != NULL) { size_t hash_entry_size; bfd_byte *bucketpos; bfd_vma chain; size_t bucketcount; size_t bucket; bucketcount = elf_hash_table (finfo->info)->bucketcount; bucket = h->u.elf_hash_value % bucketcount; hash_entry_size = elf_section_data (finfo->hash_sec)->this_hdr.sh_entsize; bucketpos = ((bfd_byte *) finfo->hash_sec->contents + (bucket + 2) * hash_entry_size); chain = bfd_get (8 * hash_entry_size, finfo->output_bfd, bucketpos); bfd_put (8 * hash_entry_size, finfo->output_bfd, h->dynindx, bucketpos); bfd_put (8 * hash_entry_size, finfo->output_bfd, chain, ((bfd_byte *) finfo->hash_sec->contents + (bucketcount + 2 + h->dynindx) * hash_entry_size)); } if (finfo->symver_sec != NULL && finfo->symver_sec->contents != NULL) { Elf_Internal_Versym iversym; Elf_External_Versym *eversym; if (!h->def_regular) { if (h->verinfo.verdef == NULL) iversym.vs_vers = 0; else iversym.vs_vers = h->verinfo.verdef->vd_exp_refno + 1; } else { if (h->verinfo.vertree == NULL) iversym.vs_vers = 1; else iversym.vs_vers = h->verinfo.vertree->vernum + 1; if (finfo->info->create_default_symver) iversym.vs_vers++; } if (h->hidden) iversym.vs_vers |= VERSYM_HIDDEN; eversym = (Elf_External_Versym *) finfo->symver_sec->contents; eversym += h->dynindx; _bfd_elf_swap_versym_out (finfo->output_bfd, &iversym, eversym); } } /* If we're stripping it, then it was just a dynamic symbol, and there's nothing else to do. */ if (strip || (input_sec->flags & SEC_EXCLUDE) != 0) return TRUE; h->indx = bfd_get_symcount (finfo->output_bfd); if (! elf_link_output_sym (finfo, h->root.root.string, &sym, input_sec, h)) { eoinfo->failed = TRUE; return FALSE; } return TRUE; } /* Return TRUE if special handling is done for relocs in SEC against symbols defined in discarded sections. */ static bfd_boolean elf_section_ignore_discarded_relocs (asection *sec) { const struct elf_backend_data *bed; switch (sec->sec_info_type) { case ELF_INFO_TYPE_STABS: case ELF_INFO_TYPE_EH_FRAME: return TRUE; default: break; } bed = get_elf_backend_data (sec->owner); if (bed->elf_backend_ignore_discarded_relocs != NULL && (*bed->elf_backend_ignore_discarded_relocs) (sec)) return TRUE; return FALSE; } /* Return a mask saying how ld should treat relocations in SEC against symbols defined in discarded sections. If this function returns COMPLAIN set, ld will issue a warning message. If this function returns PRETEND set, and the discarded section was link-once and the same size as the kept link-once section, ld will pretend that the symbol was actually defined in the kept section. Otherwise ld will zero the reloc (at least that is the intent, but some cooperation by the target dependent code is needed, particularly for REL targets). */ unsigned int _bfd_elf_default_action_discarded (asection *sec) { if (sec->flags & SEC_DEBUGGING) return PRETEND; if (strcmp (".eh_frame", sec->name) == 0) return 0; if (strcmp (".gcc_except_table", sec->name) == 0) return 0; return COMPLAIN | PRETEND; } /* Find a match between a section and a member of a section group. */ static asection * match_group_member (asection *sec, asection *group, struct bfd_link_info *info) { asection *first = elf_next_in_group (group); asection *s = first; while (s != NULL) { if (bfd_elf_match_symbols_in_sections (s, sec, info)) return s; s = elf_next_in_group (s); if (s == first) break; } return NULL; } /* Check if the kept section of a discarded section SEC can be used to replace it. Return the replacement if it is OK. Otherwise return NULL. */ asection * _bfd_elf_check_kept_section (asection *sec, struct bfd_link_info *info) { asection *kept; kept = sec->kept_section; if (kept != NULL) { if ((kept->flags & SEC_GROUP) != 0) kept = match_group_member (sec, kept, info); if (kept != NULL && sec->size != kept->size) kept = NULL; sec->kept_section = kept; } return kept; } /* Link an input file into the linker output file. This function handles all the sections and relocations of the input file at once. This is so that we only have to read the local symbols once, and don't have to keep them in memory. */ static bfd_boolean elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd) { int (*relocate_section) (bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Sym *, asection **); bfd *output_bfd; Elf_Internal_Shdr *symtab_hdr; size_t locsymcount; size_t extsymoff; Elf_Internal_Sym *isymbuf; Elf_Internal_Sym *isym; Elf_Internal_Sym *isymend; long *pindex; asection **ppsection; asection *o; const struct elf_backend_data *bed; struct elf_link_hash_entry **sym_hashes; output_bfd = finfo->output_bfd; bed = get_elf_backend_data (output_bfd); relocate_section = bed->elf_backend_relocate_section; /* If this is a dynamic object, we don't want to do anything here: we don't want the local symbols, and we don't want the section contents. */ if ((input_bfd->flags & DYNAMIC) != 0) return TRUE; symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; if (elf_bad_symtab (input_bfd)) { locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym; extsymoff = 0; } else { locsymcount = symtab_hdr->sh_info; extsymoff = symtab_hdr->sh_info; } /* Read the local symbols. */ isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; if (isymbuf == NULL && locsymcount != 0) { isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0, finfo->internal_syms, finfo->external_syms, finfo->locsym_shndx); if (isymbuf == NULL) return FALSE; } /* evaluate_complex_relocation_symbols looks for symbols in finfo->internal_syms. */ else if (isymbuf != NULL && locsymcount != 0) { bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0, finfo->internal_syms, finfo->external_syms, finfo->locsym_shndx); } /* Find local symbol sections and adjust values of symbols in SEC_MERGE sections. Write out those local symbols we know are going into the output file. */ isymend = isymbuf + locsymcount; for (isym = isymbuf, pindex = finfo->indices, ppsection = finfo->sections; isym < isymend; isym++, pindex++, ppsection++) { asection *isec; const char *name; Elf_Internal_Sym osym; *pindex = -1; if (elf_bad_symtab (input_bfd)) { if (ELF_ST_BIND (isym->st_info) != STB_LOCAL) { *ppsection = NULL; continue; } } if (isym->st_shndx == SHN_UNDEF) isec = bfd_und_section_ptr; else if (isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE) { isec = bfd_section_from_elf_index (input_bfd, isym->st_shndx); if (isec && isec->sec_info_type == ELF_INFO_TYPE_MERGE && ELF_ST_TYPE (isym->st_info) != STT_SECTION) isym->st_value = _bfd_merged_section_offset (output_bfd, &isec, elf_section_data (isec)->sec_info, isym->st_value); } else if (isym->st_shndx == SHN_ABS) isec = bfd_abs_section_ptr; else if (isym->st_shndx == SHN_COMMON) isec = bfd_com_section_ptr; else { /* Don't attempt to output symbols with st_shnx in the reserved range other than SHN_ABS and SHN_COMMON. */ *ppsection = NULL; continue; } *ppsection = isec; /* Don't output the first, undefined, symbol. */ if (ppsection == finfo->sections) continue; if (ELF_ST_TYPE (isym->st_info) == STT_SECTION) { /* We never output section symbols. Instead, we use the section symbol of the corresponding section in the output file. */ continue; } /* If we are stripping all symbols, we don't want to output this one. */ if (finfo->info->strip == strip_all) continue; /* If we are discarding all local symbols, we don't want to output this one. If we are generating a relocatable output file, then some of the local symbols may be required by relocs; we output them below as we discover that they are needed. */ if (finfo->info->discard == discard_all) continue; /* If this symbol is defined in a section which we are discarding, we don't need to keep it. */ if (isym->st_shndx != SHN_UNDEF && (isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE) && (isec == NULL || bfd_section_removed_from_list (output_bfd, isec->output_section))) continue; /* Get the name of the symbol. */ name = bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, isym->st_name); if (name == NULL) return FALSE; /* See if we are discarding symbols with this name. */ if ((finfo->info->strip == strip_some && (bfd_hash_lookup (finfo->info->keep_hash, name, FALSE, FALSE) == NULL)) || (((finfo->info->discard == discard_sec_merge && (isec->flags & SEC_MERGE) && ! finfo->info->relocatable) || finfo->info->discard == discard_l) && bfd_is_local_label_name (input_bfd, name))) continue; /* If we get here, we are going to output this symbol. */ osym = *isym; /* Adjust the section index for the output file. */ osym.st_shndx = _bfd_elf_section_from_bfd_section (output_bfd, isec->output_section); if (osym.st_shndx == SHN_BAD) return FALSE; *pindex = bfd_get_symcount (output_bfd); /* ELF symbols in relocatable files are section relative, but in executable files they are virtual addresses. Note that this code assumes that all ELF sections have an associated BFD section with a reasonable value for output_offset; below we assume that they also have a reasonable value for output_section. Any special sections must be set up to meet these requirements. */ osym.st_value += isec->output_offset; if (! finfo->info->relocatable) { osym.st_value += isec->output_section->vma; if (ELF_ST_TYPE (osym.st_info) == STT_TLS) { /* STT_TLS symbols are relative to PT_TLS segment base. */ BFD_ASSERT (elf_hash_table (finfo->info)->tls_sec != NULL); osym.st_value -= elf_hash_table (finfo->info)->tls_sec->vma; } } if (! elf_link_output_sym (finfo, name, &osym, isec, NULL)) return FALSE; } if (! evaluate_complex_relocation_symbols (input_bfd, finfo, locsymcount)) return FALSE; /* Relocate the contents of each section. */ sym_hashes = elf_sym_hashes (input_bfd); for (o = input_bfd->sections; o != NULL; o = o->next) { bfd_byte *contents; if (! o->linker_mark) { /* This section was omitted from the link. */ continue; } if ((o->flags & SEC_HAS_CONTENTS) == 0 || (o->size == 0 && (o->flags & SEC_RELOC) == 0)) continue; if ((o->flags & SEC_LINKER_CREATED) != 0) { /* Section was created by _bfd_elf_link_create_dynamic_sections or somesuch. */ continue; } /* Get the contents of the section. They have been cached by a relaxation routine. Note that o is a section in an input file, so the contents field will not have been set by any of the routines which work on output files. */ if (elf_section_data (o)->this_hdr.contents != NULL) contents = elf_section_data (o)->this_hdr.contents; else { bfd_size_type amt = o->rawsize ? o->rawsize : o->size; contents = finfo->contents; if (! bfd_get_section_contents (input_bfd, o, contents, 0, amt)) return FALSE; } if ((o->flags & SEC_RELOC) != 0) { Elf_Internal_Rela *internal_relocs; bfd_vma r_type_mask; int r_sym_shift; int ret; /* Get the swapped relocs. */ internal_relocs = _bfd_elf_link_read_relocs (input_bfd, o, finfo->external_relocs, finfo->internal_relocs, FALSE); if (internal_relocs == NULL && o->reloc_count > 0) return FALSE; if (bed->s->arch_size == 32) { r_type_mask = 0xff; r_sym_shift = 8; } else { r_type_mask = 0xffffffff; r_sym_shift = 32; } /* Run through the relocs looking for any against symbols from discarded sections and section symbols from removed link-once sections. Complain about relocs against discarded sections. Zero relocs against removed link-once sections. */ if (!elf_section_ignore_discarded_relocs (o)) { Elf_Internal_Rela *rel, *relend; unsigned int action = (*bed->action_discarded) (o); rel = internal_relocs; relend = rel + o->reloc_count * bed->s->int_rels_per_ext_rel; for ( ; rel < relend; rel++) { unsigned long r_symndx = rel->r_info >> r_sym_shift; asection **ps, *sec; struct elf_link_hash_entry *h = NULL; const char *sym_name; if (r_symndx == STN_UNDEF) continue; if (r_symndx >= locsymcount || (elf_bad_symtab (input_bfd) && finfo->sections[r_symndx] == NULL)) { h = sym_hashes[r_symndx - extsymoff]; /* Badly formatted input files can contain relocs that reference non-existant symbols. Check here so that we do not seg fault. */ if (h == NULL) { char buffer [32]; sprintf_vma (buffer, rel->r_info); (*_bfd_error_handler) (_("error: %B contains a reloc (0x%s) for section %A " "that references a non-existent global symbol"), input_bfd, o, buffer); bfd_set_error (bfd_error_bad_value); return FALSE; } while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak) continue; ps = &h->root.u.def.section; sym_name = h->root.root.string; } else { Elf_Internal_Sym *sym = isymbuf + r_symndx; ps = &finfo->sections[r_symndx]; sym_name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym, *ps); } /* Complain if the definition comes from a discarded section. */ if ((sec = *ps) != NULL && elf_discarded_section (sec)) { BFD_ASSERT (r_symndx != 0); if (action & COMPLAIN) (*finfo->info->callbacks->einfo) (_("%X`%s' referenced in section `%A' of %B: " "defined in discarded section `%A' of %B\n"), sym_name, o, input_bfd, sec, sec->owner); /* Try to do the best we can to support buggy old versions of gcc. Pretend that the symbol is really defined in the kept linkonce section. FIXME: This is quite broken. Modifying the symbol here means we will be changing all later uses of the symbol, not just in this section. */ if (action & PRETEND) { asection *kept; kept = _bfd_elf_check_kept_section (sec, finfo->info); if (kept != NULL) { *ps = kept; continue; } } } } } /* Relocate the section by invoking a back end routine. The back end routine is responsible for adjusting the section contents as necessary, and (if using Rela relocs and generating a relocatable output file) adjusting the reloc addend as necessary. The back end routine does not have to worry about setting the reloc address or the reloc symbol index. The back end routine is given a pointer to the swapped in internal symbols, and can access the hash table entries for the external symbols via elf_sym_hashes (input_bfd). When generating relocatable output, the back end routine must handle STB_LOCAL/STT_SECTION symbols specially. The output symbol is going to be a section symbol corresponding to the output section, which will require the addend to be adjusted. */ ret = (*relocate_section) (output_bfd, finfo->info, input_bfd, o, contents, internal_relocs, isymbuf, finfo->sections); if (!ret) return FALSE; if (ret == 2 || finfo->info->relocatable || finfo->info->emitrelocations) { Elf_Internal_Rela *irela; Elf_Internal_Rela *irelaend; bfd_vma last_offset; struct elf_link_hash_entry **rel_hash; struct elf_link_hash_entry **rel_hash_list; Elf_Internal_Shdr *input_rel_hdr, *input_rel_hdr2; unsigned int next_erel; bfd_boolean rela_normal; input_rel_hdr = &elf_section_data (o)->rel_hdr; rela_normal = (bed->rela_normal && (input_rel_hdr->sh_entsize == bed->s->sizeof_rela)); /* Adjust the reloc addresses and symbol indices. */ irela = internal_relocs; irelaend = irela + o->reloc_count * bed->s->int_rels_per_ext_rel; rel_hash = (elf_section_data (o->output_section)->rel_hashes + elf_section_data (o->output_section)->rel_count + elf_section_data (o->output_section)->rel_count2); rel_hash_list = rel_hash; last_offset = o->output_offset; if (!finfo->info->relocatable) last_offset += o->output_section->vma; for (next_erel = 0; irela < irelaend; irela++, next_erel++) { unsigned long r_symndx; asection *sec; Elf_Internal_Sym sym; if (next_erel == bed->s->int_rels_per_ext_rel) { rel_hash++; next_erel = 0; } irela->r_offset = _bfd_elf_section_offset (output_bfd, finfo->info, o, irela->r_offset); if (irela->r_offset >= (bfd_vma) -2) { /* This is a reloc for a deleted entry or somesuch. Turn it into an R_*_NONE reloc, at the same offset as the last reloc. elf_eh_frame.c and bfd_elf_discard_info rely on reloc offsets being ordered. */ irela->r_offset = last_offset; irela->r_info = 0; irela->r_addend = 0; continue; } irela->r_offset += o->output_offset; /* Relocs in an executable have to be virtual addresses. */ if (!finfo->info->relocatable) irela->r_offset += o->output_section->vma; last_offset = irela->r_offset; r_symndx = irela->r_info >> r_sym_shift; if (r_symndx == STN_UNDEF) continue; if (r_symndx >= locsymcount || (elf_bad_symtab (input_bfd) && finfo->sections[r_symndx] == NULL)) { struct elf_link_hash_entry *rh; unsigned long indx; /* This is a reloc against a global symbol. We have not yet output all the local symbols, so we do not know the symbol index of any global symbol. We set the rel_hash entry for this reloc to point to the global hash table entry for this symbol. The symbol index is then set at the end of bfd_elf_final_link. */ indx = r_symndx - extsymoff; rh = elf_sym_hashes (input_bfd)[indx]; while (rh->root.type == bfd_link_hash_indirect || rh->root.type == bfd_link_hash_warning) rh = (struct elf_link_hash_entry *) rh->root.u.i.link; /* Setting the index to -2 tells elf_link_output_extsym that this symbol is used by a reloc. */ BFD_ASSERT (rh->indx < 0); rh->indx = -2; *rel_hash = rh; continue; } /* This is a reloc against a local symbol. */ *rel_hash = NULL; sym = isymbuf[r_symndx]; sec = finfo->sections[r_symndx]; if (ELF_ST_TYPE (sym.st_info) == STT_SECTION) { /* I suppose the backend ought to fill in the section of any STT_SECTION symbol against a processor specific section. */ r_symndx = 0; if (bfd_is_abs_section (sec)) ; else if (sec == NULL || sec->owner == NULL) { bfd_set_error (bfd_error_bad_value); return FALSE; } else { asection *osec = sec->output_section; /* If we have discarded a section, the output section will be the absolute section. In case of discarded SEC_MERGE sections, use the kept section. relocate_section should have already handled discarded linkonce sections. */ if (bfd_is_abs_section (osec) && sec->kept_section != NULL && sec->kept_section->output_section != NULL) { osec = sec->kept_section->output_section; irela->r_addend -= osec->vma; } if (!bfd_is_abs_section (osec)) { r_symndx = osec->target_index; if (r_symndx == 0) { struct elf_link_hash_table *htab; asection *oi; htab = elf_hash_table (finfo->info); oi = htab->text_index_section; if ((osec->flags & SEC_READONLY) == 0 && htab->data_index_section != NULL) oi = htab->data_index_section; if (oi != NULL) { irela->r_addend += osec->vma - oi->vma; r_symndx = oi->target_index; } } BFD_ASSERT (r_symndx != 0); } } /* Adjust the addend according to where the section winds up in the output section. */ if (rela_normal) irela->r_addend += sec->output_offset; } else { if (finfo->indices[r_symndx] == -1) { unsigned long shlink; const char *name; asection *osec; if (finfo->info->strip == strip_all) { /* You can't do ld -r -s. */ bfd_set_error (bfd_error_invalid_operation); return FALSE; } /* This symbol was skipped earlier, but since it is needed by a reloc, we must output it now. */ shlink = symtab_hdr->sh_link; name = (bfd_elf_string_from_elf_section (input_bfd, shlink, sym.st_name)); if (name == NULL) return FALSE; osec = sec->output_section; sym.st_shndx = _bfd_elf_section_from_bfd_section (output_bfd, osec); if (sym.st_shndx == SHN_BAD) return FALSE; sym.st_value += sec->output_offset; if (! finfo->info->relocatable) { sym.st_value += osec->vma; if (ELF_ST_TYPE (sym.st_info) == STT_TLS) { /* STT_TLS symbols are relative to PT_TLS segment base. */ BFD_ASSERT (elf_hash_table (finfo->info) ->tls_sec != NULL); sym.st_value -= (elf_hash_table (finfo->info) ->tls_sec->vma); } } finfo->indices[r_symndx] = bfd_get_symcount (output_bfd); if (! elf_link_output_sym (finfo, name, &sym, sec, NULL)) return FALSE; } r_symndx = finfo->indices[r_symndx]; } irela->r_info = ((bfd_vma) r_symndx << r_sym_shift | (irela->r_info & r_type_mask)); } /* Swap out the relocs. */ if (input_rel_hdr->sh_size != 0 && !bed->elf_backend_emit_relocs (output_bfd, o, input_rel_hdr, internal_relocs, rel_hash_list)) return FALSE; input_rel_hdr2 = elf_section_data (o)->rel_hdr2; if (input_rel_hdr2 && input_rel_hdr2->sh_size != 0) { internal_relocs += (NUM_SHDR_ENTRIES (input_rel_hdr) * bed->s->int_rels_per_ext_rel); rel_hash_list += NUM_SHDR_ENTRIES (input_rel_hdr); if (!bed->elf_backend_emit_relocs (output_bfd, o, input_rel_hdr2, internal_relocs, rel_hash_list)) return FALSE; } } } /* Write out the modified section contents. */ if (bed->elf_backend_write_section && (*bed->elf_backend_write_section) (output_bfd, finfo->info, o, contents)) { /* Section written out. */ } else switch (o->sec_info_type) { case ELF_INFO_TYPE_STABS: if (! (_bfd_write_section_stabs (output_bfd, &elf_hash_table (finfo->info)->stab_info, o, &elf_section_data (o)->sec_info, contents))) return FALSE; break; case ELF_INFO_TYPE_MERGE: if (! _bfd_write_merged_section (output_bfd, o, elf_section_data (o)->sec_info)) return FALSE; break; case ELF_INFO_TYPE_EH_FRAME: { if (! _bfd_elf_write_section_eh_frame (output_bfd, finfo->info, o, contents)) return FALSE; } break; default: { if (! (o->flags & SEC_EXCLUDE) && ! bfd_set_section_contents (output_bfd, o->output_section, contents, (file_ptr) o->output_offset, o->size)) return FALSE; } break; } } return TRUE; } /* Generate a reloc when linking an ELF file. This is a reloc requested by the linker, and does not come from any input file. This is used to build constructor and destructor tables when linking with -Ur. */ static bfd_boolean elf_reloc_link_order (bfd *output_bfd, struct bfd_link_info *info, asection *output_section, struct bfd_link_order *link_order) { reloc_howto_type *howto; long indx; bfd_vma offset; bfd_vma addend; struct elf_link_hash_entry **rel_hash_ptr; Elf_Internal_Shdr *rel_hdr; const struct elf_backend_data *bed = get_elf_backend_data (output_bfd); Elf_Internal_Rela irel[MAX_INT_RELS_PER_EXT_REL]; bfd_byte *erel; unsigned int i; howto = bfd_reloc_type_lookup (output_bfd, link_order->u.reloc.p->reloc); if (howto == NULL) { bfd_set_error (bfd_error_bad_value); return FALSE; } addend = link_order->u.reloc.p->addend; /* Figure out the symbol index. */ rel_hash_ptr = (elf_section_data (output_section)->rel_hashes + elf_section_data (output_section)->rel_count + elf_section_data (output_section)->rel_count2); if (link_order->type == bfd_section_reloc_link_order) { indx = link_order->u.reloc.p->u.section->target_index; BFD_ASSERT (indx != 0); *rel_hash_ptr = NULL; } else { struct elf_link_hash_entry *h; /* Treat a reloc against a defined symbol as though it were actually against the section. */ h = ((struct elf_link_hash_entry *) bfd_wrapped_link_hash_lookup (output_bfd, info, link_order->u.reloc.p->u.name, FALSE, FALSE, TRUE)); if (h != NULL && (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak)) { asection *section; section = h->root.u.def.section; indx = section->output_section->target_index; *rel_hash_ptr = NULL; /* It seems that we ought to add the symbol value to the addend here, but in practice it has already been added because it was passed to constructor_callback. */ addend += section->output_section->vma + section->output_offset; } else if (h != NULL) { /* Setting the index to -2 tells elf_link_output_extsym that this symbol is used by a reloc. */ h->indx = -2; *rel_hash_ptr = h; indx = 0; } else { if (! ((*info->callbacks->unattached_reloc) (info, link_order->u.reloc.p->u.name, NULL, NULL, 0))) return FALSE; indx = 0; } } /* If this is an inplace reloc, we must write the addend into the object file. */ if (howto->partial_inplace && addend != 0) { bfd_size_type size; bfd_reloc_status_type rstat; bfd_byte *buf; bfd_boolean ok; const char *sym_name; size = bfd_get_reloc_size (howto); buf = bfd_zmalloc (size); if (buf == NULL) return FALSE; rstat = _bfd_relocate_contents (howto, output_bfd, addend, buf); switch (rstat) { case bfd_reloc_ok: break; default: case bfd_reloc_outofrange: abort (); case bfd_reloc_overflow: if (link_order->type == bfd_section_reloc_link_order) sym_name = bfd_section_name (output_bfd, link_order->u.reloc.p->u.section); else sym_name = link_order->u.reloc.p->u.name; if (! ((*info->callbacks->reloc_overflow) (info, NULL, sym_name, howto->name, addend, NULL, NULL, (bfd_vma) 0))) { free (buf); return FALSE; } break; } ok = bfd_set_section_contents (output_bfd, output_section, buf, link_order->offset, size); free (buf); if (! ok) return FALSE; } /* The address of a reloc is relative to the section in a relocatable file, and is a virtual address in an executable file. */ offset = link_order->offset; if (! info->relocatable) offset += output_section->vma; for (i = 0; i < bed->s->int_rels_per_ext_rel; i++) { irel[i].r_offset = offset; irel[i].r_info = 0; irel[i].r_addend = 0; } if (bed->s->arch_size == 32) irel[0].r_info = ELF32_R_INFO (indx, howto->type); else irel[0].r_info = ELF64_R_INFO (indx, howto->type); rel_hdr = &elf_section_data (output_section)->rel_hdr; erel = rel_hdr->contents; if (rel_hdr->sh_type == SHT_REL) { erel += (elf_section_data (output_section)->rel_count * bed->s->sizeof_rel); (*bed->s->swap_reloc_out) (output_bfd, irel, erel); } else { irel[0].r_addend = addend; erel += (elf_section_data (output_section)->rel_count * bed->s->sizeof_rela); (*bed->s->swap_reloca_out) (output_bfd, irel, erel); } ++elf_section_data (output_section)->rel_count; return TRUE; } /* Get the output vma of the section pointed to by the sh_link field. */ static bfd_vma elf_get_linked_section_vma (struct bfd_link_order *p) { Elf_Internal_Shdr **elf_shdrp; asection *s; int elfsec; s = p->u.indirect.section; elf_shdrp = elf_elfsections (s->owner); elfsec = _bfd_elf_section_from_bfd_section (s->owner, s); elfsec = elf_shdrp[elfsec]->sh_link; /* PR 290: The Intel C compiler generates SHT_IA_64_UNWIND with SHF_LINK_ORDER. But it doesn't set the sh_link or sh_info fields. Hence we could get the situation where elfsec is 0. */ if (elfsec == 0) { const struct elf_backend_data *bed = get_elf_backend_data (s->owner); if (bed->link_order_error_handler) bed->link_order_error_handler (_("%B: warning: sh_link not set for section `%A'"), s->owner, s); return 0; } else { s = elf_shdrp[elfsec]->bfd_section; return s->output_section->vma + s->output_offset; } } /* Compare two sections based on the locations of the sections they are linked to. Used by elf_fixup_link_order. */ static int compare_link_order (const void * a, const void * b) { bfd_vma apos; bfd_vma bpos; apos = elf_get_linked_section_vma (*(struct bfd_link_order **)a); bpos = elf_get_linked_section_vma (*(struct bfd_link_order **)b); if (apos < bpos) return -1; return apos > bpos; } /* Looks for sections with SHF_LINK_ORDER set. Rearranges them into the same order as their linked sections. Returns false if this could not be done because an output section includes both ordered and unordered sections. Ideally we'd do this in the linker proper. */ static bfd_boolean elf_fixup_link_order (bfd *abfd, asection *o) { int seen_linkorder; int seen_other; int n; struct bfd_link_order *p; bfd *sub; const struct elf_backend_data *bed = get_elf_backend_data (abfd); unsigned elfsec; struct bfd_link_order **sections; asection *s, *other_sec, *linkorder_sec; bfd_vma offset; other_sec = NULL; linkorder_sec = NULL; seen_other = 0; seen_linkorder = 0; for (p = o->map_head.link_order; p != NULL; p = p->next) { if (p->type == bfd_indirect_link_order) { s = p->u.indirect.section; sub = s->owner; if (bfd_get_flavour (sub) == bfd_target_elf_flavour && elf_elfheader (sub)->e_ident[EI_CLASS] == bed->s->elfclass && (elfsec = _bfd_elf_section_from_bfd_section (sub, s)) && elfsec < elf_numsections (sub) && elf_elfsections (sub)[elfsec]->sh_flags & SHF_LINK_ORDER) { seen_linkorder++; linkorder_sec = s; } else { seen_other++; other_sec = s; } } else seen_other++; if (seen_other && seen_linkorder) { if (other_sec && linkorder_sec) (*_bfd_error_handler) (_("%A has both ordered [`%A' in %B] and unordered [`%A' in %B] sections"), o, linkorder_sec, linkorder_sec->owner, other_sec, other_sec->owner); else (*_bfd_error_handler) (_("%A has both ordered and unordered sections"), o); bfd_set_error (bfd_error_bad_value); return FALSE; } } if (!seen_linkorder) return TRUE; sections = (struct bfd_link_order **) xmalloc (seen_linkorder * sizeof (struct bfd_link_order *)); seen_linkorder = 0; for (p = o->map_head.link_order; p != NULL; p = p->next) { sections[seen_linkorder++] = p; } /* Sort the input sections in the order of their linked section. */ qsort (sections, seen_linkorder, sizeof (struct bfd_link_order *), compare_link_order); /* Change the offsets of the sections. */ offset = 0; for (n = 0; n < seen_linkorder; n++) { s = sections[n]->u.indirect.section; offset &= ~(bfd_vma)((1 << s->alignment_power) - 1); s->output_offset = offset; sections[n]->offset = offset; offset += sections[n]->size; } return TRUE; } /* Do the final step of an ELF link. */ bfd_boolean bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info) { bfd_boolean dynamic; bfd_boolean emit_relocs; bfd *dynobj; struct elf_final_link_info finfo; register asection *o; register struct bfd_link_order *p; register bfd *sub; bfd_size_type max_contents_size; bfd_size_type max_external_reloc_size; bfd_size_type max_internal_reloc_count; bfd_size_type max_sym_count; bfd_size_type max_sym_shndx_count; file_ptr off; Elf_Internal_Sym elfsym; unsigned int i; Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Shdr *symtab_shndx_hdr; Elf_Internal_Shdr *symstrtab_hdr; const struct elf_backend_data *bed = get_elf_backend_data (abfd); struct elf_outext_info eoinfo; bfd_boolean merged; size_t relativecount = 0; asection *reldyn = 0; bfd_size_type amt; asection *attr_section = NULL; bfd_vma attr_size = 0; const char *std_attrs_section; if (! is_elf_hash_table (info->hash)) return FALSE; if (info->shared) abfd->flags |= DYNAMIC; dynamic = elf_hash_table (info)->dynamic_sections_created; dynobj = elf_hash_table (info)->dynobj; emit_relocs = (info->relocatable || info->emitrelocations); finfo.info = info; finfo.output_bfd = abfd; finfo.symstrtab = _bfd_elf_stringtab_init (); if (finfo.symstrtab == NULL) return FALSE; if (! dynamic) { finfo.dynsym_sec = NULL; finfo.hash_sec = NULL; finfo.symver_sec = NULL; } else { finfo.dynsym_sec = bfd_get_section_by_name (dynobj, ".dynsym"); finfo.hash_sec = bfd_get_section_by_name (dynobj, ".hash"); BFD_ASSERT (finfo.dynsym_sec != NULL); finfo.symver_sec = bfd_get_section_by_name (dynobj, ".gnu.version"); /* Note that it is OK if symver_sec is NULL. */ } finfo.contents = NULL; finfo.external_relocs = NULL; finfo.internal_relocs = NULL; finfo.external_syms = NULL; finfo.locsym_shndx = NULL; finfo.internal_syms = NULL; finfo.indices = NULL; finfo.sections = NULL; finfo.symbuf = NULL; finfo.symshndxbuf = NULL; finfo.symbuf_count = 0; finfo.shndxbuf_size = 0; /* The object attributes have been merged. Remove the input sections from the link, and set the contents of the output secton. */ std_attrs_section = get_elf_backend_data (abfd)->obj_attrs_section; for (o = abfd->sections; o != NULL; o = o->next) { if ((std_attrs_section && strcmp (o->name, std_attrs_section) == 0) || strcmp (o->name, ".gnu.attributes") == 0) { for (p = o->map_head.link_order; p != NULL; p = p->next) { asection *input_section; if (p->type != bfd_indirect_link_order) continue; input_section = p->u.indirect.section; /* Hack: reset the SEC_HAS_CONTENTS flag so that elf_link_input_bfd ignores this section. */ input_section->flags &= ~SEC_HAS_CONTENTS; } attr_size = bfd_elf_obj_attr_size (abfd); if (attr_size) { bfd_set_section_size (abfd, o, attr_size); attr_section = o; /* Skip this section later on. */ o->map_head.link_order = NULL; } else o->flags |= SEC_EXCLUDE; } } /* Count up the number of relocations we will output for each output section, so that we know the sizes of the reloc sections. We also figure out some maximum sizes. */ max_contents_size = 0; max_external_reloc_size = 0; max_internal_reloc_count = 0; max_sym_count = 0; max_sym_shndx_count = 0; merged = FALSE; for (o = abfd->sections; o != NULL; o = o->next) { struct bfd_elf_section_data *esdo = elf_section_data (o); o->reloc_count = 0; for (p = o->map_head.link_order; p != NULL; p = p->next) { unsigned int reloc_count = 0; struct bfd_elf_section_data *esdi = NULL; unsigned int *rel_count1; if (p->type == bfd_section_reloc_link_order || p->type == bfd_symbol_reloc_link_order) reloc_count = 1; else if (p->type == bfd_indirect_link_order) { asection *sec; sec = p->u.indirect.section; esdi = elf_section_data (sec); /* Mark all sections which are to be included in the link. This will normally be every section. We need to do this so that we can identify any sections which the linker has decided to not include. */ sec->linker_mark = TRUE; if (sec->flags & SEC_MERGE) merged = TRUE; if (info->relocatable || info->emitrelocations) reloc_count = sec->reloc_count; else if (bed->elf_backend_count_relocs) { Elf_Internal_Rela * relocs; relocs = _bfd_elf_link_read_relocs (sec->owner, sec, NULL, NULL, info->keep_memory); if (relocs != NULL) { reloc_count = (*bed->elf_backend_count_relocs) (sec, relocs); if (elf_section_data (sec)->relocs != relocs) free (relocs); } } if (sec->rawsize > max_contents_size) max_contents_size = sec->rawsize; if (sec->size > max_contents_size) max_contents_size = sec->size; /* We are interested in just local symbols, not all symbols. */ if (bfd_get_flavour (sec->owner) == bfd_target_elf_flavour && (sec->owner->flags & DYNAMIC) == 0) { size_t sym_count; if (elf_bad_symtab (sec->owner)) sym_count = (elf_tdata (sec->owner)->symtab_hdr.sh_size / bed->s->sizeof_sym); else sym_count = elf_tdata (sec->owner)->symtab_hdr.sh_info; if (sym_count > max_sym_count) max_sym_count = sym_count; if (sym_count > max_sym_shndx_count && elf_symtab_shndx (sec->owner) != 0) max_sym_shndx_count = sym_count; if ((sec->flags & SEC_RELOC) != 0) { size_t ext_size; ext_size = elf_section_data (sec)->rel_hdr.sh_size; if (ext_size > max_external_reloc_size) max_external_reloc_size = ext_size; if (sec->reloc_count > max_internal_reloc_count) max_internal_reloc_count = sec->reloc_count; } } } if (reloc_count == 0) continue; o->reloc_count += reloc_count; /* MIPS may have a mix of REL and RELA relocs on sections. To support this curious ABI we keep reloc counts in elf_section_data too. We must be careful to add the relocations from the input section to the right output count. FIXME: Get rid of one count. We have o->reloc_count == esdo->rel_count + esdo->rel_count2. */ rel_count1 = &esdo->rel_count; if (esdi != NULL) { bfd_boolean same_size; bfd_size_type entsize1; entsize1 = esdi->rel_hdr.sh_entsize; BFD_ASSERT (entsize1 == bed->s->sizeof_rel || entsize1 == bed->s->sizeof_rela); same_size = !o->use_rela_p == (entsize1 == bed->s->sizeof_rel); if (!same_size) rel_count1 = &esdo->rel_count2; if (esdi->rel_hdr2 != NULL) { bfd_size_type entsize2 = esdi->rel_hdr2->sh_entsize; unsigned int alt_count; unsigned int *rel_count2; BFD_ASSERT (entsize2 != entsize1 && (entsize2 == bed->s->sizeof_rel || entsize2 == bed->s->sizeof_rela)); rel_count2 = &esdo->rel_count2; if (!same_size) rel_count2 = &esdo->rel_count; /* The following is probably too simplistic if the backend counts output relocs unusually. */ BFD_ASSERT (bed->elf_backend_count_relocs == NULL); alt_count = NUM_SHDR_ENTRIES (esdi->rel_hdr2); *rel_count2 += alt_count; reloc_count -= alt_count; } } *rel_count1 += reloc_count; } if (o->reloc_count > 0) o->flags |= SEC_RELOC; else { /* Explicitly clear the SEC_RELOC flag. The linker tends to set it (this is probably a bug) and if it is set assign_section_numbers will create a reloc section. */ o->flags &=~ SEC_RELOC; } /* If the SEC_ALLOC flag is not set, force the section VMA to zero. This is done in elf_fake_sections as well, but forcing the VMA to 0 here will ensure that relocs against these sections are handled correctly. */ if ((o->flags & SEC_ALLOC) == 0 && ! o->user_set_vma) o->vma = 0; } if (! info->relocatable && merged) elf_link_hash_traverse (elf_hash_table (info), _bfd_elf_link_sec_merge_syms, abfd); /* Figure out the file positions for everything but the symbol table and the relocs. We set symcount to force assign_section_numbers to create a symbol table. */ bfd_get_symcount (abfd) = info->strip == strip_all ? 0 : 1; BFD_ASSERT (! abfd->output_has_begun); if (! _bfd_elf_compute_section_file_positions (abfd, info)) goto error_return; /* Set sizes, and assign file positions for reloc sections. */ for (o = abfd->sections; o != NULL; o = o->next) { if ((o->flags & SEC_RELOC) != 0) { if (!(_bfd_elf_link_size_reloc_section (abfd, &elf_section_data (o)->rel_hdr, o))) goto error_return; if (elf_section_data (o)->rel_hdr2 && !(_bfd_elf_link_size_reloc_section (abfd, elf_section_data (o)->rel_hdr2, o))) goto error_return; } /* Now, reset REL_COUNT and REL_COUNT2 so that we can use them to count upwards while actually outputting the relocations. */ elf_section_data (o)->rel_count = 0; elf_section_data (o)->rel_count2 = 0; } _bfd_elf_assign_file_positions_for_relocs (abfd); /* We have now assigned file positions for all the sections except .symtab and .strtab. We start the .symtab section at the current file position, and write directly to it. We build the .strtab section in memory. */ bfd_get_symcount (abfd) = 0; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; /* sh_name is set in prep_headers. */ symtab_hdr->sh_type = SHT_SYMTAB; /* sh_flags, sh_addr and sh_size all start off zero. */ symtab_hdr->sh_entsize = bed->s->sizeof_sym; /* sh_link is set in assign_section_numbers. */ /* sh_info is set below. */ /* sh_offset is set just below. */ symtab_hdr->sh_addralign = 1 << bed->s->log_file_align; off = elf_tdata (abfd)->next_file_pos; off = _bfd_elf_assign_file_position_for_section (symtab_hdr, off, TRUE); /* Note that at this point elf_tdata (abfd)->next_file_pos is incorrect. We do not yet know the size of the .symtab section. We correct next_file_pos below, after we do know the size. */ /* Allocate a buffer to hold swapped out symbols. This is to avoid continuously seeking to the right position in the file. */ if (! info->keep_memory || max_sym_count < 20) finfo.symbuf_size = 20; else finfo.symbuf_size = max_sym_count; amt = finfo.symbuf_size; amt *= bed->s->sizeof_sym; finfo.symbuf = bfd_malloc (amt); if (finfo.symbuf == NULL) goto error_return; if (elf_numsections (abfd) > SHN_LORESERVE) { /* Wild guess at number of output symbols. realloc'd as needed. */ amt = 2 * max_sym_count + elf_numsections (abfd) + 1000; finfo.shndxbuf_size = amt; amt *= sizeof (Elf_External_Sym_Shndx); finfo.symshndxbuf = bfd_zmalloc (amt); if (finfo.symshndxbuf == NULL) goto error_return; } /* Start writing out the symbol table. The first symbol is always a dummy symbol. */ if (info->strip != strip_all || emit_relocs) { elfsym.st_value = 0; elfsym.st_size = 0; elfsym.st_info = 0; elfsym.st_other = 0; elfsym.st_shndx = SHN_UNDEF; if (! elf_link_output_sym (&finfo, NULL, &elfsym, bfd_und_section_ptr, NULL)) goto error_return; } /* Output a symbol for each section. We output these even if we are discarding local symbols, since they are used for relocs. These symbols have no names. We store the index of each one in the index field of the section, so that we can find it again when outputting relocs. */ if (info->strip != strip_all || emit_relocs) { elfsym.st_size = 0; elfsym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION); elfsym.st_other = 0; elfsym.st_value = 0; for (i = 1; i < elf_numsections (abfd); i++) { o = bfd_section_from_elf_index (abfd, i); if (o != NULL) { o->target_index = bfd_get_symcount (abfd); elfsym.st_shndx = i; if (!info->relocatable) elfsym.st_value = o->vma; if (!elf_link_output_sym (&finfo, NULL, &elfsym, o, NULL)) goto error_return; } if (i == SHN_LORESERVE - 1) i += SHN_HIRESERVE + 1 - SHN_LORESERVE; } } /* Allocate some memory to hold information read in from the input files. */ if (max_contents_size != 0) { finfo.contents = bfd_malloc (max_contents_size); if (finfo.contents == NULL) goto error_return; } if (max_external_reloc_size != 0) { finfo.external_relocs = bfd_malloc (max_external_reloc_size); if (finfo.external_relocs == NULL) goto error_return; } if (max_internal_reloc_count != 0) { amt = max_internal_reloc_count * bed->s->int_rels_per_ext_rel; amt *= sizeof (Elf_Internal_Rela); finfo.internal_relocs = bfd_malloc (amt); if (finfo.internal_relocs == NULL) goto error_return; } if (max_sym_count != 0) { amt = max_sym_count * bed->s->sizeof_sym; finfo.external_syms = bfd_malloc (amt); if (finfo.external_syms == NULL) goto error_return; amt = max_sym_count * sizeof (Elf_Internal_Sym); finfo.internal_syms = bfd_malloc (amt); if (finfo.internal_syms == NULL) goto error_return; amt = max_sym_count * sizeof (long); finfo.indices = bfd_malloc (amt); if (finfo.indices == NULL) goto error_return; amt = max_sym_count * sizeof (asection *); finfo.sections = bfd_malloc (amt); if (finfo.sections == NULL) goto error_return; } if (max_sym_shndx_count != 0) { amt = max_sym_shndx_count * sizeof (Elf_External_Sym_Shndx); finfo.locsym_shndx = bfd_malloc (amt); if (finfo.locsym_shndx == NULL) goto error_return; } if (elf_hash_table (info)->tls_sec) { bfd_vma base, end = 0; asection *sec; for (sec = elf_hash_table (info)->tls_sec; sec && (sec->flags & SEC_THREAD_LOCAL); sec = sec->next) { bfd_size_type size = sec->size; if (size == 0 && (sec->flags & SEC_HAS_CONTENTS) == 0) { struct bfd_link_order *o = sec->map_tail.link_order; if (o != NULL) size = o->offset + o->size; } end = sec->vma + size; } base = elf_hash_table (info)->tls_sec->vma; end = align_power (end, elf_hash_table (info)->tls_sec->alignment_power); elf_hash_table (info)->tls_size = end - base; } /* Reorder SHF_LINK_ORDER sections. */ for (o = abfd->sections; o != NULL; o = o->next) { if (!elf_fixup_link_order (abfd, o)) return FALSE; } /* Since ELF permits relocations to be against local symbols, we must have the local symbols available when we do the relocations. Since we would rather only read the local symbols once, and we would rather not keep them in memory, we handle all the relocations for a single input file at the same time. Unfortunately, there is no way to know the total number of local symbols until we have seen all of them, and the local symbol indices precede the global symbol indices. This means that when we are generating relocatable output, and we see a reloc against a global symbol, we can not know the symbol index until we have finished examining all the local symbols to see which ones we are going to output. To deal with this, we keep the relocations in memory, and don't output them until the end of the link. This is an unfortunate waste of memory, but I don't see a good way around it. Fortunately, it only happens when performing a relocatable link, which is not the common case. FIXME: If keep_memory is set we could write the relocs out and then read them again; I don't know how bad the memory loss will be. */ for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) sub->output_has_begun = FALSE; for (o = abfd->sections; o != NULL; o = o->next) { for (p = o->map_head.link_order; p != NULL; p = p->next) { if (p->type == bfd_indirect_link_order && (bfd_get_flavour ((sub = p->u.indirect.section->owner)) == bfd_target_elf_flavour) && elf_elfheader (sub)->e_ident[EI_CLASS] == bed->s->elfclass) { if (! sub->output_has_begun) { if (! elf_link_input_bfd (&finfo, sub)) goto error_return; sub->output_has_begun = TRUE; } } else if (p->type == bfd_section_reloc_link_order || p->type == bfd_symbol_reloc_link_order) { if (! elf_reloc_link_order (abfd, info, o, p)) goto error_return; } else { if (! _bfd_default_link_order (abfd, info, o, p)) goto error_return; } } } /* Free symbol buffer if needed. */ if (!info->reduce_memory_overheads) { for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) if (bfd_get_flavour (sub) == bfd_target_elf_flavour && elf_tdata (sub)->symbuf) { free (elf_tdata (sub)->symbuf); elf_tdata (sub)->symbuf = NULL; } } /* Output any global symbols that got converted to local in a version script or due to symbol visibility. We do this in a separate step since ELF requires all local symbols to appear prior to any global symbols. FIXME: We should only do this if some global symbols were, in fact, converted to become local. FIXME: Will this work correctly with the Irix 5 linker? */ eoinfo.failed = FALSE; eoinfo.finfo = &finfo; eoinfo.localsyms = TRUE; elf_link_hash_traverse (elf_hash_table (info), elf_link_output_extsym, &eoinfo); if (eoinfo.failed) return FALSE; /* If backend needs to output some local symbols not present in the hash table, do it now. */ if (bed->elf_backend_output_arch_local_syms) { typedef bfd_boolean (*out_sym_func) (void *, const char *, Elf_Internal_Sym *, asection *, struct elf_link_hash_entry *); if (! ((*bed->elf_backend_output_arch_local_syms) (abfd, info, &finfo, (out_sym_func) elf_link_output_sym))) return FALSE; } /* That wrote out all the local symbols. Finish up the symbol table with the global symbols. Even if we want to strip everything we can, we still need to deal with those global symbols that got converted to local in a version script. */ /* The sh_info field records the index of the first non local symbol. */ symtab_hdr->sh_info = bfd_get_symcount (abfd); if (dynamic && finfo.dynsym_sec->output_section != bfd_abs_section_ptr) { Elf_Internal_Sym sym; bfd_byte *dynsym = finfo.dynsym_sec->contents; long last_local = 0; /* Write out the section symbols for the output sections. */ if (info->shared || elf_hash_table (info)->is_relocatable_executable) { asection *s; sym.st_size = 0; sym.st_name = 0; sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION); sym.st_other = 0; for (s = abfd->sections; s != NULL; s = s->next) { int indx; bfd_byte *dest; long dynindx; dynindx = elf_section_data (s)->dynindx; if (dynindx <= 0) continue; indx = elf_section_data (s)->this_idx; BFD_ASSERT (indx > 0); sym.st_shndx = indx; if (! check_dynsym (abfd, &sym)) return FALSE; sym.st_value = s->vma; dest = dynsym + dynindx * bed->s->sizeof_sym; if (last_local < dynindx) last_local = dynindx; bed->s->swap_symbol_out (abfd, &sym, dest, 0); } } /* Write out the local dynsyms. */ if (elf_hash_table (info)->dynlocal) { struct elf_link_local_dynamic_entry *e; for (e = elf_hash_table (info)->dynlocal; e ; e = e->next) { asection *s; bfd_byte *dest; sym.st_size = e->isym.st_size; sym.st_other = e->isym.st_other; /* Copy the internal symbol as is. Note that we saved a word of storage and overwrote the original st_name with the dynstr_index. */ sym = e->isym; if (e->isym.st_shndx != SHN_UNDEF && (e->isym.st_shndx < SHN_LORESERVE || e->isym.st_shndx > SHN_HIRESERVE)) { s = bfd_section_from_elf_index (e->input_bfd, e->isym.st_shndx); sym.st_shndx = elf_section_data (s->output_section)->this_idx; if (! check_dynsym (abfd, &sym)) return FALSE; sym.st_value = (s->output_section->vma + s->output_offset + e->isym.st_value); } if (last_local < e->dynindx) last_local = e->dynindx; dest = dynsym + e->dynindx * bed->s->sizeof_sym; bed->s->swap_symbol_out (abfd, &sym, dest, 0); } } elf_section_data (finfo.dynsym_sec->output_section)->this_hdr.sh_info = last_local + 1; } /* We get the global symbols from the hash table. */ eoinfo.failed = FALSE; eoinfo.localsyms = FALSE; eoinfo.finfo = &finfo; elf_link_hash_traverse (elf_hash_table (info), elf_link_output_extsym, &eoinfo); if (eoinfo.failed) return FALSE; /* If backend needs to output some symbols not present in the hash table, do it now. */ if (bed->elf_backend_output_arch_syms) { typedef bfd_boolean (*out_sym_func) (void *, const char *, Elf_Internal_Sym *, asection *, struct elf_link_hash_entry *); if (! ((*bed->elf_backend_output_arch_syms) (abfd, info, &finfo, (out_sym_func) elf_link_output_sym))) return FALSE; } /* Flush all symbols to the file. */ if (! elf_link_flush_output_syms (&finfo, bed)) return FALSE; /* Now we know the size of the symtab section. */ off += symtab_hdr->sh_size; symtab_shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr; if (symtab_shndx_hdr->sh_name != 0) { symtab_shndx_hdr->sh_type = SHT_SYMTAB_SHNDX; symtab_shndx_hdr->sh_entsize = sizeof (Elf_External_Sym_Shndx); symtab_shndx_hdr->sh_addralign = sizeof (Elf_External_Sym_Shndx); amt = bfd_get_symcount (abfd) * sizeof (Elf_External_Sym_Shndx); symtab_shndx_hdr->sh_size = amt; off = _bfd_elf_assign_file_position_for_section (symtab_shndx_hdr, off, TRUE); if (bfd_seek (abfd, symtab_shndx_hdr->sh_offset, SEEK_SET) != 0 || (bfd_bwrite (finfo.symshndxbuf, amt, abfd) != amt)) return FALSE; } /* Finish up and write out the symbol string table (.strtab) section. */ symstrtab_hdr = &elf_tdata (abfd)->strtab_hdr; /* sh_name was set in prep_headers. */ symstrtab_hdr->sh_type = SHT_STRTAB; symstrtab_hdr->sh_flags = 0; symstrtab_hdr->sh_addr = 0; symstrtab_hdr->sh_size = _bfd_stringtab_size (finfo.symstrtab); symstrtab_hdr->sh_entsize = 0; symstrtab_hdr->sh_link = 0; symstrtab_hdr->sh_info = 0; /* sh_offset is set just below. */ symstrtab_hdr->sh_addralign = 1; off = _bfd_elf_assign_file_position_for_section (symstrtab_hdr, off, TRUE); elf_tdata (abfd)->next_file_pos = off; if (bfd_get_symcount (abfd) > 0) { if (bfd_seek (abfd, symstrtab_hdr->sh_offset, SEEK_SET) != 0 || ! _bfd_stringtab_emit (abfd, finfo.symstrtab)) return FALSE; } /* Adjust the relocs to have the correct symbol indices. */ for (o = abfd->sections; o != NULL; o = o->next) { if ((o->flags & SEC_RELOC) == 0) continue; elf_link_adjust_relocs (abfd, &elf_section_data (o)->rel_hdr, elf_section_data (o)->rel_count, elf_section_data (o)->rel_hashes); if (elf_section_data (o)->rel_hdr2 != NULL) elf_link_adjust_relocs (abfd, elf_section_data (o)->rel_hdr2, elf_section_data (o)->rel_count2, (elf_section_data (o)->rel_hashes + elf_section_data (o)->rel_count)); /* Set the reloc_count field to 0 to prevent write_relocs from trying to swap the relocs out itself. */ o->reloc_count = 0; } if (dynamic && info->combreloc && dynobj != NULL) relativecount = elf_link_sort_relocs (abfd, info, &reldyn); /* If we are linking against a dynamic object, or generating a shared library, finish up the dynamic linking information. */ if (dynamic) { bfd_byte *dyncon, *dynconend; /* Fix up .dynamic entries. */ o = bfd_get_section_by_name (dynobj, ".dynamic"); BFD_ASSERT (o != NULL); dyncon = o->contents; dynconend = o->contents + o->size; for (; dyncon < dynconend; dyncon += bed->s->sizeof_dyn) { Elf_Internal_Dyn dyn; const char *name; unsigned int type; bed->s->swap_dyn_in (dynobj, dyncon, &dyn); switch (dyn.d_tag) { default: continue; case DT_NULL: if (relativecount > 0 && dyncon + bed->s->sizeof_dyn < dynconend) { switch (elf_section_data (reldyn)->this_hdr.sh_type) { case SHT_REL: dyn.d_tag = DT_RELCOUNT; break; case SHT_RELA: dyn.d_tag = DT_RELACOUNT; break; default: continue; } dyn.d_un.d_val = relativecount; relativecount = 0; break; } continue; case DT_INIT: name = info->init_function; goto get_sym; case DT_FINI: name = info->fini_function; get_sym: { struct elf_link_hash_entry *h; h = elf_link_hash_lookup (elf_hash_table (info), name, FALSE, FALSE, TRUE); if (h != NULL && (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak)) { dyn.d_un.d_val = h->root.u.def.value; o = h->root.u.def.section; if (o->output_section != NULL) dyn.d_un.d_val += (o->output_section->vma + o->output_offset); else { /* The symbol is imported from another shared library and does not apply to this one. */ dyn.d_un.d_val = 0; } break; } } continue; case DT_PREINIT_ARRAYSZ: name = ".preinit_array"; goto get_size; case DT_INIT_ARRAYSZ: name = ".init_array"; goto get_size; case DT_FINI_ARRAYSZ: name = ".fini_array"; get_size: o = bfd_get_section_by_name (abfd, name); if (o == NULL) { (*_bfd_error_handler) (_("%B: could not find output section %s"), abfd, name); goto error_return; } if (o->size == 0) (*_bfd_error_handler) (_("warning: %s section has zero size"), name); dyn.d_un.d_val = o->size; break; case DT_PREINIT_ARRAY: name = ".preinit_array"; goto get_vma; case DT_INIT_ARRAY: name = ".init_array"; goto get_vma; case DT_FINI_ARRAY: name = ".fini_array"; goto get_vma; case DT_HASH: name = ".hash"; goto get_vma; case DT_GNU_HASH: name = ".gnu.hash"; goto get_vma; case DT_STRTAB: name = ".dynstr"; goto get_vma; case DT_SYMTAB: name = ".dynsym"; goto get_vma; case DT_VERDEF: name = ".gnu.version_d"; goto get_vma; case DT_VERNEED: name = ".gnu.version_r"; goto get_vma; case DT_VERSYM: name = ".gnu.version"; get_vma: o = bfd_get_section_by_name (abfd, name); if (o == NULL) { (*_bfd_error_handler) (_("%B: could not find output section %s"), abfd, name); goto error_return; } dyn.d_un.d_ptr = o->vma; break; case DT_REL: case DT_RELA: case DT_RELSZ: case DT_RELASZ: if (dyn.d_tag == DT_REL || dyn.d_tag == DT_RELSZ) type = SHT_REL; else type = SHT_RELA; dyn.d_un.d_val = 0; for (i = 1; i < elf_numsections (abfd); i++) { Elf_Internal_Shdr *hdr; hdr = elf_elfsections (abfd)[i]; if (hdr->sh_type == type && (hdr->sh_flags & SHF_ALLOC) != 0) { if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ) dyn.d_un.d_val += hdr->sh_size; else { if (dyn.d_un.d_val == 0 || hdr->sh_addr < dyn.d_un.d_val) dyn.d_un.d_val = hdr->sh_addr; } } } break; } bed->s->swap_dyn_out (dynobj, &dyn, dyncon); } } /* If we have created any dynamic sections, then output them. */ if (dynobj != NULL) { if (! (*bed->elf_backend_finish_dynamic_sections) (abfd, info)) goto error_return; /* Check for DT_TEXTREL (late, in case the backend removes it). */ if (info->warn_shared_textrel && info->shared) { bfd_byte *dyncon, *dynconend; /* Fix up .dynamic entries. */ o = bfd_get_section_by_name (dynobj, ".dynamic"); BFD_ASSERT (o != NULL); dyncon = o->contents; dynconend = o->contents + o->size; for (; dyncon < dynconend; dyncon += bed->s->sizeof_dyn) { Elf_Internal_Dyn dyn; bed->s->swap_dyn_in (dynobj, dyncon, &dyn); if (dyn.d_tag == DT_TEXTREL) { info->callbacks->einfo (_("%P: warning: creating a DT_TEXTREL in a shared object.\n")); break; } } } for (o = dynobj->sections; o != NULL; o = o->next) { if ((o->flags & SEC_HAS_CONTENTS) == 0 || o->size == 0 || o->output_section == bfd_abs_section_ptr) continue; if ((o->flags & SEC_LINKER_CREATED) == 0) { /* At this point, we are only interested in sections created by _bfd_elf_link_create_dynamic_sections. */ continue; } if (elf_hash_table (info)->stab_info.stabstr == o) continue; if (elf_hash_table (info)->eh_info.hdr_sec == o) continue; if ((elf_section_data (o->output_section)->this_hdr.sh_type != SHT_STRTAB) || strcmp (bfd_get_section_name (abfd, o), ".dynstr") != 0) { if (! bfd_set_section_contents (abfd, o->output_section, o->contents, (file_ptr) o->output_offset, o->size)) goto error_return; } else { /* The contents of the .dynstr section are actually in a stringtab. */ off = elf_section_data (o->output_section)->this_hdr.sh_offset; if (bfd_seek (abfd, off, SEEK_SET) != 0 || ! _bfd_elf_strtab_emit (abfd, elf_hash_table (info)->dynstr)) goto error_return; } } } if (info->relocatable) { bfd_boolean failed = FALSE; bfd_map_over_sections (abfd, bfd_elf_set_group_contents, &failed); if (failed) goto error_return; } /* If we have optimized stabs strings, output them. */ if (elf_hash_table (info)->stab_info.stabstr != NULL) { if (! _bfd_write_stab_strings (abfd, &elf_hash_table (info)->stab_info)) goto error_return; } if (info->eh_frame_hdr) { if (! _bfd_elf_write_section_eh_frame_hdr (abfd, info)) goto error_return; } if (finfo.symstrtab != NULL) _bfd_stringtab_free (finfo.symstrtab); if (finfo.contents != NULL) free (finfo.contents); if (finfo.external_relocs != NULL) free (finfo.external_relocs); if (finfo.internal_relocs != NULL) free (finfo.internal_relocs); if (finfo.external_syms != NULL) free (finfo.external_syms); if (finfo.locsym_shndx != NULL) free (finfo.locsym_shndx); if (finfo.internal_syms != NULL) free (finfo.internal_syms); if (finfo.indices != NULL) free (finfo.indices); if (finfo.sections != NULL) free (finfo.sections); if (finfo.symbuf != NULL) free (finfo.symbuf); if (finfo.symshndxbuf != NULL) free (finfo.symshndxbuf); for (o = abfd->sections; o != NULL; o = o->next) { if ((o->flags & SEC_RELOC) != 0 && elf_section_data (o)->rel_hashes != NULL) free (elf_section_data (o)->rel_hashes); } elf_tdata (abfd)->linker = TRUE; if (attr_section) { bfd_byte *contents = bfd_malloc (attr_size); if (contents == NULL) goto error_return; bfd_elf_set_obj_attr_contents (abfd, contents, attr_size); bfd_set_section_contents (abfd, attr_section, contents, 0, attr_size); free (contents); } return TRUE; error_return: if (finfo.symstrtab != NULL) _bfd_stringtab_free (finfo.symstrtab); if (finfo.contents != NULL) free (finfo.contents); if (finfo.external_relocs != NULL) free (finfo.external_relocs); if (finfo.internal_relocs != NULL) free (finfo.internal_relocs); if (finfo.external_syms != NULL) free (finfo.external_syms); if (finfo.locsym_shndx != NULL) free (finfo.locsym_shndx); if (finfo.internal_syms != NULL) free (finfo.internal_syms); if (finfo.indices != NULL) free (finfo.indices); if (finfo.sections != NULL) free (finfo.sections); if (finfo.symbuf != NULL) free (finfo.symbuf); if (finfo.symshndxbuf != NULL) free (finfo.symshndxbuf); for (o = abfd->sections; o != NULL; o = o->next) { if ((o->flags & SEC_RELOC) != 0 && elf_section_data (o)->rel_hashes != NULL) free (elf_section_data (o)->rel_hashes); } return FALSE; } /* Garbage collect unused sections. */ /* Default gc_mark_hook. */ asection * _bfd_elf_gc_mark_hook (asection *sec, struct bfd_link_info *info ATTRIBUTE_UNUSED, Elf_Internal_Rela *rel ATTRIBUTE_UNUSED, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) { if (h != NULL) { switch (h->root.type) { case bfd_link_hash_defined: case bfd_link_hash_defweak: return h->root.u.def.section; case bfd_link_hash_common: return h->root.u.c.p->section; default: break; } } else return bfd_section_from_elf_index (sec->owner, sym->st_shndx); return NULL; } /* The mark phase of garbage collection. For a given section, mark it and any sections in this section's group, and all the sections which define symbols to which it refers. */ bfd_boolean _bfd_elf_gc_mark (struct bfd_link_info *info, asection *sec, elf_gc_mark_hook_fn gc_mark_hook) { bfd_boolean ret; bfd_boolean is_eh; asection *group_sec; sec->gc_mark = 1; /* Mark all the sections in the group. */ group_sec = elf_section_data (sec)->next_in_group; if (group_sec && !group_sec->gc_mark) if (!_bfd_elf_gc_mark (info, group_sec, gc_mark_hook)) return FALSE; /* Look through the section relocs. */ ret = TRUE; is_eh = strcmp (sec->name, ".eh_frame") == 0; if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0) { Elf_Internal_Rela *relstart, *rel, *relend; Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; size_t nlocsyms; size_t extsymoff; bfd *input_bfd = sec->owner; const struct elf_backend_data *bed = get_elf_backend_data (input_bfd); Elf_Internal_Sym *isym = NULL; int r_sym_shift; symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; sym_hashes = elf_sym_hashes (input_bfd); /* Read the local symbols. */ if (elf_bad_symtab (input_bfd)) { nlocsyms = symtab_hdr->sh_size / bed->s->sizeof_sym; extsymoff = 0; } else extsymoff = nlocsyms = symtab_hdr->sh_info; isym = (Elf_Internal_Sym *) symtab_hdr->contents; if (isym == NULL && nlocsyms != 0) { isym = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, nlocsyms, 0, NULL, NULL, NULL); if (isym == NULL) return FALSE; } /* Read the relocations. */ relstart = _bfd_elf_link_read_relocs (input_bfd, sec, NULL, NULL, info->keep_memory); if (relstart == NULL) { ret = FALSE; goto out1; } relend = relstart + sec->reloc_count * bed->s->int_rels_per_ext_rel; if (bed->s->arch_size == 32) r_sym_shift = 8; else r_sym_shift = 32; for (rel = relstart; rel < relend; rel++) { unsigned long r_symndx; asection *rsec; struct elf_link_hash_entry *h; r_symndx = rel->r_info >> r_sym_shift; if (r_symndx == 0) continue; if (r_symndx >= nlocsyms || ELF_ST_BIND (isym[r_symndx].st_info) != STB_LOCAL) { h = sym_hashes[r_symndx - extsymoff]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; rsec = (*gc_mark_hook) (sec, info, rel, h, NULL); } else { rsec = (*gc_mark_hook) (sec, info, rel, NULL, &isym[r_symndx]); } if (rsec && !rsec->gc_mark) { if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour) rsec->gc_mark = 1; else if (is_eh) rsec->gc_mark_from_eh = 1; else if (!_bfd_elf_gc_mark (info, rsec, gc_mark_hook)) { ret = FALSE; goto out2; } } } out2: if (elf_section_data (sec)->relocs != relstart) free (relstart); out1: if (isym != NULL && symtab_hdr->contents != (unsigned char *) isym) { if (! info->keep_memory) free (isym); else symtab_hdr->contents = (unsigned char *) isym; } } return ret; } /* Sweep symbols in swept sections. Called via elf_link_hash_traverse. */ struct elf_gc_sweep_symbol_info { struct bfd_link_info *info; void (*hide_symbol) (struct bfd_link_info *, struct elf_link_hash_entry *, bfd_boolean); }; static bfd_boolean elf_gc_sweep_symbol (struct elf_link_hash_entry *h, void *data) { if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if ((h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && !h->root.u.def.section->gc_mark && !(h->root.u.def.section->owner->flags & DYNAMIC)) { struct elf_gc_sweep_symbol_info *inf = data; (*inf->hide_symbol) (inf->info, h, TRUE); } return TRUE; } /* The sweep phase of garbage collection. Remove all garbage sections. */ typedef bfd_boolean (*gc_sweep_hook_fn) (bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *); static bfd_boolean elf_gc_sweep (bfd *abfd, struct bfd_link_info *info) { bfd *sub; const struct elf_backend_data *bed = get_elf_backend_data (abfd); gc_sweep_hook_fn gc_sweep_hook = bed->gc_sweep_hook; unsigned long section_sym_count; struct elf_gc_sweep_symbol_info sweep_info; for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) { asection *o; if (bfd_get_flavour (sub) != bfd_target_elf_flavour) continue; for (o = sub->sections; o != NULL; o = o->next) { /* Keep debug and special sections. */ if ((o->flags & (SEC_DEBUGGING | SEC_LINKER_CREATED)) != 0 || elf_section_data (o)->this_hdr.sh_type == SHT_NOTE || (o->flags & (SEC_ALLOC | SEC_LOAD | SEC_RELOC)) == 0) o->gc_mark = 1; if (o->gc_mark) continue; /* Skip sweeping sections already excluded. */ if (o->flags & SEC_EXCLUDE) continue; /* Since this is early in the link process, it is simple to remove a section from the output. */ o->flags |= SEC_EXCLUDE; if (info->print_gc_sections && o->size != 0) _bfd_error_handler (_("Removing unused section '%s' in file '%B'"), sub, o->name); /* But we also have to update some of the relocation info we collected before. */ if (gc_sweep_hook && (o->flags & SEC_RELOC) != 0 && o->reloc_count > 0 && !bfd_is_abs_section (o->output_section)) { Elf_Internal_Rela *internal_relocs; bfd_boolean r; internal_relocs = _bfd_elf_link_read_relocs (o->owner, o, NULL, NULL, info->keep_memory); if (internal_relocs == NULL) return FALSE; r = (*gc_sweep_hook) (o->owner, info, o, internal_relocs); if (elf_section_data (o)->relocs != internal_relocs) free (internal_relocs); if (!r) return FALSE; } } } /* Remove the symbols that were in the swept sections from the dynamic symbol table. GCFIXME: Anyone know how to get them out of the static symbol table as well? */ sweep_info.info = info; sweep_info.hide_symbol = bed->elf_backend_hide_symbol; elf_link_hash_traverse (elf_hash_table (info), elf_gc_sweep_symbol, &sweep_info); _bfd_elf_link_renumber_dynsyms (abfd, info, §ion_sym_count); return TRUE; } /* Propagate collected vtable information. This is called through elf_link_hash_traverse. */ static bfd_boolean elf_gc_propagate_vtable_entries_used (struct elf_link_hash_entry *h, void *okp) { if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* Those that are not vtables. */ if (h->vtable == NULL || h->vtable->parent == NULL) return TRUE; /* Those vtables that do not have parents, we cannot merge. */ if (h->vtable->parent == (struct elf_link_hash_entry *) -1) return TRUE; /* If we've already been done, exit. */ if (h->vtable->used && h->vtable->used[-1]) return TRUE; /* Make sure the parent's table is up to date. */ elf_gc_propagate_vtable_entries_used (h->vtable->parent, okp); if (h->vtable->used == NULL) { /* None of this table's entries were referenced. Re-use the parent's table. */ h->vtable->used = h->vtable->parent->vtable->used; h->vtable->size = h->vtable->parent->vtable->size; } else { size_t n; bfd_boolean *cu, *pu; /* Or the parent's entries into ours. */ cu = h->vtable->used; cu[-1] = TRUE; pu = h->vtable->parent->vtable->used; if (pu != NULL) { const struct elf_backend_data *bed; unsigned int log_file_align; bed = get_elf_backend_data (h->root.u.def.section->owner); log_file_align = bed->s->log_file_align; n = h->vtable->parent->vtable->size >> log_file_align; while (n--) { if (*pu) *cu = TRUE; pu++; cu++; } } } return TRUE; } static bfd_boolean elf_gc_smash_unused_vtentry_relocs (struct elf_link_hash_entry *h, void *okp) { asection *sec; bfd_vma hstart, hend; Elf_Internal_Rela *relstart, *relend, *rel; const struct elf_backend_data *bed; unsigned int log_file_align; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; /* Take care of both those symbols that do not describe vtables as well as those that are not loaded. */ if (h->vtable == NULL || h->vtable->parent == NULL) return TRUE; BFD_ASSERT (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak); sec = h->root.u.def.section; hstart = h->root.u.def.value; hend = hstart + h->size; relstart = _bfd_elf_link_read_relocs (sec->owner, sec, NULL, NULL, TRUE); if (!relstart) return *(bfd_boolean *) okp = FALSE; bed = get_elf_backend_data (sec->owner); log_file_align = bed->s->log_file_align; relend = relstart + sec->reloc_count * bed->s->int_rels_per_ext_rel; for (rel = relstart; rel < relend; ++rel) if (rel->r_offset >= hstart && rel->r_offset < hend) { /* If the entry is in use, do nothing. */ if (h->vtable->used && (rel->r_offset - hstart) < h->vtable->size) { bfd_vma entry = (rel->r_offset - hstart) >> log_file_align; if (h->vtable->used[entry]) continue; } /* Otherwise, kill it. */ rel->r_offset = rel->r_info = rel->r_addend = 0; } return TRUE; } /* Mark sections containing dynamically referenced symbols. When building shared libraries, we must assume that any visible symbol is referenced. */ bfd_boolean bfd_elf_gc_mark_dynamic_ref_symbol (struct elf_link_hash_entry *h, void *inf) { struct bfd_link_info *info = (struct bfd_link_info *) inf; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if ((h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && (h->ref_dynamic || (!info->executable && h->def_regular && ELF_ST_VISIBILITY (h->other) != STV_INTERNAL && ELF_ST_VISIBILITY (h->other) != STV_HIDDEN))) h->root.u.def.section->flags |= SEC_KEEP; return TRUE; } /* Do mark and sweep of unused sections. */ bfd_boolean bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info) { bfd_boolean ok = TRUE; bfd *sub; elf_gc_mark_hook_fn gc_mark_hook; const struct elf_backend_data *bed = get_elf_backend_data (abfd); if (!bed->can_gc_sections || info->relocatable || info->emitrelocations || !is_elf_hash_table (info->hash)) { (*_bfd_error_handler)(_("Warning: gc-sections option ignored")); return TRUE; } /* Apply transitive closure to the vtable entry usage info. */ elf_link_hash_traverse (elf_hash_table (info), elf_gc_propagate_vtable_entries_used, &ok); if (!ok) return FALSE; /* Kill the vtable relocations that were not used. */ elf_link_hash_traverse (elf_hash_table (info), elf_gc_smash_unused_vtentry_relocs, &ok); if (!ok) return FALSE; /* Mark dynamically referenced symbols. */ if (elf_hash_table (info)->dynamic_sections_created) elf_link_hash_traverse (elf_hash_table (info), bed->gc_mark_dynamic_ref, info); /* Grovel through relocs to find out who stays ... */ gc_mark_hook = bed->gc_mark_hook; for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) { asection *o; if (bfd_get_flavour (sub) != bfd_target_elf_flavour) continue; for (o = sub->sections; o != NULL; o = o->next) if ((o->flags & (SEC_EXCLUDE | SEC_KEEP)) == SEC_KEEP && !o->gc_mark) if (!_bfd_elf_gc_mark (info, o, gc_mark_hook)) return FALSE; } /* Allow the backend to mark additional target specific sections. */ if (bed->gc_mark_extra_sections) bed->gc_mark_extra_sections(info, gc_mark_hook); /* ... again for sections marked from eh_frame. */ for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) { asection *o; if (bfd_get_flavour (sub) != bfd_target_elf_flavour) continue; /* Keep .gcc_except_table.* if the associated .text.* (or the associated .gnu.linkonce.t.* if .text.* doesn't exist) is marked. This isn't very nice, but the proper solution, splitting .eh_frame up and using comdat doesn't pan out easily due to needing special relocs to handle the difference of two symbols in separate sections. Don't keep code sections referenced by .eh_frame. */ #define TEXT_PREFIX ".text." #define TEXT_PREFIX2 ".gnu.linkonce.t." #define GCC_EXCEPT_TABLE_PREFIX ".gcc_except_table." for (o = sub->sections; o != NULL; o = o->next) if (!o->gc_mark && o->gc_mark_from_eh && (o->flags & SEC_CODE) == 0) { if (CONST_STRNEQ (o->name, GCC_EXCEPT_TABLE_PREFIX)) { char *fn_name; const char *sec_name; asection *fn_text; unsigned o_name_prefix_len , fn_name_prefix_len, tmp; o_name_prefix_len = strlen (GCC_EXCEPT_TABLE_PREFIX); sec_name = o->name + o_name_prefix_len; fn_name_prefix_len = strlen (TEXT_PREFIX); tmp = strlen (TEXT_PREFIX2); if (tmp > fn_name_prefix_len) fn_name_prefix_len = tmp; fn_name = bfd_malloc (fn_name_prefix_len + strlen (sec_name) + 1); if (fn_name == NULL) return FALSE; /* Try the first prefix. */ sprintf (fn_name, "%s%s", TEXT_PREFIX, sec_name); fn_text = bfd_get_section_by_name (sub, fn_name); /* Try the second prefix. */ if (fn_text == NULL) { sprintf (fn_name, "%s%s", TEXT_PREFIX2, sec_name); fn_text = bfd_get_section_by_name (sub, fn_name); } free (fn_name); if (fn_text == NULL || !fn_text->gc_mark) continue; } /* If not using specially named exception table section, then keep whatever we are using. */ if (!_bfd_elf_gc_mark (info, o, gc_mark_hook)) return FALSE; } } /* ... and mark SEC_EXCLUDE for those that go. */ return elf_gc_sweep (abfd, info); } /* Called from check_relocs to record the existence of a VTINHERIT reloc. */ bfd_boolean bfd_elf_gc_record_vtinherit (bfd *abfd, asection *sec, struct elf_link_hash_entry *h, bfd_vma offset) { struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; struct elf_link_hash_entry **search, *child; bfd_size_type extsymcount; const struct elf_backend_data *bed = get_elf_backend_data (abfd); /* The sh_info field of the symtab header tells us where the external symbols start. We don't care about the local symbols at this point. */ extsymcount = elf_tdata (abfd)->symtab_hdr.sh_size / bed->s->sizeof_sym; if (!elf_bad_symtab (abfd)) extsymcount -= elf_tdata (abfd)->symtab_hdr.sh_info; sym_hashes = elf_sym_hashes (abfd); sym_hashes_end = sym_hashes + extsymcount; /* Hunt down the child symbol, which is in this section at the same offset as the relocation. */ for (search = sym_hashes; search != sym_hashes_end; ++search) { if ((child = *search) != NULL && (child->root.type == bfd_link_hash_defined || child->root.type == bfd_link_hash_defweak) && child->root.u.def.section == sec && child->root.u.def.value == offset) goto win; } (*_bfd_error_handler) ("%B: %A+%lu: No symbol found for INHERIT", abfd, sec, (unsigned long) offset); bfd_set_error (bfd_error_invalid_operation); return FALSE; win: if (!child->vtable) { child->vtable = bfd_zalloc (abfd, sizeof (*child->vtable)); if (!child->vtable) return FALSE; } if (!h) { /* This *should* only be the absolute section. It could potentially be that someone has defined a non-global vtable though, which would be bad. It isn't worth paging in the local symbols to be sure though; that case should simply be handled by the assembler. */ child->vtable->parent = (struct elf_link_hash_entry *) -1; } else child->vtable->parent = h; return TRUE; } /* Called from check_relocs to record the existence of a VTENTRY reloc. */ bfd_boolean bfd_elf_gc_record_vtentry (bfd *abfd ATTRIBUTE_UNUSED, asection *sec ATTRIBUTE_UNUSED, struct elf_link_hash_entry *h, bfd_vma addend) { const struct elf_backend_data *bed = get_elf_backend_data (abfd); unsigned int log_file_align = bed->s->log_file_align; if (!h->vtable) { h->vtable = bfd_zalloc (abfd, sizeof (*h->vtable)); if (!h->vtable) return FALSE; } if (addend >= h->vtable->size) { size_t size, bytes, file_align; bfd_boolean *ptr = h->vtable->used; /* While the symbol is undefined, we have to be prepared to handle a zero size. */ file_align = 1 << log_file_align; if (h->root.type == bfd_link_hash_undefined) size = addend + file_align; else { size = h->size; if (addend >= size) { /* Oops! We've got a reference past the defined end of the table. This is probably a bug -- shall we warn? */ size = addend + file_align; } } size = (size + file_align - 1) & -file_align; /* Allocate one extra entry for use as a "done" flag for the consolidation pass. */ bytes = ((size >> log_file_align) + 1) * sizeof (bfd_boolean); if (ptr) { ptr = bfd_realloc (ptr - 1, bytes); if (ptr != NULL) { size_t oldbytes; oldbytes = (((h->vtable->size >> log_file_align) + 1) * sizeof (bfd_boolean)); memset (((char *) ptr) + oldbytes, 0, bytes - oldbytes); } } else ptr = bfd_zmalloc (bytes); if (ptr == NULL) return FALSE; /* And arrange for that done flag to be at index -1. */ h->vtable->used = ptr + 1; h->vtable->size = size; } h->vtable->used[addend >> log_file_align] = TRUE; return TRUE; } struct alloc_got_off_arg { bfd_vma gotoff; unsigned int got_elt_size; }; /* We need a special top-level link routine to convert got reference counts to real got offsets. */ static bfd_boolean elf_gc_allocate_got_offsets (struct elf_link_hash_entry *h, void *arg) { struct alloc_got_off_arg *gofarg = arg; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->got.refcount > 0) { h->got.offset = gofarg->gotoff; gofarg->gotoff += gofarg->got_elt_size; } else h->got.offset = (bfd_vma) -1; return TRUE; } /* And an accompanying bit to work out final got entry offsets once we're done. Should be called from final_link. */ bfd_boolean bfd_elf_gc_common_finalize_got_offsets (bfd *abfd, struct bfd_link_info *info) { bfd *i; const struct elf_backend_data *bed = get_elf_backend_data (abfd); bfd_vma gotoff; unsigned int got_elt_size = bed->s->arch_size / 8; struct alloc_got_off_arg gofarg; if (! is_elf_hash_table (info->hash)) return FALSE; /* The GOT offset is relative to the .got section, but the GOT header is put into the .got.plt section, if the backend uses it. */ if (bed->want_got_plt) gotoff = 0; else gotoff = bed->got_header_size; /* Do the local .got entries first. */ for (i = info->input_bfds; i; i = i->link_next) { bfd_signed_vma *local_got; bfd_size_type j, locsymcount; Elf_Internal_Shdr *symtab_hdr; if (bfd_get_flavour (i) != bfd_target_elf_flavour) continue; local_got = elf_local_got_refcounts (i); if (!local_got) continue; symtab_hdr = &elf_tdata (i)->symtab_hdr; if (elf_bad_symtab (i)) locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym; else locsymcount = symtab_hdr->sh_info; for (j = 0; j < locsymcount; ++j) { if (local_got[j] > 0) { local_got[j] = gotoff; gotoff += got_elt_size; } else local_got[j] = (bfd_vma) -1; } } /* Then the global .got entries. .plt refcounts are handled by adjust_dynamic_symbol */ gofarg.gotoff = gotoff; gofarg.got_elt_size = got_elt_size; elf_link_hash_traverse (elf_hash_table (info), elf_gc_allocate_got_offsets, &gofarg); return TRUE; } /* Many folk need no more in the way of final link than this, once got entry reference counting is enabled. */ bfd_boolean bfd_elf_gc_common_final_link (bfd *abfd, struct bfd_link_info *info) { if (!bfd_elf_gc_common_finalize_got_offsets (abfd, info)) return FALSE; /* Invoke the regular ELF backend linker to do all the work. */ return bfd_elf_final_link (abfd, info); } bfd_boolean bfd_elf_reloc_symbol_deleted_p (bfd_vma offset, void *cookie) { struct elf_reloc_cookie *rcookie = cookie; if (rcookie->bad_symtab) rcookie->rel = rcookie->rels; for (; rcookie->rel < rcookie->relend; rcookie->rel++) { unsigned long r_symndx; if (! rcookie->bad_symtab) if (rcookie->rel->r_offset > offset) return FALSE; if (rcookie->rel->r_offset != offset) continue; r_symndx = rcookie->rel->r_info >> rcookie->r_sym_shift; if (r_symndx == SHN_UNDEF) return TRUE; if (r_symndx >= rcookie->locsymcount || ELF_ST_BIND (rcookie->locsyms[r_symndx].st_info) != STB_LOCAL) { struct elf_link_hash_entry *h; h = rcookie->sym_hashes[r_symndx - rcookie->extsymoff]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; if ((h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && elf_discarded_section (h->root.u.def.section)) return TRUE; else return FALSE; } else { /* It's not a relocation against a global symbol, but it could be a relocation against a local symbol for a discarded section. */ asection *isec; Elf_Internal_Sym *isym; /* Need to: get the symbol; get the section. */ isym = &rcookie->locsyms[r_symndx]; if (isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE) { isec = bfd_section_from_elf_index (rcookie->abfd, isym->st_shndx); if (isec != NULL && elf_discarded_section (isec)) return TRUE; } } return FALSE; } return FALSE; } /* Discard unneeded references to discarded sections. Returns TRUE if any section's size was changed. */ /* This function assumes that the relocations are in sorted order, which is true for all known assemblers. */ bfd_boolean bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info) { struct elf_reloc_cookie cookie; asection *stab, *eh; Elf_Internal_Shdr *symtab_hdr; const struct elf_backend_data *bed; bfd *abfd; unsigned int count; bfd_boolean ret = FALSE; if (info->traditional_format || !is_elf_hash_table (info->hash)) return FALSE; for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next) { if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) continue; bed = get_elf_backend_data (abfd); if ((abfd->flags & DYNAMIC) != 0) continue; eh = NULL; if (!info->relocatable) { eh = bfd_get_section_by_name (abfd, ".eh_frame"); if (eh != NULL && (eh->size == 0 || bfd_is_abs_section (eh->output_section))) eh = NULL; } stab = bfd_get_section_by_name (abfd, ".stab"); if (stab != NULL && (stab->size == 0 || bfd_is_abs_section (stab->output_section) || stab->sec_info_type != ELF_INFO_TYPE_STABS)) stab = NULL; if (stab == NULL && eh == NULL && bed->elf_backend_discard_info == NULL) continue; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; cookie.abfd = abfd; cookie.sym_hashes = elf_sym_hashes (abfd); cookie.bad_symtab = elf_bad_symtab (abfd); if (cookie.bad_symtab) { cookie.locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym; cookie.extsymoff = 0; } else { cookie.locsymcount = symtab_hdr->sh_info; cookie.extsymoff = symtab_hdr->sh_info; } if (bed->s->arch_size == 32) cookie.r_sym_shift = 8; else cookie.r_sym_shift = 32; cookie.locsyms = (Elf_Internal_Sym *) symtab_hdr->contents; if (cookie.locsyms == NULL && cookie.locsymcount != 0) { cookie.locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr, cookie.locsymcount, 0, NULL, NULL, NULL); if (cookie.locsyms == NULL) { info->callbacks->einfo (_("%P%X: can not read symbols: %E\n")); return FALSE; } } if (stab != NULL) { cookie.rels = NULL; count = stab->reloc_count; if (count != 0) cookie.rels = _bfd_elf_link_read_relocs (abfd, stab, NULL, NULL, info->keep_memory); if (cookie.rels != NULL) { cookie.rel = cookie.rels; cookie.relend = cookie.rels; cookie.relend += count * bed->s->int_rels_per_ext_rel; if (_bfd_discard_section_stabs (abfd, stab, elf_section_data (stab)->sec_info, bfd_elf_reloc_symbol_deleted_p, &cookie)) ret = TRUE; if (elf_section_data (stab)->relocs != cookie.rels) free (cookie.rels); } } if (eh != NULL) { cookie.rels = NULL; count = eh->reloc_count; if (count != 0) cookie.rels = _bfd_elf_link_read_relocs (abfd, eh, NULL, NULL, info->keep_memory); cookie.rel = cookie.rels; cookie.relend = cookie.rels; if (cookie.rels != NULL) cookie.relend += count * bed->s->int_rels_per_ext_rel; if (_bfd_elf_discard_section_eh_frame (abfd, info, eh, bfd_elf_reloc_symbol_deleted_p, &cookie)) ret = TRUE; if (cookie.rels != NULL && elf_section_data (eh)->relocs != cookie.rels) free (cookie.rels); } if (bed->elf_backend_discard_info != NULL && (*bed->elf_backend_discard_info) (abfd, &cookie, info)) ret = TRUE; if (cookie.locsyms != NULL && symtab_hdr->contents != (unsigned char *) cookie.locsyms) { if (! info->keep_memory) free (cookie.locsyms); else symtab_hdr->contents = (unsigned char *) cookie.locsyms; } } if (info->eh_frame_hdr && !info->relocatable && _bfd_elf_discard_section_eh_frame_hdr (output_bfd, info)) ret = TRUE; return ret; } void _bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec, struct bfd_link_info *info) { flagword flags; const char *name, *p; struct bfd_section_already_linked *l; struct bfd_section_already_linked_hash_entry *already_linked_list; if (sec->output_section == bfd_abs_section_ptr) return; flags = sec->flags; /* Return if it isn't a linkonce section. A comdat group section also has SEC_LINK_ONCE set. */ if ((flags & SEC_LINK_ONCE) == 0) return; /* Don't put group member sections on our list of already linked sections. They are handled as a group via their group section. */ if (elf_sec_group (sec) != NULL) return; /* FIXME: When doing a relocatable link, we may have trouble copying relocations in other sections that refer to local symbols in the section being discarded. Those relocations will have to be converted somehow; as of this writing I'm not sure that any of the backends handle that correctly. It is tempting to instead not discard link once sections when doing a relocatable link (technically, they should be discarded whenever we are building constructors). However, that fails, because the linker winds up combining all the link once sections into a single large link once section, which defeats the purpose of having link once sections in the first place. Also, not merging link once sections in a relocatable link causes trouble for MIPS ELF, which relies on link once semantics to handle the .reginfo section correctly. */ name = bfd_get_section_name (abfd, sec); if (CONST_STRNEQ (name, ".gnu.linkonce.") && (p = strchr (name + sizeof (".gnu.linkonce.") - 1, '.')) != NULL) p++; else p = name; already_linked_list = bfd_section_already_linked_table_lookup (p); for (l = already_linked_list->entry; l != NULL; l = l->next) { /* We may have 2 different types of sections on the list: group sections and linkonce sections. Match like sections. */ if ((flags & SEC_GROUP) == (l->sec->flags & SEC_GROUP) && strcmp (name, l->sec->name) == 0 && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL) { /* The section has already been linked. See if we should issue a warning. */ switch (flags & SEC_LINK_DUPLICATES) { default: abort (); case SEC_LINK_DUPLICATES_DISCARD: break; case SEC_LINK_DUPLICATES_ONE_ONLY: (*_bfd_error_handler) (_("%B: ignoring duplicate section `%A'"), abfd, sec); break; case SEC_LINK_DUPLICATES_SAME_SIZE: if (sec->size != l->sec->size) (*_bfd_error_handler) (_("%B: duplicate section `%A' has different size"), abfd, sec); break; case SEC_LINK_DUPLICATES_SAME_CONTENTS: if (sec->size != l->sec->size) (*_bfd_error_handler) (_("%B: duplicate section `%A' has different size"), abfd, sec); else if (sec->size != 0) { bfd_byte *sec_contents, *l_sec_contents; if (!bfd_malloc_and_get_section (abfd, sec, &sec_contents)) (*_bfd_error_handler) (_("%B: warning: could not read contents of section `%A'"), abfd, sec); else if (!bfd_malloc_and_get_section (l->sec->owner, l->sec, &l_sec_contents)) (*_bfd_error_handler) (_("%B: warning: could not read contents of section `%A'"), l->sec->owner, l->sec); else if (memcmp (sec_contents, l_sec_contents, sec->size) != 0) (*_bfd_error_handler) (_("%B: warning: duplicate section `%A' has different contents"), abfd, sec); if (sec_contents) free (sec_contents); if (l_sec_contents) free (l_sec_contents); } break; } /* Set the output_section field so that lang_add_section does not create a lang_input_section structure for this section. Since there might be a symbol in the section being discarded, we must retain a pointer to the section which we are really going to use. */ sec->output_section = bfd_abs_section_ptr; sec->kept_section = l->sec; if (flags & SEC_GROUP) { asection *first = elf_next_in_group (sec); asection *s = first; while (s != NULL) { s->output_section = bfd_abs_section_ptr; /* Record which group discards it. */ s->kept_section = l->sec; s = elf_next_in_group (s); /* These lists are circular. */ if (s == first) break; } } return; } } /* A single member comdat group section may be discarded by a linkonce section and vice versa. */ if ((flags & SEC_GROUP) != 0) { asection *first = elf_next_in_group (sec); if (first != NULL && elf_next_in_group (first) == first) /* Check this single member group against linkonce sections. */ for (l = already_linked_list->entry; l != NULL; l = l->next) if ((l->sec->flags & SEC_GROUP) == 0 && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL && bfd_elf_match_symbols_in_sections (l->sec, first, info)) { first->output_section = bfd_abs_section_ptr; first->kept_section = l->sec; sec->output_section = bfd_abs_section_ptr; break; } } else /* Check this linkonce section against single member groups. */ for (l = already_linked_list->entry; l != NULL; l = l->next) if (l->sec->flags & SEC_GROUP) { asection *first = elf_next_in_group (l->sec); if (first != NULL && elf_next_in_group (first) == first && bfd_elf_match_symbols_in_sections (first, sec, info)) { sec->output_section = bfd_abs_section_ptr; sec->kept_section = first; break; } } /* This is the first section with this name. Record it. */ bfd_section_already_linked_table_insert (already_linked_list, sec); } bfd_boolean _bfd_elf_common_definition (Elf_Internal_Sym *sym) { return sym->st_shndx == SHN_COMMON; } unsigned int _bfd_elf_common_section_index (asection *sec ATTRIBUTE_UNUSED) { return SHN_COMMON; } asection * _bfd_elf_common_section (asection *sec ATTRIBUTE_UNUSED) { return bfd_com_section_ptr; } Index: projects/clang350-import/contrib/binutils/bfd/po/bfd.pot =================================================================== --- projects/clang350-import/contrib/binutils/bfd/po/bfd.pot (revision 275386) +++ projects/clang350-import/contrib/binutils/bfd/po/bfd.pot (revision 275387) @@ -1,3724 +1,3724 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2007-07-02 16:16+0930\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: aout-adobe.c:127 msgid "%B: Unknown section type in a.out.adobe file: %x\n" msgstr "" #: aout-cris.c:202 #, c-format msgid "%s: Invalid relocation type exported: %d" msgstr "" #: aout-cris.c:245 msgid "%B: Invalid relocation type imported: %d" msgstr "" #: aout-cris.c:256 msgid "%B: Bad relocation record imported: %d" msgstr "" #: aoutx.h:1267 aoutx.h:1601 #, c-format msgid "%s: can not represent section `%s' in a.out object file format" msgstr "" #: aoutx.h:1567 #, c-format msgid "" "%s: can not represent section for symbol `%s' in a.out object file format" msgstr "" #: aoutx.h:1569 msgid "*unknown*" msgstr "" #: aoutx.h:5311 #, c-format msgid "%s: relocatable link from %s to %s not supported" msgstr "" #: archive.c:1764 msgid "Warning: writing archive was slow: rewriting timestamp\n" msgstr "" #: archive.c:2027 msgid "Reading archive file mod timestamp" msgstr "" #: archive.c:2051 msgid "Writing updated armap timestamp" msgstr "" #: bfd.c:288 msgid "No error" msgstr "" #: bfd.c:289 msgid "System call error" msgstr "" #: bfd.c:290 msgid "Invalid bfd target" msgstr "" #: bfd.c:291 msgid "File in wrong format" msgstr "" #: bfd.c:292 msgid "Archive object file in wrong format" msgstr "" #: bfd.c:293 msgid "Invalid operation" msgstr "" #: bfd.c:294 msgid "Memory exhausted" msgstr "" #: bfd.c:295 msgid "No symbols" msgstr "" #: bfd.c:296 msgid "Archive has no index; run ranlib to add one" msgstr "" #: bfd.c:297 msgid "No more archived files" msgstr "" #: bfd.c:298 msgid "Malformed archive" msgstr "" #: bfd.c:299 msgid "File format not recognized" msgstr "" #: bfd.c:300 msgid "File format is ambiguous" msgstr "" #: bfd.c:301 msgid "Section has no contents" msgstr "" #: bfd.c:302 msgid "Nonrepresentable section on output" msgstr "" #: bfd.c:303 msgid "Symbol needs debug section which does not exist" msgstr "" #: bfd.c:304 msgid "Bad value" msgstr "" #: bfd.c:305 msgid "File truncated" msgstr "" #: bfd.c:306 msgid "File too big" msgstr "" #: bfd.c:307 #, c-format msgid "Error reading %s: %s" msgstr "" #: bfd.c:308 msgid "#" msgstr "" #: bfd.c:829 #, c-format msgid "BFD %s assertion fail %s:%d" msgstr "" #: bfd.c:841 #, c-format msgid "BFD %s internal error, aborting at %s line %d in %s\n" msgstr "" #: bfd.c:845 #, c-format msgid "BFD %s internal error, aborting at %s line %d\n" msgstr "" #: bfd.c:847 msgid "Please report this bug.\n" msgstr "" #: bfdwin.c:207 #, c-format msgid "not mapping: data=%lx mapped=%d\n" msgstr "" #: bfdwin.c:210 #, c-format msgid "not mapping: env var not set\n" msgstr "" #: binary.c:283 #, c-format msgid "Warning: Writing section `%s' to huge (ie negative) file offset 0x%lx." msgstr "" #: cache.c:237 msgid "reopening %B: %s\n" msgstr "" #: coff64-rs6000.c:2125 coff-rs6000.c:3623 msgid "%B: symbol `%s' has unrecognized smclas %d" msgstr "" #: coff-alpha.c:489 msgid "" "%B: Cannot handle compressed Alpha binaries.\n" " Use compiler flags, or objZ, to generate uncompressed binaries." msgstr "" #: coff-alpha.c:646 msgid "%B: unknown/unsupported relocation type %d" msgstr "" #: coff-alpha.c:898 coff-alpha.c:935 coff-alpha.c:2023 coff-mips.c:1001 msgid "GP relative relocation used when GP not defined" msgstr "" #: coff-alpha.c:1500 msgid "using multiple gp values" msgstr "" #: coff-alpha.c:1559 msgid "%B: unsupported relocation: ALPHA_R_GPRELHIGH" msgstr "" #: coff-alpha.c:1566 msgid "%B: unsupported relocation: ALPHA_R_GPRELLOW" msgstr "" #: coff-alpha.c:1573 elf32-m32r.c:2486 elf64-alpha.c:3951 elf64-alpha.c:4104 #: elf32-ia64.c:4674 elf64-ia64.c:4674 msgid "%B: unknown relocation type %d" msgstr "" #: coff-arm.c:1035 #, c-format msgid "%B: unable to find THUMB glue '%s' for `%s'" msgstr "" #: coff-arm.c:1064 #, c-format msgid "%B: unable to find ARM glue '%s' for `%s'" msgstr "" #: coff-arm.c:1366 elf32-arm.c:4156 #, c-format msgid "" "%B(%s): warning: interworking not enabled.\n" " first occurrence: %B: arm call to thumb" msgstr "" #: coff-arm.c:1456 #, c-format msgid "" "%B(%s): warning: interworking not enabled.\n" " first occurrence: %B: thumb call to arm\n" " consider relinking with --support-old-code enabled" msgstr "" #: coff-arm.c:1749 cofflink.c:3014 coff-tic80.c:695 msgid "%B: bad reloc address 0x%lx in section `%A'" msgstr "" #: coff-arm.c:2074 msgid "%B: illegal symbol index in reloc: %d" msgstr "" #: coff-arm.c:2205 #, c-format msgid "ERROR: %B is compiled for APCS-%d, whereas %B is compiled for APCS-%d" msgstr "" #: coff-arm.c:2221 elf32-arm.c:7103 #, c-format msgid "" "ERROR: %B passes floats in float registers, whereas %B passes them in " "integer registers" msgstr "" #: coff-arm.c:2224 elf32-arm.c:7107 #, c-format msgid "" "ERROR: %B passes floats in integer registers, whereas %B passes them in " "float registers" msgstr "" #: coff-arm.c:2238 #, c-format msgid "" "ERROR: %B is compiled as position independent code, whereas target %B is " "absolute position" msgstr "" #: coff-arm.c:2241 #, c-format msgid "" "ERROR: %B is compiled as absolute position code, whereas target %B is " "position independent" msgstr "" #: coff-arm.c:2269 elf32-arm.c:7172 #, c-format msgid "Warning: %B supports interworking, whereas %B does not" msgstr "" #: coff-arm.c:2272 elf32-arm.c:7178 #, c-format msgid "Warning: %B does not support interworking, whereas %B does" msgstr "" #: coff-arm.c:2296 #, c-format msgid "private flags = %x:" msgstr "" #: coff-arm.c:2304 elf32-arm.c:7229 #, c-format msgid " [floats passed in float registers]" msgstr "" #: coff-arm.c:2306 #, c-format msgid " [floats passed in integer registers]" msgstr "" #: coff-arm.c:2309 elf32-arm.c:7232 #, c-format msgid " [position independent]" msgstr "" #: coff-arm.c:2311 #, c-format msgid " [absolute position]" msgstr "" #: coff-arm.c:2315 #, c-format msgid " [interworking flag not initialised]" msgstr "" #: coff-arm.c:2317 #, c-format msgid " [interworking supported]" msgstr "" #: coff-arm.c:2319 #, c-format msgid " [interworking not supported]" msgstr "" #: coff-arm.c:2365 elf32-arm.c:6626 #, c-format msgid "" "Warning: Not setting interworking flag of %B since it has already been " "specified as non-interworking" msgstr "" #: coff-arm.c:2369 elf32-arm.c:6630 #, c-format msgid "Warning: Clearing the interworking flag of %B due to outside request" msgstr "" #: coffcode.h:849 msgid "%B: warning: COMDAT symbol '%s' does not match section name '%s'" msgstr "" #. Generate a warning message rather using the 'unhandled' #. variable as this will allow some .sys files generate by #. other toolchains to be processed. See bugzilla issue 196. #: coffcode.h:1061 msgid "" "%B: Warning: Ignoring section flag IMAGE_SCN_MEM_NOT_PAGED in section %s" msgstr "" #: coffcode.h:1116 msgid "%B (%s): Section flag %s (0x%x) ignored" msgstr "" #: coffcode.h:2231 #, c-format msgid "Unrecognized TI COFF target id '0x%x'" msgstr "" #: coffcode.h:2546 msgid "%B: reloc against a non-existant symbol index: %ld" msgstr "" #: coffcode.h:4269 msgid "%B: warning: line number table read failed" msgstr "" #: coffcode.h:4301 msgid "%B: warning: illegal symbol index %ld in line numbers" msgstr "" #: coffcode.h:4315 msgid "%B: warning: duplicate line number information for `%s'" msgstr "" #: coffcode.h:4655 msgid "%B: Unrecognized storage class %d for %s symbol `%s'" msgstr "" #: coffcode.h:4781 msgid "warning: %B: local symbol `%s' has no section" msgstr "" #: coffcode.h:4886 coff-i860.c:600 coff-tic54x.c:393 msgid "%B: warning: illegal symbol index %ld in relocs" msgstr "" #: coffcode.h:4924 msgid "%B: illegal relocation type %d at address 0x%lx" msgstr "" #: coffgen.c:1511 msgid "%B: bad string table size %lu" msgstr "" #: coff-h8300.c:1121 #, c-format msgid "cannot handle R_MEM_INDIRECT reloc when using %s output" msgstr "" #: coff-i860.c:142 #, c-format msgid "Relocation `%s' not yet implemented\n" msgstr "" #: coff-i960.c:137 coff-i960.c:500 msgid "uncertain calling convention for non-COFF symbol" msgstr "" #: cofflink.c:509 elflink.c:4158 msgid "Warning: type of symbol `%s' changed from %d to %d in %B" msgstr "" #: cofflink.c:2292 msgid "%B: relocs in section `%A', but it has no contents" msgstr "" #: cofflink.c:2623 coffswap.h:823 #, c-format msgid "%s: %s: reloc overflow: 0x%lx > 0xffff" msgstr "" #: cofflink.c:2632 coffswap.h:809 #, c-format msgid "%s: warning: %s: line number overflow: 0x%lx > 0xffff" msgstr "" #: coff-m68k.c:505 elf32-bfin.c:5434 elf32-m68k.c:2392 msgid "unsupported reloc type" msgstr "" #: coff-maxq.c:126 msgid "Can't Make it a Short Jump" msgstr "" #: coff-maxq.c:191 msgid "Exceeds Long Jump Range" msgstr "" #: coff-maxq.c:202 coff-maxq.c:276 msgid "Absolute address Exceeds 16 bit Range" msgstr "" #: coff-maxq.c:240 msgid "Absolute address Exceeds 8 bit Range" msgstr "" #: coff-maxq.c:333 msgid "Unrecognized Reloc Type" msgstr "" #: coff-mips.c:686 elf32-mips.c:956 elf32-score.c:344 elf64-mips.c:1896 #: elfn32-mips.c:1750 msgid "GP relative relocation when _gp not defined" msgstr "" #: coff-or32.c:227 msgid "Unrecognized reloc" msgstr "" #: coff-rs6000.c:2798 #, c-format msgid "%s: unsupported relocation type 0x%02x" msgstr "" #: coff-rs6000.c:2891 #, c-format msgid "%s: TOC reloc at 0x%x to symbol `%s' with no TOC entry" msgstr "" #: coff-tic4x.c:191 coff-tic54x.c:299 coff-tic80.c:458 #, c-format msgid "Unrecognized reloc type 0x%x" msgstr "" #: coff-tic4x.c:236 #, c-format msgid "%s: warning: illegal symbol index %ld in relocs" msgstr "" #: coff-w65.c:366 #, c-format msgid "ignoring reloc %s\n" msgstr "" #: cpu-arm.c:188 cpu-arm.c:199 msgid "ERROR: %B is compiled for the EP9312, whereas %B is compiled for XScale" msgstr "" #: cpu-arm.c:331 #, c-format msgid "warning: unable to update contents of %s section in %s" msgstr "" #: dwarf2.c:329 msgid "Dwarf Error: Can't find .debug_str section." msgstr "" #: dwarf2.c:347 #, c-format msgid "" "Dwarf Error: DW_FORM_strp offset (%lu) greater than or equal to .debug_str " "size (%lu)." msgstr "" #: dwarf2.c:440 msgid "Dwarf Error: Can't find .debug_abbrev section." msgstr "" #: dwarf2.c:455 #, c-format msgid "" "Dwarf Error: Abbrev offset (%lu) greater than or equal to .debug_abbrev size " "(%lu)." msgstr "" #: dwarf2.c:670 #, c-format msgid "Dwarf Error: Invalid or unhandled FORM value: %u." msgstr "" #: dwarf2.c:873 msgid "Dwarf Error: mangled line number section (bad file number)." msgstr "" #: dwarf2.c:985 msgid "Dwarf Error: Can't find .debug_line section." msgstr "" #: dwarf2.c:1002 #, c-format msgid "" "Dwarf Error: Line offset (%lu) greater than or equal to .debug_line size (%" "lu)." msgstr "" #: dwarf2.c:1228 msgid "Dwarf Error: mangled line number section." msgstr "" #: dwarf2.c:1416 msgid "Dwarf Error: Can't find .debug_ranges section." msgstr "" #: dwarf2.c:1579 dwarf2.c:1695 dwarf2.c:1965 #, c-format msgid "Dwarf Error: Could not find abbrev number %u." msgstr "" #: dwarf2.c:1926 #, c-format msgid "" "Dwarf Error: found dwarf version '%u', this reader only handles version 2 " "information." msgstr "" #: dwarf2.c:1933 #, c-format msgid "" "Dwarf Error: found address size '%u', this reader can not handle sizes " "greater than '%u'." msgstr "" #: dwarf2.c:1956 #, c-format msgid "Dwarf Error: Bad abbrev number: %u." msgstr "" #: ecoff.c:1226 #, c-format msgid "Unknown basic type %d" msgstr "" #: ecoff.c:1483 #, c-format msgid "" "\n" " End+1 symbol: %ld" msgstr "" #: ecoff.c:1490 ecoff.c:1493 #, c-format msgid "" "\n" " First symbol: %ld" msgstr "" #: ecoff.c:1505 #, c-format msgid "" "\n" " End+1 symbol: %-7ld Type: %s" msgstr "" #: ecoff.c:1512 #, c-format msgid "" "\n" " Local symbol: %ld" msgstr "" #: ecoff.c:1520 #, c-format msgid "" "\n" " struct; End+1 symbol: %ld" msgstr "" #: ecoff.c:1525 #, c-format msgid "" "\n" " union; End+1 symbol: %ld" msgstr "" #: ecoff.c:1530 #, c-format msgid "" "\n" " enum; End+1 symbol: %ld" msgstr "" #: ecoff.c:1536 #, c-format msgid "" "\n" " Type: %s" msgstr "" #: elf32-arm.c:2516 #, c-format msgid "unable to find THUMB glue '%s' for '%s'" msgstr "" #: elf32-arm.c:2549 #, c-format msgid "unable to find ARM glue '%s' for '%s'" msgstr "" #: elf32-arm.c:3119 msgid "%B: BE8 images only valid in big-endian mode." msgstr "" #. Give a warning, but do as the user requests anyway. #: elf32-arm.c:3320 msgid "" "%B: warning: selected VFP11 erratum workaround is not necessary for target " "architecture" msgstr "" #: elf32-arm.c:3854 elf32-arm.c:3874 msgid "%B: unable to find VFP11 veneer `%s'" msgstr "" #: elf32-arm.c:3919 #, c-format msgid "Invalid TARGET2 relocation type '%s'." msgstr "" #: elf32-arm.c:4060 msgid "" "%B(%s): warning: interworking not enabled.\n" " first occurrence: %B: thumb call to arm" msgstr "" #: elf32-arm.c:4748 msgid "\\%B: Warning: Arm BLX instruction targets Arm function '%s'." msgstr "" #: elf32-arm.c:5047 msgid "%B: Warning: Thumb BLX instruction targets thumb function '%s'." msgstr "" #: elf32-arm.c:5738 msgid "%B(%A+0x%lx): R_ARM_TLS_LE32 relocation not permitted in shared object" msgstr "" #: elf32-arm.c:5939 msgid "" "%B(%A+0x%lx): Only ADD or SUB instructions are allowed for ALU group " "relocations" msgstr "" #: elf32-arm.c:5979 elf32-arm.c:6066 elf32-arm.c:6149 elf32-arm.c:6234 msgid "%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s" msgstr "" #: elf32-arm.c:6417 elf32-sh.c:3303 elf64-sh64.c:1555 msgid "%B(%A+0x%lx): %s relocation against SEC_MERGE section" msgstr "" #: elf32-arm.c:6506 elf64-ppc.c:9972 msgid "%B(%A+0x%lx): %s used with TLS symbol %s" msgstr "" #: elf32-arm.c:6507 elf64-ppc.c:9973 msgid "%B(%A+0x%lx): %s used with non-TLS symbol %s" msgstr "" #: elf32-arm.c:6530 elf32-i386.c:3305 elf32-m32r.c:2597 elf32-m68k.c:1981 #: elf32-ppc.c:6791 elf32-s390.c:3048 elf32-sh.c:3407 elf32-xtensa.c:2290 #: elf64-ppc.c:11137 elf64-s390.c:3009 elf64-sh64.c:1647 elf64-x86-64.c:3001 #: elf-hppa.h:2193 elf-m10300.c:1459 elfxx-sparc.c:3254 msgid "%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'" msgstr "" #: elf32-arm.c:6564 msgid "out of range" msgstr "" #: elf32-arm.c:6568 msgid "unsupported relocation" msgstr "" #: elf32-arm.c:6576 msgid "unknown error" msgstr "" #: elf32-arm.c:6676 msgid "" "Warning: Clearing the interworking flag of %B because non-interworking code " "in %B has been linked with it" msgstr "" #: elf32-arm.c:6778 msgid "ERROR: %B uses VFP register arguments, %B does not" msgstr "" #: elf32-arm.c:6828 msgid "ERROR: %B: Conflicting architecture profiles %c/%c" msgstr "" #: elf32-arm.c:6843 msgid "Warning: %B: Conflicting platform configuration" msgstr "" #: elf32-arm.c:6852 msgid "ERROR: %B: Conflicting use of R9" msgstr "" #: elf32-arm.c:6864 msgid "ERROR: %B: SB relative addressing conflicts with use of R9" msgstr "" #: elf32-arm.c:6886 msgid "ERROR: %B: Conflicting definitions of wchar_t" msgstr "" #: elf32-arm.c:6915 msgid "" "warning: %B uses %s enums yet the output is to use %s enums; use of enum " "values across objects may fail" msgstr "" #: elf32-arm.c:6928 msgid "ERROR: %B uses iWMMXt register arguments, %B does not" msgstr "" #: elf32-arm.c:6951 msgid "Warning: %B: Unknown EABI object attribute %d" msgstr "" #: elf32-arm.c:7076 msgid "" "ERROR: Source object %B has EABI version %d, but target %B has EABI version %" "d" msgstr "" #: elf32-arm.c:7092 msgid "ERROR: %B is compiled for APCS-%d, whereas target %B uses APCS-%d" msgstr "" #: elf32-arm.c:7117 msgid "ERROR: %B uses VFP instructions, whereas %B does not" msgstr "" #: elf32-arm.c:7121 msgid "ERROR: %B uses FPA instructions, whereas %B does not" msgstr "" #: elf32-arm.c:7131 msgid "ERROR: %B uses Maverick instructions, whereas %B does not" msgstr "" #: elf32-arm.c:7135 msgid "ERROR: %B does not use Maverick instructions, whereas %B does" msgstr "" #: elf32-arm.c:7154 msgid "ERROR: %B uses software FP, whereas %B uses hardware FP" msgstr "" #: elf32-arm.c:7158 msgid "ERROR: %B uses hardware FP, whereas %B uses software FP" msgstr "" #. Ignore init flag - it may not be set, despite the flags field #. containing valid data. #. Ignore init flag - it may not be set, despite the flags field containing valid data. #: elf32-arm.c:7205 elf32-bfin.c:4795 elf32-cris.c:3233 elf32-m68hc1x.c:1276 #: elf32-m68k.c:619 elf32-score.c:3752 elf32-vax.c:537 elfxx-mips.c:11310 #, c-format msgid "private flags = %lx:" msgstr "" #: elf32-arm.c:7214 #, c-format msgid " [interworking enabled]" msgstr "" #: elf32-arm.c:7222 #, c-format msgid " [VFP float format]" msgstr "" #: elf32-arm.c:7224 #, c-format msgid " [Maverick float format]" msgstr "" #: elf32-arm.c:7226 #, c-format msgid " [FPA float format]" msgstr "" #: elf32-arm.c:7235 #, c-format msgid " [new ABI]" msgstr "" #: elf32-arm.c:7238 #, c-format msgid " [old ABI]" msgstr "" #: elf32-arm.c:7241 #, c-format msgid " [software FP]" msgstr "" #: elf32-arm.c:7250 #, c-format msgid " [Version1 EABI]" msgstr "" #: elf32-arm.c:7253 elf32-arm.c:7264 #, c-format msgid " [sorted symbol table]" msgstr "" #: elf32-arm.c:7255 elf32-arm.c:7266 #, c-format msgid " [unsorted symbol table]" msgstr "" #: elf32-arm.c:7261 #, c-format msgid " [Version2 EABI]" msgstr "" #: elf32-arm.c:7269 #, c-format msgid " [dynamic symbols use segment index]" msgstr "" #: elf32-arm.c:7272 #, c-format msgid " [mapping symbols precede others]" msgstr "" #: elf32-arm.c:7279 #, c-format msgid " [Version3 EABI]" msgstr "" #: elf32-arm.c:7283 #, c-format msgid " [Version4 EABI]" msgstr "" #: elf32-arm.c:7287 #, c-format msgid " [Version5 EABI]" msgstr "" #: elf32-arm.c:7290 #, c-format msgid " [BE8]" msgstr "" #: elf32-arm.c:7293 #, c-format msgid " [LE8]" msgstr "" #: elf32-arm.c:7299 #, c-format msgid " " msgstr "" #: elf32-arm.c:7306 #, c-format msgid " [relocatable executable]" msgstr "" #: elf32-arm.c:7309 #, c-format msgid " [has entry point]" msgstr "" #: elf32-arm.c:7314 #, c-format msgid "" msgstr "" #: elf32-arm.c:7545 elf32-i386.c:960 elf32-s390.c:1003 elf32-xtensa.c:814 #: elf64-s390.c:958 elf64-x86-64.c:772 elfxx-sparc.c:1113 msgid "%B: bad symbol index: %d" msgstr "" #: elf32-arm.c:8091 elf32-cris.c:2399 elf32-hppa.c:1905 elf32-i370.c:506 #: elf32-i386.c:1517 elf32-m32r.c:1930 elf32-m68k.c:1330 elf32-ppc.c:4309 #: elf32-s390.c:1679 elf32-sh.c:2583 elf32-vax.c:1049 elf64-ppc.c:5897 #: elf64-s390.c:1654 elf64-sh64.c:3437 elf64-x86-64.c:1381 elf-m10300.c:4206 #: elfxx-sparc.c:1795 #, c-format msgid "dynamic variable `%s' is zero size" msgstr "" #: elf32-arm.c:8609 #, c-format msgid "Errors encountered processing file %s" msgstr "" #: elf32-arm.c:9837 elf32-arm.c:9859 msgid "%B: error: VFP11 veneer out of range" msgstr "" #: elf32-avr.c:1253 elf32-bfin.c:2795 elf32-cr16.c:887 elf32-cr16c.c:789 #: elf32-cris.c:1537 elf32-crx.c:932 elf32-d10v.c:517 elf32-fr30.c:615 #: elf32-frv.c:4128 elf32-h8300.c:515 elf32-i860.c:1217 elf32-ip2k.c:1499 #: elf32-iq2000.c:647 elf32-m32c.c:560 elf32-m32r.c:3123 elf32-m68hc1x.c:1132 #: elf32-mep.c:639 elf32-msp430.c:496 elf32-mt.c:401 elf32-openrisc.c:411 #: elf32-score.c:2455 elf32-spu.c:2813 elf32-v850.c:1700 elf32-xstormy16.c:946 #: elf64-mmix.c:1531 elf-m10200.c:455 elf-m10300.c:1522 msgid "internal error: out of range error" msgstr "" #: elf32-avr.c:1257 elf32-bfin.c:2799 elf32-cr16.c:891 elf32-cr16c.c:793 #: elf32-cris.c:1541 elf32-crx.c:936 elf32-d10v.c:521 elf32-fr30.c:619 #: elf32-frv.c:4132 elf32-h8300.c:519 elf32-i860.c:1221 elf32-iq2000.c:651 #: elf32-m32c.c:564 elf32-m32r.c:3127 elf32-m68hc1x.c:1136 elf32-mep.c:643 #: elf32-msp430.c:500 elf32-openrisc.c:415 elf32-score.c:2459 elf32-spu.c:2817 #: elf32-v850.c:1704 elf32-xstormy16.c:950 elf64-mmix.c:1535 elf-m10200.c:459 #: elf-m10300.c:1526 elfxx-mips.c:8019 msgid "internal error: unsupported relocation error" msgstr "" #: elf32-avr.c:1261 elf32-bfin.c:2803 elf32-cris.c:1545 elf32-fr30.c:623 #: elf32-frv.c:4136 elf32-i860.c:1225 elf32-ip2k.c:1510 elf32-iq2000.c:655 #: elf32-m32c.c:568 elf32-mep.c:647 elf32-msp430.c:504 elf32-mt.c:405 #: elf32-openrisc.c:419 elf32-v850.c:1708 elf32-xstormy16.c:954 #: elf64-mmix.c:1539 msgid "internal error: dangerous relocation" msgstr "" #: elf32-avr.c:1265 elf32-bfin.c:2807 elf32-cr16.c:899 elf32-cr16c.c:801 #: elf32-cris.c:1549 elf32-crx.c:944 elf32-d10v.c:529 elf32-fr30.c:627 #: elf32-frv.c:4140 elf32-h8300.c:527 elf32-i860.c:1229 elf32-ip2k.c:1514 #: elf32-iq2000.c:659 elf32-m32c.c:572 elf32-m32r.c:3135 elf32-m68hc1x.c:1144 #: elf32-mep.c:651 elf32-msp430.c:508 elf32-mt.c:409 elf32-openrisc.c:423 #: elf32-score.c:2467 elf32-spu.c:2825 elf32-v850.c:1724 elf32-xstormy16.c:958 #: elf64-mmix.c:1543 elf-m10200.c:467 elf-m10300.c:1539 msgid "internal error: unknown error" msgstr "" #: elf32-avr.c:2369 elf32-hppa.c:594 elf32-m68hc1x.c:163 elf64-ppc.c:3801 msgid "%B: cannot create stub entry %s" msgstr "" #: elf32-bfin.c:2274 msgid "%B: relocation at `%A+0x%x' references symbol `%s' with nonzero addend" msgstr "" #: elf32-bfin.c:2288 elf32-frv.c:2918 msgid "relocation references symbol not defined in the module" msgstr "" #: elf32-bfin.c:2385 msgid "R_BFIN_FUNCDESC references dynamic symbol with nonzero addend" msgstr "" #: elf32-bfin.c:2424 elf32-bfin.c:2547 elf32-frv.c:3655 elf32-frv.c:3776 msgid "cannot emit fixups in read-only section" msgstr "" #: elf32-bfin.c:2452 elf32-bfin.c:2587 elf32-frv.c:3686 elf32-frv.c:3820 msgid "cannot emit dynamic relocations in read-only section" msgstr "" #: elf32-bfin.c:2505 msgid "R_BFIN_FUNCDESC_VALUE references dynamic symbol with nonzero addend" msgstr "" #: elf32-bfin.c:2673 msgid "relocations between different segments are not supported" msgstr "" #: elf32-bfin.c:2674 msgid "warning: relocation references a different segment" msgstr "" #: elf32-bfin.c:3083 msgid "%B(%A+0x%lx): unresolvable relocation against symbol `%s'" msgstr "" #: elf32-bfin.c:3116 elf32-i386.c:3346 elf32-m68k.c:2022 elf32-s390.c:3100 #: elf64-s390.c:3061 elf64-x86-64.c:3040 msgid "%B(%A+0x%lx): reloc against `%s': error %d" msgstr "" #: elf32-bfin.c:4687 elf32-frv.c:6422 msgid "%B: unsupported relocation type %i" msgstr "" #: elf32-bfin.c:4868 elf32-frv.c:6830 #, c-format msgid "%s: cannot link non-fdpic object file into fdpic executable" msgstr "" #: elf32-bfin.c:4872 elf32-frv.c:6834 #, c-format msgid "%s: cannot link fdpic object file into non-fdpic executable" msgstr "" #: elf32-cr16.c:895 elf32-cr16c.c:797 elf32-crx.c:940 elf32-d10v.c:525 #: elf32-h8300.c:523 elf32-m32r.c:3131 elf32-m68hc1x.c:1140 elf32-score.c:2463 #: elf32-spu.c:2821 elf-m10200.c:463 msgid "internal error: dangerous error" msgstr "" #: elf32-cris.c:1059 msgid "%B, section %A: unresolvable relocation %s against symbol `%s'" msgstr "" #: elf32-cris.c:1128 msgid "%B, section %A: No PLT nor GOT for relocation %s against symbol `%s'" msgstr "" #: elf32-cris.c:1130 msgid "%B, section %A: No PLT for relocation %s against symbol `%s'" msgstr "" #: elf32-cris.c:1136 elf32-cris.c:1268 msgid "[whose name is lost]" msgstr "" #: elf32-cris.c:1254 msgid "" "%B, section %A: relocation %s with non-zero addend %d against local symbol" msgstr "" #: elf32-cris.c:1262 msgid "" "%B, section %A: relocation %s with non-zero addend %d against symbol `%s'" msgstr "" #: elf32-cris.c:1288 msgid "%B, section %A: relocation %s is not allowed for global symbol: `%s'" msgstr "" #: elf32-cris.c:1304 msgid "%B, section %A: relocation %s with no GOT created" msgstr "" #: elf32-cris.c:1422 msgid "%B: Internal inconsistency; no relocation section %s" msgstr "" #: elf32-cris.c:2510 msgid "" "%B, section %A:\n" " v10/v32 compatible object %s must not contain a PIC relocation" msgstr "" #: elf32-cris.c:2697 elf32-cris.c:2765 msgid "" "%B, section %A:\n" " relocation %s should not be used in a shared object; recompile with -fPIC" msgstr "" #: elf32-cris.c:3182 msgid "Unexpected machine number" msgstr "" #: elf32-cris.c:3236 #, c-format msgid " [symbols have a _ prefix]" msgstr "" #: elf32-cris.c:3239 #, c-format msgid " [v10 and v32]" msgstr "" #: elf32-cris.c:3242 #, c-format msgid " [v32]" msgstr "" #: elf32-cris.c:3287 msgid "%B: uses _-prefixed symbols, but writing file with non-prefixed symbols" msgstr "" #: elf32-cris.c:3288 msgid "%B: uses non-prefixed symbols, but writing file with _-prefixed symbols" msgstr "" #: elf32-cris.c:3307 msgid "%B contains CRIS v32 code, incompatible with previous objects" msgstr "" #: elf32-cris.c:3309 msgid "%B contains non-CRIS-v32 code, incompatible with previous objects" msgstr "" #: elf32-frv.c:1522 elf32-frv.c:1671 msgid "relocation requires zero addend" msgstr "" #: elf32-frv.c:2905 msgid "%B(%A+0x%x): relocation to `%s+%x' may have caused the error above" msgstr "" #: elf32-frv.c:2994 msgid "R_FRV_GETTLSOFF not applied to a call instruction" msgstr "" #: elf32-frv.c:3036 msgid "R_FRV_GOTTLSDESC12 not applied to an lddi instruction" msgstr "" #: elf32-frv.c:3107 msgid "R_FRV_GOTTLSDESCHI not applied to a sethi instruction" msgstr "" #: elf32-frv.c:3144 msgid "R_FRV_GOTTLSDESCLO not applied to a setlo or setlos instruction" msgstr "" #: elf32-frv.c:3192 msgid "R_FRV_TLSDESC_RELAX not applied to an ldd instruction" msgstr "" #: elf32-frv.c:3276 msgid "R_FRV_GETTLSOFF_RELAX not applied to a calll instruction" msgstr "" #: elf32-frv.c:3331 msgid "R_FRV_GOTTLSOFF12 not applied to an ldi instruction" msgstr "" #: elf32-frv.c:3361 msgid "R_FRV_GOTTLSOFFHI not applied to a sethi instruction" msgstr "" #: elf32-frv.c:3390 msgid "R_FRV_GOTTLSOFFLO not applied to a setlo or setlos instruction" msgstr "" #: elf32-frv.c:3421 msgid "R_FRV_TLSOFF_RELAX not applied to an ld instruction" msgstr "" #: elf32-frv.c:3466 msgid "R_FRV_TLSMOFFHI not applied to a sethi instruction" msgstr "" #: elf32-frv.c:3493 msgid "R_FRV_TLSMOFFLO not applied to a setlo or setlos instruction" msgstr "" #: elf32-frv.c:3614 msgid "R_FRV_FUNCDESC references dynamic symbol with nonzero addend" msgstr "" #: elf32-frv.c:3734 msgid "R_FRV_FUNCDESC_VALUE references dynamic symbol with nonzero addend" msgstr "" #: elf32-frv.c:3991 elf32-frv.c:4147 msgid "%B(%A+0x%lx): reloc against `%s': %s" msgstr "" #: elf32-frv.c:3993 elf32-frv.c:3997 msgid "relocation references a different segment" msgstr "" #: elf32-frv.c:6744 #, c-format msgid "" "%s: compiled with %s and linked with modules that use non-pic relocations" msgstr "" #: elf32-frv.c:6797 elf32-iq2000.c:808 elf32-m32c.c:819 #, c-format msgid "%s: compiled with %s and linked with modules compiled with %s" msgstr "" #: elf32-frv.c:6809 #, c-format msgid "" "%s: uses different unknown e_flags (0x%lx) fields than previous modules (0x%" "lx)" msgstr "" #: elf32-frv.c:6859 elf32-iq2000.c:845 elf32-m32c.c:855 elf32-mt.c:586 #, c-format msgid "private flags = 0x%lx:" msgstr "" #: elf32-gen.c:68 elf64-gen.c:68 msgid "%B: Relocations in generic ELF (EM: %d)" msgstr "" #: elf32-hppa.c:843 elf32-hppa.c:3576 msgid "%B(%A+0x%lx): cannot reach %s, recompile with -ffunction-sections" msgstr "" #: elf32-hppa.c:1252 msgid "" "%B: relocation %s can not be used when making a shared object; recompile " "with -fPIC" msgstr "" #: elf32-hppa.c:1505 #, c-format msgid "Could not find relocation section for %s" msgstr "" #: elf32-hppa.c:2794 msgid "%B: duplicate export stub %s" msgstr "" #: elf32-hppa.c:3412 msgid "" "%B(%A+0x%lx): %s fixup for insn 0x%x is not supported in a non-shared link" msgstr "" #: elf32-hppa.c:4266 msgid "%B(%A+0x%lx): cannot handle %s for %s" msgstr "" #: elf32-hppa.c:4573 msgid ".got section not immediately after .plt section" msgstr "" #: elf32-i386.c:363 elf32-ppc.c:1615 elf32-s390.c:379 elf64-ppc.c:2147 #: elf64-s390.c:403 elf64-x86-64.c:220 msgid "%B: invalid relocation type %d" msgstr "" #: elf32-i386.c:1084 elf32-s390.c:1185 elf32-sh.c:5062 elf64-s390.c:1149 #: elfxx-sparc.c:1241 msgid "%B: `%s' accessed both as normal and thread local symbol" msgstr "" #: elf32-i386.c:1199 elf32-s390.c:1294 elf64-ppc.c:4863 elf64-s390.c:1261 #: elf64-x86-64.c:1050 msgid "%B: bad relocation section name `%s'" msgstr "" #: elf32-i386.c:2252 msgid "%B: unrecognized relocation (0x%x) in section `%A'" msgstr "" #: elf32-i386.c:2474 msgid "" "%B: relocation R_386_GOTOFF against protected function `%s' can not be used " "when making a shared object" msgstr "" #: elf32-ip2k.c:868 elf32-ip2k.c:874 elf32-ip2k.c:941 elf32-ip2k.c:947 msgid "" "ip2k relaxer: switch table without complete matching relocation information." msgstr "" #: elf32-ip2k.c:891 elf32-ip2k.c:974 msgid "ip2k relaxer: switch table header corrupt." msgstr "" #: elf32-ip2k.c:1316 #, c-format msgid "ip2k linker: missing page instruction at 0x%08lx (dest = 0x%08lx)." msgstr "" #: elf32-ip2k.c:1332 #, c-format msgid "ip2k linker: redundant page instruction at 0x%08lx (dest = 0x%08lx)." msgstr "" #. Only if it's not an unresolved symbol. #: elf32-ip2k.c:1506 msgid "unsupported relocation between data/insn address spaces" msgstr "" #: elf32-iq2000.c:821 elf32-m32c.c:831 #, c-format msgid "%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)" msgstr "" #: elf32-m32r.c:1452 msgid "SDA relocation when _SDA_BASE_ not defined" msgstr "" #: elf32-m32r.c:3060 msgid "%B: The target (%s) of an %s relocation is in the wrong section (%A)" msgstr "" #: elf32-m32r.c:3588 msgid "%B: Instruction set mismatch with previous modules" msgstr "" #: elf32-m32r.c:3609 #, c-format msgid "private flags = %lx" msgstr "" #: elf32-m32r.c:3614 #, c-format msgid ": m32r instructions" msgstr "" #: elf32-m32r.c:3615 #, c-format msgid ": m32rx instructions" msgstr "" #: elf32-m32r.c:3616 #, c-format msgid ": m32r2 instructions" msgstr "" #: elf32-m68hc1x.c:1044 #, c-format msgid "" "Reference to the far symbol `%s' using a wrong relocation may result in " "incorrect execution" msgstr "" #: elf32-m68hc1x.c:1067 #, c-format msgid "" "banked address [%lx:%04lx] (%lx) is not in the same bank as current banked " "address [%lx:%04lx] (%lx)" msgstr "" #: elf32-m68hc1x.c:1086 #, c-format msgid "" "reference to a banked address [%lx:%04lx] in the normal address space at %" "04lx" msgstr "" #: elf32-m68hc1x.c:1219 msgid "" "%B: linking files compiled for 16-bit integers (-mshort) and others for 32-" "bit integers" msgstr "" #: elf32-m68hc1x.c:1226 msgid "" "%B: linking files compiled for 32-bit double (-fshort-double) and others for " "64-bit double" msgstr "" #: elf32-m68hc1x.c:1235 msgid "%B: linking files compiled for HCS12 with others compiled for HC12" msgstr "" #: elf32-m68hc1x.c:1251 elf32-ppc.c:3733 elf64-sparc.c:696 elfxx-mips.c:11271 msgid "%B: uses different e_flags (0x%lx) fields than previous modules (0x%lx)" msgstr "" #: elf32-m68hc1x.c:1279 #, c-format msgid "[abi=32-bit int, " msgstr "" #: elf32-m68hc1x.c:1281 #, c-format msgid "[abi=16-bit int, " msgstr "" #: elf32-m68hc1x.c:1284 #, c-format msgid "64-bit double, " msgstr "" #: elf32-m68hc1x.c:1286 #, c-format msgid "32-bit double, " msgstr "" #: elf32-m68hc1x.c:1289 #, c-format msgid "cpu=HC11]" msgstr "" #: elf32-m68hc1x.c:1291 #, c-format msgid "cpu=HCS12]" msgstr "" #: elf32-m68hc1x.c:1293 #, c-format msgid "cpu=HC12]" msgstr "" #: elf32-m68hc1x.c:1296 #, c-format msgid " [memory=bank-model]" msgstr "" #: elf32-m68hc1x.c:1298 #, c-format msgid " [memory=flat]" msgstr "" #: elf32-m68k.c:634 elf32-m68k.c:635 msgid "unknown" msgstr "" #: elf32-mcore.c:98 elf32-mcore.c:441 msgid "%B: Relocation %s (%d) is not currently supported.\n" msgstr "" #: elf32-mcore.c:427 msgid "%B: Unknown relocation type %d\n" msgstr "" #: elf32-mep.c:809 msgid "%B and %B are for different cores" msgstr "" #: elf32-mep.c:826 msgid "%B and %B are for different configurations" msgstr "" #: elf32-mep.c:864 #, c-format msgid "private flags = 0x%lx" msgstr "" #: elf32-mips.c:987 elf64-mips.c:1961 elfn32-mips.c:1806 msgid "literal relocation occurs for an external symbol" msgstr "" #: elf32-mips.c:1027 elf32-score.c:483 elf64-mips.c:2004 elfn32-mips.c:1847 msgid "32bits gp relative relocation occurs for an external symbol" msgstr "" #: elf32-ppc.c:1680 #, c-format msgid "generic linker can't handle %s" msgstr "" #: elf32-ppc.c:2162 msgid "corrupt or empty %s section in %B" msgstr "" #: elf32-ppc.c:2169 msgid "unable to read in %s section from %B" msgstr "" #: elf32-ppc.c:2175 msgid "corrupt %s section in %B" msgstr "" #: elf32-ppc.c:2218 msgid "warning: unable to set size of %s section in %B" msgstr "" #: elf32-ppc.c:2266 msgid "failed to allocate space for new APUinfo section." msgstr "" #: elf32-ppc.c:2285 msgid "failed to compute new APUinfo section." msgstr "" #: elf32-ppc.c:2288 msgid "failed to install new APUinfo section." msgstr "" #: elf32-ppc.c:3021 msgid "%B: relocation %s cannot be used when making a shared object" msgstr "" #. It does not make sense to have a procedure linkage #. table entry for a local symbol. #: elf32-ppc.c:3291 msgid "%B(%A+0x%lx): %s reloc against local symbol" msgstr "" #: elf32-ppc.c:3632 elf32-ppc.c:3636 elfxx-mips.c:11015 elfxx-mips.c:11034 #: elfxx-mips.c:11049 msgid "Warning: %B uses hard float, %B uses soft float" msgstr "" #: elf32-ppc.c:3639 elf32-ppc.c:3643 elfxx-mips.c:10996 elfxx-mips.c:11000 msgid "Warning: %B uses unknown floating point ABI %d" msgstr "" #: elf32-ppc.c:3698 msgid "" "%B: compiled with -mrelocatable and linked with modules compiled normally" msgstr "" #: elf32-ppc.c:3706 msgid "" "%B: compiled normally and linked with modules compiled with -mrelocatable" msgstr "" #: elf32-ppc.c:3792 msgid "Using bss-plt due to %B" msgstr "" #: elf32-ppc.c:5997 elf64-ppc.c:10489 msgid "%B: unknown relocation type %d for symbol %s" msgstr "" #: elf32-ppc.c:6247 msgid "%B(%A+0x%lx): non-zero addend on %s reloc against `%s'" msgstr "" #: elf32-ppc.c:6592 elf32-ppc.c:6618 elf32-ppc.c:6677 msgid "" "%B: the target (%s) of a %s relocation is in the wrong output section (%s)" msgstr "" #: elf32-ppc.c:6732 msgid "%B: relocation %s is not yet supported for symbol %s." msgstr "" #: elf32-ppc.c:6840 elf64-ppc.c:11184 msgid "%B(%A+0x%lx): %s reloc against `%s': error %d" msgstr "" #: elf32-s390.c:2238 elf64-s390.c:2212 msgid "%B(%A+0x%lx): invalid instruction for TLS relocation %s" msgstr "" #: elf32-score.c:1417 elfxx-mips.c:2695 msgid "not enough GOT space for local GOT entries" msgstr "" #: elf32-score.c:2549 #, c-format msgid "%B: Malformed reloc detected for section %s" msgstr "" #: elf32-score.c:2600 msgid "%B: CALL15 reloc at 0x%lx not against global symbol" msgstr "" #: elf32-score.c:3755 #, c-format msgid " [pic]" msgstr "" #: elf32-score.c:3759 #, c-format msgid " [fix dep]" msgstr "" #: elf32-score.c:3801 elfxx-mips.c:11180 msgid "%B: warning: linking PIC files with non-PIC files" msgstr "" #: elf32-sh64.c:221 elf64-sh64.c:2349 #, c-format msgid "%s: compiled as 32-bit object and %s is 64-bit" msgstr "" #: elf32-sh64.c:224 elf64-sh64.c:2352 #, c-format msgid "%s: compiled as 64-bit object and %s is 32-bit" msgstr "" #: elf32-sh64.c:226 elf64-sh64.c:2354 #, c-format msgid "%s: object size does not match that of target %s" msgstr "" #: elf32-sh64.c:449 elf64-sh64.c:2893 #, c-format msgid "%s: encountered datalabel symbol in input" msgstr "" #: elf32-sh64.c:526 msgid "PTB mismatch: a SHmedia address (bit 0 == 1)" msgstr "" #: elf32-sh64.c:529 msgid "PTA mismatch: a SHcompact address (bit 0 == 0)" msgstr "" #: elf32-sh64.c:547 #, c-format msgid "%s: GAS error: unexpected PTB insn with R_SH_PT_16" msgstr "" #: elf32-sh64.c:596 msgid "%B: error: unaligned relocation type %d at %08x reloc %p\n" msgstr "" #: elf32-sh64.c:672 #, c-format msgid "%s: could not write out added .cranges entries" msgstr "" #: elf32-sh64.c:732 #, c-format msgid "%s: could not write out sorted .cranges entries" msgstr "" #: elf32-sh.c:532 msgid "%B: 0x%lx: warning: bad R_SH_USES offset" msgstr "" #: elf32-sh.c:544 msgid "%B: 0x%lx: warning: R_SH_USES points to unrecognized insn 0x%x" msgstr "" #: elf32-sh.c:561 msgid "%B: 0x%lx: warning: bad R_SH_USES load offset" msgstr "" #: elf32-sh.c:576 msgid "%B: 0x%lx: warning: could not find expected reloc" msgstr "" #: elf32-sh.c:604 msgid "%B: 0x%lx: warning: symbol in unexpected section" msgstr "" #: elf32-sh.c:730 msgid "%B: 0x%lx: warning: could not find expected COUNT reloc" msgstr "" #: elf32-sh.c:739 msgid "%B: 0x%lx: warning: bad count" msgstr "" #: elf32-sh.c:1143 elf32-sh.c:1513 msgid "%B: 0x%lx: fatal: reloc overflow while relaxing" msgstr "" #: elf32-sh.c:3248 elf64-sh64.c:1525 msgid "Unexpected STO_SH5_ISA32 on local symbol is not handled" msgstr "" #: elf32-sh.c:3485 msgid "%B: 0x%lx: fatal: unaligned branch target for relax-support relocation" msgstr "" #: elf32-sh.c:3518 elf32-sh.c:3533 msgid "%B: 0x%lx: fatal: unaligned %s relocation 0x%lx" msgstr "" #: elf32-sh.c:3547 msgid "%B: 0x%lx: fatal: R_SH_PSHA relocation %d not in range -32..32" msgstr "" #: elf32-sh.c:3561 msgid "%B: 0x%lx: fatal: R_SH_PSHL relocation %d not in range -32..32" msgstr "" #: elf32-sh.c:5274 elf64-alpha.c:4531 msgid "%B: TLS local exec code cannot be linked into shared objects" msgstr "" #: elf32-sh-symbian.c:128 msgid "%B: IMPORT AS directive for %s conceals previous IMPORT AS" msgstr "" #: elf32-sh-symbian.c:381 msgid "%B: Unrecognised .directive command: %s" msgstr "" #: elf32-sh-symbian.c:502 msgid "%B: Failed to add renamed symbol %s" msgstr "" #: elf32-sparc.c:88 msgid "%B: compiled for a 64 bit system and target is 32 bit" msgstr "" #: elf32-sparc.c:101 msgid "%B: linking little endian files with big endian files" msgstr "" #: elf32-spu.c:995 msgid "warning: call to non-function symbol %s defined in %B" msgstr "" #: elf32-spu.c:1315 msgid "%B is not allowed to define %s" msgstr "" #: elf32-spu.c:1352 #, c-format msgid "%s in overlay section" msgstr "" #: elf32-spu.c:1363 msgid "overlay stub relocation overflow" msgstr "" #: elf32-spu.c:1820 #, c-format msgid "warning: %s overlaps %s\n" msgstr "" #: elf32-spu.c:1836 #, c-format msgid "warning: %s exceeds section size\n" msgstr "" #: elf32-spu.c:1867 msgid "%A:0x%v not found in function table\n" msgstr "" #: elf32-spu.c:1958 msgid "" "%B(%A+0x%v): call to non-code section %B(%A), stack analysis incomplete\n" msgstr "" #: elf32-spu.c:2079 #, c-format msgid "%A link_order not found\n" msgstr "" #: elf32-spu.c:2358 #, c-format msgid "Stack analysis will ignore the call from %s to %s\n" msgstr "" #: elf32-spu.c:2513 msgid "%s: 0x%v 0x%v\n" msgstr "" #: elf32-spu.c:2517 msgid " calls:\n" msgstr "" #: elf32-spu.c:2524 #, c-format msgid " %s%s %s\n" msgstr "" #: elf32-spu.c:2585 msgid "Stack size for call graph root nodes.\n" msgstr "" #: elf32-spu.c:2586 msgid "" "\n" "Stack size for functions. Annotations: '*' max stack, 't' tail call\n" msgstr "" #: elf32-spu.c:2615 msgid " %s: 0x%v\n" msgstr "" #: elf32-spu.c:2625 msgid "Maximum stack required is 0x%v\n" msgstr "" #: elf32-spu.c:2751 msgid "%B(%s+0x%lx): unresolvable %s relocation against symbol `%s'" msgstr "" #: elf32-v850.c:162 #, c-format msgid "Variable `%s' cannot occupy in multiple small data regions" msgstr "" #: elf32-v850.c:165 #, c-format msgid "" "Variable `%s' can only be in one of the small, zero, and tiny data regions" msgstr "" #: elf32-v850.c:168 #, c-format msgid "" "Variable `%s' cannot be in both small and zero data regions simultaneously" msgstr "" #: elf32-v850.c:171 #, c-format msgid "" "Variable `%s' cannot be in both small and tiny data regions simultaneously" msgstr "" #: elf32-v850.c:174 #, c-format msgid "" "Variable `%s' cannot be in both zero and tiny data regions simultaneously" msgstr "" #: elf32-v850.c:477 #, c-format msgid "FAILED to find previous HI16 reloc\n" msgstr "" #: elf32-v850.c:1712 msgid "could not locate special linker symbol __gp" msgstr "" #: elf32-v850.c:1716 msgid "could not locate special linker symbol __ep" msgstr "" #: elf32-v850.c:1720 msgid "could not locate special linker symbol __ctbp" msgstr "" #: elf32-v850.c:1870 msgid "%B: Architecture mismatch with previous modules" msgstr "" #: elf32-v850.c:1889 #, c-format msgid "private flags = %lx: " msgstr "" #: elf32-v850.c:1894 #, c-format msgid "v850 architecture" msgstr "" #: elf32-v850.c:1895 #, c-format msgid "v850e architecture" msgstr "" #: elf32-v850.c:1896 #, c-format msgid "v850e1 architecture" msgstr "" #: elf32-vax.c:540 #, c-format msgid " [nonpic]" msgstr "" #: elf32-vax.c:543 #, c-format msgid " [d-float]" msgstr "" #: elf32-vax.c:546 #, c-format msgid " [g-float]" msgstr "" #: elf32-vax.c:656 #, c-format msgid "" "%s: warning: GOT addend of %ld to `%s' does not match previous GOT addend of " "%ld" msgstr "" #: elf32-vax.c:1583 #, c-format msgid "%s: warning: PLT addend of %d to `%s' from %s section ignored" msgstr "" #: elf32-vax.c:1720 #, c-format msgid "%s: warning: %s relocation against symbol `%s' from %s section" msgstr "" #: elf32-vax.c:1726 #, c-format msgid "%s: warning: %s relocation to 0x%x from %s section" msgstr "" #: elf32-xstormy16.c:451 elf32-ia64.c:2961 elf64-ia64.c:2961 msgid "non-zero addend in @fptr reloc" msgstr "" #: elf32-xtensa.c:733 msgid "%B(%A): invalid property table" msgstr "" #: elf32-xtensa.c:2177 msgid "%B(%A+0x%lx): relocation offset out of range (size=0x%x)" msgstr "" #: elf32-xtensa.c:2234 msgid "dynamic relocation in read-only section" msgstr "" #: elf32-xtensa.c:2407 msgid "internal inconsistency in size of .got.loc section" msgstr "" #: elf32-xtensa.c:2714 msgid "%B: incompatible machine type. Output is 0x%x. Input is 0x%x" msgstr "" #: elf32-xtensa.c:3920 elf32-xtensa.c:3928 msgid "Attempt to convert L32R/CALLX to CALL failed" msgstr "" #: elf32-xtensa.c:5522 elf32-xtensa.c:5598 elf32-xtensa.c:6714 msgid "" "%B(%A+0x%lx): could not decode instruction; possible configuration mismatch" msgstr "" #: elf32-xtensa.c:6454 msgid "" "%B(%A+0x%lx): could not decode instruction for XTENSA_ASM_SIMPLIFY " "relocation; possible configuration mismatch" msgstr "" #: elf32-xtensa.c:8166 msgid "invalid relocation address" msgstr "" #: elf32-xtensa.c:8215 msgid "overflow after relaxation" msgstr "" #: elf32-xtensa.c:9341 msgid "%B(%A+0x%lx): unexpected fix for %s relocation" msgstr "" #: elf64-alpha.c:451 msgid "GPDISP relocation did not find ldah and lda instructions" msgstr "" #: elf64-alpha.c:2402 msgid "%B: .got subsegment exceeds 64K (size %d)" msgstr "" #: elf64-alpha.c:4275 elf64-alpha.c:4287 msgid "%B: gp-relative relocation against dynamic symbol %s" msgstr "" #: elf64-alpha.c:4313 elf64-alpha.c:4448 msgid "%B: pc-relative relocation against dynamic symbol %s" msgstr "" #: elf64-alpha.c:4341 msgid "%B: change in gp: BRSGP %s" msgstr "" #: elf64-alpha.c:4366 msgid "" msgstr "" #: elf64-alpha.c:4371 msgid "%B: !samegp reloc against symbol without .prologue: %s" msgstr "" #: elf64-alpha.c:4423 msgid "%B: unhandled dynamic relocation against %s" msgstr "" #: elf64-alpha.c:4455 msgid "%B: pc-relative relocation against undefined weak symbol %s" msgstr "" #: elf64-alpha.c:4515 msgid "%B: dtp-relative relocation against dynamic symbol %s" msgstr "" #: elf64-alpha.c:4538 msgid "%B: tp-relative relocation against dynamic symbol %s" msgstr "" #: elf64-hppa.c:2039 #, c-format msgid "stub entry for %s cannot load .plt, dp offset = %ld" msgstr "" #: elf64-mmix.c:1175 #, c-format msgid "" "%s: Internal inconsistency error for value for\n" " linker-allocated global register: linked: 0x%lx%08lx != relaxed: 0x%lx%" "08lx\n" msgstr "" #: elf64-mmix.c:1616 #, c-format msgid "" "%s: base-plus-offset relocation against register symbol: (unknown) in %s" msgstr "" #: elf64-mmix.c:1621 #, c-format msgid "%s: base-plus-offset relocation against register symbol: %s in %s" msgstr "" #: elf64-mmix.c:1665 #, c-format msgid "%s: register relocation against non-register symbol: (unknown) in %s" msgstr "" #: elf64-mmix.c:1670 #, c-format msgid "%s: register relocation against non-register symbol: %s in %s" msgstr "" #: elf64-mmix.c:1707 #, c-format msgid "%s: directive LOCAL valid only with a register or absolute value" msgstr "" #: elf64-mmix.c:1735 #, c-format msgid "" "%s: LOCAL directive: Register $%ld is not a local register. First global " "register is $%ld." msgstr "" #: elf64-mmix.c:2200 #, c-format msgid "" "%s: Error: multiple definition of `%s'; start of %s is set in a earlier " "linked file\n" msgstr "" #: elf64-mmix.c:2258 msgid "Register section has contents\n" msgstr "" #: elf64-mmix.c:2450 #, c-format msgid "" "Internal inconsistency: remaining %u != max %u.\n" " Please report this bug." msgstr "" #: elf64-ppc.c:2568 libbfd.c:948 msgid "%B: compiled for a big endian system and target is little endian" msgstr "" #: elf64-ppc.c:2571 libbfd.c:950 msgid "%B: compiled for a little endian system and target is big endian" msgstr "" #: elf64-ppc.c:5887 #, c-format msgid "" "copy reloc against `%s' requires lazy plt linking; avoid setting " "LD_BIND_NOW=1 or upgrade gcc" msgstr "" #: elf64-ppc.c:6315 msgid "dynreloc miscount for %B, section %A" msgstr "" #: elf64-ppc.c:6419 msgid "%B: .opd is not a regular array of opd entries" msgstr "" #: elf64-ppc.c:6428 msgid "%B: unexpected reloc type %u in .opd section" msgstr "" #: elf64-ppc.c:6449 msgid "%B: undefined sym `%s' in .opd section" msgstr "" #: elf64-ppc.c:7156 elf64-ppc.c:7536 #, c-format msgid "%s defined in removed toc entry" msgstr "" #: elf64-ppc.c:8271 #, c-format msgid "long branch stub `%s' offset overflow" msgstr "" #: elf64-ppc.c:8346 #, c-format msgid "can't find branch stub `%s'" msgstr "" #: elf64-ppc.c:8412 elf64-ppc.c:8488 #, c-format msgid "linkage table error against `%s'" msgstr "" #: elf64-ppc.c:8617 #, c-format msgid "can't build branch stub `%s'" msgstr "" #: elf64-ppc.c:9069 msgid "%B section %A exceeds stub group size" msgstr "" #: elf64-ppc.c:9681 msgid "stubs don't match calculated size" msgstr "" #: elf64-ppc.c:9693 #, c-format msgid "" "linker stubs in %u group%s\n" " branch %lu\n" " toc adjust %lu\n" " long branch %lu\n" " long toc adj %lu\n" " plt call %lu" msgstr "" #: elf64-ppc.c:10377 msgid "" "%B(%A+0x%lx): automatic multiple TOCs not supported using your crt files; " "recompile with -mminimal-toc or upgrade gcc" msgstr "" #: elf64-ppc.c:10385 msgid "" "%B(%A+0x%lx): sibling call optimization to `%s' does not allow automatic " "multiple TOCs; recompile with -mminimal-toc or -fno-optimize-sibling-calls, " "or make `%s' extern" msgstr "" #: elf64-ppc.c:11036 msgid "%B: relocation %s is not supported for symbol %s." msgstr "" #: elf64-ppc.c:11118 msgid "%B: error: relocation %s not a multiple of %d" msgstr "" #: elf64-sh64.c:1700 #, c-format msgid "%s: error: unaligned relocation type %d at %08x reloc %08x\n" msgstr "" #: elf64-sparc.c:438 msgid "%B: Only registers %%g[2367] can be declared using STT_REGISTER" msgstr "" #: elf64-sparc.c:458 msgid "Register %%g%d used incompatibly: %s in %B, previously %s in %B" msgstr "" #: elf64-sparc.c:481 msgid "Symbol `%s' has differing types: REGISTER in %B, previously %s in %B" msgstr "" #: elf64-sparc.c:526 msgid "Symbol `%s' has differing types: %s in %B, previously REGISTER in %B" msgstr "" #: elf64-sparc.c:677 msgid "%B: linking UltraSPARC specific with HAL specific code" msgstr "" #: elf64-x86-64.c:798 elf64-x86-64.c:958 elf64-x86-64.c:2359 msgid "" "%B: relocation %s against `%s' can not be used when making a shared object; " "recompile with -fPIC" msgstr "" #: elf64-x86-64.c:889 msgid "%B: %s' accessed both as normal and thread local symbol" msgstr "" #: elf64-x86-64.c:2271 msgid "" "%B: relocation R_X86_64_GOTOFF64 against protected function `%s' can not be " "used when making a shared object" msgstr "" #: elf64-x86-64.c:2355 msgid "" "%B: relocation R_X86_64_PC32 against protected function `%s' can not be used " "when making a shared object" msgstr "" #: elf-attrs.c:581 msgid "ERROR: %B: Must be processed by '%s' toolchain" msgstr "" #: elf-attrs.c:601 elf-attrs.c:620 msgid "ERROR: %B: Incompatible object tag '%s':%d" msgstr "" #: elf.c:309 msgid "%B: invalid string offset %u >= %lu for section `%s'" msgstr "" #: elf.c:411 msgid "%B symbol number %lu references nonexistent SHT_SYMTAB_SHNDX section" msgstr "" #: elf.c:564 msgid "%B: Corrupt size field in group section header: 0x%lx" msgstr "" #: elf.c:600 msgid "%B: invalid SHT_GROUP entry" msgstr "" #: elf.c:670 msgid "%B: no group info for section %A" msgstr "" #: elf.c:700 elf.c:3251 elflink.c:9120 msgid "%B: warning: sh_link not set for section `%A'" msgstr "" #: elf.c:716 msgid "%B: sh_link [%d] in section `%A' is incorrect" msgstr "" #: elf.c:751 msgid "%B: unknown [%d] section `%s' in group [%s]" msgstr "" #: elf.c:1160 #, c-format msgid "" "\n" "Program Header:\n" msgstr "" #: elf.c:1202 #, c-format msgid "" "\n" "Dynamic Section:\n" msgstr "" #: elf.c:1328 #, c-format msgid "" "\n" "Version definitions:\n" msgstr "" #: elf.c:1353 #, c-format msgid "" "\n" "Version References:\n" msgstr "" #: elf.c:1358 #, c-format msgid " required from %s:\n" msgstr "" #: elf.c:2054 msgid "%B: invalid link %lu for reloc section %s (index %u)" msgstr "" #: elf.c:2222 msgid "" "%B: don't know how to handle allocated, application specific section `%s' [0x" "%8x]" msgstr "" #: elf.c:2234 msgid "%B: don't know how to handle processor specific section `%s' [0x%8x]" msgstr "" #: elf.c:2245 msgid "%B: don't know how to handle OS specific section `%s' [0x%8x]" msgstr "" #: elf.c:2255 msgid "%B: don't know how to handle section `%s' [0x%8x]" msgstr "" #: elf.c:3208 msgid "%B: sh_link of section `%A' points to discarded section `%A' of `%B'" msgstr "" #: elf.c:3231 msgid "%B: sh_link of section `%A' points to removed section `%A' of `%B'" msgstr "" #: elf.c:4472 msgid "" "%B: The first section in the PT_DYNAMIC segment is not the .dynamic section" msgstr "" #: elf.c:4495 msgid "%B: Not enough room for program headers, try linking with -N" msgstr "" #: elf.c:4573 msgid "%B: section %A lma 0x%lx overlaps previous sections" msgstr "" #: elf.c:4671 msgid "%B: section `%A' can't be allocated in segment %d" msgstr "" #: elf.c:4720 msgid "%B: warning: allocated section `%s' not in segment" msgstr "" #: elf.c:5209 msgid "%B: symbol `%s' required but not present" msgstr "" #: elf.c:5522 msgid "%B: warning: Empty loadable segment detected, is this intentional ?\n" msgstr "" #: elf.c:6425 #, c-format msgid "" "Unable to find equivalent output section for symbol '%s' from section '%s'" msgstr "" #: elf.c:7399 msgid "%B: unsupported relocation type %s" msgstr "" #: elfcode.h:810 #, c-format msgid "warning: %s has a corrupt string table index - ignoring" msgstr "" #: elfcode.h:1176 #, c-format msgid "%s: version count (%ld) does not match symbol count (%ld)" msgstr "" #: elfcode.h:1409 #, c-format msgid "%s(%s): relocation %d has invalid symbol index %ld" msgstr "" #: elf-eh-frame.c:822 msgid "" "%P: fde encoding in %B(%A) prevents .eh_frame_hdr table being created.\n" msgstr "" #: elf-eh-frame.c:973 msgid "%P: error in %B(%A); no .eh_frame_hdr table will be created.\n" msgstr "" #: elf-hppa.h:2218 elf-hppa.h:2232 msgid "%B(%A): warning: unresolvable relocation against symbol `%s'" msgstr "" #: elflink.c:1004 msgid "" "%s: TLS definition in %B section %A mismatches non-TLS definition in %B " "section %A" msgstr "" #: elflink.c:1008 msgid "%s: TLS reference in %B mismatches non-TLS reference in %B" msgstr "" #: elflink.c:1012 msgid "%s: TLS definition in %B section %A mismatches non-TLS reference in %B" msgstr "" #: elflink.c:1016 msgid "%s: TLS reference in %B mismatches non-TLS definition in %B section %A" msgstr "" #: elflink.c:1630 msgid "%B: unexpected redefinition of indirect versioned symbol `%s'" msgstr "" #: elflink.c:1950 msgid "%B: version node not found for symbol %s" msgstr "" #: elflink.c:2098 msgid "" "%B: bad reloc symbol index (0x%lx >= 0x%lx) for offset 0x%lx in section `%A'" msgstr "" #: elflink.c:2290 msgid "%B: relocation size mismatch in %B section %A" msgstr "" #: elflink.c:2598 #, c-format msgid "warning: type and size of dynamic symbol `%s' are not defined" msgstr "" #: elflink.c:3893 msgid "%B: %s: invalid version %u (max %d)" msgstr "" #: elflink.c:3929 msgid "%B: %s: invalid needed version %d" msgstr "" #: elflink.c:4112 msgid "" "Warning: alignment %u of common symbol `%s' in %B is greater than the " "alignment (%u) of its section %A" msgstr "" #: elflink.c:4118 msgid "Warning: alignment %u of symbol `%s' in %B is smaller than %u in %B" msgstr "" #: elflink.c:4133 msgid "Warning: size of symbol `%s' changed from %lu in %B to %lu in %B" msgstr "" -#: elflink.c:4309 +#: elflink.c:4389 #, c-format -msgid "%B: invalid DSO for symbol `%s' definition" +msgid "undefined reference to symbol `%s' (try adding -l%s%.*s)" msgstr "" #: elflink.c:5535 #, c-format msgid "%s: undefined version: %s" msgstr "" #: elflink.c:5603 msgid "%B: .preinit_array section is not allowed in DSO" msgstr "" #: elflink.c:6621 #, c-format msgid "undefined %s reference in complex symbol: %s" msgstr "" #: elflink.c:6776 #, c-format msgid "unknown operator '%c' in complex symbol" msgstr "" #: elflink.c:7315 elflink.c:7332 elflink.c:7369 elflink.c:7386 msgid "%B: Unable to sort relocs - they are in more than one size" msgstr "" #: elflink.c:7346 elflink.c:7400 msgid "%B: Unable to sort relocs - they are of an unknown size" msgstr "" #: elflink.c:7449 msgid "Not enough memory to sort relocations" msgstr "" #: elflink.c:7636 msgid "%B: Too many sections: %d (>= %d)" msgstr "" #: elflink.c:7870 msgid "%B: %s symbol `%s' in %B is referenced by DSO" msgstr "" #: elflink.c:7953 msgid "%B: could not find output section %A for input section %A" msgstr "" #: elflink.c:8050 msgid "%B: %s symbol `%s' isn't defined" msgstr "" #: elflink.c:8546 msgid "" "error: %B contains a reloc (0x%s) for section %A that references a non-" "existent global symbol" msgstr "" #: elflink.c:8580 msgid "" "%X`%s' referenced in section `%A' of %B: defined in discarded section `%A' " "of %B\n" msgstr "" #: elflink.c:9198 msgid "%A has both ordered [`%A' in %B] and unordered [`%A' in %B] sections" msgstr "" #: elflink.c:9203 #, c-format msgid "%A has both ordered and unordered sections" msgstr "" #: elflink.c:10090 elflink.c:10134 msgid "%B: could not find output section %s" msgstr "" #: elflink.c:10095 #, c-format msgid "warning: %s section has zero size" msgstr "" #: elflink.c:10199 msgid "%P: warning: creating a DT_TEXTREL in a shared object.\n" msgstr "" #: elflink.c:10567 msgid "Removing unused section '%s' in file '%B'" msgstr "" #: elflink.c:10758 msgid "Warning: gc-sections option ignored" msgstr "" #: elflink.c:11249 msgid "%P%X: can not read symbols: %E\n" msgstr "" #: elflink.c:11391 msgid "%B: ignoring duplicate section `%A'" msgstr "" #: elflink.c:11398 elflink.c:11405 msgid "%B: duplicate section `%A' has different size" msgstr "" #: elflink.c:11413 elflink.c:11418 msgid "%B: warning: could not read contents of section `%A'" msgstr "" #: elflink.c:11422 msgid "%B: warning: duplicate section `%A' has different contents" msgstr "" #: elf-m10300.c:1531 msgid "" "error: inappropriate relocation type for shared library (did you forget -" "fpic?)" msgstr "" #: elf-m10300.c:1534 msgid "internal error: suspicious relocation type used in shared library" msgstr "" #: elfxx-mips.c:986 msgid "static procedure (no name)" msgstr "" #: elfxx-mips.c:4657 msgid "%B: %A+0x%lx: jump to stub routine which is not jal" msgstr "" #: elfxx-mips.c:5320 elfxx-mips.c:5540 msgid "%B: Warning: bad `%s' option size %u smaller than its header" msgstr "" #: elfxx-mips.c:6399 msgid "%B: Malformed reloc detected for section %s" msgstr "" #: elfxx-mips.c:6441 msgid "%B: GOT reloc at 0x%lx not expected in executables" msgstr "" #: elfxx-mips.c:6511 msgid "%B: CALL16 reloc at 0x%lx not against global symbol" msgstr "" #: elfxx-mips.c:7874 msgid "" "%B: Can't find matching LO16 reloc against `%s' for %s at 0x%lx in section `%" "A'" msgstr "" #: elfxx-mips.c:10640 #, c-format msgid "%s: illegal section name `%s'" msgstr "" #: elfxx-mips.c:11010 elfxx-mips.c:11029 msgid "Warning: %B uses -msingle-float, %B uses -mdouble-float" msgstr "" #: elfxx-mips.c:11085 msgid "%B: endianness incompatible with that of the selected emulation" msgstr "" #: elfxx-mips.c:11097 msgid "%B: ABI is incompatible with that of the selected emulation" msgstr "" #: elfxx-mips.c:11197 msgid "%B: linking 32-bit code with 64-bit code" msgstr "" #: elfxx-mips.c:11225 msgid "%B: linking %s module with previous %s modules" msgstr "" #: elfxx-mips.c:11248 msgid "%B: ABI mismatch: linking %s module with previous %s modules" msgstr "" #: elfxx-mips.c:11313 #, c-format msgid " [abi=O32]" msgstr "" #: elfxx-mips.c:11315 #, c-format msgid " [abi=O64]" msgstr "" #: elfxx-mips.c:11317 #, c-format msgid " [abi=EABI32]" msgstr "" #: elfxx-mips.c:11319 #, c-format msgid " [abi=EABI64]" msgstr "" #: elfxx-mips.c:11321 #, c-format msgid " [abi unknown]" msgstr "" #: elfxx-mips.c:11323 #, c-format msgid " [abi=N32]" msgstr "" #: elfxx-mips.c:11325 #, c-format msgid " [abi=64]" msgstr "" #: elfxx-mips.c:11327 #, c-format msgid " [no abi set]" msgstr "" #: elfxx-mips.c:11348 #, c-format msgid " [unknown ISA]" msgstr "" #: elfxx-mips.c:11359 #, c-format msgid " [not 32bitmode]" msgstr "" #: elfxx-sparc.c:428 #, c-format msgid "invalid relocation type %d" msgstr "" #: elfxx-sparc.c:2899 msgid "%B: probably compiled without -fPIC?" msgstr "" #: i386linux.c:454 m68klinux.c:458 sparclinux.c:453 #, c-format msgid "Output file requires shared library `%s'\n" msgstr "" #: i386linux.c:462 m68klinux.c:466 sparclinux.c:461 #, c-format msgid "Output file requires shared library `%s.so.%s'\n" msgstr "" #: i386linux.c:651 i386linux.c:701 m68klinux.c:658 m68klinux.c:706 #: sparclinux.c:651 sparclinux.c:701 #, c-format msgid "Symbol %s not defined for fixups\n" msgstr "" #: i386linux.c:725 m68klinux.c:730 sparclinux.c:725 msgid "Warning: fixup count mismatch\n" msgstr "" #: ieee.c:157 #, c-format msgid "%s: string too long (%d chars, max 65535)" msgstr "" #: ieee.c:284 #, c-format msgid "%s: unrecognized symbol `%s' flags 0x%x" msgstr "" #: ieee.c:786 msgid "%B: unimplemented ATI record %u for symbol %u" msgstr "" #: ieee.c:810 msgid "%B: unexpected ATN type %d in external part" msgstr "" #: ieee.c:832 msgid "%B: unexpected type after ATN" msgstr "" #: ihex.c:228 msgid "%B:%d: unexpected character `%s' in Intel Hex file" msgstr "" #: ihex.c:335 msgid "%B:%u: bad checksum in Intel Hex file (expected %u, found %u)" msgstr "" #: ihex.c:390 msgid "%B:%u: bad extended address record length in Intel Hex file" msgstr "" #: ihex.c:407 msgid "%B:%u: bad extended start address length in Intel Hex file" msgstr "" #: ihex.c:424 msgid "%B:%u: bad extended linear address record length in Intel Hex file" msgstr "" #: ihex.c:441 msgid "%B:%u: bad extended linear start address length in Intel Hex file" msgstr "" #: ihex.c:458 msgid "%B:%u: unrecognized ihex type %u in Intel Hex file" msgstr "" #: ihex.c:577 msgid "%B: internal error in ihex_read_section" msgstr "" #: ihex.c:611 msgid "%B: bad section length in ihex_read_section" msgstr "" #: ihex.c:823 #, c-format msgid "%s: address 0x%s out of range for Intel Hex file" msgstr "" #: libbfd.c:978 #, c-format msgid "Deprecated %s called at %s line %d in %s\n" msgstr "" #: libbfd.c:981 #, c-format msgid "Deprecated %s called\n" msgstr "" #: linker.c:1877 msgid "%B: indirect symbol `%s' to `%s' is a loop" msgstr "" #: linker.c:2743 #, c-format msgid "Attempt to do relocatable link with %s input and %s output" msgstr "" #: linker.c:3044 msgid "%B: warning: ignoring duplicate section `%A'\n" msgstr "" #: linker.c:3058 msgid "%B: warning: duplicate section `%A' has different size\n" msgstr "" #: merge.c:818 #, c-format msgid "%s: access beyond end of merged section (%ld)" msgstr "" #: mmo.c:454 #, c-format msgid "%s: No core to allocate section name %s\n" msgstr "" #: mmo.c:529 #, c-format msgid "%s: No core to allocate a symbol %d bytes long\n" msgstr "" #: mmo.c:1185 #, c-format msgid "%s: invalid mmo file: initialization value for $255 is not `Main'\n" msgstr "" #: mmo.c:1330 #, c-format msgid "" "%s: unsupported wide character sequence 0x%02X 0x%02X after symbol name " "starting with `%s'\n" msgstr "" #: mmo.c:1564 #, c-format msgid "%s: invalid mmo file: unsupported lopcode `%d'\n" msgstr "" #: mmo.c:1574 #, c-format msgid "%s: invalid mmo file: expected YZ = 1 got YZ = %d for lop_quote\n" msgstr "" #: mmo.c:1610 #, c-format msgid "%s: invalid mmo file: expected z = 1 or z = 2, got z = %d for lop_loc\n" msgstr "" #: mmo.c:1656 #, c-format msgid "" "%s: invalid mmo file: expected z = 1 or z = 2, got z = %d for lop_fixo\n" msgstr "" #: mmo.c:1695 #, c-format msgid "%s: invalid mmo file: expected y = 0, got y = %d for lop_fixrx\n" msgstr "" #: mmo.c:1704 #, c-format msgid "" "%s: invalid mmo file: expected z = 16 or z = 24, got z = %d for lop_fixrx\n" msgstr "" #: mmo.c:1727 #, c-format msgid "" "%s: invalid mmo file: leading byte of operand word must be 0 or 1, got %d " "for lop_fixrx\n" msgstr "" #: mmo.c:1750 #, c-format msgid "%s: cannot allocate file name for file number %d, %d bytes\n" msgstr "" #: mmo.c:1770 #, c-format msgid "" "%s: invalid mmo file: file number %d `%s', was already entered as `%s'\n" msgstr "" #: mmo.c:1783 #, c-format msgid "" "%s: invalid mmo file: file name for number %d was not specified before use\n" msgstr "" #: mmo.c:1890 #, c-format msgid "" "%s: invalid mmo file: fields y and z of lop_stab non-zero, y: %d, z: %d\n" msgstr "" #: mmo.c:1926 #, c-format msgid "%s: invalid mmo file: lop_end not last item in file\n" msgstr "" #: mmo.c:1939 #, c-format msgid "" "%s: invalid mmo file: YZ of lop_end (%ld) not equal to the number of tetras " "to the preceding lop_stab (%ld)\n" msgstr "" #: mmo.c:2649 #, c-format msgid "%s: invalid symbol table: duplicate symbol `%s'\n" msgstr "" #: mmo.c:2892 #, c-format msgid "" "%s: Bad symbol definition: `Main' set to %s rather than the start address %" "s\n" msgstr "" #: mmo.c:2984 #, c-format msgid "" "%s: warning: symbol table too large for mmo, larger than 65535 32-bit words: " "%d. Only `Main' will be emitted.\n" msgstr "" #: mmo.c:3029 #, c-format msgid "%s: internal error, symbol table changed size from %d to %d words\n" msgstr "" #: mmo.c:3081 #, c-format msgid "%s: internal error, internal register section %s had contents\n" msgstr "" #: mmo.c:3132 #, c-format msgid "%s: no initialized registers; section length 0\n" msgstr "" #: mmo.c:3138 #, c-format msgid "%s: too many initialized registers; section length %ld\n" msgstr "" #: mmo.c:3143 #, c-format msgid "" "%s: invalid start address for initialized registers of length %ld: 0x%lx%" "08lx\n" msgstr "" #: oasys.c:880 #, c-format msgid "%s: can not represent section `%s' in oasys" msgstr "" #: osf-core.c:137 #, c-format msgid "Unhandled OSF/1 core file section type %d\n" msgstr "" #. XXX code yet to be written. #: peicode.h:757 msgid "%B: Unhandled import type; %x" msgstr "" #: peicode.h:762 msgid "%B: Unrecognised import type; %x" msgstr "" #: peicode.h:776 msgid "%B: Unrecognised import name type; %x" msgstr "" #: peicode.h:1159 msgid "%B: Unrecognised machine type (0x%x) in Import Library Format archive" msgstr "" #: peicode.h:1171 msgid "" "%B: Recognised but unhandled machine type (0x%x) in Import Library Format " "archive" msgstr "" #: peicode.h:1189 msgid "%B: size field is zero in Import Library Format header" msgstr "" #: peicode.h:1220 msgid "%B: string not null terminated in ILF object file." msgstr "" #: pe-mips.c:605 msgid "%B: `ld -r' not supported with PE MIPS objects\n" msgstr "" #. OK, at this point the following variables are set up: #. src = VMA of the memory we're fixing up #. mem = pointer to memory we're fixing up #. val = VMA of what we need to refer to. #: pe-mips.c:721 msgid "%B: unimplemented %s\n" msgstr "" #: pe-mips.c:747 msgid "%B: jump too far away\n" msgstr "" #: pe-mips.c:773 msgid "%B: bad pair/reflo after refhi\n" msgstr "" #: ppcboot.c:412 #, c-format msgid "" "\n" "ppcboot header:\n" msgstr "" #: ppcboot.c:413 #, c-format msgid "Entry offset = 0x%.8lx (%ld)\n" msgstr "" #: ppcboot.c:414 #, c-format msgid "Length = 0x%.8lx (%ld)\n" msgstr "" #: ppcboot.c:417 #, c-format msgid "Flag field = 0x%.2x\n" msgstr "" #: ppcboot.c:423 #, c-format msgid "Partition name = \"%s\"\n" msgstr "" #: ppcboot.c:442 #, c-format msgid "" "\n" "Partition[%d] start = { 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x }\n" msgstr "" #: ppcboot.c:448 #, c-format msgid "Partition[%d] end = { 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x }\n" msgstr "" #: ppcboot.c:454 #, c-format msgid "Partition[%d] sector = 0x%.8lx (%ld)\n" msgstr "" #: ppcboot.c:455 #, c-format msgid "Partition[%d] length = 0x%.8lx (%ld)\n" msgstr "" #: som.c:5088 #, c-format msgid "" "\n" "Exec Auxiliary Header\n" msgstr "" #: som.c:5349 msgid "som_sizeof_headers unimplemented" msgstr "" #: srec.c:259 msgid "%B:%d: Unexpected character `%s' in S-record file\n" msgstr "" #: stabs.c:277 msgid "%B(%A+0x%lx): Stabs entry has invalid string index." msgstr "" #: syms.c:1056 msgid "Unsupported .stab relocation" msgstr "" #: vms-gsd.c:337 #, c-format msgid "bfd_make_section (%s) failed" msgstr "" #: vms-gsd.c:352 #, c-format msgid "bfd_set_section_flags (%s, %x) failed" msgstr "" #: vms-gsd.c:387 #, c-format msgid "Size mismatch section %s=%lx, %s=%lx" msgstr "" #: vms-gsd.c:678 #, c-format msgid "unknown gsd/egsd subtype %d" msgstr "" #: vms-hdr.c:327 msgid "Object module NOT error-free !\n" msgstr "" #: vms-misc.c:473 #, c-format msgid "Stack overflow (%d) in _bfd_vms_push" msgstr "" #: vms-misc.c:488 msgid "Stack underflow in _bfd_vms_pop" msgstr "" #: vms-misc.c:802 msgid "_bfd_vms_output_counted called with zero bytes" msgstr "" #: vms-misc.c:807 msgid "_bfd_vms_output_counted called with too many bytes" msgstr "" #: vms-misc.c:925 #, c-format msgid "Symbol %s replaced by %s\n" msgstr "" #: vms-misc.c:984 #, c-format msgid "failed to enter %s" msgstr "" #: vms-tir.c:55 msgid "No Mem !" msgstr "" #: vms-tir.c:298 #, c-format msgid "bad section index in %s" msgstr "" #: vms-tir.c:311 #, c-format msgid "unsupported STA cmd %s" msgstr "" #: vms-tir.c:316 vms-tir.c:1118 #, c-format msgid "reserved STA cmd %d" msgstr "" #: vms-tir.c:408 vms-tir.c:430 #, c-format msgid "%s: no symbol \"%s\"" msgstr "" #. Unsigned shift. #. Rotate. #. Redefine symbol to current location. #. Define a literal. #: vms-tir.c:495 vms-tir.c:604 vms-tir.c:702 vms-tir.c:719 vms-tir.c:726 #: vms-tir.c:734 vms-tir.c:1438 #, c-format msgid "%s: not supported" msgstr "" #: vms-tir.c:500 vms-tir.c:1295 #, c-format msgid "%s: not implemented" msgstr "" #: vms-tir.c:504 vms-tir.c:1299 #, c-format msgid "reserved STO cmd %d" msgstr "" #: vms-tir.c:619 vms-tir.c:1443 #, c-format msgid "reserved OPR cmd %d" msgstr "" #: vms-tir.c:679 vms-tir.c:1507 #, c-format msgid "reserved CTL cmd %d" msgstr "" #. stack byte from image #. arg: none. #: vms-tir.c:1026 msgid "stack-from-image not implemented" msgstr "" #: vms-tir.c:1044 msgid "stack-entry-mask not fully implemented" msgstr "" #. compare procedure argument #. arg: cs symbol name #. by argument index #. da argument descriptor #. #. compare argument descriptor with symbol argument (ARG$V_PASSMECH) #. and stack TRUE (args match) or FALSE (args dont match) value. #: vms-tir.c:1058 msgid "PASSMECH not fully implemented" msgstr "" #: vms-tir.c:1077 msgid "stack-local-symbol not fully implemented" msgstr "" #: vms-tir.c:1090 msgid "stack-literal not fully implemented" msgstr "" #: vms-tir.c:1111 msgid "stack-local-symbol-entry-point-mask not fully implemented" msgstr "" #: vms-tir.c:1385 vms-tir.c:1397 vms-tir.c:1409 vms-tir.c:1421 vms-tir.c:1486 #: vms-tir.c:1494 vms-tir.c:1502 #, c-format msgid "%s: not fully implemented" msgstr "" #: vms-tir.c:1560 #, c-format msgid "obj code %d not found" msgstr "" #: vms-tir.c:1868 #, c-format msgid "SEC_RELOC with no relocs in section %s" msgstr "" #: vms-tir.c:2150 #, c-format msgid "Unhandled relocation %s" msgstr "" #: xcofflink.c:565 #, c-format msgid "%s: XCOFF shared object when not producing XCOFF output" msgstr "" #: xcofflink.c:586 #, c-format msgid "%s: dynamic object with no .loader section" msgstr "" #: xcofflink.c:1149 msgid "%B: `%s' has line numbers but no enclosing section" msgstr "" #: xcofflink.c:1201 msgid "%B: class %d symbol `%s' has no aux entries" msgstr "" #: xcofflink.c:1224 msgid "%B: symbol `%s' has unrecognized csect type %d" msgstr "" #: xcofflink.c:1236 msgid "%B: bad XTY_ER symbol `%s': class %d scnum %d scnlen %d" msgstr "" #: xcofflink.c:1272 msgid "%B: XMC_TC0 symbol `%s' is class %d scnlen %d" msgstr "" #: xcofflink.c:1418 msgid "%B: csect `%s' not in enclosing section" msgstr "" #: xcofflink.c:1525 msgid "%B: misplaced XTY_LD `%s'" msgstr "" #: xcofflink.c:1841 msgid "%B: reloc %s:%d not in csect" msgstr "" #: xcofflink.c:2639 #, c-format msgid "%s: no such symbol" msgstr "" #: xcofflink.c:2868 #, c-format msgid "warning: attempt to export undefined symbol `%s'" msgstr "" #: xcofflink.c:3035 msgid "error: undefined symbol __rtinit" msgstr "" #: xcofflink.c:3655 #, c-format msgid "TOC overflow: 0x%lx > 0x10000; try -mminimal-toc when compiling" msgstr "" #: xcofflink.c:4491 msgid "%B: loader reloc in unrecognized section `%A'" msgstr "" #: xcofflink.c:4512 msgid "%B: `%s' in loader reloc but not loader sym" msgstr "" #: xcofflink.c:4527 msgid "%B: loader reloc in read-only section %A" msgstr "" #: xcofflink.c:4936 xcofflink.c:4998 xcofflink.c:5293 #, c-format msgid "%s: loader reloc in unrecognized section `%s'" msgstr "" #: xcofflink.c:5304 #, c-format msgid "%s: `%s' in loader reloc but not loader sym" msgstr "" #: elf32-ia64.c:1168 elf64-ia64.c:1168 msgid "" "%B: Can't relax br at 0x%lx in section `%A'. Please use brl or indirect " "branch." msgstr "" #: elf32-ia64.c:2909 elf64-ia64.c:2909 msgid "@pltoff reloc against local symbol" msgstr "" #: elf32-ia64.c:4522 elf64-ia64.c:4522 #, c-format msgid "%s: short data segment overflowed (0x%lx >= 0x400000)" msgstr "" #: elf32-ia64.c:4533 elf64-ia64.c:4533 #, c-format msgid "%s: __gp does not cover short data segment" msgstr "" #: elf32-ia64.c:4807 elf64-ia64.c:4807 msgid "%B: non-pic code with imm relocation against dynamic symbol `%s'" msgstr "" #: elf32-ia64.c:4874 elf64-ia64.c:4874 msgid "%B: @gprel relocation against dynamic symbol %s" msgstr "" #: elf32-ia64.c:4937 elf64-ia64.c:4937 msgid "%B: linking non-pic code in a position independent executable" msgstr "" #: elf32-ia64.c:5074 elf64-ia64.c:5074 msgid "%B: @internal branch to dynamic symbol %s" msgstr "" #: elf32-ia64.c:5076 elf64-ia64.c:5076 msgid "%B: speculation fixup to dynamic symbol %s" msgstr "" #: elf32-ia64.c:5078 elf64-ia64.c:5078 msgid "%B: @pcrel relocation against dynamic symbol %s" msgstr "" #: elf32-ia64.c:5278 elf64-ia64.c:5278 msgid "unsupported reloc" msgstr "" #: elf32-ia64.c:5311 elf64-ia64.c:5311 msgid "" "%B: Can't relax br (%s) to `%s' at 0x%lx in section `%A' with size 0x%lx (> " "0x1000000)." msgstr "" #: elf32-ia64.c:5572 elf64-ia64.c:5572 msgid "%B: linking trap-on-NULL-dereference with non-trapping files" msgstr "" #: elf32-ia64.c:5581 elf64-ia64.c:5581 msgid "%B: linking big-endian files with little-endian files" msgstr "" #: elf32-ia64.c:5590 elf64-ia64.c:5590 msgid "%B: linking 64-bit files with 32-bit files" msgstr "" #: elf32-ia64.c:5599 elf64-ia64.c:5599 msgid "%B: linking constant-gp files with non-constant-gp files" msgstr "" #: elf32-ia64.c:5609 elf64-ia64.c:5609 msgid "%B: linking auto-pic files with non-auto-pic files" msgstr "" #: peigen.c:998 pepigen.c:998 pex64igen.c:998 #, c-format msgid "%s: line number overflow: 0x%lx > 0xffff" msgstr "" #: peigen.c:1025 pepigen.c:1025 pex64igen.c:1025 msgid "Export Directory [.edata (or where ever we found it)]" msgstr "" #: peigen.c:1026 pepigen.c:1026 pex64igen.c:1026 msgid "Import Directory [parts of .idata]" msgstr "" #: peigen.c:1027 pepigen.c:1027 pex64igen.c:1027 msgid "Resource Directory [.rsrc]" msgstr "" #: peigen.c:1028 pepigen.c:1028 pex64igen.c:1028 msgid "Exception Directory [.pdata]" msgstr "" #: peigen.c:1029 pepigen.c:1029 pex64igen.c:1029 msgid "Security Directory" msgstr "" #: peigen.c:1030 pepigen.c:1030 pex64igen.c:1030 msgid "Base Relocation Directory [.reloc]" msgstr "" #: peigen.c:1031 pepigen.c:1031 pex64igen.c:1031 msgid "Debug Directory" msgstr "" #: peigen.c:1032 pepigen.c:1032 pex64igen.c:1032 msgid "Description Directory" msgstr "" #: peigen.c:1033 pepigen.c:1033 pex64igen.c:1033 msgid "Special Directory" msgstr "" #: peigen.c:1034 pepigen.c:1034 pex64igen.c:1034 msgid "Thread Storage Directory [.tls]" msgstr "" #: peigen.c:1035 pepigen.c:1035 pex64igen.c:1035 msgid "Load Configuration Directory" msgstr "" #: peigen.c:1036 pepigen.c:1036 pex64igen.c:1036 msgid "Bound Import Directory" msgstr "" #: peigen.c:1037 pepigen.c:1037 pex64igen.c:1037 msgid "Import Address Table Directory" msgstr "" #: peigen.c:1038 pepigen.c:1038 pex64igen.c:1038 msgid "Delay Import Directory" msgstr "" #: peigen.c:1039 pepigen.c:1039 pex64igen.c:1039 msgid "CLR Runtime Header" msgstr "" #: peigen.c:1040 pepigen.c:1040 pex64igen.c:1040 msgid "Reserved" msgstr "" #: peigen.c:1100 pepigen.c:1100 pex64igen.c:1100 #, c-format msgid "" "\n" "There is an import table, but the section containing it could not be found\n" msgstr "" #: peigen.c:1105 pepigen.c:1105 pex64igen.c:1105 #, c-format msgid "" "\n" "There is an import table in %s at 0x%lx\n" msgstr "" #: peigen.c:1148 pepigen.c:1148 pex64igen.c:1148 #, c-format msgid "" "\n" "Function descriptor located at the start address: %04lx\n" msgstr "" #: peigen.c:1151 pepigen.c:1151 pex64igen.c:1151 #, c-format msgid "\tcode-base %08lx toc (loadable/actual) %08lx/%08lx\n" msgstr "" #: peigen.c:1159 pepigen.c:1159 pex64igen.c:1159 #, c-format msgid "" "\n" "No reldata section! Function descriptor not decoded.\n" msgstr "" #: peigen.c:1164 pepigen.c:1164 pex64igen.c:1164 #, c-format msgid "" "\n" "The Import Tables (interpreted %s section contents)\n" msgstr "" #: peigen.c:1167 pepigen.c:1167 pex64igen.c:1167 #, c-format msgid "" " vma: Hint Time Forward DLL First\n" " Table Stamp Chain Name Thunk\n" msgstr "" #: peigen.c:1215 pepigen.c:1215 pex64igen.c:1215 #, c-format msgid "" "\n" "\tDLL Name: %s\n" msgstr "" #: peigen.c:1226 pepigen.c:1226 pex64igen.c:1226 #, c-format msgid "\tvma: Hint/Ord Member-Name Bound-To\n" msgstr "" #: peigen.c:1251 pepigen.c:1251 pex64igen.c:1251 #, c-format msgid "" "\n" "There is a first thunk, but the section containing it could not be found\n" msgstr "" #: peigen.c:1416 pepigen.c:1416 pex64igen.c:1416 #, c-format msgid "" "\n" "There is an export table, but the section containing it could not be found\n" msgstr "" #: peigen.c:1425 pepigen.c:1425 pex64igen.c:1425 #, c-format msgid "" "\n" "There is an export table in %s, but it does not fit into that section\n" msgstr "" #: peigen.c:1431 pepigen.c:1431 pex64igen.c:1431 #, c-format msgid "" "\n" "There is an export table in %s at 0x%lx\n" msgstr "" #: peigen.c:1459 pepigen.c:1459 pex64igen.c:1459 #, c-format msgid "" "\n" "The Export Tables (interpreted %s section contents)\n" "\n" msgstr "" #: peigen.c:1463 pepigen.c:1463 pex64igen.c:1463 #, c-format msgid "Export Flags \t\t\t%lx\n" msgstr "" #: peigen.c:1466 pepigen.c:1466 pex64igen.c:1466 #, c-format msgid "Time/Date stamp \t\t%lx\n" msgstr "" #: peigen.c:1469 pepigen.c:1469 pex64igen.c:1469 #, c-format msgid "Major/Minor \t\t\t%d/%d\n" msgstr "" #: peigen.c:1472 pepigen.c:1472 pex64igen.c:1472 #, c-format msgid "Name \t\t\t\t" msgstr "" #: peigen.c:1478 pepigen.c:1478 pex64igen.c:1478 #, c-format msgid "Ordinal Base \t\t\t%ld\n" msgstr "" #: peigen.c:1481 pepigen.c:1481 pex64igen.c:1481 #, c-format msgid "Number in:\n" msgstr "" #: peigen.c:1484 pepigen.c:1484 pex64igen.c:1484 #, c-format msgid "\tExport Address Table \t\t%08lx\n" msgstr "" #: peigen.c:1488 pepigen.c:1488 pex64igen.c:1488 #, c-format msgid "\t[Name Pointer/Ordinal] Table\t%08lx\n" msgstr "" #: peigen.c:1491 pepigen.c:1491 pex64igen.c:1491 #, c-format msgid "Table Addresses\n" msgstr "" #: peigen.c:1494 pepigen.c:1494 pex64igen.c:1494 #, c-format msgid "\tExport Address Table \t\t" msgstr "" #: peigen.c:1499 pepigen.c:1499 pex64igen.c:1499 #, c-format msgid "\tName Pointer Table \t\t" msgstr "" #: peigen.c:1504 pepigen.c:1504 pex64igen.c:1504 #, c-format msgid "\tOrdinal Table \t\t\t" msgstr "" #: peigen.c:1518 pepigen.c:1518 pex64igen.c:1518 #, c-format msgid "" "\n" "Export Address Table -- Ordinal Base %ld\n" msgstr "" #: peigen.c:1537 pepigen.c:1537 pex64igen.c:1537 msgid "Forwarder RVA" msgstr "" #: peigen.c:1548 pepigen.c:1548 pex64igen.c:1548 msgid "Export RVA" msgstr "" #: peigen.c:1555 pepigen.c:1555 pex64igen.c:1555 #, c-format msgid "" "\n" "[Ordinal/Name Pointer] Table\n" msgstr "" #: peigen.c:1608 pepigen.c:1608 pex64igen.c:1608 #, c-format msgid "Warning, .pdata section size (%ld) is not a multiple of %d\n" msgstr "" #: peigen.c:1612 pepigen.c:1612 pex64igen.c:1612 #, c-format msgid "" "\n" "The Function Table (interpreted .pdata section contents)\n" msgstr "" #: peigen.c:1615 pepigen.c:1615 pex64igen.c:1615 #, c-format msgid " vma:\t\t\tBegin Address End Address Unwind Info\n" msgstr "" #: peigen.c:1617 pepigen.c:1617 pex64igen.c:1617 #, c-format msgid "" " vma:\t\tBegin End EH EH PrologEnd Exception\n" " \t\tAddress Address Handler Data Address Mask\n" msgstr "" #: peigen.c:1687 pepigen.c:1687 pex64igen.c:1687 #, c-format msgid " Register save millicode" msgstr "" #: peigen.c:1690 pepigen.c:1690 pex64igen.c:1690 #, c-format msgid " Register restore millicode" msgstr "" #: peigen.c:1693 pepigen.c:1693 pex64igen.c:1693 #, c-format msgid " Glue code sequence" msgstr "" #: peigen.c:1743 pepigen.c:1743 pex64igen.c:1743 #, c-format msgid "" "\n" "\n" "PE File Base Relocations (interpreted .reloc section contents)\n" msgstr "" #: peigen.c:1773 pepigen.c:1773 pex64igen.c:1773 #, c-format msgid "" "\n" "Virtual Address: %08lx Chunk size %ld (0x%lx) Number of fixups %ld\n" msgstr "" #: peigen.c:1786 pepigen.c:1786 pex64igen.c:1786 #, c-format msgid "\treloc %4d offset %4x [%4lx] %s" msgstr "" #. The MS dumpbin program reportedly ands with 0xff0f before #. printing the characteristics field. Not sure why. No reason to #. emulate it here. #: peigen.c:1825 pepigen.c:1825 pex64igen.c:1825 #, c-format msgid "" "\n" "Characteristics 0x%x\n" msgstr "" #: peigen.c:2086 pepigen.c:2086 pex64igen.c:2086 msgid "%B: unable to fill in DataDictionary[1] because .idata$2 is missing" msgstr "" #: peigen.c:2104 pepigen.c:2104 pex64igen.c:2104 msgid "%B: unable to fill in DataDictionary[1] because .idata$4 is missing" msgstr "" #: peigen.c:2123 pepigen.c:2123 pex64igen.c:2123 msgid "%B: unable to fill in DataDictionary[12] because .idata$5 is missing" msgstr "" #: peigen.c:2141 pepigen.c:2141 pex64igen.c:2141 msgid "" "%B: unable to fill in DataDictionary[PE_IMPORT_ADDRESS_TABLE (12)] because ." "idata$6 is missing" msgstr "" #: peigen.c:2161 pepigen.c:2161 pex64igen.c:2161 msgid "%B: unable to fill in DataDictionary[9] because __tls_used is missing" msgstr "" Index: projects/clang350-import/contrib/binutils =================================================================== --- projects/clang350-import/contrib/binutils (revision 275386) +++ projects/clang350-import/contrib/binutils (revision 275387) Property changes on: projects/clang350-import/contrib/binutils ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/binutils:r275364-275386 Index: projects/clang350-import/contrib/elftoolchain/elfcopy/archive.c =================================================================== --- projects/clang350-import/contrib/elftoolchain/elfcopy/archive.c (revision 275386) +++ projects/clang350-import/contrib/elftoolchain/elfcopy/archive.c (revision 275387) @@ -1,528 +1,528 @@ /*- * Copyright (c) 2007-2009 Kai Wang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #ifndef LIBELF_AR #include #include #endif /* ! LIBELF_AR */ #include "elfcopy.h" ELFTC_VCSID("$Id: archive.c 2370 2011-12-29 12:48:12Z jkoshy $"); #define _ARMAG_LEN 8 /* length of ar magic string */ #define _ARHDR_LEN 60 /* length of ar header */ #define _INIT_AS_CAP 128 /* initial archive string table size */ #define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */ #define _INIT_SYMNAME_CAP 1024 /* initial sn table size */ #define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */ #ifndef LIBELF_AR static void ac_read_objs(struct elfcopy *ecp, int ifd); static void ac_write_cleanup(struct elfcopy *ecp); static void ac_write_data(struct archive *a, const void *buf, size_t s); static void ac_write_objs(struct elfcopy *ecp, int ofd); #endif /* ! LIBELF_AR */ static void add_to_ar_str_table(struct elfcopy *elfcopy, const char *name); static void add_to_ar_sym_table(struct elfcopy *ecp, const char *name); static void extract_arsym(struct elfcopy *ecp); static void process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj); static void sync_ar(struct elfcopy *ecp); static void process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj) { struct stat sb; char *tempfile; int fd; /* Output to a temporary file. */ create_tempfile(&tempfile, &fd); if ((ecp->eout = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL) errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1)); elf_flagelf(ecp->eout, ELF_C_SET, ELF_F_LAYOUT); create_elf(ecp); elf_end(ecp->ein); elf_end(ecp->eout); free(obj->buf); obj->buf = NULL; /* Extract archive symbols. */ if (lseek(fd, 0, SEEK_SET) < 0) err(EXIT_FAILURE, "lseek failed for '%s'", tempfile); if ((ecp->eout = elf_begin(fd, ELF_C_READ, NULL)) == NULL) errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1)); extract_arsym(ecp); elf_end(ecp->eout); if (fstat(fd, &sb) == -1) err(EXIT_FAILURE, "fstat %s failed", tempfile); if (lseek(fd, 0, SEEK_SET) < 0) err(EXIT_FAILURE, "lseek %s failed", tempfile); obj->size = sb.st_size; if ((obj->maddr = malloc(obj->size)) == NULL) err(EXIT_FAILURE, "memory allocation failed for '%s'", tempfile); if ((size_t) read(fd, obj->maddr, obj->size) != obj->size) err(EXIT_FAILURE, "read failed for '%s'", tempfile); if (unlink(tempfile)) err(EXIT_FAILURE, "unlink %s failed", tempfile); free(tempfile); close(fd); if (strlen(obj->name) > _MAXNAMELEN_SVR4) add_to_ar_str_table(ecp, obj->name); ecp->rela_off += _ARHDR_LEN + obj->size + obj->size % 2; STAILQ_INSERT_TAIL(&ecp->v_arobj, obj, objs); } /* * Append to the archive string table buffer. */ static void add_to_ar_str_table(struct elfcopy *ecp, const char *name) { if (ecp->as == NULL) { ecp->as_cap = _INIT_AS_CAP; ecp->as_sz = 0; if ((ecp->as = malloc(ecp->as_cap)) == NULL) err(EXIT_FAILURE, "malloc failed"); } /* * The space required for holding one member name in as table includes: * strlen(name) + (1 for '/') + (1 for '\n') + (possibly 1 for padding). */ while (ecp->as_sz + strlen(name) + 3 > ecp->as_cap) { ecp->as_cap *= 2; ecp->as = realloc(ecp->as, ecp->as_cap); if (ecp->as == NULL) err(EXIT_FAILURE, "realloc failed"); } strncpy(&ecp->as[ecp->as_sz], name, strlen(name)); ecp->as_sz += strlen(name); ecp->as[ecp->as_sz++] = '/'; ecp->as[ecp->as_sz++] = '\n'; } /* * Append to the archive symbol table buffer. */ static void add_to_ar_sym_table(struct elfcopy *ecp, const char *name) { if (ecp->s_so == NULL) { if ((ecp->s_so = malloc(_INIT_SYMOFF_CAP)) == NULL) err(EXIT_FAILURE, "malloc failed"); ecp->s_so_cap = _INIT_SYMOFF_CAP; ecp->s_cnt = 0; } if (ecp->s_sn == NULL) { if ((ecp->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL) err(EXIT_FAILURE, "malloc failed"); ecp->s_sn_cap = _INIT_SYMNAME_CAP; ecp->s_sn_sz = 0; } if (ecp->s_cnt * sizeof(uint32_t) >= ecp->s_so_cap) { ecp->s_so_cap *= 2; ecp->s_so = realloc(ecp->s_so, ecp->s_so_cap); if (ecp->s_so == NULL) err(EXIT_FAILURE, "realloc failed"); } ecp->s_so[ecp->s_cnt] = ecp->rela_off; ecp->s_cnt++; /* * The space required for holding one symbol name in sn table includes: * strlen(name) + (1 for '\n') + (possibly 1 for padding). */ while (ecp->s_sn_sz + strlen(name) + 2 > ecp->s_sn_cap) { ecp->s_sn_cap *= 2; ecp->s_sn = realloc(ecp->s_sn, ecp->s_sn_cap); if (ecp->s_sn == NULL) err(EXIT_FAILURE, "realloc failed"); } strncpy(&ecp->s_sn[ecp->s_sn_sz], name, strlen(name)); ecp->s_sn_sz += strlen(name); ecp->s_sn[ecp->s_sn_sz++] = '\0'; } static void sync_ar(struct elfcopy *ecp) { size_t s_sz; /* size of archive symbol table. */ size_t pm_sz; /* size of pseudo members */ int i; /* * Pad the symbol name string table. It is treated specially because * symbol name table should be padded by a '\0', not the common '\n' * for other members. The size of sn table includes the pad bit. */ if (ecp->s_cnt != 0 && ecp->s_sn_sz % 2 != 0) ecp->s_sn[ecp->s_sn_sz++] = '\0'; /* * Archive string table is padded by a "\n" as the normal members. * The difference is that the size of archive string table counts * in the pad bit, while normal members' size fileds do not. */ if (ecp->as != NULL && ecp->as_sz % 2 != 0) ecp->as[ecp->as_sz++] = '\n'; /* * If there is a symbol table, calculate the size of pseudo members, * convert previously stored relative offsets to absolute ones, and * then make them Big Endian. * * absolute_offset = htobe32(relative_offset + size_of_pseudo_members) */ if (ecp->s_cnt != 0) { s_sz = (ecp->s_cnt + 1) * sizeof(uint32_t) + ecp->s_sn_sz; pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz); if (ecp->as != NULL) pm_sz += _ARHDR_LEN + ecp->as_sz; for (i = 0; (size_t)i < ecp->s_cnt; i++) *(ecp->s_so + i) = htobe32(*(ecp->s_so + i) + pm_sz); } } /* * Extract global symbols from archive members. */ static void extract_arsym(struct elfcopy *ecp) { Elf_Scn *scn; GElf_Shdr shdr; GElf_Sym sym; Elf_Data *data; char *name; size_t n, shstrndx; int elferr, tabndx, len, i; if (elf_kind(ecp->eout) != ELF_K_ELF) { warnx("internal: cannot extract symbols from non-elf object"); return; } if (elf_getshstrndx(ecp->eout, &shstrndx) == 0) { warnx("elf_getshstrndx failed: %s", elf_errmsg(-1)); return; } tabndx = -1; scn = NULL; while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != &shdr) { warnx("elf_getshdr failed: %s", elf_errmsg(-1)); continue; } if ((name = elf_strptr(ecp->eout, shstrndx, shdr.sh_name)) == NULL) { warnx("elf_strptr failed: %s", elf_errmsg(-1)); continue; } if (strcmp(name, ".strtab") == 0) { tabndx = elf_ndxscn(scn); break; } } elferr = elf_errno(); if (elferr != 0) warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); /* Ignore members without symbol table. */ if (tabndx == -1) return; scn = NULL; while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != &shdr) { warnx("elf_getshdr failed: %s", elf_errmsg(-1)); continue; } if (shdr.sh_type != SHT_SYMTAB) continue; data = NULL; n = 0; while (n < shdr.sh_size && (data = elf_getdata(scn, data)) != NULL) { len = data->d_size / shdr.sh_entsize; for (i = 0; i < len; i++) { if (gelf_getsym(data, i, &sym) != &sym) { warnx("gelf_getsym failed: %s", elf_errmsg(-1)); continue; } /* keep only global or weak symbols */ if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL && GELF_ST_BIND(sym.st_info) != STB_WEAK) continue; /* keep only defined symbols */ if (sym.st_shndx == SHN_UNDEF) continue; if ((name = elf_strptr(ecp->eout, tabndx, sym.st_name)) == NULL) { warnx("elf_strptr failed: %s", elf_errmsg(-1)); continue; } add_to_ar_sym_table(ecp, name); } } } elferr = elf_errno(); if (elferr != 0) warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); } #ifndef LIBELF_AR /* * Convenient wrapper for general libarchive error handling. */ #define AC(CALL) do { \ if ((CALL)) \ errx(EXIT_FAILURE, "%s", archive_error_string(a)); \ } while (0) /* Earlier versions of libarchive had some functions that returned 'void'. */ #if ARCHIVE_VERSION_NUMBER >= 2000000 #define ACV(CALL) AC(CALL) #else #define ACV(CALL) do { \ (CALL); \ } while (0) #endif int ac_detect_ar(int ifd) { struct archive *a; struct archive_entry *entry; int r; r = -1; if ((a = archive_read_new()) == NULL) return (0); - archive_read_support_compression_none(a); + archive_read_support_filter_none(a); archive_read_support_format_ar(a); if (archive_read_open_fd(a, ifd, 10240) == ARCHIVE_OK) r = archive_read_next_header(a, &entry); archive_read_close(a); - archive_read_finish(a); + archive_read_free(a); return (r == ARCHIVE_OK); } void ac_create_ar(struct elfcopy *ecp, int ifd, int ofd) { ac_read_objs(ecp, ifd); sync_ar(ecp); ac_write_objs(ecp, ofd); ac_write_cleanup(ecp); } static void ac_read_objs(struct elfcopy *ecp, int ifd) { struct archive *a; struct archive_entry *entry; struct ar_obj *obj; const char *name; char *buff; size_t size; int r; ecp->rela_off = 0; if (lseek(ifd, 0, SEEK_SET) == -1) err(EXIT_FAILURE, "lseek failed"); if ((a = archive_read_new()) == NULL) errx(EXIT_FAILURE, "%s", archive_error_string(a)); - archive_read_support_compression_none(a); + archive_read_support_filter_none(a); archive_read_support_format_ar(a); AC(archive_read_open_fd(a, ifd, 10240)); for(;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_FATAL) errx(EXIT_FAILURE, "%s", archive_error_string(a)); if (r == ARCHIVE_EOF) break; if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY) warnx("%s", archive_error_string(a)); if (r == ARCHIVE_RETRY) continue; name = archive_entry_pathname(entry); /* skip pseudo members. */ if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0) continue; size = archive_entry_size(entry); if (size > 0) { if ((buff = malloc(size)) == NULL) err(EXIT_FAILURE, "malloc failed"); if (archive_read_data(a, buff, size) != (ssize_t)size) { warnx("%s", archive_error_string(a)); free(buff); continue; } if ((obj = malloc(sizeof(*obj))) == NULL) err(EXIT_FAILURE, "malloc failed"); if ((obj->name = strdup(name)) == NULL) err(EXIT_FAILURE, "strdup failed"); obj->buf = buff; obj->uid = archive_entry_uid(entry); obj->gid = archive_entry_gid(entry); obj->md = archive_entry_mode(entry); obj->mtime = archive_entry_mtime(entry); if ((ecp->ein = elf_memory(buff, size)) == NULL) errx(EXIT_FAILURE, "elf_memory() failed: %s", elf_errmsg(-1)); if (elf_kind(ecp->ein) != ELF_K_ELF) errx(EXIT_FAILURE, "file format not recognized"); process_ar_obj(ecp, obj); } } AC(archive_read_close(a)); - ACV(archive_read_finish(a)); + ACV(archive_read_free(a)); } static void ac_write_objs(struct elfcopy *ecp, int ofd) { struct archive *a; struct archive_entry *entry; struct ar_obj *obj; int nr; if ((a = archive_write_new()) == NULL) errx(EXIT_FAILURE, "%s", archive_error_string(a)); archive_write_set_format_ar_svr4(a); - archive_write_set_compression_none(a); + archive_write_add_filter_none(a); AC(archive_write_open_fd(a, ofd)); /* Write the archive symbol table, even if it's empty. */ entry = archive_entry_new(); archive_entry_copy_pathname(entry, "/"); archive_entry_set_mtime(entry, time(NULL), 0); archive_entry_set_size(entry, (ecp->s_cnt + 1) * sizeof(uint32_t) + ecp->s_sn_sz); AC(archive_write_header(a, entry)); nr = htobe32(ecp->s_cnt); ac_write_data(a, &nr, sizeof(uint32_t)); ac_write_data(a, ecp->s_so, sizeof(uint32_t) * ecp->s_cnt); ac_write_data(a, ecp->s_sn, ecp->s_sn_sz); archive_entry_free(entry); /* Write the archive string table, if exist. */ if (ecp->as != NULL) { entry = archive_entry_new(); archive_entry_copy_pathname(entry, "//"); archive_entry_set_size(entry, ecp->as_sz); AC(archive_write_header(a, entry)); ac_write_data(a, ecp->as, ecp->as_sz); archive_entry_free(entry); } /* Write normal members. */ STAILQ_FOREACH(obj, &ecp->v_arobj, objs) { entry = archive_entry_new(); archive_entry_copy_pathname(entry, obj->name); archive_entry_set_uid(entry, obj->uid); archive_entry_set_gid(entry, obj->gid); archive_entry_set_mode(entry, obj->md); archive_entry_set_size(entry, obj->size); archive_entry_set_mtime(entry, obj->mtime, 0); archive_entry_set_filetype(entry, AE_IFREG); AC(archive_write_header(a, entry)); ac_write_data(a, obj->maddr, obj->size); archive_entry_free(entry); } AC(archive_write_close(a)); - ACV(archive_write_finish(a)); + ACV(archive_write_free(a)); } static void ac_write_cleanup(struct elfcopy *ecp) { struct ar_obj *obj, *obj_temp; STAILQ_FOREACH_SAFE(obj, &ecp->v_arobj, objs, obj_temp) { STAILQ_REMOVE(&ecp->v_arobj, obj, ar_obj, objs); if (obj->maddr != NULL) free(obj->maddr); free(obj->name); free(obj); } free(ecp->as); free(ecp->s_so); free(ecp->s_sn); ecp->as = NULL; ecp->s_so = NULL; ecp->s_sn = NULL; } /* * Wrapper for archive_write_data(). */ static void ac_write_data(struct archive *a, const void *buf, size_t s) { if (archive_write_data(a, buf, s) != (ssize_t)s) errx(EXIT_FAILURE, "%s", archive_error_string(a)); } #endif /* ! LIBELF_AR */ Index: projects/clang350-import/contrib/elftoolchain/elfcopy/ascii.c =================================================================== --- projects/clang350-import/contrib/elftoolchain/elfcopy/ascii.c (revision 275386) +++ projects/clang350-import/contrib/elftoolchain/elfcopy/ascii.c (revision 275387) @@ -1,1078 +1,1079 @@ /*- * Copyright (c) 2010,2011 Kai Wang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include +#include #include #include #include #include #include "elfcopy.h" ELFTC_VCSID("$Id: ascii.c 2358 2011-12-19 18:22:32Z kaiwang27 $"); static void append_data(struct section *s, const void *buf, size_t sz); static char hex_digit(uint8_t n); static int hex_value(int x); static void finalize_data_section(struct section *s); static int ishexdigit(int x); static int ihex_read(const char *line, char *type, uint64_t *addr, uint64_t *num, uint8_t *data, size_t *sz); static void ihex_write(int ofd, int type, uint64_t addr, uint64_t num, const void *buf, size_t sz); static void ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz); static void ihex_write_01(int ofd); static void ihex_write_04(int ofd, uint16_t addr); static void ihex_write_05(int ofd, uint64_t e_entry); static struct section *new_data_section(struct elfcopy *ecp, int sec_index, uint64_t off, uint64_t addr); static int read_num(const char *line, int *len, uint64_t *num, size_t sz, int *checksum); static int srec_read(const char *line, char *type, uint64_t *addr, uint8_t *data, size_t *sz); static void srec_write(int ofd, char type, uint64_t addr, const void *buf, size_t sz); static void srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn, GElf_Shdr *sh); static void srec_write_S0(int ofd, const char *ofn); static void srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf, size_t sz, size_t rlen); static void srec_write_Se(int ofd, uint64_t e_entry, int forceS3); static void write_num(char *line, int *len, uint64_t num, size_t sz, int *checksum); #define _LINE_BUFSZ 1024 #define _DATA_BUFSZ 256 /* * Convert ELF object to S-Record. */ void create_srec(struct elfcopy *ecp, int ifd, int ofd, const char *ofn) { Elf *e; Elf_Scn *scn; Elf_Data *d; GElf_Ehdr eh; GElf_Shdr sh; uint64_t max_addr; size_t rlen; int elferr, addr_sz; char dr; if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL) errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1)); /* Output a symbol table for `symbolsrec' target. */ if (!strncmp(ecp->otgt, "symbolsrec", strlen("symbolsrec"))) { scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &sh) == NULL) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); (void) elf_errno(); continue; } if (sh.sh_type != SHT_SYMTAB) continue; srec_write_symtab(ofd, ofn, e, scn, &sh); break; } } if (ecp->flags & SREC_FORCE_S3) dr = '3'; else { /* * Find maximum address size in the first iteration. */ max_addr = 0; scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &sh) == NULL) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); (void) elf_errno(); continue; } if ((sh.sh_flags & SHF_ALLOC) == 0 || sh.sh_type == SHT_NOBITS || sh.sh_size == 0) continue; if ((uint64_t) sh.sh_addr > max_addr) max_addr = sh.sh_addr; } elferr = elf_errno(); if (elferr != 0) warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); if (max_addr <= 0xFFFF) dr = '1'; else if (max_addr <= 0xFFFFFF) dr = '2'; else dr = '3'; } if (ecp->flags & SREC_FORCE_LEN) { addr_sz = dr - '0' + 1; if (ecp->srec_len < 1) rlen = 1; else if (ecp->srec_len + addr_sz + 1 > 255) rlen = 255 - (addr_sz + 1); else rlen = ecp->srec_len; } else rlen = 16; /* Generate S0 record which contains the output filename. */ srec_write_S0(ofd, ofn); /* Generate S{1,2,3} data records for section data. */ scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &sh) == NULL) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); (void) elf_errno(); continue; } if ((sh.sh_flags & SHF_ALLOC) == 0 || sh.sh_type == SHT_NOBITS || sh.sh_size == 0) continue; if (sh.sh_addr > 0xFFFFFFFF) { warnx("address space too big for S-Record file"); continue; } (void) elf_errno(); if ((d = elf_getdata(scn, NULL)) == NULL) { elferr = elf_errno(); if (elferr != 0) warnx("elf_getdata failed: %s", elf_errmsg(-1)); continue; } if (d->d_buf == NULL || d->d_size == 0) continue; srec_write_Sd(ofd, dr, sh.sh_addr, d->d_buf, d->d_size, rlen); } elferr = elf_errno(); if (elferr != 0) warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); /* Generate S{7,8,9} end of block recrod. */ if (gelf_getehdr(e, &eh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); srec_write_Se(ofd, eh.e_entry, ecp->flags & SREC_FORCE_S3); } void create_elf_from_srec(struct elfcopy *ecp, int ifd) { char line[_LINE_BUFSZ], name[_LINE_BUFSZ]; uint8_t data[_DATA_BUFSZ]; GElf_Ehdr oeh; struct section *s, *shtab; FILE *ifp; uint64_t addr, entry, off, sec_addr; uintmax_t st_value; size_t sz; int _ifd, first, sec_index, in_symtab, symtab_created; char *rlt; char type; if ((_ifd = dup(ifd)) < 0) err(EXIT_FAILURE, "dup failed"); if ((ifp = fdopen(_ifd, "r")) == NULL) err(EXIT_FAILURE, "fdopen failed"); /* Create EHDR for output .o file. */ if (gelf_newehdr(ecp->eout, ecp->oec) == NULL) errx(EXIT_FAILURE, "gelf_newehdr failed: %s", elf_errmsg(-1)); if (gelf_getehdr(ecp->eout, &oeh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* Initialise e_ident fields. */ oeh.e_ident[EI_CLASS] = ecp->oec; oeh.e_ident[EI_DATA] = ecp->oed; /* * TODO: Set OSABI according to the OS platform where elfcopy(1) * was build. (probably) */ oeh.e_ident[EI_OSABI] = ELFOSABI_NONE; oeh.e_machine = ecp->oem; oeh.e_type = ET_REL; oeh.e_entry = 0; ecp->flags |= RELOCATABLE; /* Create .shstrtab section */ init_shstrtab(ecp); ecp->shstrtab->off = 0; /* Data sections are inserted after EHDR. */ off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT); if (off == 0) errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1)); /* Create data sections. */ s = NULL; first = 1; sec_index = 1; sec_addr = entry = 0; while (fgets(line, _LINE_BUFSZ, ifp) != NULL) { if (line[0] == '\r' || line[0] == '\n') continue; if (line[0] == '$' && line[1] == '$') { ecp->flags |= SYMTAB_EXIST; while ((rlt = fgets(line, _LINE_BUFSZ, ifp)) != NULL) { if (line[0] == '$' && line[1] == '$') break; } if (rlt == NULL) break; continue; } if (line[0] != 'S' || line[1] < '0' || line[1] > '9') { warnx("Invalid srec record"); continue; } if (srec_read(line, &type, &addr, data, &sz) < 0) { warnx("Invalid srec record or mismatched checksum"); continue; } switch (type) { case '1': case '2': case '3': if (sz == 0) break; if (first || sec_addr != addr) { if (s != NULL) finalize_data_section(s); s = new_data_section(ecp, sec_index, off, addr); if (s == NULL) { warnx("new_data_section failed"); break; } sec_index++; sec_addr = addr; first = 0; } append_data(s, data, sz); off += sz; sec_addr += sz; break; case '7': case '8': case '9': entry = addr; break; default: break; } } if (s != NULL) finalize_data_section(s); if (ferror(ifp)) warn("fgets failed"); /* Insert .shstrtab after data sections. */ if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL) errx(EXIT_FAILURE, "elf_newscn failed: %s", elf_errmsg(-1)); insert_to_sec_list(ecp, ecp->shstrtab, 1); /* Insert section header table here. */ shtab = insert_shtab(ecp, 1); /* * Rescan and create symbol table if we found '$$' section in * the first scan. */ symtab_created = 0; in_symtab = 0; if (ecp->flags & SYMTAB_EXIST) { if (fseek(ifp, 0, SEEK_SET) < 0) { warn("fseek failed"); ecp->flags &= ~SYMTAB_EXIST; goto done; } while (fgets(line, _LINE_BUFSZ, ifp) != NULL) { if (in_symtab) { if (line[0] == '$' && line[1] == '$') { in_symtab = 0; continue; } if (sscanf(line, "%s $%jx", name, &st_value) != 2) { warnx("Invalid symbolsrec record"); continue; } if (!symtab_created) { create_external_symtab(ecp); symtab_created = 1; } add_to_symtab(ecp, name, st_value, 0, SHN_ABS, ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1); } if (line[0] == '$' && line[1] == '$') { in_symtab = 1; continue; } } } if (ferror(ifp)) warn("fgets failed"); if (symtab_created) { finalize_external_symtab(ecp); create_symtab_data(ecp); /* Count in .symtab and .strtab section headers. */ shtab->sz += gelf_fsize(ecp->eout, ELF_T_SHDR, 2, EV_CURRENT); } else ecp->flags &= ~SYMTAB_EXIST; done: fclose(ifp); /* Set entry point. */ oeh.e_entry = entry; /* * Write the underlying ehdr. Note that it should be called * before elf_setshstrndx() since it will overwrite e->e_shstrndx. */ if (gelf_update_ehdr(ecp->eout, &oeh) == 0) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); /* Generate section name string table (.shstrtab). */ set_shstrtab(ecp); /* Update sh_name pointer for each section header entry. */ update_shdr(ecp, 0); /* Renew oeh to get the updated e_shstrndx. */ if (gelf_getehdr(ecp->eout, &oeh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* Resync section offsets. */ resync_sections(ecp); /* Store SHDR offset in EHDR. */ oeh.e_shoff = shtab->off; /* Update ehdr since we modified e_shoff. */ if (gelf_update_ehdr(ecp->eout, &oeh) == 0) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); /* Write out the output elf object. */ if (elf_update(ecp->eout, ELF_C_WRITE) < 0) errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1)); /* Release allocated resource. */ free_elf(ecp); } void create_ihex(int ifd, int ofd) { Elf *e; Elf_Scn *scn; Elf_Data *d; GElf_Ehdr eh; GElf_Shdr sh; int elferr; uint16_t addr_hi, old_addr_hi; if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL) errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1)); old_addr_hi = 0; scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &sh) == NULL) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); (void) elf_errno(); continue; } if ((sh.sh_flags & SHF_ALLOC) == 0 || sh.sh_type == SHT_NOBITS || sh.sh_size == 0) continue; if (sh.sh_addr > 0xFFFFFFFF) { warnx("address space too big for Intel Hex file"); continue; } (void) elf_errno(); if ((d = elf_getdata(scn, NULL)) == NULL) { elferr = elf_errno(); if (elferr != 0) warnx("elf_getdata failed: %s", elf_errmsg(-1)); continue; } if (d->d_buf == NULL || d->d_size == 0) continue; addr_hi = (sh.sh_addr >> 16) & 0xFFFF; if (addr_hi > 0 && addr_hi != old_addr_hi) { /* Write 04 record if addr_hi is new. */ old_addr_hi = addr_hi; ihex_write_04(ofd, addr_hi); } ihex_write_00(ofd, sh.sh_addr, d->d_buf, d->d_size); } elferr = elf_errno(); if (elferr != 0) warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); if (gelf_getehdr(e, &eh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); ihex_write_05(ofd, eh.e_entry); ihex_write_01(ofd); } void create_elf_from_ihex(struct elfcopy *ecp, int ifd) { char line[_LINE_BUFSZ]; uint8_t data[_DATA_BUFSZ]; GElf_Ehdr oeh; struct section *s, *shtab; FILE *ifp; uint64_t addr, addr_base, entry, num, off, rec_addr, sec_addr; size_t sz; int _ifd, first, sec_index; char type; if ((_ifd = dup(ifd)) < 0) err(EXIT_FAILURE, "dup failed"); if ((ifp = fdopen(_ifd, "r")) == NULL) err(EXIT_FAILURE, "fdopen failed"); /* Create EHDR for output .o file. */ if (gelf_newehdr(ecp->eout, ecp->oec) == NULL) errx(EXIT_FAILURE, "gelf_newehdr failed: %s", elf_errmsg(-1)); if (gelf_getehdr(ecp->eout, &oeh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* Initialise e_ident fields. */ oeh.e_ident[EI_CLASS] = ecp->oec; oeh.e_ident[EI_DATA] = ecp->oed; /* * TODO: Set OSABI according to the OS platform where elfcopy(1) * was build. (probably) */ oeh.e_ident[EI_OSABI] = ELFOSABI_NONE; oeh.e_machine = ecp->oem; oeh.e_type = ET_REL; oeh.e_entry = 0; ecp->flags |= RELOCATABLE; /* Create .shstrtab section */ init_shstrtab(ecp); ecp->shstrtab->off = 0; /* Data sections are inserted after EHDR. */ off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT); if (off == 0) errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1)); /* Create data sections. */ s = NULL; first = 1; sec_index = 1; addr_base = rec_addr = sec_addr = entry = 0; while (fgets(line, _LINE_BUFSZ, ifp) != NULL) { if (line[0] == '\r' || line[0] == '\n') continue; if (line[0] != ':') { warnx("Invalid ihex record"); continue; } if (ihex_read(line, &type, &addr, &num, data, &sz) < 0) { warnx("Invalid ihex record or mismatched checksum"); continue; } switch (type) { case '0': /* Data record. */ if (sz == 0) break; rec_addr = addr_base + addr; if (first || sec_addr != rec_addr) { if (s != NULL) finalize_data_section(s); s = new_data_section(ecp, sec_index, off, rec_addr); if (s == NULL) { warnx("new_data_section failed"); break; } sec_index++; sec_addr = rec_addr; first = 0; } append_data(s, data, sz); off += sz; sec_addr += sz; break; case '1': /* End of file record. */ goto done; case '2': /* Extended segment address record. */ addr_base = addr << 4; break; case '3': /* Start segment address record (CS:IP). Ignored. */ break; case '4': /* Extended linear address record. */ addr_base = num << 16; break; case '5': /* Start linear address record. */ entry = num; break; default: break; } } done: if (s != NULL) finalize_data_section(s); if (ferror(ifp)) warn("fgets failed"); fclose(ifp); /* Insert .shstrtab after data sections. */ if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL) errx(EXIT_FAILURE, "elf_newscn failed: %s", elf_errmsg(-1)); insert_to_sec_list(ecp, ecp->shstrtab, 1); /* Insert section header table here. */ shtab = insert_shtab(ecp, 1); /* Set entry point. */ oeh.e_entry = entry; /* * Write the underlying ehdr. Note that it should be called * before elf_setshstrndx() since it will overwrite e->e_shstrndx. */ if (gelf_update_ehdr(ecp->eout, &oeh) == 0) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); /* Generate section name string table (.shstrtab). */ set_shstrtab(ecp); /* Update sh_name pointer for each section header entry. */ update_shdr(ecp, 0); /* Renew oeh to get the updated e_shstrndx. */ if (gelf_getehdr(ecp->eout, &oeh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* Resync section offsets. */ resync_sections(ecp); /* Store SHDR offset in EHDR. */ oeh.e_shoff = shtab->off; /* Update ehdr since we modified e_shoff. */ if (gelf_update_ehdr(ecp->eout, &oeh) == 0) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); /* Write out the output elf object. */ if (elf_update(ecp->eout, ELF_C_WRITE) < 0) errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1)); /* Release allocated resource. */ free_elf(ecp); } #define _SEC_NAMESZ 64 #define _SEC_INIT_CAP 1024 static struct section * new_data_section(struct elfcopy *ecp, int sec_index, uint64_t off, uint64_t addr) { char *name; if ((name = malloc(_SEC_NAMESZ)) == NULL) errx(EXIT_FAILURE, "malloc failed"); snprintf(name, _SEC_NAMESZ, ".sec%d", sec_index); return (create_external_section(ecp, name, name, NULL, 0, off, SHT_PROGBITS, ELF_T_BYTE, SHF_ALLOC | SHF_WRITE, 1, addr, 0)); } static void finalize_data_section(struct section *s) { Elf_Data *od; if ((od = elf_newdata(s->os)) == NULL) errx(EXIT_FAILURE, "elf_newdata() failed: %s", elf_errmsg(-1)); od->d_align = s->align; od->d_off = 0; od->d_buf = s->buf; od->d_size = s->sz; od->d_version = EV_CURRENT; } static void append_data(struct section *s, const void *buf, size_t sz) { uint8_t *p; if (s->buf == NULL) { s->sz = 0; s->cap = _SEC_INIT_CAP; if ((s->buf = malloc(s->cap)) == NULL) err(EXIT_FAILURE, "malloc failed"); } while (sz + s->sz > s->cap) { s->cap *= 2; if ((s->buf = realloc(s->buf, s->cap)) == NULL) err(EXIT_FAILURE, "realloc failed"); } p = s->buf; memcpy(&p[s->sz], buf, sz); s->sz += sz; } static int srec_read(const char *line, char *type, uint64_t *addr, uint8_t *data, size_t *sz) { uint64_t count, _checksum, num; size_t addr_sz; int checksum, i, len; checksum = 0; len = 2; if (read_num(line, &len, &count, 1, &checksum) < 0) return (-1); *type = line[1]; switch (*type) { case '0': case '1': case '5': case '9': addr_sz = 2; break; case '2': case '8': addr_sz = 3; break; case '3': case '7': addr_sz = 4; break; default: return (-1); } if (read_num(line, &len, addr, addr_sz, &checksum) < 0) return (-1); count -= addr_sz + 1; if (*type >= '0' && *type <= '3') { for (i = 0; (uint64_t) i < count; i++) { if (read_num(line, &len, &num, 1, &checksum) < 0) return -1; data[i] = (uint8_t) num; } *sz = count; } else *sz = 0; if (read_num(line, &len, &_checksum, 1, NULL) < 0) return (-1); if ((int) _checksum != (~checksum & 0xFF)) return (-1); return (0); } static void srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn, GElf_Shdr *sh) { char line[_LINE_BUFSZ]; GElf_Sym sym; Elf_Data *d; const char *name; size_t sc; int elferr, i; #define _WRITE_LINE do { \ if (write(ofd, line, strlen(line)) != (ssize_t) strlen(line)) \ errx(EXIT_FAILURE, "write failed"); \ } while (0) (void) elf_errno(); if ((d = elf_getdata(scn, NULL)) == NULL) { elferr = elf_errno(); if (elferr != 0) warnx("elf_getdata failed: %s", elf_errmsg(-1)); return; } if (d->d_buf == NULL || d->d_size == 0) return; snprintf(line, sizeof(line), "$$ %s\r\n", ofn); _WRITE_LINE; sc = d->d_size / sh->sh_entsize; for (i = 1; (size_t) i < sc; i++) { if (gelf_getsym(d, i, &sym) != &sym) { warnx("gelf_getsym failed: %s", elf_errmsg(-1)); continue; } if (GELF_ST_TYPE(sym.st_info) == STT_SECTION || GELF_ST_TYPE(sym.st_info) == STT_FILE) continue; if ((name = elf_strptr(e, sh->sh_link, sym.st_name)) == NULL) { warnx("elf_strptr failed: %s", elf_errmsg(-1)); continue; } snprintf(line, sizeof(line), " %s $%jx\r\n", name, (uintmax_t) sym.st_value); _WRITE_LINE; } snprintf(line, sizeof(line), "$$ \r\n"); _WRITE_LINE; #undef _WRITE_LINE } static void srec_write_S0(int ofd, const char *ofn) { srec_write(ofd, '0', 0, ofn, strlen(ofn)); } static void srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf, size_t sz, size_t rlen) { const uint8_t *p, *pe; p = buf; pe = p + sz; while (pe - p >= (int) rlen) { srec_write(ofd, dr, addr, p, rlen); addr += rlen; p += rlen; } if (pe - p > 0) srec_write(ofd, dr, addr, p, pe - p); } static void srec_write_Se(int ofd, uint64_t e_entry, int forceS3) { char er; if (e_entry > 0xFFFFFFFF) { warnx("address space too big for S-Record file"); return; } if (forceS3) er = '7'; else { if (e_entry <= 0xFFFF) er = '9'; else if (e_entry <= 0xFFFFFF) er = '8'; else er = '7'; } srec_write(ofd, er, e_entry, NULL, 0); } static void srec_write(int ofd, char type, uint64_t addr, const void *buf, size_t sz) { char line[_LINE_BUFSZ]; const uint8_t *p, *pe; int len, addr_sz, checksum; if (type == '0' || type == '1' || type == '5' || type == '9') addr_sz = 2; else if (type == '2' || type == '8') addr_sz = 3; else addr_sz = 4; checksum = 0; line[0] = 'S'; line[1] = type; len = 2; write_num(line, &len, addr_sz + sz + 1, 1, &checksum); write_num(line, &len, addr, addr_sz, &checksum); for (p = buf, pe = p + sz; p < pe; p++) write_num(line, &len, *p, 1, &checksum); write_num(line, &len, ~checksum & 0xFF, 1, NULL); line[len++] = '\r'; line[len++] = '\n'; if (write(ofd, line, len) != (ssize_t) len) err(EXIT_FAILURE, "write failed"); } static void ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz) { uint16_t addr_hi, old_addr_hi; const uint8_t *p, *pe; old_addr_hi = (addr >> 16) & 0xFFFF; p = buf; pe = p + sz; while (pe - p >= 16) { ihex_write(ofd, 0, addr, 0, p, 16); addr += 16; p += 16; addr_hi = (addr >> 16) & 0xFFFF; if (addr_hi != old_addr_hi) { old_addr_hi = addr_hi; ihex_write_04(ofd, addr_hi); } } if (pe - p > 0) ihex_write(ofd, 0, addr, 0, p, pe - p); } static int ihex_read(const char *line, char *type, uint64_t *addr, uint64_t *num, uint8_t *data, size_t *sz) { uint64_t count, _checksum; int checksum, i, len; *sz = 0; checksum = 0; len = 1; if (read_num(line, &len, &count, 1, &checksum) < 0) return (-1); if (read_num(line, &len, addr, 2, &checksum) < 0) return (-1); if (line[len++] != '0') return (-1); *type = line[len++]; checksum += *type - '0'; switch (*type) { case '0': for (i = 0; (uint64_t) i < count; i++) { if (read_num(line, &len, num, 1, &checksum) < 0) return (-1); data[i] = (uint8_t) *num; } *sz = count; break; case '1': if (count != 0) return (-1); break; case '2': case '4': if (count != 2) return (-1); if (read_num(line, &len, num, 2, &checksum) < 0) return (-1); break; case '3': case '5': if (count != 4) return (-1); if (read_num(line, &len, num, 4, &checksum) < 0) return (-1); break; default: return (-1); } if (read_num(line, &len, &_checksum, 1, &checksum) < 0) return (-1); if ((checksum & 0xFF) != 0) { return (-1); } return (0); } static void ihex_write_01(int ofd) { ihex_write(ofd, 1, 0, 0, NULL, 0); } static void ihex_write_04(int ofd, uint16_t addr) { ihex_write(ofd, 4, 0, addr, NULL, 2); } static void ihex_write_05(int ofd, uint64_t e_entry) { if (e_entry > 0xFFFFFFFF) { warnx("address space too big for Intel Hex file"); return; } ihex_write(ofd, 5, 0, e_entry, NULL, 4); } static void ihex_write(int ofd, int type, uint64_t addr, uint64_t num, const void *buf, size_t sz) { char line[_LINE_BUFSZ]; const uint8_t *p, *pe; int len, checksum; if (sz > 16) errx(EXIT_FAILURE, "Internal: ihex_write() sz too big"); checksum = 0; line[0] = ':'; len = 1; write_num(line, &len, sz, 1, &checksum); write_num(line, &len, addr, 2, &checksum); write_num(line, &len, type, 1, &checksum); if (sz > 0) { if (buf != NULL) { for (p = buf, pe = p + sz; p < pe; p++) write_num(line, &len, *p, 1, &checksum); } else write_num(line, &len, num, sz, &checksum); } write_num(line, &len, (~checksum + 1) & 0xFF, 1, NULL); line[len++] = '\r'; line[len++] = '\n'; if (write(ofd, line, len) != (ssize_t) len) err(EXIT_FAILURE, "write failed"); } static int read_num(const char *line, int *len, uint64_t *num, size_t sz, int *checksum) { uint8_t b; *num = 0; for (; sz > 0; sz--) { if (!ishexdigit(line[*len]) || !ishexdigit(line[*len + 1])) return (-1); b = (hex_value(line[*len]) << 4) | hex_value(line[*len + 1]); *num = (*num << 8) | b; *len += 2; if (checksum != NULL) *checksum = (*checksum + b) & 0xFF; } return (0); } static void write_num(char *line, int *len, uint64_t num, size_t sz, int *checksum) { uint8_t b; for (; sz > 0; sz--) { b = (num >> ((sz - 1) * 8)) & 0xFF; line[*len] = hex_digit((b >> 4) & 0xF); line[*len + 1] = hex_digit(b & 0xF); *len += 2; if (checksum != NULL) *checksum = (*checksum + b) & 0xFF; } } static char hex_digit(uint8_t n) { return ((n < 10) ? '0' + n : 'A' + (n - 10)); } static int hex_value(int x) { if (isdigit(x)) return (x - '0'); else if (x >= 'a' && x <= 'f') return (x - 'a' + 10); else return (x - 'A' + 10); } static int ishexdigit(int x) { if (isdigit(x)) return (1); if ((x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F')) return (1); return (0); } Index: projects/clang350-import/contrib/elftoolchain/elfcopy/segments.c =================================================================== --- projects/clang350-import/contrib/elftoolchain/elfcopy/segments.c (revision 275386) +++ projects/clang350-import/contrib/elftoolchain/elfcopy/segments.c (revision 275387) @@ -1,493 +1,494 @@ /*- * Copyright (c) 2007-2010,2012 Kai Wang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include +#include #include #include #include #include "elfcopy.h" ELFTC_VCSID("$Id: segments.c 2542 2012-08-12 16:14:15Z kaiwang27 $"); static void insert_to_inseg_list(struct segment *seg, struct section *sec); /* * elfcopy's segment handling is relatively simpler and less powerful than * libbfd. Program headers are modified or copied from input to output objects, * but never re-generated. As a result, if the input object has incorrect * program headers, the output object's program headers will remain incorrect * or become even worse. */ /* * Check whether a section is "loadable". If so, add it to the * corresponding segment list(s) and return 1. */ int add_to_inseg_list(struct elfcopy *ecp, struct section *s) { struct segment *seg; int loadable; if (ecp->ophnum == 0) return (0); /* * Segment is a different view of an ELF object. One segment can * contain one or more sections, and one section can be included * in one or more segments, or not included in any segment at all. * We call those sections which can be found in one or more segments * "loadable" sections, and call the rest "unloadable" sections. * We keep track of "loadable" sections in their containing * segment(s)' v_sec queue. These information are later used to * recalculate the extents of segments, when sections are removed, * for example. */ loadable = 0; STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (s->off < seg->off) continue; if (s->off + s->sz > seg->off + seg->fsz && s->type != SHT_NOBITS) continue; if (s->off + s->sz > seg->off + seg->msz) continue; insert_to_inseg_list(seg, s); if (seg->type == PT_LOAD) s->seg = seg; s->lma = seg->addr + (s->off - seg->off); loadable = 1; } return (loadable); } void adjust_addr(struct elfcopy *ecp) { struct section *s, *s0; struct segment *seg; struct sec_action *sac; uint64_t dl, lma, old_vma, start, end; int found, i; /* * Apply VMA and global LMA changes in the first iteration. */ TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { /* Only adjust loadable section's address. */ if (!s->loadable || s->seg == NULL) continue; /* Apply global LMA adjustment. */ if (ecp->change_addr != 0) s->lma += ecp->change_addr; if (!s->pseudo) { old_vma = s->vma; /* Apply global VMA adjustment. */ if (ecp->change_addr != 0) s->vma += ecp->change_addr; /* Apply section VMA adjustment. */ sac = lookup_sec_act(ecp, s->name, 0); if (sac == NULL) continue; if (sac->setvma) s->vma = sac->vma; if (sac->vma_adjust != 0) s->vma += sac->vma_adjust; } } /* * Apply sections LMA change in the second iteration. */ TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { /* Only adjust loadable section's LMA. */ if (!s->loadable || s->seg == NULL) continue; /* * Check if there is a LMA change request for this * section. */ sac = lookup_sec_act(ecp, s->name, 0); if (sac == NULL) continue; if (!sac->setlma && sac->lma_adjust == 0) continue; lma = s->lma; if (sac->setlma) lma = sac->lma; if (sac->lma_adjust != 0) lma += sac->lma_adjust; if (lma == s->lma) continue; /* * Check if the LMA change is viable. * * 1. Check if the new LMA is properly aligned accroding to * section alignment. * * 2. Compute the new extent of segment that contains this * section, make sure it doesn't overlap with other * segments. */ #ifdef DEBUG printf("LMA for section %s: %#jx\n", s->name, lma); #endif if (lma % s->align != 0) errx(EXIT_FAILURE, "The load address %#jx for " "section %s is not aligned to %ju", (uintmax_t) lma, s->name, s->align); if (lma < s->lma) { /* Move section to lower address. */ if (lma < s->lma - s->seg->addr) errx(EXIT_FAILURE, "Not enough space to move " "section %s load address to %#jx", s->name, (uintmax_t) lma); start = lma - (s->lma - s->seg->addr); if (s == s->seg->v_sec[s->seg->nsec - 1]) end = start + s->seg->msz; else end = s->seg->addr + s->seg->msz; } else { /* Move section to upper address. */ if (s == s->seg->v_sec[0]) start = lma; else start = s->seg->addr; end = lma + (s->seg->addr + s->seg->msz - s->lma); if (end < start) errx(EXIT_FAILURE, "Not enough space to move " "section %s load address to %#jx", s->name, (uintmax_t) lma); } #ifdef DEBUG printf("new extent for segment containing %s: (%#jx,%#jx)\n", s->name, start, end); #endif STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (seg == s->seg || seg->type != PT_LOAD) continue; if (start > seg->addr + seg->msz) continue; if (end < seg->addr) continue; errx(EXIT_FAILURE, "The extent of segment containing " "section %s overlaps with segment(%#jx,%#jx)", s->name, seg->addr, seg->addr + seg->msz); } /* * Update section LMA and file offset. */ if (lma < s->lma) { /* * To move a section to lower load address, we decrease * the load addresses of the section and all the * sections that are before it, and we increase the * file offsets of all the sections that are after it. */ dl = s->lma - lma; for (i = 0; i < s->seg->nsec; i++) { s0 = s->seg->v_sec[i]; s0->lma -= dl; #ifdef DEBUG printf("section %s LMA set to %#jx\n", s0->name, (uintmax_t) s0->lma); #endif if (s0 == s) break; } for (i = i + 1; i < s->seg->nsec; i++) { s0 = s->seg->v_sec[i]; s0->off += dl; #ifdef DEBUG printf("section %s offset set to %#jx\n", s0->name, (uintmax_t) s0->off); #endif } } else { /* * To move a section to upper load address, we increase * the load addresses of the section and all the * sections that are after it, and we increase the * their file offsets too unless the section in question * is the first in its containing segment. */ dl = lma - s->lma; for (i = 0; i < s->seg->nsec; i++) if (s->seg->v_sec[i] == s) break; if (i >= s->seg->nsec) errx(EXIT_FAILURE, "Internal: section `%s' not" " found in its containing segement", s->name); for (; i < s->seg->nsec; i++) { s0 = s->seg->v_sec[i]; s0->lma += dl; #ifdef DEBUG printf("section %s LMA set to %#jx\n", s0->name, (uintmax_t) s0->lma); #endif if (s != s->seg->v_sec[0]) { s0->off += dl; #ifdef DEBUG printf("section %s offset set to %#jx\n", s0->name, (uintmax_t) s0->off); #endif } } } } /* * Apply load address padding. */ if (ecp->pad_to != 0) { /* * Find the section with highest load address. */ s = NULL; STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (seg->type != PT_LOAD) continue; for (i = seg->nsec - 1; i >= 0; i--) if (seg->v_sec[i]->type != SHT_NOBITS) break; if (i < 0) continue; if (s == NULL) s = seg->v_sec[i]; else { s0 = seg->v_sec[i]; if (s0->lma > s->lma) s = s0; } } if (s == NULL) goto issue_warn; /* No need to pad if the pad_to address is lower. */ if (ecp->pad_to <= s->lma + s->sz) goto issue_warn; s->pad_sz = ecp->pad_to - (s->lma + s->sz); #ifdef DEBUG printf("pad section %s load to address %#jx by %#jx\n", s->name, (uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz); #endif } issue_warn: /* * Issue a warning if there are VMA/LMA adjust requests for * some nonexistent sections. */ if ((ecp->flags & NO_CHANGE_WARN) == 0) { STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) { if (!sac->setvma && !sac->setlma && !sac->vma_adjust && !sac->lma_adjust) continue; found = 0; TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { if (s->pseudo || s->name == NULL) continue; if (!strcmp(s->name, sac->name)) { found = 1; break; } } if (!found) warnx("cannot find section `%s'", sac->name); } } } static void insert_to_inseg_list(struct segment *seg, struct section *sec) { struct section *s; int i; seg->nsec++; seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec)); if (seg->v_sec == NULL) err(EXIT_FAILURE, "realloc failed"); /* * Sort the section in order of offset. */ for (i = seg->nsec - 1; i > 0; i--) { s = seg->v_sec[i - 1]; if (sec->off >= s->off) { seg->v_sec[i] = sec; break; } else seg->v_sec[i] = s; } if (i == 0) seg->v_sec[0] = sec; } void setup_phdr(struct elfcopy *ecp) { struct segment *seg; GElf_Phdr iphdr; size_t iphnum; int i; if (elf_getphnum(ecp->ein, &iphnum) == 0) errx(EXIT_FAILURE, "elf_getphnum failed: %s", elf_errmsg(-1)); ecp->ophnum = ecp->iphnum = iphnum; if (iphnum == 0) return; /* If --only-keep-debug is specified, discard all program headers. */ if (ecp->strip == STRIP_NONDEBUG) { ecp->ophnum = 0; return; } for (i = 0; (size_t)i < iphnum; i++) { if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr) errx(EXIT_FAILURE, "gelf_getphdr failed: %s", elf_errmsg(-1)); if ((seg = calloc(1, sizeof(*seg))) == NULL) err(EXIT_FAILURE, "calloc failed"); seg->addr = iphdr.p_vaddr; seg->off = iphdr.p_offset; seg->fsz = iphdr.p_filesz; seg->msz = iphdr.p_memsz; seg->type = iphdr.p_type; STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list); } } void copy_phdr(struct elfcopy *ecp) { struct segment *seg; struct section *s; GElf_Phdr iphdr, ophdr; int i; STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (seg->type == PT_PHDR) { if (!TAILQ_EMPTY(&ecp->v_sec)) { s = TAILQ_FIRST(&ecp->v_sec); if (s->pseudo) seg->addr = s->lma + gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT); } seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR, ecp->ophnum, EV_CURRENT); continue; } seg->fsz = seg->msz = 0; for (i = 0; i < seg->nsec; i++) { s = seg->v_sec[i]; seg->msz = s->off + s->sz - seg->off; if (s->type != SHT_NOBITS) seg->fsz = seg->msz; } } /* * Allocate space for program headers, note that libelf keep * track of the number in internal variable, and a call to * elf_update is needed to update e_phnum of ehdr. */ if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL) errx(EXIT_FAILURE, "gelf_newphdr() failed: %s", elf_errmsg(-1)); /* * This elf_update() call is to update the e_phnum field in * ehdr. It's necessary because later we will call gelf_getphdr(), * which does sanity check by comparing ndx argument with e_phnum. */ if (elf_update(ecp->eout, ELF_C_NULL) < 0) errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1)); /* * iphnum == ophnum, since we don't remove program headers even if * they no longer contain sections. */ i = 0; STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (i >= ecp->iphnum) break; if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr) errx(EXIT_FAILURE, "gelf_getphdr failed: %s", elf_errmsg(-1)); if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr) errx(EXIT_FAILURE, "gelf_getphdr failed: %s", elf_errmsg(-1)); ophdr.p_type = iphdr.p_type; ophdr.p_vaddr = seg->addr; ophdr.p_paddr = seg->addr; ophdr.p_flags = iphdr.p_flags; ophdr.p_align = iphdr.p_align; ophdr.p_offset = seg->off; ophdr.p_filesz = seg->fsz; ophdr.p_memsz = seg->msz; if (!gelf_update_phdr(ecp->eout, i, &ophdr)) err(EXIT_FAILURE, "gelf_update_phdr failed :%s", elf_errmsg(-1)); i++; } } Index: projects/clang350-import/contrib/elftoolchain/libelftc/libelftc_dem_gnu3.c =================================================================== --- projects/clang350-import/contrib/elftoolchain/libelftc/libelftc_dem_gnu3.c (revision 275386) +++ projects/clang350-import/contrib/elftoolchain/libelftc/libelftc_dem_gnu3.c (revision 275387) @@ -1,3238 +1,3238 @@ /*- * Copyright (c) 2007 Hyogeol Lee * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "_libelftc.h" ELFTC_VCSID("$Id: libelftc_dem_gnu3.c 2179 2011-11-18 03:05:47Z jkoshy $"); /** * @file cpp_demangle.c * @brief Decode IA-64 C++ ABI style implementation. * * IA-64 standard ABI(Itanium C++ ABI) references. * * http://www.codesourcery.com/cxx-abi/abi.html#mangling \n * http://www.codesourcery.com/cxx-abi/abi-mangling.html */ enum type_qualifier { TYPE_PTR, TYPE_REF, TYPE_CMX, TYPE_IMG, TYPE_EXT, TYPE_RST, TYPE_VAT, TYPE_CST }; struct vector_type_qualifier { size_t size, capacity; enum type_qualifier *q_container; struct vector_str ext_name; }; enum read_cmd { READ_FAIL, READ_NEST, READ_TMPL, READ_EXPR, READ_EXPL, READ_LOCAL, READ_TYPE, READ_FUNC, READ_PTRMEM }; struct vector_read_cmd { size_t size, capacity; enum read_cmd *r_container; }; struct cpp_demangle_data { struct vector_str output; /* output string vector */ struct vector_str output_tmp; struct vector_str subst; /* substitution string vector */ struct vector_str tmpl; struct vector_str class_type; struct vector_read_cmd cmd; bool paren; /* parenthesis opened */ bool pfirst; /* first element of parameter */ bool mem_rst; /* restrict member function */ bool mem_vat; /* volatile member function */ bool mem_cst; /* const member function */ int func_type; const char *cur; /* current mangled name ptr */ const char *last_sname; /* last source name */ }; #define CPP_DEMANGLE_TRY_LIMIT 128 #define FLOAT_SPRINTF_TRY_LIMIT 5 #define FLOAT_QUADRUPLE_BYTES 16 #define FLOAT_EXTENED_BYTES 10 #define SIMPLE_HASH(x,y) (64 * x + y) static void cpp_demangle_data_dest(struct cpp_demangle_data *); static int cpp_demangle_data_init(struct cpp_demangle_data *, const char *); static int cpp_demangle_get_subst(struct cpp_demangle_data *, size_t); static int cpp_demangle_get_tmpl_param(struct cpp_demangle_data *, size_t); static int cpp_demangle_push_fp(struct cpp_demangle_data *, char *(*)(const char *, size_t)); static int cpp_demangle_push_str(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_push_subst(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_push_subst_v(struct cpp_demangle_data *, struct vector_str *); static int cpp_demangle_push_type_qualifier(struct cpp_demangle_data *, struct vector_type_qualifier *, const char *); static int cpp_demangle_read_array(struct cpp_demangle_data *); static int cpp_demangle_read_encoding(struct cpp_demangle_data *); static int cpp_demangle_read_expr_primary(struct cpp_demangle_data *); static int cpp_demangle_read_expression(struct cpp_demangle_data *); static int cpp_demangle_read_expression_binary(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_read_expression_unary(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_read_expression_trinary(struct cpp_demangle_data *, const char *, size_t, const char *, size_t); static int cpp_demangle_read_function(struct cpp_demangle_data *, int *, struct vector_type_qualifier *); static int cpp_demangle_local_source_name(struct cpp_demangle_data *ddata); static int cpp_demangle_read_local_name(struct cpp_demangle_data *); static int cpp_demangle_read_name(struct cpp_demangle_data *); static int cpp_demangle_read_nested_name(struct cpp_demangle_data *); static int cpp_demangle_read_number(struct cpp_demangle_data *, long *); static int cpp_demangle_read_nv_offset(struct cpp_demangle_data *); static int cpp_demangle_read_offset(struct cpp_demangle_data *); static int cpp_demangle_read_offset_number(struct cpp_demangle_data *); static int cpp_demangle_read_pointer_to_member(struct cpp_demangle_data *); static int cpp_demangle_read_sname(struct cpp_demangle_data *); static int cpp_demangle_read_subst(struct cpp_demangle_data *); static int cpp_demangle_read_subst_std(struct cpp_demangle_data *); static int cpp_demangle_read_subst_stdtmpl(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_read_tmpl_arg(struct cpp_demangle_data *); static int cpp_demangle_read_tmpl_args(struct cpp_demangle_data *); static int cpp_demangle_read_tmpl_param(struct cpp_demangle_data *); static int cpp_demangle_read_type(struct cpp_demangle_data *, int); static int cpp_demangle_read_uqname(struct cpp_demangle_data *); static int cpp_demangle_read_v_offset(struct cpp_demangle_data *); static char *decode_fp_to_double(const char *, size_t); static char *decode_fp_to_float(const char *, size_t); static char *decode_fp_to_float128(const char *, size_t); static char *decode_fp_to_float80(const char *, size_t); static char *decode_fp_to_long_double(const char *, size_t); static int hex_to_dec(char); static void vector_read_cmd_dest(struct vector_read_cmd *); static int vector_read_cmd_find(struct vector_read_cmd *, enum read_cmd); static int vector_read_cmd_init(struct vector_read_cmd *); static int vector_read_cmd_pop(struct vector_read_cmd *); static int vector_read_cmd_push(struct vector_read_cmd *, enum read_cmd); static void vector_type_qualifier_dest(struct vector_type_qualifier *); static int vector_type_qualifier_init(struct vector_type_qualifier *); static int vector_type_qualifier_push(struct vector_type_qualifier *, enum type_qualifier); -int cpp_demangle_gnu3_push_head; +static int cpp_demangle_gnu3_push_head; /** * @brief Decode the input string by IA-64 C++ ABI style. * * GNU GCC v3 use IA-64 standard ABI. * @return New allocated demangled string or NULL if failed. * @todo 1. Testing and more test case. 2. Code cleaning. */ char * cpp_demangle_gnu3(const char *org) { struct cpp_demangle_data ddata; ssize_t org_len; unsigned int limit; char *rtn; if (org == NULL || (org_len = strlen(org)) < 2) return (NULL); if (org_len > 11 && !strncmp(org, "_GLOBAL__I_", 11)) { if ((rtn = malloc(org_len + 19)) == NULL) return (NULL); snprintf(rtn, org_len + 19, "global constructors keyed to %s", org + 11); return (rtn); } if (org[0] != '_' || org[1] != 'Z') return (NULL); if (!cpp_demangle_data_init(&ddata, org + 2)) return (NULL); cpp_demangle_gnu3_push_head = 0; rtn = NULL; if (!cpp_demangle_read_encoding(&ddata)) goto clean; limit = 0; while (*ddata.cur != '\0') { /* * Breaking at some gcc info at tail. e.g) @@GLIBCXX_3.4 */ if (*ddata.cur == '@' && *(ddata.cur + 1) == '@') break; if (!cpp_demangle_read_type(&ddata, 1)) goto clean; if (limit++ > CPP_DEMANGLE_TRY_LIMIT) goto clean; } if (ddata.output.size == 0) goto clean; if (ddata.paren && !vector_str_push(&ddata.output, ")", 1)) goto clean; if (ddata.mem_vat && !vector_str_push(&ddata.output, " volatile", 9)) goto clean; if (ddata.mem_cst && !vector_str_push(&ddata.output, " const", 6)) goto clean; if (ddata.mem_rst && !vector_str_push(&ddata.output, " restrict", 9)) goto clean; rtn = vector_str_get_flat(&ddata.output, (size_t *) NULL); clean: cpp_demangle_data_dest(&ddata); return (rtn); } static void cpp_demangle_data_dest(struct cpp_demangle_data *d) { if (d == NULL) return; vector_read_cmd_dest(&d->cmd); vector_str_dest(&d->class_type); vector_str_dest(&d->tmpl); vector_str_dest(&d->subst); vector_str_dest(&d->output_tmp); vector_str_dest(&d->output); } static int cpp_demangle_data_init(struct cpp_demangle_data *d, const char *cur) { if (d == NULL || cur == NULL) return (0); if (!vector_str_init(&d->output)) return (0); if (!vector_str_init(&d->output_tmp)) goto clean1; if (!vector_str_init(&d->subst)) goto clean2; if (!vector_str_init(&d->tmpl)) goto clean3; if (!vector_str_init(&d->class_type)) goto clean4; if (!vector_read_cmd_init(&d->cmd)) goto clean5; assert(d->output.container != NULL); assert(d->output_tmp.container != NULL); assert(d->subst.container != NULL); assert(d->tmpl.container != NULL); assert(d->class_type.container != NULL); d->paren = false; d->pfirst = false; d->mem_rst = false; d->mem_vat = false; d->mem_cst = false; d->func_type = 0; d->cur = cur; d->last_sname = NULL; return (1); clean5: vector_str_dest(&d->class_type); clean4: vector_str_dest(&d->tmpl); clean3: vector_str_dest(&d->subst); clean2: vector_str_dest(&d->output_tmp); clean1: vector_str_dest(&d->output); return (0); } static int cpp_demangle_push_fp(struct cpp_demangle_data *ddata, char *(*decoder)(const char *, size_t)) { size_t len; int rtn; const char *fp; char *f; if (ddata == NULL || decoder == NULL) return (0); fp = ddata->cur; while (*ddata->cur != 'E') ++ddata->cur; ++ddata->cur; if ((f = decoder(fp, ddata->cur - fp)) == NULL) return (0); rtn = 0; if ((len = strlen(f)) > 0) rtn = cpp_demangle_push_str(ddata, f, len); free(f); return (rtn); } static int cpp_demangle_push_str(struct cpp_demangle_data *ddata, const char *str, size_t len) { if (ddata == NULL || str == NULL || len == 0) return (0); if (cpp_demangle_gnu3_push_head > 0) return (vector_str_push(&ddata->output_tmp, str, len)); return (vector_str_push(&ddata->output, str, len)); } static int cpp_demangle_push_subst(struct cpp_demangle_data *ddata, const char *str, size_t len) { if (ddata == NULL || str == NULL || len == 0) return (0); if (!vector_str_find(&ddata->subst, str, len)) return (vector_str_push(&ddata->subst, str, len)); return (1); } static int cpp_demangle_push_subst_v(struct cpp_demangle_data *ddata, struct vector_str *v) { size_t str_len; int rtn; char *str; if (ddata == NULL || v == NULL) return (0); if ((str = vector_str_get_flat(v, &str_len)) == NULL) return (0); rtn = cpp_demangle_push_subst(ddata, str, str_len); free(str); return (rtn); } static int cpp_demangle_push_type_qualifier(struct cpp_demangle_data *ddata, struct vector_type_qualifier *v, const char *type_str) { struct vector_str subst_v; size_t idx, e_idx, e_len; int rtn; char *buf; if (ddata == NULL || v == NULL) return (0); if ((idx = v->size) == 0) return (1); rtn = 0; if (type_str != NULL) { if (!vector_str_init(&subst_v)) return (0); if (!vector_str_push(&subst_v, type_str, strlen(type_str))) goto clean; } e_idx = 0; while (idx > 0) { switch (v->q_container[idx - 1]) { case TYPE_PTR: if (!cpp_demangle_push_str(ddata, "*", 1)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, "*", 1)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_REF: if (!cpp_demangle_push_str(ddata, "&", 1)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, "&", 1)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_CMX: if (!cpp_demangle_push_str(ddata, " complex", 8)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " complex", 8)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_IMG: if (!cpp_demangle_push_str(ddata, " imaginary", 10)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " imaginary", 10)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_EXT: if (e_idx > v->ext_name.size - 1) goto clean; if ((e_len = strlen(v->ext_name.container[e_idx])) == 0) goto clean; if ((buf = malloc(sizeof(char) * (e_len + 1))) == NULL) goto clean; memcpy(buf, " ", 1); memcpy(buf + 1, v->ext_name.container[e_idx], e_len); if (!cpp_demangle_push_str(ddata, buf, e_len + 1)) { free(buf); goto clean; } if (type_str != NULL) { if (!vector_str_push(&subst_v, buf, e_len + 1)) { free(buf); goto clean; } if (!cpp_demangle_push_subst_v(ddata, &subst_v)) { free(buf); goto clean; } } free(buf); ++e_idx; break; case TYPE_RST: if (!cpp_demangle_push_str(ddata, " restrict", 9)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " restrict", 9)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_VAT: if (!cpp_demangle_push_str(ddata, " volatile", 9)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " volatile", 9)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_CST: if (!cpp_demangle_push_str(ddata, " const", 6)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " const", 6)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; }; --idx; } rtn = 1; clean: if (type_str != NULL) vector_str_dest(&subst_v); return (rtn); } static int cpp_demangle_get_subst(struct cpp_demangle_data *ddata, size_t idx) { size_t len; if (ddata == NULL || ddata->subst.size <= idx) return (0); if ((len = strlen(ddata->subst.container[idx])) == 0) return (0); if (!cpp_demangle_push_str(ddata, ddata->subst.container[idx], len)) return (0); /* skip '_' */ ++ddata->cur; return (1); } static int cpp_demangle_get_tmpl_param(struct cpp_demangle_data *ddata, size_t idx) { size_t len; if (ddata == NULL || ddata->tmpl.size <= idx) return (0); if ((len = strlen(ddata->tmpl.container[idx])) == 0) return (0); if (!cpp_demangle_push_str(ddata, ddata->tmpl.container[idx], len)) return (0); ++ddata->cur; return (1); } static int cpp_demangle_read_array(struct cpp_demangle_data *ddata) { size_t i, num_len, exp_len, p_idx, idx; const char *num; char *exp; if (ddata == NULL || *(++ddata->cur) == '\0') return (0); if (*ddata->cur == '_') { if (*(++ddata->cur) == '\0') return (0); if (!cpp_demangle_read_type(ddata, 0)) return (0); if (!cpp_demangle_push_str(ddata, "[]", 2)) return (0); } else { if (ELFTC_ISDIGIT(*ddata->cur) != 0) { num = ddata->cur; while (ELFTC_ISDIGIT(*ddata->cur) != 0) ++ddata->cur; if (*ddata->cur != '_') return (0); num_len = ddata->cur - num; assert(num_len > 0); if (*(++ddata->cur) == '\0') return (0); if (!cpp_demangle_read_type(ddata, 0)) return (0); if (!cpp_demangle_push_str(ddata, "[", 1)) return (0); if (!cpp_demangle_push_str(ddata, num, num_len)) return (0); if (!cpp_demangle_push_str(ddata, "]", 1)) return (0); } else { p_idx = ddata->output.size; if (!cpp_demangle_read_expression(ddata)) return (0); if ((exp = vector_str_substr(&ddata->output, p_idx, ddata->output.size - 1, &exp_len)) == NULL) return (0); idx = ddata->output.size; for (i = p_idx; i < idx; ++i) if (!vector_str_pop(&ddata->output)) { free(exp); return (0); } if (*ddata->cur != '_') { free(exp); return (0); } ++ddata->cur; if (*ddata->cur == '\0') { free(exp); return (0); } if (!cpp_demangle_read_type(ddata, 0)) { free(exp); return (0); } if (!cpp_demangle_push_str(ddata, "[", 1)) { free(exp); return (0); } if (!cpp_demangle_push_str(ddata, exp, exp_len)) { free(exp); return (0); } if (!cpp_demangle_push_str(ddata, "]", 1)) { free(exp); return (0); } free(exp); } } return (1); } static int cpp_demangle_read_expr_primary(struct cpp_demangle_data *ddata) { const char *num; if (ddata == NULL || *(++ddata->cur) == '\0') return (0); if (*ddata->cur == '_' && *(ddata->cur + 1) == 'Z') { ddata->cur += 2; if (*ddata->cur == '\0') return (0); if (!cpp_demangle_read_encoding(ddata)) return (0); ++ddata->cur; return (1); } switch (*ddata->cur) { case 'b': switch (*(++ddata->cur)) { case '0': return (cpp_demangle_push_str(ddata, "false", 5)); case '1': return (cpp_demangle_push_str(ddata, "true", 4)); default: return (0); }; case 'd': ++ddata->cur; return (cpp_demangle_push_fp(ddata, decode_fp_to_double)); case 'e': ++ddata->cur; if (sizeof(long double) == 10) return (cpp_demangle_push_fp(ddata, decode_fp_to_double)); return (cpp_demangle_push_fp(ddata, decode_fp_to_float80)); case 'f': ++ddata->cur; return (cpp_demangle_push_fp(ddata, decode_fp_to_float)); case 'g': ++ddata->cur; if (sizeof(long double) == 16) return (cpp_demangle_push_fp(ddata, decode_fp_to_double)); return (cpp_demangle_push_fp(ddata, decode_fp_to_float128)); case 'i': case 'j': case 'l': case 'm': case 'n': case 's': case 't': case 'x': case 'y': if (*(++ddata->cur) == 'n') { if (!cpp_demangle_push_str(ddata, "-", 1)) return (0); ++ddata->cur; } num = ddata->cur; while (*ddata->cur != 'E') { if (!ELFTC_ISDIGIT(*ddata->cur)) return (0); ++ddata->cur; } ++ddata->cur; return (cpp_demangle_push_str(ddata, num, ddata->cur - num)); default: return (0); }; } static int cpp_demangle_read_expression(struct cpp_demangle_data *ddata) { if (ddata == NULL || *ddata->cur == '\0') return (0); switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('s', 't'): ddata->cur += 2; return (cpp_demangle_read_type(ddata, 0)); case SIMPLE_HASH('s', 'r'): ddata->cur += 2; if (!cpp_demangle_read_type(ddata, 0)) return (0); if (!cpp_demangle_read_uqname(ddata)) return (0); if (*ddata->cur == 'I') return (cpp_demangle_read_tmpl_args(ddata)); return (1); case SIMPLE_HASH('a', 'a'): /* operator && */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "&&", 2)); case SIMPLE_HASH('a', 'd'): /* operator & (unary) */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "&", 1)); case SIMPLE_HASH('a', 'n'): /* operator & */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "&", 1)); case SIMPLE_HASH('a', 'N'): /* operator &= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "&=", 2)); case SIMPLE_HASH('a', 'S'): /* operator = */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "=", 1)); case SIMPLE_HASH('c', 'l'): /* operator () */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "()", 2)); case SIMPLE_HASH('c', 'm'): /* operator , */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ",", 1)); case SIMPLE_HASH('c', 'o'): /* operator ~ */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "~", 1)); case SIMPLE_HASH('c', 'v'): /* operator (cast) */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "(cast)", 6)); case SIMPLE_HASH('d', 'a'): /* operator delete [] */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "delete []", 9)); case SIMPLE_HASH('d', 'e'): /* operator * (unary) */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "*", 1)); case SIMPLE_HASH('d', 'l'): /* operator delete */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "delete", 6)); case SIMPLE_HASH('d', 'v'): /* operator / */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "/", 1)); case SIMPLE_HASH('d', 'V'): /* operator /= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "/=", 2)); case SIMPLE_HASH('e', 'o'): /* operator ^ */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "^", 1)); case SIMPLE_HASH('e', 'O'): /* operator ^= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "^=", 2)); case SIMPLE_HASH('e', 'q'): /* operator == */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "==", 2)); case SIMPLE_HASH('g', 'e'): /* operator >= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ">=", 2)); case SIMPLE_HASH('g', 't'): /* operator > */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ">", 1)); case SIMPLE_HASH('i', 'x'): /* operator [] */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "[]", 2)); case SIMPLE_HASH('l', 'e'): /* operator <= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "<=", 2)); case SIMPLE_HASH('l', 's'): /* operator << */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "<<", 2)); case SIMPLE_HASH('l', 'S'): /* operator <<= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "<<=", 3)); case SIMPLE_HASH('l', 't'): /* operator < */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "<", 1)); case SIMPLE_HASH('m', 'i'): /* operator - */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "-", 1)); case SIMPLE_HASH('m', 'I'): /* operator -= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "-=", 2)); case SIMPLE_HASH('m', 'l'): /* operator * */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "*", 1)); case SIMPLE_HASH('m', 'L'): /* operator *= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "*=", 2)); case SIMPLE_HASH('m', 'm'): /* operator -- */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "--", 2)); case SIMPLE_HASH('n', 'a'): /* operator new[] */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "new []", 6)); case SIMPLE_HASH('n', 'e'): /* operator != */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "!=", 2)); case SIMPLE_HASH('n', 'g'): /* operator - (unary) */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "-", 1)); case SIMPLE_HASH('n', 't'): /* operator ! */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "!", 1)); case SIMPLE_HASH('n', 'w'): /* operator new */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "new", 3)); case SIMPLE_HASH('o', 'o'): /* operator || */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "||", 2)); case SIMPLE_HASH('o', 'r'): /* operator | */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "|", 1)); case SIMPLE_HASH('o', 'R'): /* operator |= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "|=", 2)); case SIMPLE_HASH('p', 'l'): /* operator + */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "+", 1)); case SIMPLE_HASH('p', 'L'): /* operator += */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "+=", 2)); case SIMPLE_HASH('p', 'm'): /* operator ->* */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "->*", 3)); case SIMPLE_HASH('p', 'p'): /* operator ++ */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "++", 2)); case SIMPLE_HASH('p', 's'): /* operator + (unary) */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "+", 1)); case SIMPLE_HASH('p', 't'): /* operator -> */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "->", 2)); case SIMPLE_HASH('q', 'u'): /* operator ? */ ddata->cur += 2; return (cpp_demangle_read_expression_trinary(ddata, "?", 1, ":", 1)); case SIMPLE_HASH('r', 'm'): /* operator % */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "%", 1)); case SIMPLE_HASH('r', 'M'): /* operator %= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "%=", 2)); case SIMPLE_HASH('r', 's'): /* operator >> */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ">>", 2)); case SIMPLE_HASH('r', 'S'): /* operator >>= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ">>=", 3)); case SIMPLE_HASH('r', 'z'): /* operator sizeof */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "sizeof", 6)); case SIMPLE_HASH('s', 'v'): /* operator sizeof */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "sizeof", 6)); }; switch (*ddata->cur) { case 'L': return (cpp_demangle_read_expr_primary(ddata)); case 'T': return (cpp_demangle_read_tmpl_param(ddata)); }; return (0); } static int cpp_demangle_read_expression_binary(struct cpp_demangle_data *ddata, const char *name, size_t len) { if (ddata == NULL || name == NULL || len == 0) return (0); if (!cpp_demangle_read_expression(ddata)) return (0); if (!cpp_demangle_push_str(ddata, name, len)) return (0); return (cpp_demangle_read_expression(ddata)); } static int cpp_demangle_read_expression_unary(struct cpp_demangle_data *ddata, const char *name, size_t len) { if (ddata == NULL || name == NULL || len == 0) return (0); if (!cpp_demangle_read_expression(ddata)) return (0); return (cpp_demangle_push_str(ddata, name, len)); } static int cpp_demangle_read_expression_trinary(struct cpp_demangle_data *ddata, const char *name1, size_t len1, const char *name2, size_t len2) { if (ddata == NULL || name1 == NULL || len1 == 0 || name2 == NULL || len2 == 0) return (0); if (!cpp_demangle_read_expression(ddata)) return (0); if (!cpp_demangle_push_str(ddata, name1, len1)) return (0); if (!cpp_demangle_read_expression(ddata)) return (0); if (!cpp_demangle_push_str(ddata, name2, len2)) return (0); return (cpp_demangle_read_expression(ddata)); } static int cpp_demangle_read_function(struct cpp_demangle_data *ddata, int *ext_c, struct vector_type_qualifier *v) { size_t class_type_size, class_type_len, limit; const char *class_type; if (ddata == NULL || *ddata->cur != 'F' || v == NULL) return (0); ++ddata->cur; if (*ddata->cur == 'Y') { if (ext_c != NULL) *ext_c = 1; ++ddata->cur; } if (!cpp_demangle_read_type(ddata, 0)) return (0); if (*ddata->cur != 'E') { if (!cpp_demangle_push_str(ddata, "(", 1)) return (0); if (vector_read_cmd_find(&ddata->cmd, READ_PTRMEM)) { if ((class_type_size = ddata->class_type.size) == 0) return (0); class_type = ddata->class_type.container[class_type_size - 1]; if (class_type == NULL) return (0); if ((class_type_len = strlen(class_type)) == 0) return (0); if (!cpp_demangle_push_str(ddata, class_type, class_type_len)) return (0); if (!cpp_demangle_push_str(ddata, "::*", 3)) return (0); ++ddata->func_type; } else { if (!cpp_demangle_push_type_qualifier(ddata, v, (const char *) NULL)) return (0); vector_type_qualifier_dest(v); if (!vector_type_qualifier_init(v)) return (0); } if (!cpp_demangle_push_str(ddata, ")(", 2)) return (0); limit = 0; for (;;) { if (!cpp_demangle_read_type(ddata, 0)) return (0); if (*ddata->cur == 'E') break; if (limit++ > CPP_DEMANGLE_TRY_LIMIT) return (0); } if (vector_read_cmd_find(&ddata->cmd, READ_PTRMEM) == 1) { if (!cpp_demangle_push_type_qualifier(ddata, v, (const char *) NULL)) return (0); vector_type_qualifier_dest(v); if (!vector_type_qualifier_init(v)) return (0); } if (!cpp_demangle_push_str(ddata, ")", 1)) return (0); } ++ddata->cur; return (1); } /* read encoding, encoding are function name, data name, special-name */ static int cpp_demangle_read_encoding(struct cpp_demangle_data *ddata) { if (ddata == NULL || *ddata->cur == '\0') return (0); /* special name */ switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('G', 'V'): /* sentry object for 1 time init */ if (!cpp_demangle_push_str(ddata, "guard variable for ", 20)) return (0); ddata->cur += 2; break; case SIMPLE_HASH('T', 'c'): /* virtual function covariant override thunk */ if (!cpp_demangle_push_str(ddata, "virtual function covariant override ", 36)) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); if (!cpp_demangle_read_offset(ddata)) return (0); if (!cpp_demangle_read_offset(ddata)) return (0); return (cpp_demangle_read_encoding(ddata)); case SIMPLE_HASH('T', 'D'): /* typeinfo common proxy */ break; case SIMPLE_HASH('T', 'h'): /* virtual function non-virtual override thunk */ if (cpp_demangle_push_str(ddata, "virtual function non-virtual override ", 38) == 0) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); if (!cpp_demangle_read_nv_offset(ddata)) return (0); return (cpp_demangle_read_encoding(ddata)); case SIMPLE_HASH('T', 'I'): /* typeinfo structure */ /* FALLTHROUGH */ case SIMPLE_HASH('T', 'S'): /* RTTI name (NTBS) */ if (!cpp_demangle_push_str(ddata, "typeinfo for ", 14)) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); return (cpp_demangle_read_type(ddata, 1)); case SIMPLE_HASH('T', 'T'): /* VTT table */ if (!cpp_demangle_push_str(ddata, "VTT for ", 8)) return (0); ddata->cur += 2; return (cpp_demangle_read_type(ddata, 1)); case SIMPLE_HASH('T', 'v'): /* virtual function virtual override thunk */ if (!cpp_demangle_push_str(ddata, "virtual function virtual override ", 34)) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); if (!cpp_demangle_read_v_offset(ddata)) return (0); return (cpp_demangle_read_encoding(ddata)); case SIMPLE_HASH('T', 'V'): /* virtual table */ if (!cpp_demangle_push_str(ddata, "vtable for ", 12)) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); return (cpp_demangle_read_type(ddata, 1)); }; return (cpp_demangle_read_name(ddata)); } static int cpp_demangle_read_local_name(struct cpp_demangle_data *ddata) { size_t limit; if (ddata == NULL) return (0); if (*(++ddata->cur) == '\0') return (0); if (!cpp_demangle_read_encoding(ddata)) return (0); limit = 0; for (;;) { if (!cpp_demangle_read_type(ddata, 1)) return (0); if (*ddata->cur == 'E') break; if (limit++ > CPP_DEMANGLE_TRY_LIMIT) return (0); } if (*(++ddata->cur) == '\0') return (0); if (ddata->paren == true) { if (!cpp_demangle_push_str(ddata, ")", 1)) return (0); ddata->paren = false; } if (*ddata->cur == 's') ++ddata->cur; else { if (!cpp_demangle_push_str(ddata, "::", 2)) return (0); if (!cpp_demangle_read_name(ddata)) return (0); } if (*ddata->cur == '_') { ++ddata->cur; while (ELFTC_ISDIGIT(*ddata->cur) != 0) ++ddata->cur; } return (1); } static int cpp_demangle_read_name(struct cpp_demangle_data *ddata) { struct vector_str *output, v; size_t p_idx, subst_str_len; int rtn; char *subst_str; if (ddata == NULL || *ddata->cur == '\0') return (0); output = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; subst_str = NULL; switch (*ddata->cur) { case 'S': return (cpp_demangle_read_subst(ddata)); case 'N': return (cpp_demangle_read_nested_name(ddata)); case 'Z': return (cpp_demangle_read_local_name(ddata)); }; if (!vector_str_init(&v)) return (0); p_idx = output->size; rtn = 0; if (!cpp_demangle_read_uqname(ddata)) goto clean; if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (subst_str_len > 8 && strstr(subst_str, "operator") != NULL) { rtn = 1; goto clean; } if (!vector_str_push(&v, subst_str, subst_str_len)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; if (*ddata->cur == 'I') { p_idx = output->size; if (!cpp_demangle_read_tmpl_args(ddata)) goto clean; free(subst_str); if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (!vector_str_push(&v, subst_str, subst_str_len)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; } rtn = 1; clean: free(subst_str); vector_str_dest(&v); return (rtn); } static int cpp_demangle_read_nested_name(struct cpp_demangle_data *ddata) { struct vector_str *output, v; size_t limit, p_idx, subst_str_len; int rtn; char *subst_str; if (ddata == NULL || *ddata->cur != 'N') return (0); if (*(++ddata->cur) == '\0') return (0); while (*ddata->cur == 'r' || *ddata->cur == 'V' || *ddata->cur == 'K') { switch (*ddata->cur) { case 'r': ddata->mem_rst = true; break; case 'V': ddata->mem_vat = true; break; case 'K': ddata->mem_cst = true; break; }; ++ddata->cur; } output = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; if (!vector_str_init(&v)) return (0); rtn = 0; limit = 0; for (;;) { p_idx = output->size; switch (*ddata->cur) { case 'I': if (!cpp_demangle_read_tmpl_args(ddata)) goto clean; break; case 'S': if (!cpp_demangle_read_subst(ddata)) goto clean; break; case 'T': if (!cpp_demangle_read_tmpl_param(ddata)) goto clean; break; default: if (!cpp_demangle_read_uqname(ddata)) goto clean; }; if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (!vector_str_push(&v, subst_str, subst_str_len)) { free(subst_str); goto clean; } free(subst_str); if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; if (*ddata->cur == 'E') break; else if (*ddata->cur != 'I' && *ddata->cur != 'C' && *ddata->cur != 'D') { if (!cpp_demangle_push_str(ddata, "::", 2)) goto clean; if (!vector_str_push(&v, "::", 2)) goto clean; } if (limit++ > CPP_DEMANGLE_TRY_LIMIT) goto clean; } ++ddata->cur; rtn = 1; clean: vector_str_dest(&v); return (rtn); } /* * read number * number ::= [n] */ static int cpp_demangle_read_number(struct cpp_demangle_data *ddata, long *rtn) { long len, negative_factor; if (ddata == NULL || rtn == NULL) return (0); negative_factor = 1; if (*ddata->cur == 'n') { negative_factor = -1; ++ddata->cur; } if (ELFTC_ISDIGIT(*ddata->cur) == 0) return (0); errno = 0; if ((len = strtol(ddata->cur, (char **) NULL, 10)) == 0 && errno != 0) return (0); while (ELFTC_ISDIGIT(*ddata->cur) != 0) ++ddata->cur; assert(len >= 0); assert(negative_factor == 1 || negative_factor == -1); *rtn = len * negative_factor; return (1); } static int cpp_demangle_read_nv_offset(struct cpp_demangle_data *ddata) { if (ddata == NULL) return (0); if (!cpp_demangle_push_str(ddata, "offset : ", 9)) return (0); return (cpp_demangle_read_offset_number(ddata)); } /* read offset, offset are nv-offset, v-offset */ static int cpp_demangle_read_offset(struct cpp_demangle_data *ddata) { if (ddata == NULL) return (0); if (*ddata->cur == 'h') { ++ddata->cur; return (cpp_demangle_read_nv_offset(ddata)); } else if (*ddata->cur == 'v') { ++ddata->cur; return (cpp_demangle_read_v_offset(ddata)); } return (0); } static int cpp_demangle_read_offset_number(struct cpp_demangle_data *ddata) { bool negative; const char *start; if (ddata == NULL || *ddata->cur == '\0') return (0); /* offset could be negative */ if (*ddata->cur == 'n') { negative = true; start = ddata->cur + 1; } else { negative = false; start = ddata->cur; } while (*ddata->cur != '_') ++ddata->cur; if (negative && !cpp_demangle_push_str(ddata, "-", 1)) return (0); assert(start != NULL); if (!cpp_demangle_push_str(ddata, start, ddata->cur - start)) return (0); if (!cpp_demangle_push_str(ddata, " ", 1)) return (0); ++ddata->cur; return (1); } static int cpp_demangle_read_pointer_to_member(struct cpp_demangle_data *ddata) { size_t class_type_len, i, idx, p_idx; int p_func_type, rtn; char *class_type; if (ddata == NULL || *ddata->cur != 'M' || *(++ddata->cur) == '\0') return (0); p_idx = ddata->output.size; if (!cpp_demangle_read_type(ddata, 0)) return (0); if ((class_type = vector_str_substr(&ddata->output, p_idx, ddata->output.size - 1, &class_type_len)) == NULL) return (0); rtn = 0; idx = ddata->output.size; for (i = p_idx; i < idx; ++i) if (!vector_str_pop(&ddata->output)) goto clean1; if (!vector_read_cmd_push(&ddata->cmd, READ_PTRMEM)) goto clean1; if (!vector_str_push(&ddata->class_type, class_type, class_type_len)) goto clean2; p_func_type = ddata->func_type; if (!cpp_demangle_read_type(ddata, 0)) goto clean3; if (p_func_type == ddata->func_type) { if (!cpp_demangle_push_str(ddata, " ", 1)) goto clean3; if (!cpp_demangle_push_str(ddata, class_type, class_type_len)) goto clean3; if (!cpp_demangle_push_str(ddata, "::*", 3)) goto clean3; } rtn = 1; clean3: if (!vector_str_pop(&ddata->class_type)) rtn = 0; clean2: if (!vector_read_cmd_pop(&ddata->cmd)) rtn = 0; clean1: free(class_type); return (rtn); } /* read source-name, source-name is */ static int cpp_demangle_read_sname(struct cpp_demangle_data *ddata) { long len; if (ddata == NULL || cpp_demangle_read_number(ddata, &len) == 0 || len <= 0 || cpp_demangle_push_str(ddata, ddata->cur, len) == 0) return (0); assert(ddata->output.size > 0); if (vector_read_cmd_find(&ddata->cmd, READ_TMPL) == 0) ddata->last_sname = ddata->output.container[ddata->output.size - 1]; ddata->cur += len; return (1); } static int cpp_demangle_read_subst(struct cpp_demangle_data *ddata) { long nth; if (ddata == NULL || *ddata->cur == '\0') return (0); /* abbreviations of the form Sx */ switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('S', 'a'): /* std::allocator */ if (cpp_demangle_push_str(ddata, "std::allocator", 14) == 0) return (0); ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::allocator", 14)); return (1); case SIMPLE_HASH('S', 'b'): /* std::basic_string */ if (!cpp_demangle_push_str(ddata, "std::basic_string", 17)) return (0); ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::basic_string", 17)); return (1); case SIMPLE_HASH('S', 'd'): /* std::basic_iostream > */ if (!cpp_demangle_push_str(ddata, "std::iostream", 19)) return (0); ddata->last_sname = "iostream"; ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::iostream", 19)); return (1); case SIMPLE_HASH('S', 'i'): /* std::basic_istream > */ if (!cpp_demangle_push_str(ddata, "std::istream", 18)) return (0); ddata->last_sname = "istream"; ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::istream", 18)); return (1); case SIMPLE_HASH('S', 'o'): /* std::basic_ostream > */ if (!cpp_demangle_push_str(ddata, "std::ostream", 18)) return (0); ddata->last_sname = "istream"; ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::ostream", 18)); return (1); case SIMPLE_HASH('S', 's'): /* * std::basic_string, * std::allocator > * * a.k.a std::string */ if (!cpp_demangle_push_str(ddata, "std::string", 11)) return (0); ddata->last_sname = "string"; ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::string", 11)); return (1); case SIMPLE_HASH('S', 't'): /* std:: */ return (cpp_demangle_read_subst_std(ddata)); }; if (*(++ddata->cur) == '\0') return (0); /* substitution */ if (*ddata->cur == '_') return (cpp_demangle_get_subst(ddata, 0)); else { errno = 0; /* substitution number is base 36 */ if ((nth = strtol(ddata->cur, (char **) NULL, 36)) == 0 && errno != 0) return (0); /* first was '_', so increase one */ ++nth; while (*ddata->cur != '_') ++ddata->cur; assert(nth > 0); return (cpp_demangle_get_subst(ddata, nth)); } /* NOTREACHED */ return (0); } static int cpp_demangle_read_subst_std(struct cpp_demangle_data *ddata) { struct vector_str *output, v; size_t p_idx, subst_str_len; int rtn; char *subst_str; if (ddata == NULL) return (0); if (!vector_str_init(&v)) return (0); subst_str = NULL; rtn = 0; if (!cpp_demangle_push_str(ddata, "std::", 5)) goto clean; if (!vector_str_push(&v, "std::", 5)) goto clean; ddata->cur += 2; output = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; p_idx = output->size; if (!cpp_demangle_read_uqname(ddata)) goto clean; if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (!vector_str_push(&v, subst_str, subst_str_len)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; if (*ddata->cur == 'I') { p_idx = output->size; if (!cpp_demangle_read_tmpl_args(ddata)) goto clean; free(subst_str); if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (!vector_str_push(&v, subst_str, subst_str_len)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; } rtn = 1; clean: free(subst_str); vector_str_dest(&v); return (rtn); } static int cpp_demangle_read_subst_stdtmpl(struct cpp_demangle_data *ddata, const char *str, size_t len) { struct vector_str *output; size_t p_idx, substr_len; int rtn; char *subst_str, *substr; if (ddata == NULL || str == NULL || len == 0) return (0); output = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; p_idx = output->size; substr = NULL; subst_str = NULL; if (!cpp_demangle_read_tmpl_args(ddata)) return (0); if ((substr = vector_str_substr(output, p_idx, output->size - 1, &substr_len)) == NULL) return (0); rtn = 0; if ((subst_str = malloc(sizeof(char) * (substr_len + len + 1))) == NULL) goto clean; memcpy(subst_str, str, len); memcpy(subst_str + len, substr, substr_len); subst_str[substr_len + len] = '\0'; if (!cpp_demangle_push_subst(ddata, subst_str, substr_len + len)) goto clean; rtn = 1; clean: free(subst_str); free(substr); return (rtn); } static int cpp_demangle_read_tmpl_arg(struct cpp_demangle_data *ddata) { if (ddata == NULL || *ddata->cur == '\0') return (0); switch (*ddata->cur) { case 'L': return (cpp_demangle_read_expr_primary(ddata)); case 'X': return (cpp_demangle_read_expression(ddata)); }; return (cpp_demangle_read_type(ddata, 0)); } static int cpp_demangle_read_tmpl_args(struct cpp_demangle_data *ddata) { struct vector_str *v; size_t arg_len, idx, limit, size; char *arg; if (ddata == NULL || *ddata->cur == '\0') return (0); ++ddata->cur; if (!vector_read_cmd_push(&ddata->cmd, READ_TMPL)) return (0); if (!cpp_demangle_push_str(ddata, "<", 1)) return (0); limit = 0; v = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; for (;;) { idx = v->size; if (!cpp_demangle_read_tmpl_arg(ddata)) return (0); if ((arg = vector_str_substr(v, idx, v->size - 1, &arg_len)) == NULL) return (0); if (!vector_str_find(&ddata->tmpl, arg, arg_len) && !vector_str_push(&ddata->tmpl, arg, arg_len)) { free(arg); return (0); } free(arg); if (*ddata->cur == 'E') { ++ddata->cur; size = v->size; assert(size > 0); if (!strncmp(v->container[size - 1], ">", 1)) { if (!cpp_demangle_push_str(ddata, " >", 2)) return (0); } else if (!cpp_demangle_push_str(ddata, ">", 1)) return (0); break; } else if (*ddata->cur != 'I' && !cpp_demangle_push_str(ddata, ", ", 2)) return (0); if (limit++ > CPP_DEMANGLE_TRY_LIMIT) return (0); } return (vector_read_cmd_pop(&ddata->cmd)); } /* * Read template parameter that forms in 'T[number]_'. * This function much like to read_subst but only for types. */ static int cpp_demangle_read_tmpl_param(struct cpp_demangle_data *ddata) { long nth; if (ddata == NULL || *ddata->cur != 'T') return (0); ++ddata->cur; if (*ddata->cur == '_') return (cpp_demangle_get_tmpl_param(ddata, 0)); else { errno = 0; if ((nth = strtol(ddata->cur, (char **) NULL, 36)) == 0 && errno != 0) return (0); /* T_ is first */ ++nth; while (*ddata->cur != '_') ++ddata->cur; assert(nth > 0); return (cpp_demangle_get_tmpl_param(ddata, nth)); } /* NOTREACHED */ return (0); } static int cpp_demangle_read_type(struct cpp_demangle_data *ddata, int delimit) { struct vector_type_qualifier v; struct vector_str *output; size_t p_idx, type_str_len; int extern_c, is_builtin; long len; char *type_str; if (ddata == NULL) return (0); output = &ddata->output; if (!strncmp(ddata->output.container[ddata->output.size - 1], ">", 1)) { cpp_demangle_gnu3_push_head++; output = &ddata->output_tmp; } else if (delimit == 1) { if (ddata->paren == false) { if (!cpp_demangle_push_str(ddata, "(", 1)) return (0); if (ddata->output.size < 2) return (0); ddata->paren = true; ddata->pfirst = true; /* Need pop function name */ if (ddata->subst.size == 1 && !vector_str_pop(&ddata->subst)) return (0); } if (ddata->pfirst) ddata->pfirst = false; else if (*ddata->cur != 'I' && !cpp_demangle_push_str(ddata, ", ", 2)) return (0); } assert(output != NULL); /* * [r, V, K] [P, R, C, G, U] builtin, function, class-enum, array * pointer-to-member, template-param, template-template-param, subst */ if (!vector_type_qualifier_init(&v)) return (0); extern_c = 0; is_builtin = 1; p_idx = output->size; type_str = NULL; again: /* builtin type */ switch (*ddata->cur) { case 'a': /* signed char */ if (!cpp_demangle_push_str(ddata, "signed char", 11)) goto clean; ++ddata->cur; goto rtn; case 'A': /* array type */ if (!cpp_demangle_read_array(ddata)) goto clean; is_builtin = 0; goto rtn; case 'b': /* bool */ if (!cpp_demangle_push_str(ddata, "bool", 4)) goto clean; ++ddata->cur; goto rtn; case 'C': /* complex pair */ if (!vector_type_qualifier_push(&v, TYPE_CMX)) goto clean; ++ddata->cur; goto again; case 'c': /* char */ if (!cpp_demangle_push_str(ddata, "char", 4)) goto clean; ++ddata->cur; goto rtn; case 'd': /* double */ if (!cpp_demangle_push_str(ddata, "double", 6)) goto clean; ++ddata->cur; goto rtn; case 'e': /* long double */ if (!cpp_demangle_push_str(ddata, "long double", 11)) goto clean; ++ddata->cur; goto rtn; case 'f': /* float */ if (!cpp_demangle_push_str(ddata, "float", 5)) goto clean; ++ddata->cur; goto rtn; case 'F': /* function */ if (!cpp_demangle_read_function(ddata, &extern_c, &v)) goto clean; is_builtin = 0; goto rtn; case 'g': /* __float128 */ if (!cpp_demangle_push_str(ddata, "__float128", 10)) goto clean; ++ddata->cur; goto rtn; case 'G': /* imaginary */ if (!vector_type_qualifier_push(&v, TYPE_IMG)) goto clean; ++ddata->cur; goto again; case 'h': /* unsigned char */ if (!cpp_demangle_push_str(ddata, "unsigned char", 13)) goto clean; ++ddata->cur; goto rtn; case 'i': /* int */ if (!cpp_demangle_push_str(ddata, "int", 3)) goto clean; ++ddata->cur; goto rtn; case 'j': /* unsigned int */ if (!cpp_demangle_push_str(ddata, "unsigned int", 12)) goto clean; ++ddata->cur; goto rtn; case 'K': /* const */ if (!vector_type_qualifier_push(&v, TYPE_CST)) goto clean; ++ddata->cur; goto again; case 'l': /* long */ if (!cpp_demangle_push_str(ddata, "long", 4)) goto clean; ++ddata->cur; goto rtn; case 'm': /* unsigned long */ if (!cpp_demangle_push_str(ddata, "unsigned long", 13)) goto clean; ++ddata->cur; goto rtn; case 'M': /* pointer to member */ if (!cpp_demangle_read_pointer_to_member(ddata)) goto clean; is_builtin = 0; goto rtn; case 'n': /* __int128 */ if (!cpp_demangle_push_str(ddata, "__int128", 8)) goto clean; ++ddata->cur; goto rtn; case 'o': /* unsigned __int128 */ if (!cpp_demangle_push_str(ddata, "unsigned _;int128", 17)) goto clean; ++ddata->cur; goto rtn; case 'P': /* pointer */ if (!vector_type_qualifier_push(&v, TYPE_PTR)) goto clean; ++ddata->cur; goto again; case 'r': /* restrict */ if (!vector_type_qualifier_push(&v, TYPE_RST)) goto clean; ++ddata->cur; goto again; case 'R': /* reference */ if (!vector_type_qualifier_push(&v, TYPE_REF)) goto clean; ++ddata->cur; goto again; case 's': /* short, local string */ if (!cpp_demangle_push_str(ddata, "short", 5)) goto clean; ++ddata->cur; goto rtn; case 'S': /* substitution */ if (!cpp_demangle_read_subst(ddata)) goto clean; is_builtin = 0; goto rtn; case 't': /* unsigned short */ if (!cpp_demangle_push_str(ddata, "unsigned short", 14)) goto clean; ++ddata->cur; goto rtn; case 'T': /* template parameter */ if (!cpp_demangle_read_tmpl_param(ddata)) goto clean; is_builtin = 0; goto rtn; case 'u': /* vendor extended builtin */ ++ddata->cur; if (!cpp_demangle_read_sname(ddata)) goto clean; is_builtin = 0; goto rtn; case 'U': /* vendor extended type qualifier */ if (!cpp_demangle_read_number(ddata, &len)) goto clean; if (len <= 0) goto clean; if (!vector_str_push(&v.ext_name, ddata->cur, len)) return (0); ddata->cur += len; goto again; case 'v': /* void */ if (!cpp_demangle_push_str(ddata, "void", 4)) goto clean; ++ddata->cur; goto rtn; case 'V': /* volatile */ if (!vector_type_qualifier_push(&v, TYPE_VAT)) goto clean; ++ddata->cur; goto again; case 'w': /* wchar_t */ if (!cpp_demangle_push_str(ddata, "wchar_t", 6)) goto clean; ++ddata->cur; goto rtn; case 'x': /* long long */ if (!cpp_demangle_push_str(ddata, "long long", 9)) goto clean; ++ddata->cur; goto rtn; case 'y': /* unsigned long long */ if (!cpp_demangle_push_str(ddata, "unsigned long long", 18)) goto clean; ++ddata->cur; goto rtn; case 'z': /* ellipsis */ if (!cpp_demangle_push_str(ddata, "ellipsis", 8)) goto clean; ++ddata->cur; goto rtn; }; if (!cpp_demangle_read_name(ddata)) goto clean; is_builtin = 0; rtn: if ((type_str = vector_str_substr(output, p_idx, output->size - 1, &type_str_len)) == NULL) goto clean; if (is_builtin == 0) { if (!vector_str_find(&ddata->subst, type_str, type_str_len) && !vector_str_push(&ddata->subst, type_str, type_str_len)) goto clean; } if (!cpp_demangle_push_type_qualifier(ddata, &v, type_str)) goto clean; free(type_str); vector_type_qualifier_dest(&v); if (cpp_demangle_gnu3_push_head > 0) { if (*ddata->cur == 'I' && cpp_demangle_read_tmpl_args(ddata) == 0) return (0); if (--cpp_demangle_gnu3_push_head > 0) return (1); if (!vector_str_push(&ddata->output_tmp, " ", 1)) return (0); if (!vector_str_push_vector_head(&ddata->output, &ddata->output_tmp)) return (0); vector_str_dest(&ddata->output_tmp); if (!vector_str_init(&ddata->output_tmp)) return (0); if (!cpp_demangle_push_str(ddata, "(", 1)) return (0); ddata->paren = true; ddata->pfirst = true; } return (1); clean: free(type_str); vector_type_qualifier_dest(&v); return (0); } /* * read unqualified-name, unqualified name are operator-name, ctor-dtor-name, * source-name */ static int cpp_demangle_read_uqname(struct cpp_demangle_data *ddata) { size_t len; if (ddata == NULL || *ddata->cur == '\0') return (0); /* operator name */ switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('a', 'a'): /* operator && */ if (!cpp_demangle_push_str(ddata, "operator&&", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('a', 'd'): /* operator & (unary) */ if (!cpp_demangle_push_str(ddata, "operator&", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('a', 'n'): /* operator & */ if (!cpp_demangle_push_str(ddata, "operator&", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('a', 'N'): /* operator &= */ if (!cpp_demangle_push_str(ddata, "operator&=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('a', 'S'): /* operator = */ if (!cpp_demangle_push_str(ddata, "operator=", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('c', 'l'): /* operator () */ if (!cpp_demangle_push_str(ddata, "operator()", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('c', 'm'): /* operator , */ if (!cpp_demangle_push_str(ddata, "operator,", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('c', 'o'): /* operator ~ */ if (!cpp_demangle_push_str(ddata, "operator~", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('c', 'v'): /* operator (cast) */ if (!cpp_demangle_push_str(ddata, "operator(cast)", 14)) return (0); ddata->cur += 2; return (cpp_demangle_read_type(ddata, 1)); case SIMPLE_HASH('d', 'a'): /* operator delete [] */ if (!cpp_demangle_push_str(ddata, "operator delete []", 18)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('d', 'e'): /* operator * (unary) */ if (!cpp_demangle_push_str(ddata, "operator*", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('d', 'l'): /* operator delete */ if (!cpp_demangle_push_str(ddata, "operator delete", 15)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('d', 'v'): /* operator / */ if (!cpp_demangle_push_str(ddata, "operator/", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('d', 'V'): /* operator /= */ if (!cpp_demangle_push_str(ddata, "operator/=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('e', 'o'): /* operator ^ */ if (!cpp_demangle_push_str(ddata, "operator^", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('e', 'O'): /* operator ^= */ if (!cpp_demangle_push_str(ddata, "operator^=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('e', 'q'): /* operator == */ if (!cpp_demangle_push_str(ddata, "operator==", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('g', 'e'): /* operator >= */ if (!cpp_demangle_push_str(ddata, "operator>=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('g', 't'): /* operator > */ if (!cpp_demangle_push_str(ddata, "operator>", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('i', 'x'): /* operator [] */ if (!cpp_demangle_push_str(ddata, "operator[]", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('l', 'e'): /* operator <= */ if (!cpp_demangle_push_str(ddata, "operator<=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('l', 's'): /* operator << */ if (!cpp_demangle_push_str(ddata, "operator<<", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('l', 'S'): /* operator <<= */ if (!cpp_demangle_push_str(ddata, "operator<<=", 11)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('l', 't'): /* operator < */ if (!cpp_demangle_push_str(ddata, "operator<", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'i'): /* operator - */ if (!cpp_demangle_push_str(ddata, "operator-", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'I'): /* operator -= */ if (!cpp_demangle_push_str(ddata, "operator-=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'l'): /* operator * */ if (!cpp_demangle_push_str(ddata, "operator*", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'L'): /* operator *= */ if (!cpp_demangle_push_str(ddata, "operator*=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'm'): /* operator -- */ if (!cpp_demangle_push_str(ddata, "operator--", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 'a'): /* operator new[] */ if (!cpp_demangle_push_str(ddata, "operator new []", 15)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 'e'): /* operator != */ if (!cpp_demangle_push_str(ddata, "operator!=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 'g'): /* operator - (unary) */ if (!cpp_demangle_push_str(ddata, "operator-", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 't'): /* operator ! */ if (!cpp_demangle_push_str(ddata, "operator!", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 'w'): /* operator new */ if (!cpp_demangle_push_str(ddata, "operator new", 12)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('o', 'o'): /* operator || */ if (!cpp_demangle_push_str(ddata, "operator||", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('o', 'r'): /* operator | */ if (!cpp_demangle_push_str(ddata, "operator|", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('o', 'R'): /* operator |= */ if (!cpp_demangle_push_str(ddata, "operator|=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 'l'): /* operator + */ if (!cpp_demangle_push_str(ddata, "operator+", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 'L'): /* operator += */ if (!cpp_demangle_push_str(ddata, "operator+=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 'm'): /* operator ->* */ if (!cpp_demangle_push_str(ddata, "operator->*", 11)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 'p'): /* operator ++ */ if (!cpp_demangle_push_str(ddata, "operator++", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 's'): /* operator + (unary) */ if (!cpp_demangle_push_str(ddata, "operator+", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 't'): /* operator -> */ if (!cpp_demangle_push_str(ddata, "operator->", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('q', 'u'): /* operator ? */ if (!cpp_demangle_push_str(ddata, "operator?", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 'm'): /* operator % */ if (!cpp_demangle_push_str(ddata, "operator%", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 'M'): /* operator %= */ if (!cpp_demangle_push_str(ddata, "operator%=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 's'): /* operator >> */ if (!cpp_demangle_push_str(ddata, "operator>>", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 'S'): /* operator >>= */ if (!cpp_demangle_push_str(ddata, "operator>>=", 11)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 'z'): /* operator sizeof */ if (!cpp_demangle_push_str(ddata, "operator sizeof ", 16)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('s', 'r'): /* scope resolution operator */ if (!cpp_demangle_push_str(ddata, "scope resolution operator ", 26)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('s', 'v'): /* operator sizeof */ if (!cpp_demangle_push_str(ddata, "operator sizeof ", 16)) return (0); ddata->cur += 2; return (1); }; /* vendor extened operator */ if (*ddata->cur == 'v' && ELFTC_ISDIGIT(*(ddata->cur + 1))) { if (!cpp_demangle_push_str(ddata, "vendor extened operator ", 24)) return (0); if (!cpp_demangle_push_str(ddata, ddata->cur + 1, 1)) return (0); ddata->cur += 2; return (cpp_demangle_read_sname(ddata)); } /* ctor-dtor-name */ switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('C', '1'): /* FALLTHROUGH */ case SIMPLE_HASH('C', '2'): /* FALLTHROUGH */ case SIMPLE_HASH('C', '3'): if (ddata->last_sname == NULL) return (0); if ((len = strlen(ddata->last_sname)) == 0) return (0); if (!cpp_demangle_push_str(ddata, "::", 2)) return (0); if (!cpp_demangle_push_str(ddata, ddata->last_sname, len)) return (0); ddata->cur +=2; return (1); case SIMPLE_HASH('D', '0'): /* FALLTHROUGH */ case SIMPLE_HASH('D', '1'): /* FALLTHROUGH */ case SIMPLE_HASH('D', '2'): if (ddata->last_sname == NULL) return (0); if ((len = strlen(ddata->last_sname)) == 0) return (0); if (!cpp_demangle_push_str(ddata, "::~", 3)) return (0); if (!cpp_demangle_push_str(ddata, ddata->last_sname, len)) return (0); ddata->cur +=2; return (1); }; /* source name */ if (ELFTC_ISDIGIT(*ddata->cur) != 0) return (cpp_demangle_read_sname(ddata)); /* local source name */ if (*ddata->cur == 'L') return (cpp_demangle_local_source_name(ddata)); return (1); } /* * Read local source name. * * References: * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 * http://gcc.gnu.org/viewcvs?view=rev&revision=124467 */ static int cpp_demangle_local_source_name(struct cpp_demangle_data *ddata) { /* L */ if (ddata == NULL || *ddata->cur != 'L') return (0); ++ddata->cur; /* source name */ if (!cpp_demangle_read_sname(ddata)) return (0); /* discriminator */ if (*ddata->cur == '_') { ++ddata->cur; while (ELFTC_ISDIGIT(*ddata->cur) != 0) ++ddata->cur; } return (1); } static int cpp_demangle_read_v_offset(struct cpp_demangle_data *ddata) { if (ddata == NULL) return (0); if (!cpp_demangle_push_str(ddata, "offset : ", 9)) return (0); if (!cpp_demangle_read_offset_number(ddata)) return (0); if (!cpp_demangle_push_str(ddata, "virtual offset : ", 17)) return (0); return (!cpp_demangle_read_offset_number(ddata)); } /* * Decode floating point representation to string * Return new allocated string or NULL * * Todo * Replace these functions to macro. */ static char * decode_fp_to_double(const char *p, size_t len) { double f; size_t rtn_len, limit, i; int byte; char *rtn; if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(double)) return (NULL); memset(&f, 0, sizeof(double)); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN ((unsigned char *)&f)[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ ((unsigned char *)&f)[sizeof(double) - i - 1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } rtn_len = 64; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%fld", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return rtn; } static char * decode_fp_to_float(const char *p, size_t len) { size_t i, rtn_len, limit; float f; int byte; char *rtn; if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(float)) return (NULL); memset(&f, 0, sizeof(float)); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN ((unsigned char *)&f)[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ ((unsigned char *)&f)[sizeof(float) - i - 1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } rtn_len = 64; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%ff", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return rtn; } static char * decode_fp_to_float128(const char *p, size_t len) { long double f; size_t rtn_len, limit, i; int byte; unsigned char buf[FLOAT_QUADRUPLE_BYTES]; char *rtn; switch(sizeof(long double)) { case FLOAT_QUADRUPLE_BYTES: return (decode_fp_to_long_double(p, len)); case FLOAT_EXTENED_BYTES: if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > FLOAT_QUADRUPLE_BYTES) return (NULL); memset(buf, 0, FLOAT_QUADRUPLE_BYTES); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN buf[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ buf[FLOAT_QUADRUPLE_BYTES - i -1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } memset(&f, 0, FLOAT_EXTENED_BYTES); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN memcpy(&f, buf, FLOAT_EXTENED_BYTES); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ memcpy(&f, buf + 6, FLOAT_EXTENED_BYTES); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ rtn_len = 256; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return (rtn); default: return (NULL); } } static char * decode_fp_to_float80(const char *p, size_t len) { long double f; size_t rtn_len, limit, i; int byte; unsigned char buf[FLOAT_EXTENED_BYTES]; char *rtn; switch(sizeof(long double)) { case FLOAT_QUADRUPLE_BYTES: if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > FLOAT_EXTENED_BYTES) return (NULL); memset(buf, 0, FLOAT_EXTENED_BYTES); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN buf[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ buf[FLOAT_EXTENED_BYTES - i -1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } memset(&f, 0, FLOAT_QUADRUPLE_BYTES); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN memcpy(&f, buf, FLOAT_EXTENED_BYTES); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ memcpy((unsigned char *)(&f) + 6, buf, FLOAT_EXTENED_BYTES); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ rtn_len = 256; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return (rtn); case FLOAT_EXTENED_BYTES: return (decode_fp_to_long_double(p, len)); default: return (NULL); } } static char * decode_fp_to_long_double(const char *p, size_t len) { long double f; size_t rtn_len, limit, i; int byte; char *rtn; if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(long double)) return (NULL); memset(&f, 0, sizeof(long double)); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN ((unsigned char *)&f)[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ ((unsigned char *)&f)[sizeof(long double) - i - 1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } rtn_len = 256; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return (rtn); } /* Simple hex to integer function used by decode_to_* function. */ static int hex_to_dec(char c) { switch (c) { case '0': return (0); case '1': return (1); case '2': return (2); case '3': return (3); case '4': return (4); case '5': return (5); case '6': return (6); case '7': return (7); case '8': return (8); case '9': return (9); case 'a': return (10); case 'b': return (11); case 'c': return (12); case 'd': return (13); case 'e': return (14); case 'f': return (15); default: return (-1); }; } /** * @brief Test input string is mangled by IA-64 C++ ABI style. * * Test string heads with "_Z" or "_GLOBAL__I_". * @return Return 0 at false. */ bool is_cpp_mangled_gnu3(const char *org) { size_t len; len = strlen(org); return ((len > 2 && *org == '_' && *(org + 1) == 'Z') || (len > 11 && !strncmp(org, "_GLOBAL__I_", 11))); } static void vector_read_cmd_dest(struct vector_read_cmd *v) { if (v == NULL) return; free(v->r_container); } /* return -1 at failed, 0 at not found, 1 at found. */ static int vector_read_cmd_find(struct vector_read_cmd *v, enum read_cmd dst) { size_t i; if (v == NULL || dst == READ_FAIL) return (-1); for (i = 0; i < v->size; ++i) if (v->r_container[i] == dst) return (1); return (0); } static int vector_read_cmd_init(struct vector_read_cmd *v) { if (v == NULL) return (0); v->size = 0; v->capacity = VECTOR_DEF_CAPACITY; if ((v->r_container = malloc(sizeof(enum read_cmd) * v->capacity)) == NULL) return (0); return (1); } static int vector_read_cmd_pop(struct vector_read_cmd *v) { if (v == NULL || v->size == 0) return (0); --v->size; v->r_container[v->size] = READ_FAIL; return (1); } static int vector_read_cmd_push(struct vector_read_cmd *v, enum read_cmd cmd) { enum read_cmd *tmp_r_ctn; size_t tmp_cap; size_t i; if (v == NULL) return (0); if (v->size == v->capacity) { tmp_cap = v->capacity * BUFFER_GROWFACTOR; if ((tmp_r_ctn = malloc(sizeof(enum read_cmd) * tmp_cap)) == NULL) return (0); for (i = 0; i < v->size; ++i) tmp_r_ctn[i] = v->r_container[i]; free(v->r_container); v->r_container = tmp_r_ctn; v->capacity = tmp_cap; } v->r_container[v->size] = cmd; ++v->size; return (1); } static void vector_type_qualifier_dest(struct vector_type_qualifier *v) { if (v == NULL) return; free(v->q_container); vector_str_dest(&v->ext_name); } /* size, capacity, ext_name */ static int vector_type_qualifier_init(struct vector_type_qualifier *v) { if (v == NULL) return (0); v->size = 0; v->capacity = VECTOR_DEF_CAPACITY; if ((v->q_container = malloc(sizeof(enum type_qualifier) * v->capacity)) == NULL) return (0); assert(v->q_container != NULL); if (vector_str_init(&v->ext_name) == false) { free(v->q_container); return (0); } return (1); } static int vector_type_qualifier_push(struct vector_type_qualifier *v, enum type_qualifier t) { enum type_qualifier *tmp_ctn; size_t tmp_cap; size_t i; if (v == NULL) return (0); if (v->size == v->capacity) { tmp_cap = v->capacity * BUFFER_GROWFACTOR; if ((tmp_ctn = malloc(sizeof(enum type_qualifier) * tmp_cap)) == NULL) return (0); for (i = 0; i < v->size; ++i) tmp_ctn[i] = v->q_container[i]; free(v->q_container); v->q_container = tmp_ctn; v->capacity = tmp_cap; } v->q_container[v->size] = t; ++v->size; return (1); } Index: projects/clang350-import/contrib/elftoolchain/nm/nm.c =================================================================== --- projects/clang350-import/contrib/elftoolchain/nm/nm.c (revision 275386) +++ projects/clang350-import/contrib/elftoolchain/nm/nm.c (revision 275387) @@ -1,2096 +1,2096 @@ /*- * Copyright (c) 2007 Hyogeol Lee * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "_elftc.h" ELFTC_VCSID("$Id: nm.c 2484 2012-04-07 15:52:05Z kaiwang27 $"); /* symbol information list */ STAILQ_HEAD(sym_head, sym_entry); struct sym_entry { char *name; GElf_Sym *sym; STAILQ_ENTRY(sym_entry) sym_entries; }; typedef int (*fn_sort)(const void *, const void *); typedef void (*fn_elem_print)(char, const char *, const GElf_Sym *, const char *); typedef void (*fn_sym_print)(const GElf_Sym *); typedef int (*fn_filter)(char, const GElf_Sym *, const char *); /* output filter list */ -SLIST_HEAD(filter_head, filter_entry) nm_out_filter = +static SLIST_HEAD(filter_head, filter_entry) nm_out_filter = SLIST_HEAD_INITIALIZER(nm_out_filter); struct filter_entry { fn_filter fn; SLIST_ENTRY(filter_entry) filter_entries; }; struct sym_print_data { struct sym_head *headp; size_t sh_num, list_num; const char *t_table, **s_table, *filename, *objname; }; struct nm_prog_info { const char *name; const char *def_filename; }; /* List for line number information. */ struct line_info_entry { uint64_t addr; /* address */ uint64_t line; /* line number */ char *file; /* file name with path */ SLIST_ENTRY(line_info_entry) entries; }; SLIST_HEAD(line_info_head, line_info_entry); /* List for function line number information. */ struct func_info_entry { char *name; /* function name */ char *file; /* file name with path */ uint64_t lowpc; /* low address */ uint64_t highpc; /* high address */ uint64_t line; /* line number */ SLIST_ENTRY(func_info_entry) entries; }; SLIST_HEAD(func_info_head, func_info_entry); /* List for variable line number information. */ struct var_info_entry { char *name; /* variable name */ char *file; /* file name with path */ uint64_t addr; /* address */ uint64_t line; /* line number */ SLIST_ENTRY(var_info_entry) entries; }; SLIST_HEAD(var_info_head, var_info_entry); /* output numric type */ enum radix { RADIX_OCT, RADIX_HEX, RADIX_DEC }; /* output symbol type, PRINT_SYM_DYN for dynamic symbol only */ enum print_symbol { PRINT_SYM_SYM, PRINT_SYM_DYN }; /* output name type */ enum print_name { PRINT_NAME_NONE, PRINT_NAME_FULL, PRINT_NAME_MULTI }; struct nm_prog_options { enum print_symbol print_symbol; enum print_name print_name; enum radix t; int demangle_type; bool print_debug; bool print_armap; int print_size; bool debug_line; int def_only; bool undef_only; int sort_size; bool sort_reverse; int no_demangle; /* * function pointer to sort symbol list. * possible function - cmp_name, cmp_none, cmp_size, cmp_value */ fn_sort sort_fn; /* * function pointer to print symbol elem. * possible function - sym_elem_print_all * sym_elem_print_all_portable * sym_elem_print_all_sysv */ fn_elem_print elem_print_fn; fn_sym_print value_print_fn; fn_sym_print size_print_fn; }; #define CHECK_SYM_PRINT_DATA(p) (p->headp == NULL || p->sh_num == 0 || \ p->t_table == NULL || p->s_table == NULL || p->filename == NULL) #define IS_SYM_TYPE(t) ((t) == '?' || isalpha((t)) != 0) #define IS_UNDEF_SYM_TYPE(t) ((t) == 'U' || (t) == 'v' || (t) == 'w') #define UNUSED(p) ((void)p) static int cmp_name(const void *, const void *); static int cmp_none(const void *, const void *); static int cmp_size(const void *, const void *); static int cmp_value(const void *, const void *); static void filter_dest(void); static int filter_insert(fn_filter); static void get_opt(int, char **); static int get_sym(Elf *, struct sym_head *, int, size_t, size_t, const char *, const char **, int); static const char * get_sym_name(Elf *, const GElf_Sym *, size_t, const char **, int); static char get_sym_type(const GElf_Sym *, const char *); static void global_dest(void); static void global_init(void); static bool is_sec_data(GElf_Shdr *); static bool is_sec_debug(const char *); static bool is_sec_nobits(GElf_Shdr *); static bool is_sec_readonly(GElf_Shdr *); static bool is_sec_text(GElf_Shdr *); static void print_ar_index(int, Elf *); static void print_header(const char *, const char *); static void print_version(void); static int read_elf(Elf *, const char *, Elf_Kind); static int read_object(const char *); static int read_files(int, char **); static void set_opt_value_print_fn(enum radix); static int sym_elem_def(char, const GElf_Sym *, const char *); static int sym_elem_global(char, const GElf_Sym *, const char *); static int sym_elem_global_static(char, const GElf_Sym *, const char *); static int sym_elem_nondebug(char, const GElf_Sym *, const char *); static int sym_elem_nonzero_size(char, const GElf_Sym *, const char *); static void sym_elem_print_all(char, const char *, const GElf_Sym *, const char *); static void sym_elem_print_all_portable(char, const char *, const GElf_Sym *, const char *); static void sym_elem_print_all_sysv(char, const char *, const GElf_Sym *, const char *); static int sym_elem_undef(char, const GElf_Sym *, const char *); static void sym_list_dest(struct sym_head *); static int sym_list_insert(struct sym_head *, const char *, const GElf_Sym *); static void sym_list_print(struct sym_print_data *, struct func_info_head *, struct var_info_head *, struct line_info_head *); static void sym_list_print_each(struct sym_entry *, struct sym_print_data *, struct func_info_head *, struct var_info_head *, struct line_info_head *); static struct sym_entry *sym_list_sort(struct sym_print_data *); static void sym_size_oct_print(const GElf_Sym *); static void sym_size_hex_print(const GElf_Sym *); static void sym_size_dec_print(const GElf_Sym *); static void sym_value_oct_print(const GElf_Sym *); static void sym_value_hex_print(const GElf_Sym *); static void sym_value_dec_print(const GElf_Sym *); static void usage(int); static struct nm_prog_info nm_info; static struct nm_prog_options nm_opts; static int nm_elfclass; /* * Point to current sym_print_data to use portable qsort function. * (e.g. There is no qsort_r function in NetBSD.) * * Using in sym_list_sort. */ static struct sym_print_data *nm_print_data; static const struct option nm_longopts[] = { { "debug-syms", no_argument, NULL, 'a' }, { "defined-only", no_argument, &nm_opts.def_only, 1}, { "demangle", optional_argument, NULL, 'C' }, { "dynamic", no_argument, NULL, 'D' }, { "format", required_argument, NULL, 'F' }, { "help", no_argument, NULL, 'h' }, { "line-numbers", no_argument, NULL, 'l' }, { "no-demangle", no_argument, &nm_opts.no_demangle, 1}, { "no-sort", no_argument, NULL, 'p' }, { "numeric-sort", no_argument, NULL, 'v' }, { "print-armap", no_argument, NULL, 's' }, { "print-file-name", no_argument, NULL, 'A' }, { "print-size", no_argument, NULL, 'S' }, { "radix", required_argument, NULL, 't' }, { "reverse-sort", no_argument, NULL, 'r' }, { "size-sort", no_argument, &nm_opts.sort_size, 1}, { "undefined-only", no_argument, NULL, 'u' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; #if defined(ELFTC_NEED_BYTEORDER_EXTENSIONS) static __inline uint32_t be32dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); } static __inline uint32_t le32dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); } static __inline uint64_t be64dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return (((uint64_t)be32dec(p) << 32) | be32dec(p + 4)); } static __inline uint64_t le64dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return (((uint64_t)le32dec(p + 4) << 32) | le32dec(p)); } #endif static int cmp_name(const void *l, const void *r) { assert(l != NULL); assert(r != NULL); assert(((const struct sym_entry *)l)->name != NULL); assert(((const struct sym_entry *)r)->name != NULL); return (strcmp(((const struct sym_entry *)l)->name, ((const struct sym_entry *)r)->name)); } static int cmp_none(const void *l, const void *r) { UNUSED(l); UNUSED(r); return (0); } /* Size comparison. If l and r have same size, compare their name. */ static int cmp_size(const void *lp, const void *rp) { const struct sym_entry *l, *r; l = lp; r = rp; assert(l != NULL); assert(l->name != NULL); assert(l->sym != NULL); assert(r != NULL); assert(r->name != NULL); assert(r->sym != NULL); if (l->sym->st_size == r->sym->st_size) return (strcmp(l->name, r->name)); return (l->sym->st_size - r->sym->st_size); } /* Value comparison. Undefined symbols come first. */ static int cmp_value(const void *lp, const void *rp) { const struct sym_entry *l, *r; const char *ttable; int l_is_undef, r_is_undef; l = lp; r = rp; assert(nm_print_data != NULL); ttable = nm_print_data->t_table; assert(l != NULL); assert(l->name != NULL); assert(l->sym != NULL); assert(r != NULL); assert(r->name != NULL); assert(r->sym != NULL); assert(ttable != NULL); l_is_undef = IS_UNDEF_SYM_TYPE(get_sym_type(l->sym, ttable)) ? 1 : 0; r_is_undef = IS_UNDEF_SYM_TYPE(get_sym_type(r->sym, ttable)) ? 1 : 0; assert(l_is_undef + r_is_undef >= 0); assert(l_is_undef + r_is_undef <= 2); switch (l_is_undef + r_is_undef) { case 0: /* Both defined */ if (l->sym->st_value == r->sym->st_value) return (strcmp(l->name, r->name)); return (l->sym->st_value - r->sym->st_value); case 1: /* One undefined */ return (l_is_undef == 0 ? 1 : -1); case 2: /* Both undefined */ return (strcmp(l->name, r->name)); } /* NOTREACHED */ return (l->sym->st_value - r->sym->st_value); } static void filter_dest(void) { struct filter_entry *e; while (!SLIST_EMPTY(&nm_out_filter)) { e = SLIST_FIRST(&nm_out_filter); SLIST_REMOVE_HEAD(&nm_out_filter, filter_entries); free(e); } } static int filter_insert(fn_filter filter_fn) { struct filter_entry *e; assert(filter_fn != NULL); if ((e = malloc(sizeof(struct filter_entry))) == NULL) { warn("malloc"); return (0); } e->fn = filter_fn; SLIST_INSERT_HEAD(&nm_out_filter, e, filter_entries); return (1); } static int parse_demangle_option(const char *opt) { if (opt == NULL) return (ELFTC_DEM_UNKNOWN); else if (!strncasecmp(opt, "gnu-v2", 6)) return (ELFTC_DEM_GNU2); else if (!strncasecmp(opt, "gnu-v3", 6)) return (ELFTC_DEM_GNU3); else if (!strncasecmp(opt, "arm", 3)) return (ELFTC_DEM_ARM); else errx(EXIT_FAILURE, "unknown demangling style '%s'", opt); /* NOTREACHED */ return (0); } static void get_opt(int argc, char **argv) { int ch; bool is_posix, oflag; if (argc <= 0 || argv == NULL) return; oflag = is_posix = false; nm_opts.t = RADIX_HEX; while ((ch = getopt_long(argc, argv, "ABCDF:PSVaefghlnoprst:uvx", nm_longopts, NULL)) != -1) { switch (ch) { case 'A': nm_opts.print_name = PRINT_NAME_FULL; break; case 'B': nm_opts.elem_print_fn = &sym_elem_print_all; break; case 'C': nm_opts.demangle_type = parse_demangle_option(optarg); break; case 'D': nm_opts.print_symbol = PRINT_SYM_DYN; break; case 'F': /* sysv, bsd, posix */ switch (optarg[0]) { case 'B': case 'b': nm_opts.elem_print_fn = &sym_elem_print_all; break; case 'P': case 'p': is_posix = true; nm_opts.elem_print_fn = &sym_elem_print_all_portable; break; case 'S': case 's': nm_opts.elem_print_fn = &sym_elem_print_all_sysv; break; default: warnx("%s: Invalid format", optarg); usage(1); } break; case 'P': is_posix = true; nm_opts.elem_print_fn = &sym_elem_print_all_portable; break; case 'S': nm_opts.print_size = 1; break; case 'V': print_version(); /* NOTREACHED */ case 'a': nm_opts.print_debug = true; break; case 'e': filter_insert(sym_elem_global_static); break; case 'f': break; case 'g': filter_insert(sym_elem_global); break; case 'h': usage(0); break; case 'l': nm_opts.debug_line = true; break; case 'n': case 'v': nm_opts.sort_fn = &cmp_value; break; case 'o': oflag = true; break; case 'p': nm_opts.sort_fn = &cmp_none; break; case 'r': nm_opts.sort_reverse = true; break; case 's': nm_opts.print_armap = true; break; case 't': /* t require always argument to getopt_long */ switch (optarg[0]) { case 'd': nm_opts.t = RADIX_DEC; break; case 'o': nm_opts.t = RADIX_OCT; break; case 'x': nm_opts.t = RADIX_HEX; break; default: warnx("%s: Invalid radix", optarg); usage(1); } break; case 'u': filter_insert(sym_elem_undef); nm_opts.undef_only = true; break; /* case 'v': see case 'n' above. */ case 'x': nm_opts.t = RADIX_HEX; break; case 0: if (nm_opts.sort_size != 0) { nm_opts.sort_fn = &cmp_size; filter_insert(sym_elem_def); filter_insert(sym_elem_nonzero_size); } if (nm_opts.def_only != 0) filter_insert(sym_elem_def); if (nm_opts.no_demangle != 0) nm_opts.demangle_type = -1; break; default : usage(1); } } /* * In POSIX mode, the '-o' option controls the output radix. * In non-POSIX mode, the option is a synonym for the '-A' and * '--print-file-name' options. */ if (oflag) { if (is_posix) nm_opts.t = RADIX_OCT; else nm_opts.print_name = PRINT_NAME_FULL; } assert(nm_opts.sort_fn != NULL && "nm_opts.sort_fn is null"); assert(nm_opts.elem_print_fn != NULL && "nm_opts.elem_print_fn is null"); assert(nm_opts.value_print_fn != NULL && "nm_opts.value_print_fn is null"); set_opt_value_print_fn(nm_opts.t); if (nm_opts.undef_only == true) { if (nm_opts.sort_fn == &cmp_size) errx(EXIT_FAILURE, "--size-sort with -u is meaningless"); if (nm_opts.def_only != 0) errx(EXIT_FAILURE, "-u with --defined-only is meaningless"); } if (nm_opts.print_debug == false) filter_insert(sym_elem_nondebug); if (nm_opts.sort_reverse == true && nm_opts.sort_fn == cmp_none) nm_opts.sort_reverse = false; } /* * Get symbol information from elf. */ static int get_sym(Elf *elf, struct sym_head *headp, int shnum, size_t dynndx, size_t strndx, const char *type_table, const char **sec_table, int sec_table_size) { Elf_Scn *scn; Elf_Data *data; GElf_Shdr shdr; GElf_Sym sym; struct filter_entry *fep; size_t ndx; int rtn; const char *sym_name; char type; bool filter; int i, j; assert(elf != NULL); assert(headp != NULL); rtn = 0; for (i = 1; i < shnum; i++) { if ((scn = elf_getscn(elf, i)) == NULL) { warnx("elf_getscn failed: %s", elf_errmsg(-1)); continue; } if (gelf_getshdr(scn, &shdr) != &shdr) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); continue; } if (shdr.sh_type == SHT_SYMTAB) { if (nm_opts.print_symbol != PRINT_SYM_SYM) continue; } else if (shdr.sh_type == SHT_DYNSYM) { if (nm_opts.print_symbol != PRINT_SYM_DYN) continue; } else continue; ndx = shdr.sh_type == SHT_DYNSYM ? dynndx : strndx; data = NULL; while ((data = elf_getdata(scn, data)) != NULL) { j = 1; while (gelf_getsym(data, j++, &sym) != NULL) { sym_name = get_sym_name(elf, &sym, ndx, sec_table, sec_table_size); filter = false; type = get_sym_type(&sym, type_table); SLIST_FOREACH(fep, &nm_out_filter, filter_entries) { if (!fep->fn(type, &sym, sym_name)) { filter = true; break; } } if (filter == false) { if (sym_list_insert(headp, sym_name, &sym) == 0) return (0); rtn++; } } } } return (rtn); } static const char * get_sym_name(Elf *elf, const GElf_Sym *sym, size_t ndx, const char **sec_table, int sec_table_size) { const char *sym_name; sym_name = NULL; /* Show section name as symbol name for STT_SECTION symbols. */ if (GELF_ST_TYPE(sym->st_info) == STT_SECTION) { if (sec_table != NULL && sym->st_shndx < sec_table_size) sym_name = sec_table[sym->st_shndx]; } else sym_name = elf_strptr(elf, ndx, sym->st_name); if (sym_name == NULL) sym_name = "(null)"; return (sym_name); } static char get_sym_type(const GElf_Sym *sym, const char *type_table) { bool is_local; if (sym == NULL || type_table == NULL) return ('?'); is_local = sym->st_info >> 4 == STB_LOCAL; if (sym->st_shndx == SHN_ABS) /* absolute */ return (is_local ? 'a' : 'A'); if (sym->st_shndx == SHN_COMMON) /* common */ return ('C'); if ((sym->st_info) >> 4 == STB_WEAK) { /* weak */ if ((sym->st_info & 0xf) == STT_OBJECT) return (sym->st_shndx == SHN_UNDEF ? 'v' : 'V'); return (sym->st_shndx == SHN_UNDEF ? 'w' : 'W'); } if (sym->st_shndx == SHN_UNDEF) /* undefined */ return ('U'); return (is_local == true && type_table[sym->st_shndx] != 'N' ? tolower((unsigned char) type_table[sym->st_shndx]) : type_table[sym->st_shndx]); } static void global_dest(void) { filter_dest(); } static void global_init(void) { if (elf_version(EV_CURRENT) == EV_NONE) errx(EXIT_FAILURE, "elf_version error"); nm_info.name = ELFTC_GETPROGNAME(); nm_info.def_filename = "a.out"; nm_opts.print_symbol = PRINT_SYM_SYM; nm_opts.print_name = PRINT_NAME_NONE; nm_opts.demangle_type = -1; nm_opts.print_debug = false; nm_opts.print_armap = false; nm_opts.print_size = 0; nm_opts.debug_line = false; nm_opts.def_only = 0; nm_opts.undef_only = false; nm_opts.sort_size = 0; nm_opts.sort_reverse = false; nm_opts.no_demangle = 0; nm_opts.sort_fn = &cmp_name; nm_opts.elem_print_fn = &sym_elem_print_all; nm_opts.value_print_fn = &sym_value_dec_print; nm_opts.size_print_fn = &sym_size_dec_print; SLIST_INIT(&nm_out_filter); } static bool is_sec_data(GElf_Shdr *s) { assert(s != NULL && "shdr is NULL"); return (((s->sh_flags & SHF_ALLOC) != 0) && s->sh_type != SHT_NOBITS); } static bool is_sec_debug(const char *shname) { const char *dbg_sec[] = { ".debug", ".gnu.linkonce.wi.", ".line", ".rel.debug", ".rela.debug", ".stab", NULL }; const char **p; assert(shname != NULL && "shname is NULL"); for (p = dbg_sec; *p; p++) { if (!strncmp(shname, *p, strlen(*p))) return (true); } return (false); } static bool is_sec_nobits(GElf_Shdr *s) { assert(s != NULL && "shdr is NULL"); return (s->sh_type == SHT_NOBITS); } static bool is_sec_readonly(GElf_Shdr *s) { assert(s != NULL && "shdr is NULL"); return ((s->sh_flags & SHF_WRITE) == 0); } static bool is_sec_text(GElf_Shdr *s) { assert(s != NULL && "shdr is NULL"); return ((s->sh_flags & SHF_EXECINSTR) != 0); } static void print_ar_index(int fd, Elf *arf) { Elf *elf; Elf_Arhdr *arhdr; Elf_Arsym *arsym; Elf_Cmd cmd; off_t start; size_t arsym_size; if (arf == NULL) return; if ((arsym = elf_getarsym(arf, &arsym_size)) == NULL) return; printf("\nArchive index:\n"); start = arsym->as_off; cmd = ELF_C_READ; while (arsym_size > 1) { if (elf_rand(arf, arsym->as_off) == arsym->as_off && (elf = elf_begin(fd, cmd, arf)) != NULL) { if ((arhdr = elf_getarhdr(elf)) != NULL) printf("%s in %s\n", arsym->as_name, arhdr->ar_name != NULL ? arhdr->ar_name : arhdr->ar_rawname); elf_end(elf); } ++arsym; --arsym_size; } elf_rand(arf, start); } #define DEMANGLED_BUFFER_SIZE (8 * 1024) #define PRINT_DEMANGLED_NAME(FORMAT, NAME) do { \ char _demangled[DEMANGLED_BUFFER_SIZE]; \ if (nm_opts.demangle_type < 0 || \ elftc_demangle((NAME), _demangled, sizeof(_demangled), \ nm_opts.demangle_type) < 0) \ printf((FORMAT), (NAME)); \ else \ printf((FORMAT), _demangled); \ } while (0) static void print_header(const char *file, const char *obj) { if (file == NULL) return; if (nm_opts.elem_print_fn == &sym_elem_print_all_sysv) { printf("\n\n%s from %s", nm_opts.undef_only == false ? "Symbols" : "Undefined symbols", file); if (obj != NULL) printf("[%s]", obj); printf(":\n\n"); printf("\ Name Value Class Type Size Line Section\n\n"); } else { /* archive file without -A option and POSIX */ if (nm_opts.print_name != PRINT_NAME_FULL && obj != NULL) { if (nm_opts.elem_print_fn == sym_elem_print_all_portable) printf("%s[%s]:\n", file, obj); else if (nm_opts.elem_print_fn == sym_elem_print_all) printf("\n%s:\n", obj); /* multiple files(not archive) without -A option */ } else if (nm_opts.print_name == PRINT_NAME_MULTI) { if (nm_opts.elem_print_fn == sym_elem_print_all) printf("\n"); printf("%s:\n", file); } } } static void print_version(void) { (void) printf("%s (%s)\n", nm_info.name, elftc_version()); exit(0); } static uint64_t get_block_value(Dwarf_Debug dbg, Dwarf_Block *block) { Elf *elf; GElf_Ehdr eh; Dwarf_Error de; if (dwarf_get_elf(dbg, &elf, &de) != DW_DLV_OK) { warnx("dwarf_get_elf failed: %s", dwarf_errmsg(de)); return (0); } if (gelf_getehdr(elf, &eh) != &eh) { warnx("gelf_getehdr failed: %s", elf_errmsg(-1)); return (0); } if (block->bl_len == 5) { if (eh.e_ident[EI_DATA] == ELFDATA2LSB) return (le32dec((uint8_t *) block->bl_data + 1)); else return (be32dec((uint8_t *) block->bl_data + 1)); } else if (block->bl_len == 9) { if (eh.e_ident[EI_DATA] == ELFDATA2LSB) return (le64dec((uint8_t *) block->bl_data + 1)); else return (be64dec((uint8_t *) block->bl_data + 1)); } return (0); } static void search_line_attr(Dwarf_Debug dbg, struct func_info_head *func_info, struct var_info_head *var_info, Dwarf_Die die, char **src_files, Dwarf_Signed filecount) { Dwarf_Attribute at; Dwarf_Unsigned udata; Dwarf_Half tag; Dwarf_Block *block; Dwarf_Bool flag; Dwarf_Die ret_die; Dwarf_Error de; struct func_info_entry *func; struct var_info_entry *var; const char *str; int ret; if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) { warnx("dwarf_tag failed: %s", dwarf_errmsg(de)); goto cont_search; } /* We're interested in DIEs which define functions or variables. */ if (tag != DW_TAG_subprogram && tag != DW_TAG_entry_point && tag != DW_TAG_inlined_subroutine && tag != DW_TAG_variable) goto cont_search; if (tag == DW_TAG_variable) { /* Ignore "artificial" variable. */ if (dwarf_attrval_flag(die, DW_AT_artificial, &flag, &de) == DW_DLV_OK && flag) goto cont_search; /* Ignore pure declaration. */ if (dwarf_attrval_flag(die, DW_AT_declaration, &flag, &de) == DW_DLV_OK && flag) goto cont_search; /* Ignore stack varaibles. */ if (dwarf_attrval_flag(die, DW_AT_external, &flag, &de) != DW_DLV_OK || !flag) goto cont_search; if ((var = calloc(1, sizeof(*var))) == NULL) { warn("calloc failed"); goto cont_search; } if (dwarf_attrval_unsigned(die, DW_AT_decl_file, &udata, &de) == DW_DLV_OK && udata > 0 && (Dwarf_Signed) (udata - 1) < filecount) { var->file = strdup(src_files[udata - 1]); if (var->file == NULL) { warn("strdup"); free(var); goto cont_search; } } if (dwarf_attrval_unsigned(die, DW_AT_decl_line, &udata, &de) == DW_DLV_OK) var->line = udata; if (dwarf_attrval_string(die, DW_AT_name, &str, &de) == DW_DLV_OK) { var->name = strdup(str); if (var->name == NULL) { warn("strdup"); if (var->file) free(var->file); free(var); goto cont_search; } } if (dwarf_attr(die, DW_AT_location, &at, &de) == DW_DLV_OK && dwarf_formblock(at, &block, &de) == DW_DLV_OK) { /* * Since we ignored stack variables, the rest are the * external varaibles which should always use DW_OP_addr * operator for DW_AT_location value. */ if (*((uint8_t *)block->bl_data) == DW_OP_addr) var->addr = get_block_value(dbg, block); } SLIST_INSERT_HEAD(var_info, var, entries); } else { if ((func = calloc(1, sizeof(*func))) == NULL) { warn("calloc failed"); goto cont_search; } /* * Note that dwarf_attrval_unsigned() handles DW_AT_abstract_origin * internally, so it can retrieve DW_AT_decl_file/DW_AT_decl_line * attributes for inlined functions as well. */ if (dwarf_attrval_unsigned(die, DW_AT_decl_file, &udata, &de) == DW_DLV_OK && udata > 0 && (Dwarf_Signed) (udata - 1) < filecount) { func->file = strdup(src_files[udata - 1]); if (func->file == NULL) { warn("strdup"); free(func); goto cont_search; } } if (dwarf_attrval_unsigned(die, DW_AT_decl_line, &udata, &de) == DW_DLV_OK) func->line = udata; if (dwarf_attrval_string(die, DW_AT_name, &str, &de) == DW_DLV_OK) { func->name = strdup(str); if (func->name == NULL) { warn("strdup"); if (func->file) free(func->file); free(func); goto cont_search; } } if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &udata, &de) == DW_DLV_OK) func->lowpc = udata; if (dwarf_attrval_unsigned(die, DW_AT_high_pc, &udata, &de) == DW_DLV_OK) func->highpc = udata; SLIST_INSERT_HEAD(func_info, func, entries); } cont_search: /* Search children. */ ret = dwarf_child(die, &ret_die, &de); if (ret == DW_DLV_ERROR) warnx("dwarf_child: %s", dwarf_errmsg(de)); else if (ret == DW_DLV_OK) search_line_attr(dbg, func_info, var_info, ret_die, src_files, filecount); /* Search sibling. */ ret = dwarf_siblingof(dbg, die, &ret_die, &de); if (ret == DW_DLV_ERROR) warnx("dwarf_siblingof: %s", dwarf_errmsg(de)); else if (ret == DW_DLV_OK) search_line_attr(dbg, func_info, var_info, ret_die, src_files, filecount); dwarf_dealloc(dbg, die, DW_DLA_DIE); } /* * Read elf file and collect symbol information, sort them, print. * Return 1 at failed, 0 at success. */ static int read_elf(Elf *elf, const char *filename, Elf_Kind kind) { Dwarf_Debug dbg; Dwarf_Die die; Dwarf_Error de; Dwarf_Half tag; Elf_Arhdr *arhdr; Elf_Scn *scn; GElf_Shdr shdr; GElf_Half i; Dwarf_Line *lbuf; Dwarf_Unsigned lineno; Dwarf_Signed lcount, filecount; Dwarf_Addr lineaddr; struct sym_print_data p_data; struct sym_head list_head; struct line_info_head *line_info; struct func_info_head *func_info; struct var_info_head *var_info; struct line_info_entry *lie; struct func_info_entry *func; struct var_info_entry *var; const char *shname, *objname; char *type_table, **sec_table, *sfile, **src_files; size_t shstrndx, shnum, dynndx, strndx; int ret, rtn, e_err; #define OBJNAME (objname == NULL ? filename : objname) assert(filename != NULL && "filename is null"); STAILQ_INIT(&list_head); type_table = NULL; sec_table = NULL; line_info = NULL; func_info = NULL; var_info = NULL; objname = NULL; dynndx = SHN_UNDEF; strndx = SHN_UNDEF; rtn = 0; nm_elfclass = gelf_getclass(elf); if (kind == ELF_K_AR) { if ((arhdr = elf_getarhdr(elf)) == NULL) goto next_cmd; objname = arhdr->ar_name != NULL ? arhdr->ar_name : arhdr->ar_rawname; } if (!elf_getshnum(elf, &shnum)) { if ((e_err = elf_errno()) != 0) warnx("%s: %s", OBJNAME, elf_errmsg(e_err)); else warnx("%s: cannot get section number", OBJNAME); rtn = 1; goto next_cmd; } if (shnum == 0) { warnx("%s: has no section", OBJNAME); rtn = 1; goto next_cmd; } if (!elf_getshstrndx(elf, &shstrndx)) { warnx("%s: cannot get str index", OBJNAME); rtn = 1; goto next_cmd; } /* type_table for type determine */ if ((type_table = malloc(sizeof(char) * shnum)) == NULL) { warn("%s: malloc", OBJNAME); rtn = 1; goto next_cmd; } /* sec_table for section name to display in sysv format */ if ((sec_table = calloc(shnum, sizeof(char *))) == NULL) { warn("%s: calloc", OBJNAME); rtn = 1; goto next_cmd; } type_table[0] = 'U'; if ((sec_table[0] = strdup("*UND*")) == NULL) { warn("strdup"); goto next_cmd; } for (i = 1; i < shnum; ++i) { type_table[i] = 'U'; if ((scn = elf_getscn(elf, i)) == NULL) { if ((e_err = elf_errno()) != 0) warnx("%s: %s", OBJNAME, elf_errmsg(e_err)); else warnx("%s: cannot get section", OBJNAME); rtn = 1; goto next_cmd; } if (gelf_getshdr(scn, &shdr) == NULL) goto next_cmd; /* * Cannot test by type and attribute for dynstr, strtab */ shname = elf_strptr(elf, shstrndx, (size_t) shdr.sh_name); if (shname != NULL) { if ((sec_table[i] = strdup(shname)) == NULL) { warn("strdup"); goto next_cmd; } if (!strncmp(shname, ".dynstr", 7)) { dynndx = elf_ndxscn(scn); if (dynndx == SHN_UNDEF) { warnx("%s: elf_ndxscn failed: %s", OBJNAME, elf_errmsg(-1)); goto next_cmd; } } if (!strncmp(shname, ".strtab", 7)) { strndx = elf_ndxscn(scn); if (strndx == SHN_UNDEF) { warnx("%s: elf_ndxscn failed: %s", OBJNAME, elf_errmsg(-1)); goto next_cmd; } } } else { sec_table[i] = strdup("*UND*"); if (sec_table[i] == NULL) { warn("strdup"); goto next_cmd; } } if (is_sec_text(&shdr)) type_table[i] = 'T'; else if (is_sec_data(&shdr)) { if (is_sec_readonly(&shdr)) type_table[i] = 'R'; else type_table[i] = 'D'; } else if (is_sec_nobits(&shdr)) type_table[i] = 'B'; else if (is_sec_debug(shname)) type_table[i] = 'N'; else if (is_sec_readonly(&shdr) && !is_sec_nobits(&shdr)) type_table[i] = 'n'; } print_header(filename, objname); if ((dynndx == SHN_UNDEF && nm_opts.print_symbol == PRINT_SYM_DYN) || (strndx == SHN_UNDEF && nm_opts.print_symbol == PRINT_SYM_SYM)) { warnx("%s: no symbols", OBJNAME); /* This is not an error case */ goto next_cmd; } STAILQ_INIT(&list_head); if (!nm_opts.debug_line) goto process_sym; /* * Collect dwarf line number information. */ if (dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dbg, &de) != DW_DLV_OK) { warnx("dwarf_elf_init failed: %s", dwarf_errmsg(de)); goto process_sym; } line_info = malloc(sizeof(struct line_info_head)); func_info = malloc(sizeof(struct func_info_head)); var_info = malloc(sizeof(struct var_info_head)); if (line_info == NULL || func_info == NULL || var_info == NULL) { warn("malloc"); (void) dwarf_finish(dbg, &de); goto process_sym; } SLIST_INIT(line_info); SLIST_INIT(func_info); SLIST_INIT(var_info); while ((ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, &de)) == DW_DLV_OK) { die = NULL; while (dwarf_siblingof(dbg, die, &die, &de) == DW_DLV_OK) { if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) { warnx("dwarf_tag failed: %s", dwarf_errmsg(de)); continue; } /* XXX: What about DW_TAG_partial_unit? */ if (tag == DW_TAG_compile_unit) break; } if (die == NULL) { warnx("could not find DW_TAG_compile_unit die"); continue; } /* Retrieve source file list. */ ret = dwarf_srcfiles(die, &src_files, &filecount, &de); if (ret == DW_DLV_ERROR) warnx("dwarf_srclines: %s", dwarf_errmsg(de)); if (ret != DW_DLV_OK) continue; /* * Retrieve line number information from .debug_line section. */ ret = dwarf_srclines(die, &lbuf, &lcount, &de); if (ret == DW_DLV_ERROR) warnx("dwarf_srclines: %s", dwarf_errmsg(de)); if (ret != DW_DLV_OK) goto line_attr; for (i = 0; (Dwarf_Signed) i < lcount; i++) { if (dwarf_lineaddr(lbuf[i], &lineaddr, &de)) { warnx("dwarf_lineaddr: %s", dwarf_errmsg(de)); continue; } if (dwarf_lineno(lbuf[i], &lineno, &de)) { warnx("dwarf_lineno: %s", dwarf_errmsg(de)); continue; } if (dwarf_linesrc(lbuf[i], &sfile, &de)) { warnx("dwarf_linesrc: %s", dwarf_errmsg(de)); continue; } if ((lie = malloc(sizeof(*lie))) == NULL) { warn("malloc"); continue; } lie->addr = lineaddr; lie->line = lineno; lie->file = strdup(sfile); if (lie->file == NULL) { warn("strdup"); free(lie); continue; } SLIST_INSERT_HEAD(line_info, lie, entries); } line_attr: /* Retrieve line number information from DIEs. */ search_line_attr(dbg, func_info, var_info, die, src_files, filecount); } (void) dwarf_finish(dbg, &de); process_sym: p_data.list_num = get_sym(elf, &list_head, shnum, dynndx, strndx, type_table, (void *) sec_table, shnum); if (p_data.list_num == 0) goto next_cmd; p_data.headp = &list_head; p_data.sh_num = shnum; p_data.t_table = type_table; p_data.s_table = (void *) sec_table; p_data.filename = filename; p_data.objname = objname; sym_list_print(&p_data, func_info, var_info, line_info); next_cmd: if (nm_opts.debug_line) { if (func_info != NULL) { while (!SLIST_EMPTY(func_info)) { func = SLIST_FIRST(func_info); SLIST_REMOVE_HEAD(func_info, entries); free(func->file); free(func->name); free(func); } free(func_info); func_info = NULL; } if (var_info != NULL) { while (!SLIST_EMPTY(var_info)) { var = SLIST_FIRST(var_info); SLIST_REMOVE_HEAD(var_info, entries); free(var->file); free(var->name); free(var); } free(var_info); var_info = NULL; } if (line_info != NULL) { while (!SLIST_EMPTY(line_info)) { lie = SLIST_FIRST(line_info); SLIST_REMOVE_HEAD(line_info, entries); free(lie->file); free(lie); } free(line_info); line_info = NULL; } } if (sec_table != NULL) for (i = 0; i < shnum; ++i) free(sec_table[i]); free(sec_table); free(type_table); sym_list_dest(&list_head); return (rtn); #undef OBJNAME } static int read_object(const char *filename) { Elf *elf, *arf; Elf_Cmd elf_cmd; Elf_Kind kind; int fd, rtn, e_err; assert(filename != NULL && "filename is null"); if ((fd = open(filename, O_RDONLY)) == -1) { warn("'%s'", filename); return (1); } elf_cmd = ELF_C_READ; if ((arf = elf_begin(fd, elf_cmd, (Elf *) NULL)) == NULL) { if ((e_err = elf_errno()) != 0) warnx("elf_begin error: %s", elf_errmsg(e_err)); else warnx("elf_begin error"); close(fd); return (1); } assert(arf != NULL && "arf is null."); rtn = 0; if ((kind = elf_kind(arf)) == ELF_K_NONE) { warnx("%s: File format not recognized", filename); elf_end(arf); close(fd); return (1); } if (kind == ELF_K_AR) { if (nm_opts.print_name == PRINT_NAME_MULTI && nm_opts.elem_print_fn == sym_elem_print_all) printf("\n%s:\n", filename); if (nm_opts.print_armap == true) print_ar_index(fd, arf); } while ((elf = elf_begin(fd, elf_cmd, arf)) != NULL) { rtn |= read_elf(elf, filename, kind); /* * If file is not archive, elf_next return ELF_C_NULL and * stop the loop. */ elf_cmd = elf_next(elf); elf_end(elf); } elf_end(arf); close(fd); return (rtn); } static int read_files(int argc, char **argv) { int rtn = 0; if (argc < 0 || argv == NULL) return (1); if (argc == 0) rtn |= read_object(nm_info.def_filename); else { if (nm_opts.print_name == PRINT_NAME_NONE && argc > 1) nm_opts.print_name = PRINT_NAME_MULTI; while (argc > 0) { rtn |= read_object(*argv); --argc; ++argv; } } return (rtn); } static void print_lineno(struct sym_entry *ep, struct func_info_head *func_info, struct var_info_head *var_info, struct line_info_head *line_info) { struct func_info_entry *func; struct var_info_entry *var; struct line_info_entry *lie; /* For function symbol, search the function line information list. */ if ((ep->sym->st_info & 0xf) == STT_FUNC && func_info != NULL) { SLIST_FOREACH(func, func_info, entries) { if (!strcmp(ep->name, func->name) && ep->sym->st_value >= func->lowpc && ep->sym->st_value < func->highpc) { printf("\t%s:%" PRIu64, func->file, func->line); return; } } } /* For variable symbol, search the variable line information list. */ if ((ep->sym->st_info & 0xf) == STT_OBJECT && var_info != NULL) { SLIST_FOREACH(var, var_info, entries) { if (!strcmp(ep->name, var->name) && ep->sym->st_value == var->addr) { printf("\t%s:%" PRIu64, var->file, var->line); return; } } } /* Otherwise search line number information the .debug_line section. */ if (line_info != NULL) { SLIST_FOREACH(lie, line_info, entries) { if (ep->sym->st_value == lie->addr) { printf("\t%s:%" PRIu64, lie->file, lie->line); return; } } } } static void set_opt_value_print_fn(enum radix t) { switch (t) { case RADIX_OCT: nm_opts.value_print_fn = &sym_value_oct_print; nm_opts.size_print_fn = &sym_size_oct_print; break; case RADIX_DEC: nm_opts.value_print_fn = &sym_value_dec_print; nm_opts.size_print_fn = &sym_size_dec_print; break; case RADIX_HEX: default : nm_opts.value_print_fn = &sym_value_hex_print; nm_opts.size_print_fn = &sym_size_hex_print; } assert(nm_opts.value_print_fn != NULL && "nm_opts.value_print_fn is null"); } static void sym_elem_print_all(char type, const char *sec, const GElf_Sym *sym, const char *name) { if (sec == NULL || sym == NULL || name == NULL || nm_opts.value_print_fn == NULL) return; if (IS_UNDEF_SYM_TYPE(type)) { if (nm_opts.t == RADIX_HEX && nm_elfclass == ELFCLASS32) printf("%-8s", ""); else printf("%-16s", ""); } else { switch ((nm_opts.sort_fn == & cmp_size ? 2 : 0) + nm_opts.print_size) { case 3: if (sym->st_size != 0) { nm_opts.value_print_fn(sym); printf(" "); nm_opts.size_print_fn(sym); } break; case 2: if (sym->st_size != 0) nm_opts.size_print_fn(sym); break; case 1: nm_opts.value_print_fn(sym); if (sym->st_size != 0) { printf(" "); nm_opts.size_print_fn(sym); } break; case 0: default: nm_opts.value_print_fn(sym); } } printf(" %c ", type); PRINT_DEMANGLED_NAME("%s", name); } static void sym_elem_print_all_portable(char type, const char *sec, const GElf_Sym *sym, const char *name) { if (sec == NULL || sym == NULL || name == NULL || nm_opts.value_print_fn == NULL) return; PRINT_DEMANGLED_NAME("%s", name); printf(" %c ", type); if (!IS_UNDEF_SYM_TYPE(type)) { nm_opts.value_print_fn(sym); printf(" "); if (sym->st_size != 0) nm_opts.size_print_fn(sym); } else printf(" "); } static void sym_elem_print_all_sysv(char type, const char *sec, const GElf_Sym *sym, const char *name) { if (sec == NULL || sym == NULL || name == NULL || nm_opts.value_print_fn == NULL) return; PRINT_DEMANGLED_NAME("%-20s|", name); if (IS_UNDEF_SYM_TYPE(type)) printf(" "); else nm_opts.value_print_fn(sym); printf("| %c |", type); switch (sym->st_info & 0xf) { case STT_OBJECT: printf("%18s|", "OBJECT"); break; case STT_FUNC: printf("%18s|", "FUNC"); break; case STT_SECTION: printf("%18s|", "SECTION"); break; case STT_FILE: printf("%18s|", "FILE"); break; case STT_LOPROC: printf("%18s|", "LOPROC"); break; case STT_HIPROC: printf("%18s|", "HIPROC"); break; case STT_NOTYPE: default: printf("%18s|", "NOTYPE"); }; if (sym->st_size != 0) nm_opts.size_print_fn(sym); else printf(" "); printf("| |%s", sec); } static int sym_elem_def(char type, const GElf_Sym *sym, const char *name) { assert(IS_SYM_TYPE((unsigned char) type)); UNUSED(sym); UNUSED(name); return (!IS_UNDEF_SYM_TYPE((unsigned char) type)); } static int sym_elem_global(char type, const GElf_Sym *sym, const char *name) { assert(IS_SYM_TYPE((unsigned char) type)); UNUSED(sym); UNUSED(name); /* weak symbols resemble global. */ return (isupper((unsigned char) type) || type == 'w'); } static int sym_elem_global_static(char type, const GElf_Sym *sym, const char *name) { unsigned char info; assert(sym != NULL); UNUSED(type); UNUSED(name); info = sym->st_info >> 4; return (info == STB_LOCAL || info == STB_GLOBAL || info == STB_WEAK); } static int sym_elem_nondebug(char type, const GElf_Sym *sym, const char *name) { assert(sym != NULL); UNUSED(type); UNUSED(name); if (sym->st_value == 0 && (sym->st_info & 0xf) == STT_FILE) return (0); if (sym->st_name == 0) return (0); return (1); } static int sym_elem_nonzero_size(char type, const GElf_Sym *sym, const char *name) { assert(sym != NULL); UNUSED(type); UNUSED(name); return (sym->st_size > 0); } static int sym_elem_undef(char type, const GElf_Sym *sym, const char *name) { assert(IS_SYM_TYPE((unsigned char) type)); UNUSED(sym); UNUSED(name); return (IS_UNDEF_SYM_TYPE((unsigned char) type)); } static void sym_list_dest(struct sym_head *headp) { struct sym_entry *ep, *ep_n; if (headp == NULL) return; ep = STAILQ_FIRST(headp); while (ep != NULL) { ep_n = STAILQ_NEXT(ep, sym_entries); free(ep->sym); free(ep->name); free(ep); ep = ep_n; } } static int sym_list_insert(struct sym_head *headp, const char *name, const GElf_Sym *sym) { struct sym_entry *e; if (headp == NULL || name == NULL || sym == NULL) return (0); if ((e = malloc(sizeof(struct sym_entry))) == NULL) { warn("malloc"); return (0); } if ((e->name = strdup(name)) == NULL) { warn("strdup"); free(e); return (0); } if ((e->sym = malloc(sizeof(GElf_Sym))) == NULL) { warn("malloc"); free(e->name); free(e); return (0); } memcpy(e->sym, sym, sizeof(GElf_Sym)); /* Display size instead of value for common symbol. */ if (sym->st_shndx == SHN_COMMON) e->sym->st_value = sym->st_size; STAILQ_INSERT_TAIL(headp, e, sym_entries); return (1); } /* If file has not .debug_info, line_info will be NULL */ static void sym_list_print(struct sym_print_data *p, struct func_info_head *func_info, struct var_info_head *var_info, struct line_info_head *line_info) { struct sym_entry *e_v; size_t si; int i; if (p == NULL || CHECK_SYM_PRINT_DATA(p)) return; if ((e_v = sym_list_sort(p)) == NULL) return; if (nm_opts.sort_reverse == false) for (si = 0; si != p->list_num; ++si) sym_list_print_each(&e_v[si], p, func_info, var_info, line_info); else for (i = p->list_num - 1; i != -1; --i) sym_list_print_each(&e_v[i], p, func_info, var_info, line_info); free(e_v); } /* If file has not .debug_info, line_info will be NULL */ static void sym_list_print_each(struct sym_entry *ep, struct sym_print_data *p, struct func_info_head *func_info, struct var_info_head *var_info, struct line_info_head *line_info) { const char *sec; char type; if (ep == NULL || CHECK_SYM_PRINT_DATA(p)) return; assert(ep->name != NULL); assert(ep->sym != NULL); type = get_sym_type(ep->sym, p->t_table); if (nm_opts.print_name == PRINT_NAME_FULL) { printf("%s", p->filename); if (nm_opts.elem_print_fn == &sym_elem_print_all_portable) { if (p->objname != NULL) printf("[%s]", p->objname); printf(": "); } else { if (p->objname != NULL) printf(":%s", p->objname); printf(":"); } } switch (ep->sym->st_shndx) { case SHN_LOPROC: /* LOPROC or LORESERVE */ sec = "*LOPROC*"; break; case SHN_HIPROC: sec = "*HIPROC*"; break; case SHN_LOOS: sec = "*LOOS*"; break; case SHN_HIOS: sec = "*HIOS*"; break; case SHN_ABS: sec = "*ABS*"; break; case SHN_COMMON: sec = "*COM*"; break; case SHN_HIRESERVE: /* HIRESERVE or XINDEX */ sec = "*HIRESERVE*"; break; default: if (ep->sym->st_shndx > p->sh_num) return; sec = p->s_table[ep->sym->st_shndx]; break; }; nm_opts.elem_print_fn(type, sec, ep->sym, ep->name); if (nm_opts.debug_line == true && !IS_UNDEF_SYM_TYPE(type)) print_lineno(ep, func_info, var_info, line_info); printf("\n"); } static struct sym_entry * sym_list_sort(struct sym_print_data *p) { struct sym_entry *ep, *e_v; int idx; if (p == NULL || CHECK_SYM_PRINT_DATA(p)) return (NULL); if ((e_v = malloc(sizeof(struct sym_entry) * p->list_num)) == NULL) { warn("malloc"); return (NULL); } idx = 0; STAILQ_FOREACH(ep, p->headp, sym_entries) { if (ep->name != NULL && ep->sym != NULL) { e_v[idx].name = ep->name; e_v[idx].sym = ep->sym; ++idx; } } assert((size_t)idx == p->list_num); if (nm_opts.sort_fn != &cmp_none) { nm_print_data = p; assert(nm_print_data != NULL); qsort(e_v, p->list_num, sizeof(struct sym_entry), nm_opts.sort_fn); } return (e_v); } static void sym_size_oct_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); printf("%016" PRIo64, sym->st_size); } static void sym_size_hex_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); if (nm_elfclass == ELFCLASS32) printf("%08" PRIx64, sym->st_size); else printf("%016" PRIx64, sym->st_size); } static void sym_size_dec_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); printf("%016" PRId64, sym->st_size); } static void sym_value_oct_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); printf("%016" PRIo64, sym->st_value); } static void sym_value_hex_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); if (nm_elfclass == ELFCLASS32) printf("%08" PRIx64, sym->st_value); else printf("%016" PRIx64, sym->st_value); } static void sym_value_dec_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); printf("%016" PRId64, sym->st_value); } static void usage(int exitcode) { printf("Usage: %s [options] file ...\ \n Display symbolic information in file.\n\ \n Options: \ \n -A, --print-file-name Write the full pathname or library name of an\ \n object on each line.\ \n -a, --debug-syms Display all symbols include debugger-only\ \n symbols.", nm_info.name); printf("\ \n -B Equivalent to specifying \"--format=bsd\".\ \n -C, --demangle[=style] Decode low-level symbol names.\ \n --no-demangle Do not demangle low-level symbol names.\ \n -D, --dynamic Display only dynamic symbols.\ \n -e Display only global and static symbols."); printf("\ \n -f Produce full output (default).\ \n --format=format Display output in specific format. Allowed\ \n formats are: \"bsd\", \"posix\" and \"sysv\".\ \n -g Display only global symbol information.\ \n -h, --help Show this help message.\ \n -l, --line-numbers Display filename and linenumber using\ \n debugging information.\ \n -n, --numeric-sort Sort symbols numerically by value."); printf("\ \n -o Write numeric values in octal. Equivalent to\ \n specifying \"-t o\".\ \n -p, --no-sort Do not sort symbols.\ \n -P Write information in a portable output format.\ \n Equivalent to specifying \"--format=posix\".\ \n -r, --reverse-sort Reverse the order of the sort.\ \n -S, --print-size Print symbol sizes instead values.\ \n -s, --print-armap Include an index of archive members.\ \n --size-sort Sort symbols by size."); printf("\ \n -t, --radix=format Write each numeric value in the specified\ \n format:\ \n d In decimal,\ \n o In octal,\ \n x In hexadecimal."); printf("\ \n -u, --undefined-only Display only undefined symbols.\ \n --defined-only Display only defined symbols.\ \n -V, --version Show the version identifier for %s.\ \n -v Sort output by value.\ \n -x Write numeric values in hexadecimal.\ \n Equivalent to specifying \"-t x\".", nm_info.name); printf("\n\ \n The default options are: output in bsd format, use a hexadecimal radix,\ \n sort by symbol name, do not demangle names.\n"); exit(exitcode); } /* * Display symbolic information in file. * Return 0 at success, >0 at failed. */ int main(int argc, char **argv) { int rtn; global_init(); get_opt(argc, argv); rtn = read_files(argc - optind, argv + optind); global_dest(); exit(rtn); } Index: projects/clang350-import/contrib/elftoolchain/size/size.c =================================================================== --- projects/clang350-import/contrib/elftoolchain/size/size.c (revision 275386) +++ projects/clang350-import/contrib/elftoolchain/size/size.c (revision 275387) @@ -1,914 +1,918 @@ /*- * Copyright (c) 2007 S.Sam Arun Raj * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "_elftc.h" ELFTC_VCSID("$Id: size.c 2350 2011-12-19 10:20:06Z jkoshy $"); #define BUF_SIZE 1024 #define ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1)) #define SIZE_VERSION_STRING "size 1.0" enum return_code { RETURN_OK, RETURN_NOINPUT, RETURN_DATAERR, RETURN_USAGE }; enum output_style { STYLE_BERKELEY, STYLE_SYSV }; enum radix_style { RADIX_OCTAL, RADIX_DECIMAL, RADIX_HEX }; static uint64_t bss_size, data_size, text_size, total_size; static uint64_t bss_size_total, data_size_total, text_size_total; static int show_totals; static int size_option; static enum radix_style radix = RADIX_DECIMAL; static enum output_style style = STYLE_BERKELEY; static const char *default_args[2] = { "a.out", NULL }; static struct { int row; int col; int *width; char ***tbl; } *tb; enum { OPT_FORMAT, OPT_RADIX }; static struct option size_longopts[] = { { "format", required_argument, &size_option, OPT_FORMAT }, { "help", no_argument, NULL, 'h' }, { "radix", required_argument, &size_option, OPT_RADIX }, { "totals", no_argument, NULL, 't' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; static void berkeley_calc(GElf_Shdr *); static void berkeley_footer(const char *, const char *, const char *); static void berkeley_header(void); static void berkeley_totals(void); static int handle_core(char const *, Elf *elf, GElf_Ehdr *); static void handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **); static int handle_elf(char const *); static void handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t, const char *); static void show_version(void); static void sysv_header(const char *, Elf_Arhdr *); static void sysv_footer(void); static void sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *); static void usage(void); static void tbl_new(int); static void tbl_print(const char *, int); static void tbl_print_num(uint64_t, enum radix_style, int); static void tbl_append(void); static void tbl_flush(void); /* * size utility using elf(3) and gelf(3) API to list section sizes and * total in elf files. Supports only elf files (core dumps in elf * included) that can be opened by libelf, other formats are not supported. */ int main(int argc, char **argv) { int ch, r, rc; const char **files, *fn; rc = RETURN_OK; if (elf_version(EV_CURRENT) == EV_NONE) errx(EXIT_FAILURE, "ELF library initialization failed: %s", elf_errmsg(-1)); while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts, NULL)) != -1) switch((char)ch) { case 'A': style = STYLE_SYSV; break; case 'B': style = STYLE_BERKELEY; break; case 'V': show_version(); break; case 'd': radix = RADIX_DECIMAL; break; case 'o': radix = RADIX_OCTAL; break; case 't': show_totals = 1; break; case 'x': radix = RADIX_HEX; break; case 0: switch (size_option) { case OPT_FORMAT: if (*optarg == 's' || *optarg == 'S') style = STYLE_SYSV; else if (*optarg == 'b' || *optarg == 'B') style = STYLE_BERKELEY; else { warnx("unrecognized format \"%s\".", optarg); usage(); } break; case OPT_RADIX: r = strtol(optarg, NULL, 10); if (r == 8) radix = RADIX_OCTAL; else if (r == 10) radix = RADIX_DECIMAL; else if (r == 16) radix = RADIX_HEX; else { warnx("unsupported radix \"%s\".", optarg); usage(); } break; default: err(EXIT_FAILURE, "Error in option handling."); /*NOTREACHED*/ } break; case 'h': case '?': default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; files = (argc == 0) ? default_args : (void *) argv; while ((fn = *files) != NULL) { rc = handle_elf(fn); if (rc != RETURN_OK) warnx(rc == RETURN_NOINPUT ? "'%s': No such file" : "%s: File format not recognized", fn); files++; } if (style == STYLE_BERKELEY) { if (show_totals) berkeley_totals(); tbl_flush(); } return (rc); } static Elf_Data * xlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst, Elf_Type type, size_t size) { Elf_Data src, dst; src.d_buf = _src; src.d_type = type; src.d_version = elfhdr->e_version; src.d_size = size; dst.d_buf = _dst; dst.d_version = elfhdr->e_version; dst.d_size = size; return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA])); } #define NOTE_OFFSET_32(nhdr, namesz, offset) \ ((char *)nhdr + sizeof(Elf32_Nhdr) + \ ELF_ALIGN((int32_t)namesz, 4) + offset) #define NOTE_OFFSET_64(nhdr, namesz, offset) \ ((char *)nhdr + sizeof(Elf32_Nhdr) + \ ELF_ALIGN((int32_t)namesz, 8) + offset) #define PID32(nhdr, namesz, offset) \ (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr, \ namesz, offset))); #define PID64(nhdr, namesz, offset) \ (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr, \ namesz, offset))); #define NEXT_NOTE(elfhdr, descsz, namesz, offset) do { \ if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { \ offset += ELF_ALIGN((int32_t)descsz, 4) + \ sizeof(Elf32_Nhdr) + \ ELF_ALIGN((int32_t)namesz, 4); \ } else { \ offset += ELF_ALIGN((int32_t)descsz, 8) + \ sizeof(Elf32_Nhdr) + \ ELF_ALIGN((int32_t)namesz, 8); \ } \ } while (0) /* * Parse individual note entries inside a PT_NOTE segment. */ static void handle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, char **cmd_line) { size_t max_size; uint64_t raw_size; GElf_Off offset; static pid_t pid; uintptr_t ver; Elf32_Nhdr *nhdr, nhdr_l; - static int reg_pseudo = 0, reg2_pseudo = 0, regxfp_pseudo = 0; + static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/; char buf[BUF_SIZE], *data, *name; if (elf == NULL || elfhdr == NULL || phdr == NULL) return; data = elf_rawfile(elf, &max_size); offset = phdr->p_offset; while (data != NULL && offset < phdr->p_offset + phdr->p_filesz) { nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset); memset(&nhdr_l, 0, sizeof(Elf32_Nhdr)); if (!xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type, ELF_T_WORD, sizeof(Elf32_Word)) || !xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz, ELF_T_WORD, sizeof(Elf32_Word)) || !xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz, ELF_T_WORD, sizeof(Elf32_Word))) break; name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr)); switch (nhdr_l.n_type) { case NT_PRSTATUS: { raw_size = 0; if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD && nhdr_l.n_namesz == 0x8 && !strcmp(name,"FreeBSD")) { if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { raw_size = (uint64_t)*((uint32_t *) (uintptr_t)(name + ELF_ALIGN((int32_t) nhdr_l.n_namesz, 4) + 8)); ver = (uintptr_t)NOTE_OFFSET_32(nhdr, nhdr_l.n_namesz,0); if (*((int *)ver) == 1) pid = PID32(nhdr, nhdr_l.n_namesz, 24); } else { raw_size = *((uint64_t *)(uintptr_t) (name + ELF_ALIGN((int32_t) nhdr_l.n_namesz, 8) + 16)); ver = (uintptr_t)NOTE_OFFSET_64(nhdr, nhdr_l.n_namesz,0); if (*((int *)ver) == 1) pid = PID64(nhdr, nhdr_l.n_namesz, 40); } xlatetom(elf, elfhdr, &raw_size, &raw_size, ELF_T_WORD, sizeof(uint64_t)); xlatetom(elf, elfhdr, &pid, &pid, ELF_T_WORD, sizeof(pid_t)); } if (raw_size != 0 && style == STYLE_SYSV) { (void) snprintf(buf, BUF_SIZE, "%s/%d", ".reg", pid); tbl_append(); tbl_print(buf, 0); tbl_print_num(raw_size, radix, 1); tbl_print_num(0, radix, 2); if (!reg_pseudo) { tbl_append(); tbl_print(".reg", 0); tbl_print_num(raw_size, radix, 1); tbl_print_num(0, radix, 2); reg_pseudo = 1; text_size_total += raw_size; } text_size_total += raw_size; } } break; case NT_FPREGSET: /* same as NT_PRFPREG */ if (style == STYLE_SYSV) { (void) snprintf(buf, BUF_SIZE, "%s/%d", ".reg2", pid); tbl_append(); tbl_print(buf, 0); tbl_print_num(nhdr_l.n_descsz, radix, 1); tbl_print_num(0, radix, 2); if (!reg2_pseudo) { tbl_append(); tbl_print(".reg2", 0); tbl_print_num(nhdr_l.n_descsz, radix, 1); tbl_print_num(0, radix, 2); reg2_pseudo = 1; text_size_total += nhdr_l.n_descsz; } text_size_total += nhdr_l.n_descsz; } break; +#if 0 case NT_AUXV: if (style == STYLE_SYSV) { tbl_append(); tbl_print(".auxv", 0); tbl_print_num(nhdr_l.n_descsz, radix, 1); tbl_print_num(0, radix, 2); text_size_total += nhdr_l.n_descsz; } break; case NT_PRXFPREG: if (style == STYLE_SYSV) { (void) snprintf(buf, BUF_SIZE, "%s/%d", ".reg-xfp", pid); tbl_append(); tbl_print(buf, 0); tbl_print_num(nhdr_l.n_descsz, radix, 1); tbl_print_num(0, radix, 2); if (!regxfp_pseudo) { tbl_append(); tbl_print(".reg-xfp", 0); tbl_print_num(nhdr_l.n_descsz, radix, 1); tbl_print_num(0, radix, 2); regxfp_pseudo = 1; text_size_total += nhdr_l.n_descsz; } text_size_total += nhdr_l.n_descsz; } break; case NT_PSINFO: +#endif case NT_PRPSINFO: { /* FreeBSD 64-bit */ if (nhdr_l.n_descsz == 0x78 && !strcmp(name,"FreeBSD")) { *cmd_line = strdup(NOTE_OFFSET_64(nhdr, nhdr_l.n_namesz, 33)); /* FreeBSD 32-bit */ } else if (nhdr_l.n_descsz == 0x6c && !strcmp(name,"FreeBSD")) { *cmd_line = strdup(NOTE_OFFSET_32(nhdr, nhdr_l.n_namesz, 25)); } /* Strip any trailing spaces */ if (*cmd_line != NULL) { char *s; s = *cmd_line + strlen(*cmd_line); while (s > *cmd_line) { if (*(s-1) != 0x20) break; s--; } *s = 0; } break; } +#if 0 case NT_PSTATUS: case NT_LWPSTATUS: +#endif default: break; } NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset); } } /* * Handles program headers except for PT_NOTE, when sysv output stlye is * choosen, prints out the segment name and length. For berkely output * style only PT_LOAD segments are handled, and text, * data, bss size is calculated for them. */ static void handle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, uint32_t idx, const char *name) { uint64_t addr, size; int split; char buf[BUF_SIZE]; if (elf == NULL || elfhdr == NULL || phdr == NULL) return; size = addr = 0; split = (phdr->p_memsz > 0) && (phdr->p_filesz > 0) && (phdr->p_memsz > phdr->p_filesz); if (style == STYLE_SYSV) { (void) snprintf(buf, BUF_SIZE, "%s%d%s", name, idx, (split ? "a" : "")); tbl_append(); tbl_print(buf, 0); tbl_print_num(phdr->p_filesz, radix, 1); tbl_print_num(phdr->p_vaddr, radix, 2); text_size_total += phdr->p_filesz; if (split) { size = phdr->p_memsz - phdr->p_filesz; addr = phdr->p_vaddr + phdr->p_filesz; (void) snprintf(buf, BUF_SIZE, "%s%d%s", name, idx, "b"); text_size_total += phdr->p_memsz - phdr->p_filesz; tbl_append(); tbl_print(buf, 0); tbl_print_num(size, radix, 1); tbl_print_num(addr, radix, 2); } } else { if (phdr->p_type != PT_LOAD) return; if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) { data_size += phdr->p_filesz; if (split) data_size += phdr->p_memsz - phdr->p_filesz; } else { text_size += phdr->p_filesz; if (split) text_size += phdr->p_memsz - phdr->p_filesz; } } } /* * Given a core dump file, this function maps program headers to segments. */ static int handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr) { GElf_Phdr phdr; uint32_t i; char *core_cmdline; const char *seg_name; if (name == NULL || elf == NULL || elfhdr == NULL) return (RETURN_DATAERR); if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE) return (RETURN_DATAERR); seg_name = core_cmdline = NULL; if (style == STYLE_SYSV) sysv_header(name, NULL); else berkeley_header(); for (i = 0; i < elfhdr->e_phnum; i++) { if (gelf_getphdr(elf, i, &phdr) != NULL) { if (phdr.p_type == PT_NOTE) { handle_phdr(elf, elfhdr, &phdr, i, "note"); handle_core_note(elf, elfhdr, &phdr, &core_cmdline); } else { switch(phdr.p_type) { case PT_NULL: seg_name = "null"; break; case PT_LOAD: seg_name = "load"; break; case PT_DYNAMIC: seg_name = "dynamic"; break; case PT_INTERP: seg_name = "interp"; break; case PT_SHLIB: seg_name = "shlib"; break; case PT_PHDR: seg_name = "phdr"; break; case PT_GNU_EH_FRAME: seg_name = "eh_frame_hdr"; break; case PT_GNU_STACK: seg_name = "stack"; break; default: seg_name = "segment"; } handle_phdr(elf, elfhdr, &phdr, i, seg_name); } } } if (style == STYLE_BERKELEY) { if (core_cmdline != NULL) { berkeley_footer(core_cmdline, name, "core file invoked as"); } else { berkeley_footer(core_cmdline, name, "core file"); } } else { sysv_footer(); if (core_cmdline != NULL) { (void) printf(" (core file invoked as %s)\n\n", core_cmdline); } else { (void) printf(" (core file)\n\n"); } } free(core_cmdline); return (RETURN_OK); } /* * Given an elf object,ar(1) filename, and based on the output style * and radix format the various sections and their length will be printed * or the size of the text, data, bss sections will be printed out. */ static int handle_elf(char const *name) { GElf_Ehdr elfhdr; GElf_Shdr shdr; Elf *elf, *elf1; Elf_Arhdr *arhdr; Elf_Scn *scn; Elf_Cmd elf_cmd; int exit_code, fd; if (name == NULL) return (RETURN_NOINPUT); if ((fd = open(name, O_RDONLY, 0)) < 0) return (RETURN_NOINPUT); elf_cmd = ELF_C_READ; elf1 = elf_begin(fd, elf_cmd, NULL); while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) { arhdr = elf_getarhdr(elf); if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) { (void) elf_end(elf); (void) elf_end(elf1); (void) close(fd); return (RETURN_DATAERR); } if (elf_kind(elf) != ELF_K_ELF || (gelf_getehdr(elf, &elfhdr) == NULL)) { elf_cmd = elf_next(elf); (void) elf_end(elf); warnx("%s: File format not recognized", arhdr->ar_name); continue; } /* Core dumps are handled seperately */ if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) { exit_code = handle_core(name, elf, &elfhdr); (void) elf_end(elf); (void) elf_end(elf1); (void) close(fd); return (exit_code); } else { scn = NULL; if (style == STYLE_BERKELEY) { berkeley_header(); while ((scn = elf_nextscn(elf, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != NULL) berkeley_calc(&shdr); } } else { sysv_header(name, arhdr); scn = NULL; while ((scn = elf_nextscn(elf, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != NULL) sysv_calc(elf, &elfhdr, &shdr); } } if (style == STYLE_BERKELEY) { if (arhdr != NULL) { berkeley_footer(name, arhdr->ar_name, "ex"); } else { berkeley_footer(name, NULL, "ex"); } } else { sysv_footer(); } } elf_cmd = elf_next(elf); (void) elf_end(elf); } (void) elf_end(elf1); (void) close(fd); return (RETURN_OK); } /* * Sysv formatting helper functions. */ static void sysv_header(const char *name, Elf_Arhdr *arhdr) { text_size_total = 0; if (arhdr != NULL) (void) printf("%s (ex %s):\n", arhdr->ar_name, name); else (void) printf("%s :\n", name); tbl_new(3); tbl_append(); tbl_print("section", 0); tbl_print("size", 1); tbl_print("addr", 2); } static void sysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr) { char *section_name; section_name = elf_strptr(elf, elfhdr->e_shstrndx, (size_t) shdr->sh_name); if ((shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA || shdr->sh_type == SHT_REL) && shdr->sh_addr == 0) return; tbl_append(); tbl_print(section_name, 0); tbl_print_num(shdr->sh_size, radix, 1); tbl_print_num(shdr->sh_addr, radix, 2); text_size_total += shdr->sh_size; } static void sysv_footer(void) { tbl_append(); tbl_print("Total", 0); tbl_print_num(text_size_total, radix, 1); tbl_flush(); putchar('\n'); } /* * berkeley style output formatting helper functions. */ static void berkeley_header(void) { static int printed; text_size = data_size = bss_size = 0; if (!printed) { tbl_new(6); tbl_append(); tbl_print("text", 0); tbl_print("data", 1); tbl_print("bss", 2); if (radix == RADIX_OCTAL) tbl_print("oct", 3); else tbl_print("dec", 3); tbl_print("hex", 4); tbl_print("filename", 5); printed = 1; } } static void berkeley_calc(GElf_Shdr *shdr) { if (shdr != NULL) { if (!(shdr->sh_flags & SHF_ALLOC)) return; if ((shdr->sh_flags & SHF_ALLOC) && ((shdr->sh_flags & SHF_EXECINSTR) || !(shdr->sh_flags & SHF_WRITE))) text_size += shdr->sh_size; else if ((shdr->sh_flags & SHF_ALLOC) && (shdr->sh_flags & SHF_WRITE) && (shdr->sh_type != SHT_NOBITS)) data_size += shdr->sh_size; else bss_size += shdr->sh_size; } } static void berkeley_totals(void) { long unsigned int grand_total; grand_total = text_size_total + data_size_total + bss_size_total; tbl_append(); tbl_print_num(text_size_total, radix, 0); tbl_print_num(data_size_total, radix, 1); tbl_print_num(bss_size_total, radix, 2); if (radix == RADIX_OCTAL) tbl_print_num(grand_total, RADIX_OCTAL, 3); else tbl_print_num(grand_total, RADIX_DECIMAL, 3); tbl_print_num(grand_total, RADIX_HEX, 4); } static void berkeley_footer(const char *name, const char *ar_name, const char *msg) { char buf[BUF_SIZE]; total_size = text_size + data_size + bss_size; if (show_totals) { text_size_total += text_size; bss_size_total += bss_size; data_size_total += data_size; } tbl_append(); tbl_print_num(text_size, radix, 0); tbl_print_num(data_size, radix, 1); tbl_print_num(bss_size, radix, 2); if (radix == RADIX_OCTAL) tbl_print_num(total_size, RADIX_OCTAL, 3); else tbl_print_num(total_size, RADIX_DECIMAL, 3); tbl_print_num(total_size, RADIX_HEX, 4); if (ar_name != NULL && name != NULL) (void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg, name); else if (ar_name != NULL && name == NULL) (void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg); else (void) snprintf(buf, BUF_SIZE, "%s", name); tbl_print(buf, 5); } static void tbl_new(int col) { assert(tb == NULL); assert(col > 0); if ((tb = calloc(1, sizeof(*tb))) == NULL) err(EXIT_FAILURE, "calloc"); if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL) err(EXIT_FAILURE, "calloc"); if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL) err(EXIT_FAILURE, "calloc"); tb->col = col; tb->row = 0; } static void tbl_print(const char *s, int col) { int len; assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col); assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL); if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL) err(EXIT_FAILURE, "strdup"); len = strlen(s); if (len > tb->width[col]) tb->width[col] = len; } static void tbl_print_num(uint64_t num, enum radix_style rad, int col) { char buf[BUF_SIZE]; (void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" : ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num); tbl_print(buf, col); } static void tbl_append(void) { int i; assert(tb != NULL && tb->col > 0); tb->row++; for (i = 0; i < tb->col; i++) { tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row); if (tb->tbl[i] == NULL) err(EXIT_FAILURE, "realloc"); tb->tbl[i][tb->row - 1] = NULL; } } static void tbl_flush(void) { const char *str; int i, j; if (tb == NULL) return; assert(tb->col > 0); for (i = 0; i < tb->row; i++) { if (style == STYLE_BERKELEY) printf(" "); for (j = 0; j < tb->col; j++) { str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : ""); if (style == STYLE_SYSV && j == 0) printf("%-*s", tb->width[j], str); else if (style == STYLE_BERKELEY && j == tb->col - 1) printf("%s", str); else printf("%*s", tb->width[j], str); if (j == tb->col -1) putchar('\n'); else printf(" "); } } for (i = 0; i < tb->col; i++) { for (j = 0; j < tb->row; j++) { if (tb->tbl[i][j]) free(tb->tbl[i][j]); } free(tb->tbl[i]); } free(tb->tbl); free(tb->width); free(tb); tb = NULL; } #define USAGE_MESSAGE "\ Usage: %s [options] file ...\n\ Display sizes of ELF sections.\n\n\ Options:\n\ --format=format Display output in specified format. Supported\n\ values are `berkeley' and `sysv'.\n\ --help Display this help message and exit.\n\ --radix=radix Display numeric values in the specified radix.\n\ Supported values are: 8, 10 and 16.\n\ --totals Show cumulative totals of section sizes.\n\ --version Display a version identifier and exit.\n\ -A Equivalent to `--format=sysv'.\n\ -B Equivalent to `--format=berkeley'.\n\ -V Equivalent to `--version'.\n\ -d Equivalent to `--radix=10'.\n\ -h Same as option --help.\n\ -o Equivalent to `--radix=8'.\n\ -t Equivalent to option --totals.\n\ -x Equivalent to `--radix=16'.\n" static void usage(void) { (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); exit(EXIT_FAILURE); } static void show_version(void) { (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); exit(EXIT_SUCCESS); } Index: projects/clang350-import/contrib/elftoolchain/strings/strings.c =================================================================== --- projects/clang350-import/contrib/elftoolchain/strings/strings.c (revision 275386) +++ projects/clang350-import/contrib/elftoolchain/strings/strings.c (revision 275387) @@ -1,454 +1,454 @@ /*- * Copyright (c) 2007 S.Sam Arun Raj * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "_elftc.h" ELFTC_VCSID("$Id: strings.c 2351 2011-12-19 11:20:37Z jkoshy $"); enum return_code { RETURN_OK, RETURN_NOINPUT, RETURN_SOFTWARE }; enum radix_style { RADIX_DECIMAL, RADIX_HEX, RADIX_OCTAL }; enum encoding_style { ENCODING_7BIT, ENCODING_8BIT, ENCODING_16BIT_BIG, ENCODING_16BIT_LITTLE, ENCODING_32BIT_BIG, ENCODING_32BIT_LITTLE }; #define PRINTABLE(c) \ ((c) >= 0 && (c) <= 255 && \ ((c) == '\t' || isprint((c)) || \ (encoding == ENCODING_8BIT && (c) > 127))) -int encoding_size, entire_file, min_len, show_filename, show_loc; -enum encoding_style encoding; -enum radix_style radix; +static int encoding_size, entire_file, min_len, show_filename, show_loc; +static enum encoding_style encoding; +static enum radix_style radix; static struct option strings_longopts[] = { { "all", no_argument, NULL, 'a'}, { "bytes", required_argument, NULL, 'n'}, { "encoding", required_argument, NULL, 'e'}, { "help", no_argument, NULL, 'h'}, { "print-file-name", no_argument, NULL, 'f'}, { "radix", required_argument, NULL, 't'}, { "version", no_argument, NULL, 'v'}, { NULL, 0, NULL, 0 } }; long getcharacter(void); int handle_file(const char *); int handle_elf(const char *, int); int handle_binary(const char *, int); int find_strings(const char *, off_t, off_t); void show_version(void); void usage(void); /* * strings(1) extracts text(contiguous printable characters) * from elf and binary files. */ int main(int argc, char **argv) { int ch, rc; rc = RETURN_OK; min_len = 0; encoding_size = 1; if (elf_version(EV_CURRENT) == EV_NONE) errx(EXIT_FAILURE, "ELF library initialization failed: %s", elf_errmsg(-1)); while ((ch = getopt_long(argc, argv, "1234567890ae:fhn:ot:Vv", strings_longopts, NULL)) != -1) switch((char)ch) { case 'a': entire_file = 1; break; case 'e': if (*optarg == 's') { encoding = ENCODING_7BIT; } else if (*optarg == 'S') { encoding = ENCODING_8BIT; } else if (*optarg == 'b') { encoding = ENCODING_16BIT_BIG; encoding_size = 2; } else if (*optarg == 'B') { encoding = ENCODING_32BIT_BIG; encoding_size = 4; } else if (*optarg == 'l') { encoding = ENCODING_16BIT_LITTLE; encoding_size = 2; } else if (*optarg == 'L') { encoding = ENCODING_32BIT_LITTLE; encoding_size = 4; } else usage(); /* NOTREACHED */ break; case 'f': show_filename = 1; break; case 'n': min_len = (int)strtoimax(optarg, (char**)NULL, 10); break; case 'o': show_loc = 1; radix = RADIX_OCTAL; break; case 't': show_loc = 1; if (*optarg == 'd') radix = RADIX_DECIMAL; else if (*optarg == 'o') radix = RADIX_OCTAL; else if (*optarg == 'x') radix = RADIX_HEX; else usage(); /* NOTREACHED */ break; case 'v': case 'V': show_version(); /* NOTREACHED */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': min_len *= 10; min_len += ch - '0'; break; case 'h': case '?': default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; if (!min_len) min_len = 4; if (!*argv) rc = handle_file("{standard input}"); else while (*argv) { rc = handle_file(*argv); argv++; } return (rc); } int handle_file(const char *name) { int fd, rt; if (name == NULL) return (RETURN_NOINPUT); if (strcmp("{standard input}", name) != 0) { if (freopen(name, "rb", stdin) == NULL) { warnx("'%s': %s", name, strerror(errno)); return (RETURN_NOINPUT); } } else { return (find_strings(name, (off_t)0, (off_t)0)); } fd = fileno(stdin); if (fd < 0) return (RETURN_NOINPUT); rt = handle_elf(name, fd); return (rt); } /* * Files not understood by handle_elf, will be passed off here and will * treated as a binary file. This would include text file, core dumps ... */ int handle_binary(const char *name, int fd) { struct stat buf; memset(&buf, 0, sizeof(struct stat)); (void) lseek(fd, (off_t)0, SEEK_SET); if (!fstat(fd, &buf)) return (find_strings(name, (off_t)0, buf.st_size)); return (RETURN_SOFTWARE); } /* * Will analyse a file to see if it ELF, other files including ar(1), * core dumps are passed off and treated as flat binary files. Unlike * GNU size in FreeBSD this routine will not treat ELF object from * different archs as flat binary files(has to overridden using -a). */ int handle_elf(const char *name, int fd) { GElf_Ehdr elfhdr; GElf_Shdr shdr; Elf *elf; Elf_Scn *scn; int rc; rc = RETURN_OK; /* If entire file is choosen, treat it as a binary file */ if (entire_file) return (handle_binary(name, fd)); (void) lseek(fd, (off_t)0, SEEK_SET); elf = elf_begin(fd, ELF_C_READ, NULL); if (elf_kind(elf) != ELF_K_ELF) { (void) elf_end(elf); return (handle_binary(name, fd)); } if (gelf_getehdr(elf, &elfhdr) == NULL) { (void) elf_end(elf); warnx("%s: ELF file could not be processed", name); return (RETURN_SOFTWARE); } if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) { (void) elf_end(elf); return (handle_binary(name, fd)); } else { scn = NULL; while ((scn = elf_nextscn(elf, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) == NULL) continue; if (shdr.sh_type != SHT_NOBITS && (shdr.sh_flags & SHF_ALLOC) != 0) { rc = find_strings(name, shdr.sh_offset, shdr.sh_size); } } } (void) elf_end(elf); return (rc); } /* * Retrieves a character from input stream based on the encoding * type requested. */ long getcharacter(void) { long rt; int i; char buf[4], c; rt = EOF; for(i = 0; i < encoding_size; i++) { c = getc(stdin); if (feof(stdin)) return (EOF); buf[i] = c; } switch(encoding) { case ENCODING_7BIT: case ENCODING_8BIT: rt = buf[0]; break; case ENCODING_16BIT_BIG: rt = (buf[0] << 8) | buf[1]; break; case ENCODING_16BIT_LITTLE: rt = buf[0] | (buf[1] << 8); break; case ENCODING_32BIT_BIG: rt = ((long) buf[0] << 24) | ((long) buf[1] << 16) | ((long) buf[2] << 8) | buf[3]; break; case ENCODING_32BIT_LITTLE: rt = buf[0] | ((long) buf[1] << 8) | ((long) buf[2] << 16) | ((long) buf[3] << 24); break; } return (rt); } /* * Input stream stdin is read until the end of file is reached or until * the section size is reached in case of ELF files. Contiguous * characters of >= min_size(default 4) will be displayed. */ int find_strings(const char *name, off_t offset, off_t size) { off_t cur_off, start_off; char *obuf; long c; int i; if ((obuf = (char*)calloc(1, min_len + 1)) == NULL) { (void) fprintf(stderr, "Unable to allocate memory: %s\n", strerror(errno)); return (RETURN_SOFTWARE); } (void) fseeko(stdin, offset, SEEK_SET); cur_off = offset; start_off = 0; while(1) { if ((offset + size) && (cur_off >= offset + size)) break; start_off = cur_off; memset(obuf, 0, min_len+1); for(i = 0; i < min_len; i++) { c = getcharacter(); if (c == EOF && feof(stdin)) goto _exit1; if (PRINTABLE(c)) { obuf[i] = c; obuf[i+1] = 0; cur_off += encoding_size; } else { if (encoding == ENCODING_8BIT && (uint8_t)c > 127) { obuf[i] = c; obuf[i+1] = 0; cur_off += encoding_size; continue; } cur_off += encoding_size; break; } } if (i >= min_len && ((cur_off <= offset + size) || !(offset + size))) { if (show_filename) printf ("%s: ", name); if (show_loc) { switch(radix) { case RADIX_DECIMAL: (void) printf("%7ju ", (uintmax_t)start_off); break; case RADIX_HEX: (void) printf("%7jx ", (uintmax_t)start_off); break; case RADIX_OCTAL: (void) printf("%7jo ", (uintmax_t)start_off); break; } } printf("%s", obuf); while(1) { if ((offset + size) && (cur_off >= offset + size)) break; c = getcharacter(); cur_off += encoding_size; if (encoding == ENCODING_8BIT && (uint8_t)c > 127) { putchar(c); continue; } if (!PRINTABLE(c) || c == EOF) break; putchar(c); } putchar('\n'); } } _exit1: free(obuf); return (RETURN_OK); } #define USAGE_MESSAGE "\ Usage: %s [options] [file...]\n\ Print contiguous sequences of printable characters.\n\n\ Options:\n\ -a | --all Scan the entire file for strings.\n\ -e ENC | --encoding=ENC Select the character encoding to use.\n\ -f | --print-file-name Print the file name before each string.\n\ -h | --help Print a help message and exit.\n\ -n N | --bytes=N | -N Print sequences with 'N' or more characters.\n\ -o Print offsets in octal.\n\ -t R | --radix=R Print offsets using the radix named by 'R'.\n\ -v | --version Print a version identifier and exit.\n" void usage(void) { (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); exit(EXIT_FAILURE); } void show_version(void) { (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); exit(EXIT_SUCCESS); } Index: projects/clang350-import/contrib/gcc/config/arm/libunwind.S =================================================================== --- projects/clang350-import/contrib/gcc/config/arm/libunwind.S (revision 275386) +++ projects/clang350-import/contrib/gcc/config/arm/libunwind.S (revision 275387) @@ -1,121 +1,136 @@ /* Support functions for the unwinder. Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. Contributed by Paul Brook This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. In addition to the permissions in the GNU General Public License, the Free Software Foundation gives you unlimited permission to link the compiled version of this file into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combine executable.) This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + +/* Allow the use of VFP instructions */ +#if __ARM_ARCH >= 7 +.fpu vfp +#endif + #ifndef __symbian__ #include "lib1funcs.asm" .macro UNPREFIX name .global SYM (\name) EQUIV SYM (\name), SYM (__\name) .endm /* r0 points to a 16-word block. Upload these values to the actual core state. */ ARM_FUNC_START restore_core_regs /* We must use sp as the base register when restoring sp. Push the last 3 registers onto the top of the current stack to achieve this. */ add r1, r0, #52 ldmia r1, {r3, r4, r5} /* {sp, lr, pc}. */ #ifdef __INTERWORKING__ /* Restore pc into ip. */ mov r2, r5 stmfd sp!, {r2, r3, r4} #else stmfd sp!, {r3, r4, r5} #endif /* Don't bother restoring ip. */ ldmia r0, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp} /* Pop the three registers we pushed earlier. */ #ifdef __INTERWORKING__ ldmfd sp, {ip, sp, lr} bx ip #else ldmfd sp, {sp, lr, pc} #endif FUNC_END restore_core_regs UNPREFIX restore_core_regs /* Load VFP registers d0-d15 from the address in r0. */ ARM_FUNC_START gnu_Unwind_Restore_VFP /* Use the generic coprocessor form so that gas doesn't complain on soft-float targets. */ +#if __ARM_ARCH >= 7 + fldmiax r0, {d0-d15} +#else ldc p11,cr0,[r0],{0x21} /* fldmiax r0, {d0-d15} */ +#endif RET /* Store VFR regsters d0-d15 to the address in r0. */ ARM_FUNC_START gnu_Unwind_Save_VFP /* Use the generic coprocessor form so that gas doesn't complain on soft-float targets. */ +#if __ARM_ARCH >= 7 + fstmiax r0, {d0-d15} +#else stc p11,cr0,[r0],{0x21} /* fstmiax r0, {d0-d15} */ +#endif RET /* Wrappers to save core registers, then call the real routine. */ .macro UNWIND_WRAPPER name nargs ARM_FUNC_START \name /* Create a phase2_vrs structure. */ /* Split reg push in two to ensure the correct value for sp. */ stmfd sp!, {sp, lr, pc} stmfd sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip} /* Demand-save flags, plus an extra word for alignment. */ mov r3, #0 stmfd sp!, {r2, r3} /* Point r1 at the block. Pass r[0..nargs) unchanged. */ add r\nargs, sp, #4 #if defined(__thumb__) /* Switch back to thumb mode to avoid interworking hassle. */ adr ip, .L1_\name orr ip, ip, #1 bx ip .thumb .L1_\name: bl SYM (__gnu\name) __PLT__ ldr r3, [sp, #64] add sp, #72 bx r3 #else bl SYM (__gnu\name) __PLT__ ldr lr, [sp, #64] add sp, sp, #72 RET #endif FUNC_END \name UNPREFIX \name .endm UNWIND_WRAPPER _Unwind_RaiseException 1 UNWIND_WRAPPER _Unwind_Resume 1 UNWIND_WRAPPER _Unwind_Resume_or_Rethrow 1 UNWIND_WRAPPER _Unwind_ForcedUnwind 3 UNWIND_WRAPPER _Unwind_Backtrace 2 #endif /* ndef __symbian__ */ Index: projects/clang350-import/contrib/gcc =================================================================== --- projects/clang350-import/contrib/gcc (revision 275386) +++ projects/clang350-import/contrib/gcc (revision 275387) Property changes on: projects/clang350-import/contrib/gcc ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/gcc:r275364-275386 Index: projects/clang350-import/contrib/subversion/subversion/svn/util.c =================================================================== --- projects/clang350-import/contrib/subversion/subversion/svn/util.c (revision 275386) +++ projects/clang350-import/contrib/subversion/subversion/svn/util.c (revision 275387) @@ -1,1168 +1,1174 @@ /* * util.c: Subversion command line client utility functions. Any * functions that need to be shared across subcommands should be put * in here. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== */ /* ==================================================================== */ /*** Includes. ***/ #include #include #include #include #include #include #include #include #include #include #include "svn_pools.h" #include "svn_error.h" #include "svn_ctype.h" #include "svn_client.h" #include "svn_cmdline.h" #include "svn_string.h" #include "svn_dirent_uri.h" #include "svn_path.h" #include "svn_hash.h" #include "svn_io.h" #include "svn_utf.h" #include "svn_subst.h" #include "svn_config.h" #include "svn_wc.h" #include "svn_xml.h" #include "svn_time.h" #include "svn_props.h" #include "svn_private_config.h" #include "cl.h" #include "private/svn_token.h" #include "private/svn_opt_private.h" #include "private/svn_client_private.h" #include "private/svn_cmdline_private.h" #include "private/svn_string_private.h" #ifdef HAS_ORGANIZATION_NAME #include "freebsd-organization.h" #endif svn_error_t * svn_cl__print_commit_info(const svn_commit_info_t *commit_info, void *baton, apr_pool_t *pool) { if (SVN_IS_VALID_REVNUM(commit_info->revision)) SVN_ERR(svn_cmdline_printf(pool, _("\nCommitted revision %ld%s.\n"), commit_info->revision, commit_info->revision == 42 && getenv("SVN_I_LOVE_PANGALACTIC_GARGLE_BLASTERS") ? _(" (the answer to life, the universe, " "and everything)") : "")); /* Writing to stdout, as there maybe systems that consider the * presence of stderr as an indication of commit failure. * OTOH, this is only of informational nature to the user as * the commit has succeeded. */ if (commit_info->post_commit_err) SVN_ERR(svn_cmdline_printf(pool, _("\nWarning: %s\n"), commit_info->post_commit_err)); return SVN_NO_ERROR; } svn_error_t * svn_cl__merge_file_externally(const char *base_path, const char *their_path, const char *my_path, const char *merged_path, const char *wc_path, apr_hash_t *config, svn_boolean_t *remains_in_conflict, apr_pool_t *pool) { char *merge_tool; /* Error if there is no editor specified */ if (apr_env_get(&merge_tool, "SVN_MERGE", pool) != APR_SUCCESS) { struct svn_config_t *cfg; merge_tool = NULL; cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; /* apr_env_get wants char **, this wants const char ** */ svn_config_get(cfg, (const char **)&merge_tool, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_MERGE_TOOL_CMD, NULL); } if (merge_tool) { const char *c; for (c = merge_tool; *c; c++) if (!svn_ctype_isspace(*c)) break; if (! *c) return svn_error_create (SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL, _("The SVN_MERGE environment variable is empty or " "consists solely of whitespace. Expected a shell command.\n")); } else return svn_error_create (SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL, _("The environment variable SVN_MERGE and the merge-tool-cmd run-time " "configuration option were not set.\n")); { const char *arguments[7] = { 0 }; char *cwd; int exitcode; apr_status_t status = apr_filepath_get(&cwd, APR_FILEPATH_NATIVE, pool); if (status != 0) return svn_error_wrap_apr(status, NULL); arguments[0] = merge_tool; arguments[1] = base_path; arguments[2] = their_path; arguments[3] = my_path; arguments[4] = merged_path; arguments[5] = wc_path; arguments[6] = NULL; SVN_ERR(svn_io_run_cmd(svn_dirent_internal_style(cwd, pool), merge_tool, arguments, &exitcode, NULL, TRUE, NULL, NULL, NULL, pool)); /* Exit code 0 means the merge was successful. * Exit code 1 means the file was left in conflict but it * is OK to continue with the merge. * Any other exit code means there was a real problem. */ if (exitcode != 0 && exitcode != 1) return svn_error_createf (SVN_ERR_EXTERNAL_PROGRAM, NULL, _("The external merge tool exited with exit code %d"), exitcode); else if (remains_in_conflict) *remains_in_conflict = exitcode == 1; } return SVN_NO_ERROR; } /* A svn_client_ctx_t's log_msg_baton3, for use with svn_cl__make_log_msg_baton(). */ struct log_msg_baton { const char *editor_cmd; /* editor specified via --editor-cmd, else NULL */ const char *message; /* the message. */ const char *message_encoding; /* the locale/encoding of the message. */ const char *base_dir; /* the base directory for an external edit. UTF-8! */ const char *tmpfile_left; /* the tmpfile left by an external edit. UTF-8! */ svn_boolean_t non_interactive; /* if true, don't pop up an editor */ apr_hash_t *config; /* client configuration hash */ svn_boolean_t keep_locks; /* Keep repository locks? */ apr_pool_t *pool; /* a pool. */ }; svn_error_t * svn_cl__make_log_msg_baton(void **baton, svn_cl__opt_state_t *opt_state, const char *base_dir /* UTF-8! */, apr_hash_t *config, apr_pool_t *pool) { struct log_msg_baton *lmb = apr_palloc(pool, sizeof(*lmb)); if (opt_state->filedata) { if (strlen(opt_state->filedata->data) < opt_state->filedata->len) { /* The data contains a zero byte, and therefore can't be represented as a C string. Punt now; it's probably not a deliberate encoding, and even if it is, we still can't handle it. */ return svn_error_create(SVN_ERR_CL_BAD_LOG_MESSAGE, NULL, _("Log message contains a zero byte")); } lmb->message = opt_state->filedata->data; } else { lmb->message = opt_state->message; } lmb->editor_cmd = opt_state->editor_cmd; if (opt_state->encoding) { lmb->message_encoding = opt_state->encoding; } else if (config) { svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG); svn_config_get(cfg, &(lmb->message_encoding), SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_LOG_ENCODING, NULL); } lmb->base_dir = base_dir ? base_dir : ""; lmb->tmpfile_left = NULL; lmb->config = config; lmb->keep_locks = opt_state->no_unlock; lmb->non_interactive = opt_state->non_interactive; lmb->pool = pool; *baton = lmb; return SVN_NO_ERROR; } svn_error_t * svn_cl__cleanup_log_msg(void *log_msg_baton, svn_error_t *commit_err, apr_pool_t *pool) { struct log_msg_baton *lmb = log_msg_baton; svn_error_t *err; /* If there was no tmpfile left, or there is no log message baton, return COMMIT_ERR. */ if ((! lmb) || (! lmb->tmpfile_left)) return commit_err; /* If there was no commit error, cleanup the tmpfile and return. */ if (! commit_err) return svn_io_remove_file2(lmb->tmpfile_left, FALSE, lmb->pool); /* There was a commit error; there is a tmpfile. Leave the tmpfile around, and add message about its presence to the commit error chain. Then return COMMIT_ERR. If the conversion from UTF-8 to native encoding fails, we have to compose that error with the commit error chain, too. */ err = svn_error_createf(commit_err->apr_err, NULL, _(" '%s'"), svn_dirent_local_style(lmb->tmpfile_left, pool)); svn_error_compose(commit_err, svn_error_create(commit_err->apr_err, err, _("Your commit message was left in " "a temporary file:"))); return commit_err; } /* Remove line-starting PREFIX and everything after it from BUFFER. If NEW_LEN is non-NULL, return the new length of BUFFER in *NEW_LEN. */ static void truncate_buffer_at_prefix(apr_size_t *new_len, char *buffer, const char *prefix) { char *substring = buffer; assert(buffer && prefix); /* Initialize *NEW_LEN. */ if (new_len) *new_len = strlen(buffer); while (1) { /* Find PREFIX in BUFFER. */ substring = strstr(substring, prefix); if (! substring) return; /* We found PREFIX. Is it really a PREFIX? Well, if it's the first thing in the file, or if the character before it is a line-terminator character, it sure is. */ if ((substring == buffer) || (*(substring - 1) == '\r') || (*(substring - 1) == '\n')) { *substring = '\0'; if (new_len) *new_len = substring - buffer; } else if (substring) { /* Well, it wasn't really a prefix, so just advance by 1 character and continue. */ substring++; } } /* NOTREACHED */ } /* * Since we're adding freebsd-specific tokens to the log message, * clean out any leftovers to avoid accidently sending them to other * projects that won't be expecting them. */ static const char *prefixes[] = { "PR:", + "Differential Revision:", "Submitted by:", "Reviewed by:", "Approved by:", "Obtained from:", "MFC after:", + "MFH:", "Relnotes:", "Security:", "Sponsored by:" }; void cleanmsg(apr_size_t *l, char *s) { int i; char *pos; char *kw; char *p; int empty; for (i = 0; i < sizeof(prefixes) / sizeof(prefixes[0]); i++) { pos = s; while ((kw = strstr(pos, prefixes[i])) != NULL) { /* Check to see if keyword is at start of line (or buffer) */ if (!(kw == s || kw[-1] == '\r' || kw[-1] == '\n')) { pos = kw + 1; continue; } p = kw + strlen(prefixes[i]); empty = 1; while (1) { if (*p == ' ' || *p == '\t') { p++; continue; } if (*p == '\0' || *p == '\r' || *p == '\n') break; empty = 0; break; } if (empty && (*p == '\r' || *p == '\n')) { memmove(kw, p + 1, strlen(p + 1) + 1); if (l) *l -= (p + 1 - kw); } else if (empty) { *kw = '\0'; if (l) *l -= (p - kw); } else { pos = p; } } } } #define EDITOR_EOF_PREFIX _("--This line, and those below, will be ignored--") svn_error_t * svn_cl__get_log_message(const char **log_msg, const char **tmp_file, const apr_array_header_t *commit_items, void *baton, apr_pool_t *pool) { svn_stringbuf_t *default_msg = NULL; struct log_msg_baton *lmb = baton; svn_stringbuf_t *message = NULL; /* Set default message. */ default_msg = svn_stringbuf_create(APR_EOL_STR, pool); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "PR:\t\t" APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "Differential Revision:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Submitted by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Reviewed by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Approved by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Obtained from:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "MFC after:\t" APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "MFH:\t\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Relnotes:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Security:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Sponsored by:\t" #ifdef HAS_ORGANIZATION_NAME ORGANIZATION_NAME #endif APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, EDITOR_EOF_PREFIX); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Description of fields to fill in above: 76 columns --|" APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> PR: If a Bugzilla PR is affected by the change." APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> Submitted by: If someone else sent in the change." APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> Reviewed by: If someone else reviewed your modification." APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> Approved by: If you needed approval for this commit." APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> Obtained from: If the change is from a third party." APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> MFC after: N [day[s]|week[s]|month[s]]. Request a reminder email." APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> Relnotes: Set to 'yes' for mention in release notes." APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> Security: Vulnerability reference (one per line) or description." APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> Sponsored by: If the change was sponsored by an organization." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> PR: If a Bugzilla PR is affected by the change." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> Differential Revision: https://reviews.freebsd.org/D### (*full* phabric URL needed)." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> Submitted by: If someone else sent in the change." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> Reviewed by: If someone else reviewed your modification." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> Approved by: If you needed approval for this commit." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> Obtained from: If the change is from a third party." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> MFC after: N [day[s]|week[s]|month[s]]. Request a reminder email." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> MFH: Ports tree branch name. Request approval for merge." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> Relnotes: Set to 'yes' for mention in release notes." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> Security: Vulnerability reference (one per line) or description." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> Sponsored by: If the change was sponsored by an organization." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Empty fields above will be automatically removed." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); *tmp_file = NULL; if (lmb->message) { svn_stringbuf_t *log_msg_buf = svn_stringbuf_create(lmb->message, pool); svn_string_t *log_msg_str = apr_pcalloc(pool, sizeof(*log_msg_str)); /* Trim incoming messages of the EOF marker text and the junk that follows it. */ truncate_buffer_at_prefix(&(log_msg_buf->len), log_msg_buf->data, EDITOR_EOF_PREFIX); cleanmsg(NULL, (char*)log_msg_buf->data); /* Make a string from a stringbuf, sharing the data allocation. */ log_msg_str->data = log_msg_buf->data; log_msg_str->len = log_msg_buf->len; SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, FALSE, FALSE, log_msg_str, lmb->message_encoding, FALSE, pool, pool), _("Error normalizing log message to internal format")); *log_msg = log_msg_str->data; return SVN_NO_ERROR; } if (! commit_items->nelts) { *log_msg = ""; return SVN_NO_ERROR; } while (! message) { /* We still don't have a valid commit message. Use $EDITOR to get one. Note that svn_cl__edit_string_externally will still return a UTF-8'ized log message. */ int i; svn_stringbuf_t *tmp_message = svn_stringbuf_dup(default_msg, pool); svn_error_t *err = SVN_NO_ERROR; svn_string_t *msg_string = svn_string_create_empty(pool); for (i = 0; i < commit_items->nelts; i++) { svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *); const char *path = item->path; char text_mod = '_', prop_mod = ' ', unlock = ' '; if (! path) path = item->url; else if (! *path) path = "."; if (! svn_path_is_url(path) && lmb->base_dir) path = svn_dirent_is_child(lmb->base_dir, path, pool); /* If still no path, then just use current directory. */ if (! path) path = "."; if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)) text_mod = 'R'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) text_mod = 'A'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) text_mod = 'D'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) text_mod = 'M'; if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) prop_mod = 'M'; if (! lmb->keep_locks && item->state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN) unlock = 'U'; svn_stringbuf_appendbyte(tmp_message, text_mod); svn_stringbuf_appendbyte(tmp_message, prop_mod); svn_stringbuf_appendbyte(tmp_message, unlock); if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) /* History included via copy/move. */ svn_stringbuf_appendcstr(tmp_message, "+ "); else svn_stringbuf_appendcstr(tmp_message, " "); svn_stringbuf_appendcstr(tmp_message, path); svn_stringbuf_appendcstr(tmp_message, APR_EOL_STR); } msg_string->data = tmp_message->data; msg_string->len = tmp_message->len; /* Use the external edit to get a log message. */ if (! lmb->non_interactive) { err = svn_cmdline__edit_string_externally(&msg_string, &lmb->tmpfile_left, lmb->editor_cmd, lmb->base_dir, msg_string, "svn-commit", lmb->config, TRUE, lmb->message_encoding, pool); } else /* non_interactive flag says we can't pop up an editor, so error */ { return svn_error_create (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, _("Cannot invoke editor to get log message " "when non-interactive")); } /* Dup the tmpfile path into its baton's pool. */ *tmp_file = lmb->tmpfile_left = apr_pstrdup(lmb->pool, lmb->tmpfile_left); /* If the edit returned an error, handle it. */ if (err) { if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR) err = svn_error_quick_wrap (err, _("Could not use external editor to fetch log message; " "consider setting the $SVN_EDITOR environment variable " "or using the --message (-m) or --file (-F) options")); return svn_error_trace(err); } if (msg_string) message = svn_stringbuf_create_from_string(msg_string, pool); /* Strip the prefix from the buffer. */ if (message) truncate_buffer_at_prefix(&message->len, message->data, EDITOR_EOF_PREFIX); /* * Since we're adding freebsd-specific tokens to the log message, * clean out any leftovers to avoid accidently sending them to other * projects that won't be expecting them. */ if (message) cleanmsg(&message->len, message->data); if (message) { /* We did get message, now check if it is anything more than just white space as we will consider white space only as empty */ apr_size_t len; for (len = 0; len < message->len; len++) { /* FIXME: should really use an UTF-8 whitespace test rather than svn_ctype_isspace, which is ASCII only */ if (! svn_ctype_isspace(message->data[len])) break; } if (len == message->len) message = NULL; } if (! message) { const char *reply; SVN_ERR(svn_cmdline_prompt_user2 (&reply, _("\nLog message unchanged or not specified\n" "(a)bort, (c)ontinue, (e)dit:\n"), NULL, pool)); if (reply) { int letter = apr_tolower(reply[0]); /* If the user chooses to abort, we cleanup the temporary file and exit the loop with a NULL message. */ if ('a' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; break; } /* If the user chooses to continue, we make an empty message, which will cause us to exit the loop. We also cleanup the temporary file. */ if ('c' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; message = svn_stringbuf_create_empty(pool); } /* If the user chooses anything else, the loop will continue on the NULL message. */ } } } *log_msg = message ? message->data : NULL; return SVN_NO_ERROR; } /* ### The way our error wrapping currently works, the error returned * from here will look as though it originates in this source file, * instead of in the caller's source file. This can be a bit * misleading, until one starts debugging. Ideally, there'd be a way * to wrap an error while preserving its FILE/LINE info. */ svn_error_t * svn_cl__may_need_force(svn_error_t *err) { if (err && (err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE || err->apr_err == SVN_ERR_CLIENT_MODIFIED)) { /* Should this svn_error_compose a new error number? Probably not, the error hasn't changed. */ err = svn_error_quick_wrap (err, _("Use --force to override this restriction (local modifications " "may be lost)")); } return svn_error_trace(err); } svn_error_t * svn_cl__error_checked_fputs(const char *string, FILE* stream) { /* On POSIX systems, errno will be set on an error in fputs, but this might not be the case on other platforms. We reset errno and only use it if it was set by the below fputs call. Else, we just return a generic error. */ errno = 0; if (fputs(string, stream) == EOF) { if (errno) return svn_error_wrap_apr(errno, _("Write error")); else return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL); } return SVN_NO_ERROR; } svn_error_t * svn_cl__try(svn_error_t *err, apr_array_header_t *errors_seen, svn_boolean_t quiet, ...) { if (err) { apr_status_t apr_err; va_list ap; va_start(ap, quiet); while ((apr_err = va_arg(ap, apr_status_t)) != APR_SUCCESS) { if (errors_seen) { int i; svn_boolean_t add = TRUE; /* Don't report duplicate error codes. */ for (i = 0; i < errors_seen->nelts; i++) { if (APR_ARRAY_IDX(errors_seen, i, apr_status_t) == err->apr_err) { add = FALSE; break; } } if (add) APR_ARRAY_PUSH(errors_seen, apr_status_t) = err->apr_err; } if (err->apr_err == apr_err) { if (! quiet) svn_handle_warning2(stderr, err, "svn: "); svn_error_clear(err); return SVN_NO_ERROR; } } va_end(ap); } return svn_error_trace(err); } void svn_cl__xml_tagged_cdata(svn_stringbuf_t **sb, apr_pool_t *pool, const char *tagname, const char *string) { if (string) { svn_xml_make_open_tag(sb, pool, svn_xml_protect_pcdata, tagname, NULL); svn_xml_escape_cdata_cstring(sb, string, pool); svn_xml_make_close_tag(sb, pool, tagname); } } void svn_cl__print_xml_commit(svn_stringbuf_t **sb, svn_revnum_t revision, const char *author, const char *date, apr_pool_t *pool) { /* "" */ svn_xml_make_open_tag(sb, pool, svn_xml_normal, "commit", "revision", apr_psprintf(pool, "%ld", revision), NULL); /* "xx" */ if (author) svn_cl__xml_tagged_cdata(sb, pool, "author", author); /* "xx" */ if (date) svn_cl__xml_tagged_cdata(sb, pool, "date", date); /* "" */ svn_xml_make_close_tag(sb, pool, "commit"); } void svn_cl__print_xml_lock(svn_stringbuf_t **sb, const svn_lock_t *lock, apr_pool_t *pool) { /* "" */ svn_xml_make_open_tag(sb, pool, svn_xml_normal, "lock", NULL); /* "xx" */ svn_cl__xml_tagged_cdata(sb, pool, "token", lock->token); /* "xx" */ svn_cl__xml_tagged_cdata(sb, pool, "owner", lock->owner); /* "xx" */ svn_cl__xml_tagged_cdata(sb, pool, "comment", lock->comment); /* "xx" */ svn_cl__xml_tagged_cdata(sb, pool, "created", svn_time_to_cstring(lock->creation_date, pool)); /* "xx" */ if (lock->expiration_date != 0) svn_cl__xml_tagged_cdata(sb, pool, "expires", svn_time_to_cstring(lock->expiration_date, pool)); /* "" */ svn_xml_make_close_tag(sb, pool, "lock"); } svn_error_t * svn_cl__xml_print_header(const char *tagname, apr_pool_t *pool) { svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); /* */ svn_xml_make_header2(&sb, "UTF-8", pool); /* "" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, tagname, NULL); return svn_cl__error_checked_fputs(sb->data, stdout); } svn_error_t * svn_cl__xml_print_footer(const char *tagname, apr_pool_t *pool) { svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); /* "" */ svn_xml_make_close_tag(&sb, pool, tagname); return svn_cl__error_checked_fputs(sb->data, stdout); } /* A map for svn_node_kind_t values to XML strings */ static const svn_token_map_t map_node_kind_xml[] = { { "none", svn_node_none }, { "file", svn_node_file }, { "dir", svn_node_dir }, { "", svn_node_unknown }, { NULL, 0 } }; /* A map for svn_node_kind_t values to human-readable strings */ static const svn_token_map_t map_node_kind_human[] = { { N_("none"), svn_node_none }, { N_("file"), svn_node_file }, { N_("dir"), svn_node_dir }, { "", svn_node_unknown }, { NULL, 0 } }; const char * svn_cl__node_kind_str_xml(svn_node_kind_t kind) { return svn_token__to_word(map_node_kind_xml, kind); } const char * svn_cl__node_kind_str_human_readable(svn_node_kind_t kind) { return _(svn_token__to_word(map_node_kind_human, kind)); } /* A map for svn_wc_operation_t values to XML strings */ static const svn_token_map_t map_wc_operation_xml[] = { { "none", svn_wc_operation_none }, { "update", svn_wc_operation_update }, { "switch", svn_wc_operation_switch }, { "merge", svn_wc_operation_merge }, { NULL, 0 } }; /* A map for svn_wc_operation_t values to human-readable strings */ static const svn_token_map_t map_wc_operation_human[] = { { N_("none"), svn_wc_operation_none }, { N_("update"), svn_wc_operation_update }, { N_("switch"), svn_wc_operation_switch }, { N_("merge"), svn_wc_operation_merge }, { NULL, 0 } }; const char * svn_cl__operation_str_xml(svn_wc_operation_t operation, apr_pool_t *pool) { return svn_token__to_word(map_wc_operation_xml, operation); } const char * svn_cl__operation_str_human_readable(svn_wc_operation_t operation, apr_pool_t *pool) { return _(svn_token__to_word(map_wc_operation_human, operation)); } svn_error_t * svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets, apr_getopt_t *os, const apr_array_header_t *known_targets, svn_client_ctx_t *ctx, svn_boolean_t keep_last_origpath_on_truepath_collision, apr_pool_t *pool) { svn_error_t *err = svn_client_args_to_target_array2(targets, os, known_targets, ctx, keep_last_origpath_on_truepath_collision, pool); if (err) { if (err->apr_err == SVN_ERR_RESERVED_FILENAME_SPECIFIED) { svn_handle_error2(err, stderr, FALSE, "svn: Skipping argument: "); svn_error_clear(err); } else return svn_error_trace(err); } return SVN_NO_ERROR; } /* Helper for svn_cl__get_changelist(); implements svn_changelist_receiver_t. */ static svn_error_t * changelist_receiver(void *baton, const char *path, const char *changelist, apr_pool_t *pool) { /* No need to check CHANGELIST; our caller only asked about one of them. */ apr_array_header_t *paths = baton; APR_ARRAY_PUSH(paths, const char *) = apr_pstrdup(paths->pool, path); return SVN_NO_ERROR; } svn_error_t * svn_cl__changelist_paths(apr_array_header_t **paths, const apr_array_header_t *changelists, const apr_array_header_t *targets, svn_depth_t depth, svn_client_ctx_t *ctx, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { apr_array_header_t *found; apr_hash_t *paths_hash; apr_pool_t *iterpool; int i; if (! (changelists && changelists->nelts)) { *paths = (apr_array_header_t *)targets; return SVN_NO_ERROR; } found = apr_array_make(scratch_pool, 8, sizeof(const char *)); iterpool = svn_pool_create(scratch_pool); for (i = 0; i < targets->nelts; i++) { const char *target = APR_ARRAY_IDX(targets, i, const char *); svn_pool_clear(iterpool); SVN_ERR(svn_client_get_changelists(target, changelists, depth, changelist_receiver, found, ctx, iterpool)); } svn_pool_destroy(iterpool); SVN_ERR(svn_hash_from_cstring_keys(&paths_hash, found, result_pool)); return svn_error_trace(svn_hash_keys(paths, paths_hash, result_pool)); } svn_cl__show_revs_t svn_cl__show_revs_from_word(const char *word) { if (strcmp(word, SVN_CL__SHOW_REVS_MERGED) == 0) return svn_cl__show_revs_merged; if (strcmp(word, SVN_CL__SHOW_REVS_ELIGIBLE) == 0) return svn_cl__show_revs_eligible; /* word is an invalid flavor. */ return svn_cl__show_revs_invalid; } svn_error_t * svn_cl__time_cstring_to_human_cstring(const char **human_cstring, const char *data, apr_pool_t *pool) { svn_error_t *err; apr_time_t when; err = svn_time_from_cstring(&when, data, pool); if (err && err->apr_err == SVN_ERR_BAD_DATE) { svn_error_clear(err); *human_cstring = _("(invalid date)"); return SVN_NO_ERROR; } else if (err) return svn_error_trace(err); *human_cstring = svn_time_to_human_cstring(when, pool); return SVN_NO_ERROR; } const char * svn_cl__node_description(const svn_wc_conflict_version_t *node, const char *wc_repos_root_URL, apr_pool_t *pool) { const char *root_str = "^"; const char *path_str = "..."; if (!node) /* Printing "(none)" the harder way to ensure conformity (mostly with * translations). */ return apr_psprintf(pool, "(%s)", svn_cl__node_kind_str_human_readable(svn_node_none)); /* Construct a "caret notation" ^/URL if NODE matches WC_REPOS_ROOT_URL. * Otherwise show the complete URL, and if we can't, show dots. */ if (node->repos_url && (wc_repos_root_URL == NULL || strcmp(node->repos_url, wc_repos_root_URL) != 0)) root_str = node->repos_url; if (node->path_in_repos) path_str = node->path_in_repos; return apr_psprintf(pool, "(%s) %s@%ld", svn_cl__node_kind_str_human_readable(node->node_kind), svn_path_url_add_component2(root_str, path_str, pool), node->peg_rev); } svn_error_t * svn_cl__eat_peg_revisions(apr_array_header_t **true_targets_p, const apr_array_header_t *targets, apr_pool_t *pool) { int i; apr_array_header_t *true_targets; true_targets = apr_array_make(pool, targets->nelts, sizeof(const char *)); for (i = 0; i < targets->nelts; i++) { const char *target = APR_ARRAY_IDX(targets, i, const char *); const char *true_target, *peg; SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg, target, pool)); if (peg[0] && peg[1]) return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, _("'%s': a peg revision is not allowed here"), target); APR_ARRAY_PUSH(true_targets, const char *) = true_target; } SVN_ERR_ASSERT(true_targets_p); *true_targets_p = true_targets; return SVN_NO_ERROR; } svn_error_t * svn_cl__assert_homogeneous_target_type(const apr_array_header_t *targets) { svn_error_t *err; err = svn_client__assert_homogeneous_target_type(targets); if (err && err->apr_err == SVN_ERR_ILLEGAL_TARGET) return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err, NULL); return err; } svn_error_t * svn_cl__check_target_is_local_path(const char *target) { if (svn_path_is_url(target)) return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("'%s' is not a local path"), target); return SVN_NO_ERROR; } svn_error_t * svn_cl__check_targets_are_local_paths(const apr_array_header_t *targets) { int i; for (i = 0; i < targets->nelts; i++) { const char *target = APR_ARRAY_IDX(targets, i, const char *); SVN_ERR(svn_cl__check_target_is_local_path(target)); } return SVN_NO_ERROR; } const char * svn_cl__local_style_skip_ancestor(const char *parent_path, const char *path, apr_pool_t *pool) { const char *relpath = NULL; if (parent_path) relpath = svn_dirent_skip_ancestor(parent_path, path); return svn_dirent_local_style(relpath ? relpath : path, pool); } svn_error_t * svn_cl__propset_print_binary_mime_type_warning(apr_array_header_t *targets, const char *propname, const svn_string_t *propval, apr_pool_t *scratch_pool) { if (strcmp(propname, SVN_PROP_MIME_TYPE) == 0) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); int i; for (i = 0; i < targets->nelts; i++) { const char *detected_mimetype; const char *target = APR_ARRAY_IDX(targets, i, const char *); const char *local_abspath; const svn_string_t *canon_propval; svn_node_kind_t node_kind; svn_pool_clear(iterpool); SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool)); SVN_ERR(svn_io_check_path(local_abspath, &node_kind, iterpool)); if (node_kind != svn_node_file) continue; SVN_ERR(svn_wc_canonicalize_svn_prop(&canon_propval, propname, propval, local_abspath, svn_node_file, FALSE, NULL, NULL, iterpool)); if (svn_mime_type_is_binary(canon_propval->data)) { SVN_ERR(svn_io_detect_mimetype2(&detected_mimetype, local_abspath, NULL, iterpool)); if (detected_mimetype == NULL || !svn_mime_type_is_binary(detected_mimetype)) svn_error_clear(svn_cmdline_fprintf(stderr, iterpool, _("svn: warning: '%s' is a binary mime-type but file '%s' " "looks like text; diff, merge, blame, and other " "operations will stop working on this file\n"), canon_propval->data, svn_dirent_local_style(local_abspath, iterpool))); } } svn_pool_destroy(iterpool); } return SVN_NO_ERROR; } Index: projects/clang350-import/contrib/subversion =================================================================== --- projects/clang350-import/contrib/subversion (revision 275386) +++ projects/clang350-import/contrib/subversion (revision 275387) Property changes on: projects/clang350-import/contrib/subversion ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/subversion:r274961,275076-275386 Index: projects/clang350-import/gnu/usr.bin/binutils/Makefile =================================================================== --- projects/clang350-import/gnu/usr.bin/binutils/Makefile (revision 275386) +++ projects/clang350-import/gnu/usr.bin/binutils/Makefile (revision 275387) @@ -1,19 +1,29 @@ # $FreeBSD$ +.include + SUBDIR= libiberty \ libbfd \ libopcodes \ libbinutils \ - addr2line \ + ${_addr2line} \ as \ ld \ - nm \ + ${_nm} \ objcopy \ objdump \ readelf \ - size \ - strings \ - strip \ + ${_size} \ + ${_strings} \ + ${_strip} \ doc + +.if ${MK_ELFTOOLCHAIN_TOOLS} == "no" +_addr2line= addr2line +_nm= nm +_size= size +_strings= strings +_strip= strip +.endif .include Index: projects/clang350-import/gnu/usr.bin/binutils =================================================================== --- projects/clang350-import/gnu/usr.bin/binutils (revision 275386) +++ projects/clang350-import/gnu/usr.bin/binutils (revision 275387) Property changes on: projects/clang350-import/gnu/usr.bin/binutils ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/gnu/usr.bin/binutils:r275210-275386 Index: projects/clang350-import/lib/Makefile =================================================================== --- projects/clang350-import/lib/Makefile (revision 275386) +++ projects/clang350-import/lib/Makefile (revision 275387) @@ -1,309 +1,314 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 # $FreeBSD$ .include # The SUBDIR_ORDERED list is a small set of libraries which are used by many # of the other libraries. These are built first with a .WAIT between them # and the main list to avoid needing a SUBDIR_DEPEND line on every library # naming just these few items. SUBDIR_ORDERED= ${_csu} \ .WAIT \ libc \ libc_nonshared \ libcompiler_rt \ ${_libcplusplus} \ ${_libcxxrt} \ libelf \ msun # The main list; please keep these sorted alphabetically. SUBDIR= ${SUBDIR_ORDERED} \ .WAIT \ libalias \ libarchive \ ${_libatm} \ libauditd \ libbegemot \ libblocksruntime \ ${_libbluetooth} \ ${_libbsnmp} \ libbsdstat \ libbsm \ libbz2 \ libcalendar \ libcam \ ${_libcapsicum} \ ${_libcasper} \ ${_libcom_err} \ libcompat \ libcrypt \ libdevinfo \ libdevstat \ libdpv \ libdwarf \ libedit \ + ${_libelftc} \ ${_libevent} \ libexecinfo \ libexpat \ libfetch \ libfigpar \ libgeom \ ${_libgpib} \ libgpio \ ${_libgssapi} \ ${_librpcsec_gss} \ ${_libiconv_modules} \ libipsec \ libjail \ libkiconv \ libkvm \ ${_libldns} \ liblzma \ libmagic \ libmandoc \ libmemstat \ libmd \ ${_libmilter} \ ${_libmp} \ ${_libnandfs} \ libnetbsd \ ${_libnetgraph} \ ${_libngatm} \ libnv \ libohash \ libopie \ libpam \ libpcap \ libpjdlog \ ${_libpmc} \ ${_libproc} \ libprocstat \ libradius \ librpcsvc \ librt \ ${_librtld_db} \ libsbuf \ ${_libsdp} \ ${_libsm} \ ${_libsmb} \ ${_libsmdb} \ ${_libsmutil} \ libsqlite3 \ libstand \ libstdbuf \ libstdthreads \ libtacplus \ ${_libtelnet} \ ${_libthr} \ libthread_db \ libucl \ libufs \ libugidfw \ libulog \ ${_libunbound} \ ${_libusbhid} \ ${_libusb} \ libutil \ ${_libvgl} \ ${_libvmmapi} \ libwrap \ libxo \ liby \ ${_libypclnt} \ libz \ ncurses \ ${_atf} \ ${_clang} \ ${_cuse} \ ${_tests} # Inter-library dependencies. When the makefile for a library contains LDADD # libraries, those libraries should be listed as build order dependencies here. SUBDIR_DEPEND_libarchive= libz libbz2 libexpat liblzma libmd SUBDIR_DEPEND_libatm= libmd SUBDIR_DEPEND_libauditdm= libbsm SUBDIR_DEPEND_libbsnmp= ${_libnetgraph} SUBDIR_DEPEND_libc++= libcxxrt SUBDIR_DEPEND_libc= libcompiler_rt SUBDIR_DEPEND_libcam= libsbuf SUBDIR_DEPEND_libcapsicum= libnv SUBDIR_DEPEND_libcasper= libcapsicum libnv libpjdlog SUBDIR_DEPEND_libdevstat= libkvm SUBDIR_DEPEND_libdpv= libfigpar ncurses libutil SUBDIR_DEPEND_libedit= ncurses SUBDIR_DEPEND_libg++= msun SUBDIR_DEPEND_libgeom= libexpat libsbuf SUBDIR_DEPEND_liblibrpcsec_gss= libgssapi SUBDIR_DEPEND_libmagic= libz SUBDIR_DEPEND_libmemstat= libkvm SUBDIR_DEPEND_libopie= libmd SUBDIR_DEPEND_libpam= libcrypt libopie libradius librpcsvc libtacplus libutil ${_libypclnt} ${_libcom_err} SUBDIR_DEPEND_libpjdlog= libutil SUBDIR_DEPEND_libprocstat= libkvm libutil SUBDIR_DEPEND_libradius= libmd SUBDIR_DEPEND_libreadline= ncurses SUBDIR_DEPEND_libsmb= libkiconv SUBDIR_DEPEND_libstdc++= msun SUBDIR_DEPEND_libtacplus= libmd SUBDIR_DEPEND_libulog= libmd SUBDIR_DEPEND_libunbound= ${_libldns} .if exists(${.CURDIR}/csu/${MACHINE_ARCH}-elf) _csu=csu/${MACHINE_ARCH}-elf .elif exists(${.CURDIR}/csu/${MACHINE_ARCH}) _csu=csu/${MACHINE_ARCH} .elif exists(${.CURDIR}/csu/${MACHINE_CPUARCH}/Makefile) _csu=csu/${MACHINE_CPUARCH} .else _csu=csu .endif # NB: keep these sorted by MK_* knobs .if ${MK_ATM} != "no" _libngatm= libngatm .endif .if ${MK_BLUETOOTH} != "no" _libbluetooth= libbluetooth _libsdp= libsdp .endif .if ${MK_BSNMP} != "no" _libbsnmp= libbsnmp .endif .if ${MK_CASPER} != "no" _libcapsicum= libcapsicum _libcasper= libcasper .endif .if ${MK_CLANG} != "no" && !defined(COMPAT_32BIT) _clang= clang .endif .if ${MK_CUSE} != "no" _cuse= libcuse +.endif + +.if ${MK_ELFTOOLCHAIN_TOOLS} != "no" +_libelftc= libelftc .endif .if ${MK_GPIB} != "no" _libgpib= libgpib .endif .if ${MK_GSSAPI} != "no" _libgssapi= libgssapi _librpcsec_gss= librpcsec_gss .endif .if ${MK_ICONV} != "no" _libiconv_modules= libiconv_modules .endif .if ${MK_KERBEROS_SUPPORT} != "no" _libcom_err= libcom_err .endif .if ${MK_LDNS} != "no" _libldns= libldns .endif .if ${MK_LIBCPLUSPLUS} != "no" _libcxxrt= libcxxrt _libcplusplus= libc++ .endif .if ${MK_LIBTHR} != "no" _libthr= libthr .endif .if ${MK_NAND} != "no" _libnandfs= libnandfs .endif .if ${MK_NETGRAPH} != "no" _libnetgraph= libnetgraph .endif .if ${MK_NIS} != "no" _libypclnt= libypclnt .endif .if ${MK_PF} != "no" _libevent= libevent .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _libsmb= libsmb _libvgl= libvgl _libproc= libproc _librtld_db= librtld_db .endif .if ${MACHINE_CPUARCH} == "amd64" _libvmmapi= libvmmapi .endif .if ${MACHINE_CPUARCH} == "mips" _libproc= libproc _librtld_db= librtld_db .endif .if ${MACHINE_CPUARCH} == "powerpc" _libproc= libproc _librtld_db= librtld_db _libsmb= libsmb .endif .if ${MACHINE_CPUARCH} == "sparc64" _libsmb= libsmb .endif .if ${MK_OPENSSL} != "no" _libmp= libmp .endif .if ${MK_PMC} != "no" _libpmc= libpmc .endif .if ${MK_SENDMAIL} != "no" _libmilter= libmilter _libsm= libsm _libsmdb= libsmdb _libsmutil= libsmutil .endif .if ${MK_TELNET} != "no" _libtelnet= libtelnet .endif .if ${MK_TESTS_SUPPORT} != "no" _atf= atf .endif .if ${MK_TESTS} != "no" _tests= tests .endif .if ${MK_UNBOUND} != "no" _libunbound= libunbound .endif .if ${MK_USB} != "no" _libusbhid= libusbhid _libusb= libusb .endif .if !defined(LIBRARIES_ONLY) afterinstall: ${INSTALL_SYMLINK} ../include ${DESTDIR}/usr/lib/include .endif .if !make(install) SUBDIR_PARALLEL= .endif .include Index: projects/clang350-import/lib/libelftc/Makefile =================================================================== --- projects/clang350-import/lib/libelftc/Makefile (nonexistent) +++ projects/clang350-import/lib/libelftc/Makefile (revision 275387) @@ -0,0 +1,30 @@ +# $FreeBSD$ +.include + +INTERNALLIB= + +ELFTCDIR= ${.CURDIR}/../../contrib/elftoolchain + +.PATH: ${ELFTCDIR}/libelftc + +LIB= elftc + +SRCS= elftc_bfdtarget.c \ + elftc_copyfile.c \ + elftc_demangle.c \ + elftc_set_timestamps.c \ + elftc_string_table.c \ + elftc_version.c \ + libelftc_bfdtarget.c \ + libelftc_dem_arm.c \ + libelftc_dem_gnu2.c \ + libelftc_dem_gnu3.c \ + libelftc_hash.c \ + libelftc_vstr.c + +INCS= libelftc.h +CFLAGS+=-I${ELFTCDIR}/libelftc -I${ELFTCDIR}/common + +NO_MAN= yes + +.include Property changes on: projects/clang350-import/lib/libelftc/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang350-import/lib/libelftc/elftc_version.c =================================================================== --- projects/clang350-import/lib/libelftc/elftc_version.c (nonexistent) +++ projects/clang350-import/lib/libelftc/elftc_version.c (revision 275387) @@ -0,0 +1,10 @@ +/* $FreeBSD$ */ + +#include +#include + +const char * +elftc_version(void) +{ + return "libelftc r2974"; +} Property changes on: projects/clang350-import/lib/libelftc/elftc_version.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang350-import/sbin/sysctl/sysctl.c =================================================================== --- projects/clang350-import/sbin/sysctl/sysctl.c (revision 275386) +++ projects/clang350-import/sbin/sysctl/sysctl.c (revision 275387) @@ -1,987 +1,990 @@ /* * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)from: sysctl.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #ifdef __amd64__ #include #include #endif #if defined(__amd64__) || defined(__i386__) #include #endif #include #include #include #include #include #include #include #include #include #include #include static const char *conffile; static int aflag, bflag, dflag, eflag, hflag, iflag; static int Nflag, nflag, oflag, qflag, Tflag, Wflag, xflag; static int oidfmt(int *, int, char *, u_int *); static int parsefile(const char *); static int parse(const char *, int); static int show_var(int *, int); static int sysctl_all(int *oid, int len); static int name2oid(const char *, int *); static int strIKtoi(const char *, char **); static int ctl_sign[CTLTYPE+1] = { [CTLTYPE_INT] = 1, [CTLTYPE_LONG] = 1, [CTLTYPE_S64] = 1, }; static int ctl_size[CTLTYPE+1] = { [CTLTYPE_INT] = sizeof(int), [CTLTYPE_UINT] = sizeof(u_int), [CTLTYPE_LONG] = sizeof(long), [CTLTYPE_ULONG] = sizeof(u_long), [CTLTYPE_S64] = sizeof(int64_t), [CTLTYPE_U64] = sizeof(uint64_t), }; static const char *ctl_typename[CTLTYPE+1] = { [CTLTYPE_INT] = "integer", [CTLTYPE_UINT] = "unsigned integer", [CTLTYPE_LONG] = "long integer", [CTLTYPE_ULONG] = "unsigned long", [CTLTYPE_S64] = "int64_t", [CTLTYPE_U64] = "uint64_t", }; static void usage(void) { (void)fprintf(stderr, "%s\n%s\n", "usage: sysctl [-bdehiNnoqTWx] [-f filename] name[=value] ...", " sysctl [-bdehNnoqTWx] -a"); exit(1); } int main(int argc, char **argv) { int ch; int warncount = 0; setlocale(LC_NUMERIC, ""); setbuf(stdout,0); setbuf(stderr,0); while ((ch = getopt(argc, argv, "Aabdef:hiNnoqTwWxX")) != -1) { switch (ch) { case 'A': /* compatibility */ aflag = oflag = 1; break; case 'a': aflag = 1; break; case 'b': bflag = 1; break; case 'd': dflag = 1; break; case 'e': eflag = 1; break; case 'f': conffile = optarg; break; case 'h': hflag = 1; break; case 'i': iflag = 1; break; case 'N': Nflag = 1; break; case 'n': nflag = 1; break; case 'o': oflag = 1; break; case 'q': qflag = 1; break; case 'T': Tflag = 1; break; case 'w': /* compatibility */ /* ignored */ break; case 'W': Wflag = 1; break; case 'X': /* compatibility */ aflag = xflag = 1; break; case 'x': xflag = 1; break; default: usage(); } } argc -= optind; argv += optind; if (Nflag && nflag) usage(); if (aflag && argc == 0) exit(sysctl_all(0, 0)); if (argc == 0 && conffile == NULL) usage(); warncount = 0; if (conffile != NULL) warncount += parsefile(conffile); while (argc-- > 0) warncount += parse(*argv++, 0); return (warncount); } /* * Parse a name into a MIB entry. * Lookup and print out the MIB entry if it exists. * Set a new value if requested. */ static int parse(const char *string, int lineno) { int len, i, j; const void *newval; const char *newvalstr = NULL; int intval; unsigned int uintval; long longval; unsigned long ulongval; size_t newsize = 0; int64_t i64val; uint64_t u64val; int mib[CTL_MAXNAME]; char *cp, *bufp, buf[BUFSIZ], *endptr = NULL, fmt[BUFSIZ], line[BUFSIZ]; u_int kind; if (lineno) snprintf(line, sizeof(line), " at line %d", lineno); else line[0] = '\0'; cp = buf; if (snprintf(buf, BUFSIZ, "%s", string) >= BUFSIZ) { warnx("oid too long: '%s'%s", string, line); return (1); } bufp = strsep(&cp, "=:"); if (cp != NULL) { /* Tflag just lists tunables, do not allow assignment */ if (Tflag || Wflag) { warnx("Can't set variables when using -T or -W"); usage(); } while (isspace(*cp)) cp++; /* Strip a pair of " or ' if any. */ switch (*cp) { case '\"': case '\'': if (cp[strlen(cp) - 1] == *cp) cp[strlen(cp) - 1] = '\0'; cp++; } newvalstr = cp; newsize = strlen(cp); } len = name2oid(bufp, mib); if (len < 0) { if (iflag) return (0); if (qflag) return (1); else { warn("unknown oid '%s'%s", bufp, line); return (1); } } if (oidfmt(mib, len, fmt, &kind)) { warn("couldn't find format of oid '%s'%s", bufp, line); if (iflag) return (1); else exit(1); } if (newvalstr == NULL || dflag) { if ((kind & CTLTYPE) == CTLTYPE_NODE) { if (dflag) { i = show_var(mib, len); if (!i && !bflag) putchar('\n'); } sysctl_all(mib, len); } else { i = show_var(mib, len); if (!i && !bflag) putchar('\n'); } } else { if ((kind & CTLTYPE) == CTLTYPE_NODE) { warnx("oid '%s' isn't a leaf node%s", bufp, line); return (1); } if (!(kind & CTLFLAG_WR)) { if (kind & CTLFLAG_TUN) { warnx("oid '%s' is a read only tunable%s", bufp, line); warnx("Tunable values are set in /boot/loader.conf"); } else warnx("oid '%s' is read only%s", bufp, line); return (1); } switch (kind & CTLTYPE) { case CTLTYPE_INT: case CTLTYPE_UINT: case CTLTYPE_LONG: case CTLTYPE_ULONG: case CTLTYPE_S64: case CTLTYPE_U64: if (strlen(newvalstr) == 0) { warnx("empty numeric value"); return (1); } /* FALLTHROUGH */ case CTLTYPE_STRING: break; default: warnx("oid '%s' is type %d," " cannot set that%s", bufp, kind & CTLTYPE, line); return (1); } errno = 0; switch (kind & CTLTYPE) { case CTLTYPE_INT: if (strcmp(fmt, "IK") == 0) intval = strIKtoi(newvalstr, &endptr); else intval = (int)strtol(newvalstr, &endptr, 0); newval = &intval; newsize = sizeof(intval); break; case CTLTYPE_UINT: uintval = (int) strtoul(newvalstr, &endptr, 0); newval = &uintval; newsize = sizeof(uintval); break; case CTLTYPE_LONG: longval = strtol(newvalstr, &endptr, 0); newval = &longval; newsize = sizeof(longval); break; case CTLTYPE_ULONG: ulongval = strtoul(newvalstr, &endptr, 0); newval = &ulongval; newsize = sizeof(ulongval); break; case CTLTYPE_STRING: newval = newvalstr; break; case CTLTYPE_S64: i64val = strtoimax(newvalstr, &endptr, 0); newval = &i64val; newsize = sizeof(i64val); break; case CTLTYPE_U64: u64val = strtoumax(newvalstr, &endptr, 0); newval = &u64val; newsize = sizeof(u64val); break; default: /* NOTREACHED */ abort(); } if (errno != 0 || endptr == newvalstr || (endptr != NULL && *endptr != '\0')) { warnx("invalid %s '%s'%s", ctl_typename[kind & CTLTYPE], newvalstr, line); return (1); } i = show_var(mib, len); if (sysctl(mib, len, 0, 0, newval, newsize) == -1) { if (!i && !bflag) putchar('\n'); switch (errno) { case EOPNOTSUPP: warnx("%s: value is not available%s", string, line); return (1); case ENOTDIR: warnx("%s: specification is incomplete%s", string, line); return (1); case ENOMEM: warnx("%s: type is unknown to this program%s", string, line); return (1); default: warn("%s%s", string, line); return (1); } } if (!bflag) printf(" -> "); i = nflag; nflag = 1; j = show_var(mib, len); if (!j && !bflag) putchar('\n'); nflag = i; } return (0); } static int parsefile(const char *filename) { FILE *file; char line[BUFSIZ], *p, *pq, *pdq; int warncount = 0, lineno = 0; file = fopen(filename, "r"); if (file == NULL) err(EX_NOINPUT, "%s", filename); while (fgets(line, sizeof(line), file) != NULL) { lineno++; p = line; pq = strchr(line, '\''); pdq = strchr(line, '\"'); /* Replace the first # with \0. */ while((p = strchr(p, '#')) != NULL) { if (pq != NULL && p > pq) { if ((p = strchr(pq+1, '\'')) != NULL) *(++p) = '\0'; break; } else if (pdq != NULL && p > pdq) { if ((p = strchr(pdq+1, '\"')) != NULL) *(++p) = '\0'; break; } else if (p == line || *(p-1) != '\\') { *p = '\0'; break; } p++; } /* Trim spaces */ p = line + strlen(line) - 1; while (p >= line && isspace((int)*p)) { *p = '\0'; p--; } p = line; while (isspace((int)*p)) p++; if (*p == '\0') continue; else warncount += parse(p, lineno); } fclose(file); return (warncount); } /* These functions will dump out various interesting structures. */ static int S_clockinfo(size_t l2, void *p) { struct clockinfo *ci = (struct clockinfo*)p; if (l2 != sizeof(*ci)) { warnx("S_clockinfo %zu != %zu", l2, sizeof(*ci)); return (1); } printf(hflag ? "{ hz = %'d, tick = %'d, profhz = %'d, stathz = %'d }" : "{ hz = %d, tick = %d, profhz = %d, stathz = %d }", ci->hz, ci->tick, ci->profhz, ci->stathz); return (0); } static int S_loadavg(size_t l2, void *p) { struct loadavg *tv = (struct loadavg*)p; if (l2 != sizeof(*tv)) { warnx("S_loadavg %zu != %zu", l2, sizeof(*tv)); return (1); } printf(hflag ? "{ %'.2f %'.2f %'.2f }" : "{ %.2f %.2f %.2f }", (double)tv->ldavg[0]/(double)tv->fscale, (double)tv->ldavg[1]/(double)tv->fscale, (double)tv->ldavg[2]/(double)tv->fscale); return (0); } static int S_timeval(size_t l2, void *p) { struct timeval *tv = (struct timeval*)p; time_t tv_sec; char *p1, *p2; if (l2 != sizeof(*tv)) { warnx("S_timeval %zu != %zu", l2, sizeof(*tv)); return (1); } printf(hflag ? "{ sec = %'jd, usec = %'ld } " : "{ sec = %jd, usec = %ld } ", (intmax_t)tv->tv_sec, tv->tv_usec); tv_sec = tv->tv_sec; p1 = strdup(ctime(&tv_sec)); for (p2=p1; *p2 ; p2++) if (*p2 == '\n') *p2 = '\0'; fputs(p1, stdout); free(p1); return (0); } static int S_vmtotal(size_t l2, void *p) { struct vmtotal *v = (struct vmtotal *)p; int pageKilo = getpagesize() / 1024; if (l2 != sizeof(*v)) { warnx("S_vmtotal %zu != %zu", l2, sizeof(*v)); return (1); } printf( "\nSystem wide totals computed every five seconds:" " (values in kilobytes)\n"); printf("===============================================\n"); printf( "Processes:\t\t(RUNQ: %hd Disk Wait: %hd Page Wait: " "%hd Sleep: %hd)\n", v->t_rq, v->t_dw, v->t_pw, v->t_sl); printf( "Virtual Memory:\t\t(Total: %dK Active: %dK)\n", v->t_vm * pageKilo, v->t_avm * pageKilo); printf("Real Memory:\t\t(Total: %dK Active: %dK)\n", v->t_rm * pageKilo, v->t_arm * pageKilo); printf("Shared Virtual Memory:\t(Total: %dK Active: %dK)\n", v->t_vmshr * pageKilo, v->t_avmshr * pageKilo); printf("Shared Real Memory:\t(Total: %dK Active: %dK)\n", v->t_rmshr * pageKilo, v->t_armshr * pageKilo); printf("Free Memory:\t%dK", v->t_free * pageKilo); return (0); } #ifdef __amd64__ #define efi_next_descriptor(ptr, size) \ ((struct efi_md *)(((uint8_t *) ptr) + size)) static int S_efi_map(size_t l2, void *p) { struct efi_map_header *efihdr; struct efi_md *map; const char *type; size_t efisz; int ndesc, i; static const char *types[] = { "Reserved", "LoaderCode", "LoaderData", "BootServicesCode", "BootServicesData", "RuntimeServicesCode", "RuntimeServicesData", "ConventionalMemory", "UnusableMemory", "ACPIReclaimMemory", "ACPIMemoryNVS", "MemoryMappedIO", "MemoryMappedIOPortSpace", "PalCode" }; /* * Memory map data provided by UEFI via the GetMemoryMap * Boot Services API. */ if (l2 < sizeof(*efihdr)) { warnx("S_efi_map length less than header"); return (1); } efihdr = p; efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; map = (struct efi_md *)((uint8_t *)efihdr + efisz); if (efihdr->descriptor_size == 0) return (0); if (l2 != efisz + efihdr->memory_size) { warnx("S_efi_map length mismatch %zu vs %zu", l2, efisz + efihdr->memory_size); return (1); } ndesc = efihdr->memory_size / efihdr->descriptor_size; printf("\n%23s %12s %12s %8s %4s", "Type", "Physical", "Virtual", "#Pages", "Attr"); for (i = 0; i < ndesc; i++, map = efi_next_descriptor(map, efihdr->descriptor_size)) { if (map->md_type <= EFI_MD_TYPE_PALCODE) type = types[map->md_type]; else type = ""; printf("\n%23s %012lx %12p %08lx ", type, map->md_phys, map->md_virt, map->md_pages); if (map->md_attr & EFI_MD_ATTR_UC) printf("UC "); if (map->md_attr & EFI_MD_ATTR_WC) printf("WC "); if (map->md_attr & EFI_MD_ATTR_WT) printf("WT "); if (map->md_attr & EFI_MD_ATTR_WB) printf("WB "); if (map->md_attr & EFI_MD_ATTR_UCE) printf("UCE "); if (map->md_attr & EFI_MD_ATTR_WP) printf("WP "); if (map->md_attr & EFI_MD_ATTR_RP) printf("RP "); if (map->md_attr & EFI_MD_ATTR_XP) printf("XP "); if (map->md_attr & EFI_MD_ATTR_RT) printf("RUNTIME"); } return (0); } #endif #if defined(__amd64__) || defined(__i386__) static int S_bios_smap_xattr(size_t l2, void *p) { struct bios_smap_xattr *smap, *end; if (l2 % sizeof(*smap) != 0) { warnx("S_bios_smap_xattr %zu is not a multiple of %zu", l2, sizeof(*smap)); return (1); } end = (struct bios_smap_xattr *)((char *)p + l2); for (smap = p; smap < end; smap++) printf("\nSMAP type=%02x, xattr=%02x, base=%016jx, len=%016jx", smap->type, smap->xattr, (uintmax_t)smap->base, (uintmax_t)smap->length); return (0); } #endif static int strIKtoi(const char *str, char **endptrp) { int kelv; float temp; size_t len; const char *p; assert(errno == 0); len = strlen(str); /* caller already checked this */ assert(len > 0); p = &str[len - 1]; if (*p == 'C' || *p == 'F') { temp = strtof(str, endptrp); - if (*endptrp != str && *endptrp == p && errno != 0) { + if (*endptrp != str && *endptrp == p && errno == 0) { if (*p == 'F') temp = (temp - 32) * 5 / 9; + *endptrp = NULL; return (temp * 10 + 2732); } } else { kelv = (int)strtol(str, endptrp, 10); - if (*endptrp != str && *endptrp == p && errno != 0) + if (*endptrp != str && *endptrp == p && errno == 0) { + *endptrp = NULL; return (kelv); + } } errno = ERANGE; return (0); } /* * These functions uses a presently undocumented interface to the kernel * to walk the tree and get the type so it can print the value. * This interface is under work and consideration, and should probably * be killed with a big axe by the first person who can find the time. * (be aware though, that the proper interface isn't as obvious as it * may seem, there are various conflicting requirements. */ static int name2oid(const char *name, int *oidp) { int oid[2]; int i; size_t j; oid[0] = 0; oid[1] = 3; j = CTL_MAXNAME * sizeof(int); i = sysctl(oid, 2, oidp, &j, name, strlen(name)); if (i < 0) return (i); j /= sizeof(int); return (j); } static int oidfmt(int *oid, int len, char *fmt, u_int *kind) { int qoid[CTL_MAXNAME+2]; u_char buf[BUFSIZ]; int i; size_t j; qoid[0] = 0; qoid[1] = 4; memcpy(qoid + 2, oid, len * sizeof(int)); j = sizeof(buf); i = sysctl(qoid, len + 2, buf, &j, 0, 0); if (i) err(1, "sysctl fmt %d %zu %d", i, j, errno); if (kind) *kind = *(u_int *)buf; if (fmt) strcpy(fmt, (char *)(buf + sizeof(u_int))); return (0); } /* * This formats and outputs the value of one variable * * Returns zero if anything was actually output. * Returns one if didn't know what to do with this. * Return minus one if we had errors. */ static int show_var(int *oid, int nlen) { u_char buf[BUFSIZ], *val, *oval, *p; char name[BUFSIZ], fmt[BUFSIZ]; const char *sep, *sep1; int qoid[CTL_MAXNAME+2]; uintmax_t umv; intmax_t mv; int i, hexlen, sign, ctltype; size_t intlen; size_t j, len; u_int kind; int (*func)(size_t, void *); /* Silence GCC. */ umv = mv = intlen = 0; bzero(buf, BUFSIZ); bzero(fmt, BUFSIZ); bzero(name, BUFSIZ); qoid[0] = 0; memcpy(qoid + 2, oid, nlen * sizeof(int)); qoid[1] = 1; j = sizeof(name); i = sysctl(qoid, nlen + 2, name, &j, 0, 0); if (i || !j) err(1, "sysctl name %d %zu %d", i, j, errno); oidfmt(oid, nlen, fmt, &kind); /* if Wflag then only list sysctls that are writeable and not stats. */ if (Wflag && ((kind & CTLFLAG_WR) == 0 || (kind & CTLFLAG_STATS) != 0)) return 1; /* if Tflag then only list sysctls that are tuneables. */ if (Tflag && (kind & CTLFLAG_TUN) == 0) return 1; if (Nflag) { printf("%s", name); return (0); } if (eflag) sep = "="; else sep = ": "; if (dflag) { /* just print description */ qoid[1] = 5; j = sizeof(buf); i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); if (!nflag) printf("%s%s", name, sep); printf("%s", buf); return (0); } /* find an estimate of how much we need for this var */ j = 0; i = sysctl(oid, nlen, 0, &j, 0, 0); j += j; /* we want to be sure :-) */ val = oval = malloc(j + 1); if (val == NULL) { warnx("malloc failed"); return (1); } ctltype = (kind & CTLTYPE); len = j; i = sysctl(oid, nlen, val, &len, 0, 0); if (i != 0 || (len == 0 && ctltype != CTLTYPE_STRING)) { free(oval); return (1); } if (bflag) { fwrite(val, 1, len, stdout); free(oval); return (0); } val[len] = '\0'; p = val; sign = ctl_sign[ctltype]; intlen = ctl_size[ctltype]; switch (ctltype) { case CTLTYPE_STRING: if (!nflag) printf("%s%s", name, sep); printf("%.*s", (int)len, p); free(oval); return (0); case CTLTYPE_INT: case CTLTYPE_UINT: case CTLTYPE_LONG: case CTLTYPE_ULONG: case CTLTYPE_S64: case CTLTYPE_U64: if (!nflag) printf("%s%s", name, sep); hexlen = 2 + (intlen * CHAR_BIT + 3) / 4; sep1 = ""; while (len >= intlen) { switch (kind & CTLTYPE) { case CTLTYPE_INT: case CTLTYPE_UINT: umv = *(u_int *)p; mv = *(int *)p; break; case CTLTYPE_LONG: case CTLTYPE_ULONG: umv = *(u_long *)p; mv = *(long *)p; break; case CTLTYPE_S64: case CTLTYPE_U64: umv = *(uint64_t *)p; mv = *(int64_t *)p; break; } fputs(sep1, stdout); if (xflag) printf("%#0*jx", hexlen, umv); else if (!sign) printf(hflag ? "%'ju" : "%ju", umv); else if (fmt[1] == 'K') { if (mv < 0) printf("%jd", mv); else printf("%.1fC", (mv - 2732.0) / 10); } else printf(hflag ? "%'jd" : "%jd", mv); sep1 = " "; len -= intlen; p += intlen; } free(oval); return (0); case CTLTYPE_OPAQUE: i = 0; if (strcmp(fmt, "S,clockinfo") == 0) func = S_clockinfo; else if (strcmp(fmt, "S,timeval") == 0) func = S_timeval; else if (strcmp(fmt, "S,loadavg") == 0) func = S_loadavg; else if (strcmp(fmt, "S,vmtotal") == 0) func = S_vmtotal; #ifdef __amd64__ else if (strcmp(fmt, "S,efi_map_header") == 0) func = S_efi_map; #endif #if defined(__amd64__) || defined(__i386__) else if (strcmp(fmt, "S,bios_smap_xattr") == 0) func = S_bios_smap_xattr; #endif else func = NULL; if (func) { if (!nflag) printf("%s%s", name, sep); i = (*func)(len, p); free(oval); return (i); } /* FALLTHROUGH */ default: if (!oflag && !xflag) { free(oval); return (1); } if (!nflag) printf("%s%s", name, sep); printf("Format:%s Length:%zu Dump:0x", fmt, len); while (len-- && (xflag || p < val + 16)) printf("%02x", *p++); if (!xflag && len > 16) printf("..."); free(oval); return (0); } free(oval); return (1); } static int sysctl_all(int *oid, int len) { int name1[22], name2[22]; int i, j; size_t l1, l2; name1[0] = 0; name1[1] = 2; l1 = 2; if (len) { memcpy(name1+2, oid, len * sizeof(int)); l1 += len; } else { name1[2] = 1; l1++; } for (;;) { l2 = sizeof(name2); j = sysctl(name1, l1, name2, &l2, 0, 0); if (j < 0) { if (errno == ENOENT) return (0); else err(1, "sysctl(getnext) %d %zu", j, l2); } l2 /= sizeof(int); if (len < 0 || l2 < (unsigned int)len) return (0); for (i = 0; i < len; i++) if (name2[i] != oid[i]) return (0); i = show_var(name2, l2); if (!i && !bflag) putchar('\n'); memcpy(name1+2, name2, l2 * sizeof(int)); l1 = 2 + l2; } } Index: projects/clang350-import/sbin =================================================================== --- projects/clang350-import/sbin (revision 275386) +++ projects/clang350-import/sbin (revision 275387) Property changes on: projects/clang350-import/sbin ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sbin:r275364-275386 Index: projects/clang350-import/share/man/man4/isp.4 =================================================================== --- projects/clang350-import/share/man/man4/isp.4 (revision 275386) +++ projects/clang350-import/share/man/man4/isp.4 (revision 275387) @@ -1,238 +1,242 @@ .\" $NetBSD: isp.4,v 1.5 1999/12/18 18:33:05 mjacob Exp $ .\" .\" Copyright (c) 1998, 1999, 2001 .\" Matthew Jacob, for NASA/Ames Research Center .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" Additional Copyright (c) 2006 by Marcus Alves Grando .\" .\" $FreeBSD$ .\" -.Dd February 28, 2007 +.Dd December 1, 2014 .Dt ISP 4 .Os .Sh NAME .Nm isp .Nd Qlogic based SCSI and FibreChannel SCSI Host Adapters .Sh SYNOPSIS To compile this driver into the kernel, place the following lines in your kernel configuration file: .Bd -ragged -offset indent .Cd "device scbus" .Cd "device isp" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent isp_load="YES" .Ed .Sh DESCRIPTION This driver provides access to .Tn SCSI or .Tn FibreChannel devices. .Pp SCSI features include support for Ultra SCSI and wide mode transactions for .Tn SCSI , Ultra2 LVD (for the ISP1080 and ISP1280), and Ultra3 LVD (for the ISP12160). .Pp Fibre Channel support uses FCP SCSI profile for .Tn FibreChannel , and utilizes Class 3 and Class 2 connections (Qlogic 2100 is Class 3 only, minor patches to the Qlogic 2200 to force Class 2 mode). Support is available for Public and Private loops, and for point-to-point connections (Qlogic 2200 only). The newer 2-Gigabit cards (2300, 2312, 2322) and 4-Gigabit (2422, 2432) are also supported. Command tagging is supported for all (in fact, .Tn FibreChannel requires tagging). Fabric support is enabled by default for other than 2100 cards. Fabric support for 2100 cards has been so problematic and these cards are so old now that it is just not worth your time to try it. .Sh FIRMWARE Firmware is available if the .Xr ispfw 4 module is loaded during bootstrap (q.v.). .Pp It is .Ar strongly recommended that you use the firmware available from .Xr ispfw 4 as it is the most likely to have been tested with this driver. .Sh HARDWARE Cards supported by the .Nm driver include: .Bl -tag -width xxxxxx -offset indent .It ISP1000 SBus Fast Wide, Ultra Fast Wide cards, Single Ended or Differential cards. .It ISP1020 Qlogic 1020 Fast Wide and Differential Fast Wide PCI cards. .It ISP1040 Qlogic 1040 Ultra Wide and Differential Ultra Wide PCI cards. Also known as the DEC KZPBA-CA (single ended) and KZPBA-CB (HVD differential). .It Qlogic 1240 Qlogic 1240 Dual Bus Ultra Wide and Differential Ultra Wide PCI cards. .It Qlogic 1020 Qlogic 1020 SCSI cards. .It Qlogic 1040 Qlogic 1040 Ultra SCSI cards. .It Qlogic 1080 Qlogic 1280 LVD Ultra2 Wide PCI cards. .It Qlogic 1280 Qlogic 1280 Dual Bus LVD Ultra2 Wide PCI cards. .It Qlogic 12160 Qlogic 12160 Dual Bus LVD Ultra3 Wide PCI cards. .It Qlogic 210X Qlogic 2100 and 2100A Copper and Optical Fibre Channel Arbitrated Loop (single, dual). .It Qlogic 220X Qlogic 2200 Copper and Optical Fibre Channel Arbitrated Loop PCI cards (single, dual, quad). .It Qlogic 2300 Qlogic 2300 Optical Fibre Channel PCI cards. .It Qlogic 2312 Qlogic 2312 Optical Fibre Channel PCI cards. .It Qlogic 234X Qlogic 234X Optical Fibre Channel PCI cards (2312 chipset, single and dual attach). .It Qlogic 2322 Qlogic 2322 Optical Fibre Channel PCIe cards. .It Qlogic 200 Dell Branded version of the QLogic 2312 Fibre Channel PCI cards. .It Qlogic 2422 Qlogic 2422 Optical Fibre Channel PCI cards (4 Gigabit) .It Qlogic 2432 Qlogic 2432 Optical Fibre Channel PCIe cards (4 Gigabit) +.It Qlogic 2432 +Qlogic 2532 Optical Fibre Channel PCIe cards (8 Gigabit) .El .Sh CONFIGURATION OPTIONS Target mode support may be enabled with the .Pp .Cd options ISP_TARGET_MODE .Pp option. .Sh BOOT OPTIONS The following options are switchable by setting values in .Pa /boot/device.hints . .Pp They are: .Bl -tag -width indent .It Va hint.isp.0.disable A hint value to disable driver in kernel. .It Va hint.isp.0.fwload_disable A hint value to disable loading of firmware .Xr ispfw 4 . .It Va hint.isp.0.prefer_memmap A hint value to use PCI memory space instead of I/O space access for. .It Va hint.isp.0.prefer_iomap A hint value to use PCI I/O space instead of Memory space access for. .It Va hint.isp.0.ignore_nvram A hint value to ignore board NVRAM settings for. Otherwise use NVRAM settings. .It Va hint.isp.0.fullduplex A hint value to set full duplex mode. .It Va hint.isp.0.topology A hint value to select topology of connection. Supported values are: .Pp .Bl -tag -width ".Li lport-only" -compact .It Li lport Prefer loopback and fallback to point to point. .It Li nport Prefer point to point and fallback to loopback. .It Li lport-only Loopback only. .It Li nport-only Point to point only. .El .It Va hint.isp.0.portwwn This should be the full 64 bit World Wide Port Name you would like to use, overriding the value in NVRAM for the card. .It Va hint.isp.0.nodewwn This should be the full 64 bit World Wide Node Name you would like to use, overriding the value in NVRAM for the card. .It Va hint.isp.0.iid A hint to override or set the Initiator ID or Loop ID. For Fibre Channel cards in Local Loop topologies it is .Ar strongly recommended that you set this value to non-zero. .It Va hint.isp.0.role A hint to define default role for isp instance (target, initiator, both). .It Va hint.isp.0.debug A hint value for a driver debug level (see the file .Pa /usr/src/sys/dev/isp/ispvar.h for the values. +.It Va hint.isp.0.vports +A hint to create specified number of additional virtual ports. .El .Sh SYSCTL OPTIONS .Bl -tag -width indent .It Va dev.isp.N.loop_down_limit This value says how long to wait in seconds after loop has gone down before giving up and expiring all of the devices that were visible. The default is 300 seconds (5 minutes). A separate (nonadjustable) timeout is used when booting to not stop booting on lack of FC connectivity. .It Va dev.isp.N.gone_device_time This value says how long to wait for devices to reappear if they (temporarily) disappear due to loop or fabric events. While this timeout is running, I/O to those devices will simply be held. .It Va dev.isp.N.wwnn This is the readonly World Wide Node Name value for this port. .It Va dev.isp.N.wwpn This is the readonly World Wide Port Name value for this port. .El .Sh SEE ALSO .Xr da 4 , .Xr intro 4 , .Xr ispfw 4 , .Xr sa 4 , .Xr scsi 4 , .Xr gmultipath 8 .Sh AUTHORS The .Nm driver was written by Matthew Jacob originally for NetBSD at NASA/Ames Research Center. .Sh BUGS The driver currently ignores some NVRAM settings. .Pp Target mode support is not completely reliable yet. It works reasonably well for Fibre Channel, somewhat well for Qlogic 1040 cards, but does not yet work for the other cards (due to last minute unannounced changes in firmware interfaces). Index: projects/clang350-import/share/man/man4 =================================================================== --- projects/clang350-import/share/man/man4 (revision 275386) +++ projects/clang350-import/share/man/man4 (revision 275387) Property changes on: projects/clang350-import/share/man/man4 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share/man/man4:r275364-275386 Index: projects/clang350-import/share/mk/bsd.cpu.mk =================================================================== --- projects/clang350-import/share/mk/bsd.cpu.mk (revision 275386) +++ projects/clang350-import/share/mk/bsd.cpu.mk (revision 275387) @@ -1,263 +1,263 @@ # $FreeBSD$ # Set default CPU compile flags and baseline CPUTYPE for each arch. The # compile flags must support the minimum CPU type for each architecture but # may tune support for more advanced processors. .if !defined(CPUTYPE) || empty(CPUTYPE) _CPUCFLAGS = . if ${MACHINE_CPUARCH} == "i386" MACHINE_CPU = i486 . elif ${MACHINE_CPUARCH} == "amd64" MACHINE_CPU = amd64 sse2 sse mmx . elif ${MACHINE_CPUARCH} == "powerpc" MACHINE_CPU = aim . elif ${MACHINE_CPUARCH} == "sparc64" MACHINE_CPU = ultrasparc . elif ${MACHINE_CPUARCH} == "arm" MACHINE_CPU = arm . elif ${MACHINE_CPUARCH} == "mips" MACHINE_CPU = mips . endif .else # Handle aliases (not documented in make.conf to avoid user confusion # between e.g. i586 and pentium) . if ${MACHINE_CPUARCH} == "i386" . if ${CPUTYPE} == "barcelona" CPUTYPE = amdfam10 . elif ${CPUTYPE} == "k7" CPUTYPE = athlon . elif ${CPUTYPE} == "westmere" || ${CPUTYPE} == "nehalem" CPUTYPE = corei7 . elif ${CPUTYPE} == "core" CPUTYPE = prescott . elif ${CPUTYPE} == "p4" CPUTYPE = pentium4 . elif ${CPUTYPE} == "p4m" CPUTYPE = pentium4m . elif ${CPUTYPE} == "p3" CPUTYPE = pentium3 . elif ${CPUTYPE} == "p3m" CPUTYPE = pentium3m . elif ${CPUTYPE} == "p-m" CPUTYPE = pentium-m . elif ${CPUTYPE} == "p2" CPUTYPE = pentium2 . elif ${CPUTYPE} == "i686" CPUTYPE = pentiumpro . elif ${CPUTYPE} == "i586/mmx" CPUTYPE = pentium-mmx . elif ${CPUTYPE} == "i586" CPUTYPE = pentium . endif . elif ${MACHINE_CPUARCH} == "amd64" . if ${CPUTYPE} == "barcelona" CPUTYPE = amdfam10 . elif ${CPUTYPE} == "westmere" || ${CPUTYPE} == "nehalem" CPUTYPE = corei7 . elif ${CPUTYPE} == "prescott" CPUTYPE = nocona . endif . elif ${MACHINE_ARCH} == "sparc64" . if ${CPUTYPE} == "us" CPUTYPE = ultrasparc . elif ${CPUTYPE} == "us3" CPUTYPE = ultrasparc3 . endif . endif ############################################################################### # Logic to set up correct gcc optimization flag. This must be included # after /etc/make.conf so it can react to the local value of CPUTYPE # defined therein. Consult: # http://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html # http://gcc.gnu.org/onlinedocs/gcc/IA_002d64-Options.html # http://gcc.gnu.org/onlinedocs/gcc/RS_002f6000-and-PowerPC-Options.html # http://gcc.gnu.org/onlinedocs/gcc/MIPS-Options.html # http://gcc.gnu.org/onlinedocs/gcc/SPARC-Options.html # http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html . if ${MACHINE_CPUARCH} == "i386" . if ${CPUTYPE} == "crusoe" _CPUCFLAGS = -march=i686 -falign-functions=0 -falign-jumps=0 -falign-loops=0 . elif ${CPUTYPE} == "k5" _CPUCFLAGS = -march=pentium . elif ${CPUTYPE} == "c7" _CPUCFLAGS = -march=c3-2 . else _CPUCFLAGS = -march=${CPUTYPE} . endif . elif ${MACHINE_CPUARCH} == "amd64" _CPUCFLAGS = -march=${CPUTYPE} . elif ${MACHINE_CPUARCH} == "arm" . if ${CPUTYPE} == "xscale" #XXX: gcc doesn't seem to like -mcpu=xscale, and dies while rebuilding itself #_CPUCFLAGS = -mcpu=xscale _CPUCFLAGS = -march=armv5te -D__XSCALE__ . elif ${CPUTYPE} == "armv6" _CPUCFLAGS = -march=${CPUTYPE} -DARM_ARCH_6=1 . elif ${CPUTYPE} == "cortexa" -_CPUCFLAGS = -DARM_ARCH_6=1 -mfpu=vfp +_CPUCFLAGS = -march=armv7 -DARM_ARCH_6=1 -mfpu=vfp . else _CPUCFLAGS = -mcpu=${CPUTYPE} . endif . elif ${MACHINE_ARCH} == "powerpc" . if ${CPUTYPE} == "e500" _CPUCFLAGS = -Wa,-me500 -msoft-float . else _CPUCFLAGS = -mcpu=${CPUTYPE} -mno-powerpc64 . endif . elif ${MACHINE_ARCH} == "powerpc64" _CPUCFLAGS = -mcpu=${CPUTYPE} . elif ${MACHINE_CPUARCH} == "mips" . if ${CPUTYPE} == "mips32" _CPUCFLAGS = -march=mips32 . elif ${CPUTYPE} == "mips32r2" _CPUCFLAGS = -march=mips32r2 . elif ${CPUTYPE} == "mips64" _CPUCFLAGS = -march=mips64 . elif ${CPUTYPE} == "mips64r2" _CPUCFLAGS = -march=mips64r2 . elif ${CPUTYPE} == "mips4kc" _CPUCFLAGS = -march=4kc . elif ${CPUTYPE} == "mips24kc" _CPUCFLAGS = -march=24kc . endif . elif ${MACHINE_ARCH} == "sparc64" . if ${CPUTYPE} == "v9" _CPUCFLAGS = -mcpu=v9 . elif ${CPUTYPE} == "ultrasparc" _CPUCFLAGS = -mcpu=ultrasparc . elif ${CPUTYPE} == "ultrasparc3" _CPUCFLAGS = -mcpu=ultrasparc3 . endif . endif # Set up the list of CPU features based on the CPU type. This is an # unordered list to make it easy for client makefiles to test for the # presence of a CPU feature. . if ${MACHINE_CPUARCH} == "i386" . if ${CPUTYPE} == "bdver3" || ${CPUTYPE} == "bdver2" || \ ${CPUTYPE} == "bdver1" MACHINE_CPU = xop avx sse42 sse41 ssse3 sse4a sse3 sse2 sse mmx k6 k5 i586 . elif ${CPUTYPE} == "btver2" MACHINE_CPU = avx sse42 sse41 ssse3 sse4a sse3 sse2 sse mmx k6 k5 i586 . elif ${CPUTYPE} == "btver1" MACHINE_CPU = ssse3 sse4a sse3 sse2 sse mmx k6 k5 i586 . elif ${CPUTYPE} == "amdfam10" MACHINE_CPU = athlon-xp athlon k7 3dnow sse4a sse3 sse2 sse mmx k6 k5 i586 . elif ${CPUTYPE} == "opteron-sse3" || ${CPUTYPE} == "athlon64-sse3" MACHINE_CPU = athlon-xp athlon k7 3dnow sse3 sse2 sse mmx k6 k5 i586 . elif ${CPUTYPE} == "opteron" || ${CPUTYPE} == "athlon64" || \ ${CPUTYPE} == "athlon-fx" MACHINE_CPU = athlon-xp athlon k7 3dnow sse2 sse mmx k6 k5 i586 . elif ${CPUTYPE} == "athlon-mp" || ${CPUTYPE} == "athlon-xp" || \ ${CPUTYPE} == "athlon-4" MACHINE_CPU = athlon-xp athlon k7 3dnow sse mmx k6 k5 i586 . elif ${CPUTYPE} == "athlon" || ${CPUTYPE} == "athlon-tbird" MACHINE_CPU = athlon k7 3dnow mmx k6 k5 i586 . elif ${CPUTYPE} == "k6-3" || ${CPUTYPE} == "k6-2" || ${CPUTYPE} == "geode" MACHINE_CPU = 3dnow mmx k6 k5 i586 . elif ${CPUTYPE} == "k6" MACHINE_CPU = mmx k6 k5 i586 . elif ${CPUTYPE} == "k5" MACHINE_CPU = k5 i586 . elif ${CPUTYPE} == "core-avx2" MACHINE_CPU = avx2 avx sse42 sse41 ssse3 sse3 sse2 sse i686 mmx i586 . elif ${CPUTYPE} == "core-avx-i" || ${CPUTYPE} == "corei7-avx" MACHINE_CPU = avx sse42 sse41 ssse3 sse3 sse2 sse i686 mmx i586 . elif ${CPUTYPE} == "slm" || ${CPUTYPE} == "corei7" MACHINE_CPU = sse42 sse41 ssse3 sse3 sse2 sse i686 mmx i586 . elif ${CPUTYPE} == "penryn" MACHINE_CPU = sse41 ssse3 sse3 sse2 sse i686 mmx i586 . elif ${CPUTYPE} == "atom" || ${CPUTYPE} == "core2" MACHINE_CPU = ssse3 sse3 sse2 sse i686 mmx i586 . elif ${CPUTYPE} == "yonah" || ${CPUTYPE} == "prescott" MACHINE_CPU = sse3 sse2 sse i686 mmx i586 . elif ${CPUTYPE} == "pentium4" || ${CPUTYPE} == "pentium4m" || \ ${CPUTYPE} == "pentium-m" MACHINE_CPU = sse2 sse i686 mmx i586 . elif ${CPUTYPE} == "pentium3" || ${CPUTYPE} == "pentium3m" MACHINE_CPU = sse i686 mmx i586 . elif ${CPUTYPE} == "pentium2" MACHINE_CPU = i686 mmx i586 . elif ${CPUTYPE} == "pentiumpro" MACHINE_CPU = i686 i586 . elif ${CPUTYPE} == "pentium-mmx" MACHINE_CPU = mmx i586 . elif ${CPUTYPE} == "pentium" MACHINE_CPU = i586 . elif ${CPUTYPE} == "c7" MACHINE_CPU = sse3 sse2 sse i686 mmx i586 . elif ${CPUTYPE} == "c3-2" MACHINE_CPU = sse i686 mmx i586 . elif ${CPUTYPE} == "c3" MACHINE_CPU = 3dnow mmx i586 . elif ${CPUTYPE} == "winchip2" MACHINE_CPU = 3dnow mmx . elif ${CPUTYPE} == "winchip-c6" MACHINE_CPU = mmx . endif MACHINE_CPU += i486 . elif ${MACHINE_CPUARCH} == "amd64" . if ${CPUTYPE} == "bdver3" || ${CPUTYPE} == "bdver2" || \ ${CPUTYPE} == "bdver1" MACHINE_CPU = xop avx sse42 sse41 ssse3 sse4a sse3 . elif ${CPUTYPE} == "btver2" MACHINE_CPU = avx sse42 sse41 ssse3 sse4a sse3 . elif ${CPUTYPE} == "btver1" MACHINE_CPU = ssse3 sse4a sse3 . elif ${CPUTYPE} == "amdfam10" MACHINE_CPU = k8 3dnow sse4a sse3 . elif ${CPUTYPE} == "opteron-sse3" || ${CPUTYPE} == "athlon64-sse3" || \ ${CPUTYPE} == "k8-sse3" MACHINE_CPU = k8 3dnow sse3 . elif ${CPUTYPE} == "opteron" || ${CPUTYPE} == "athlon64" || \ ${CPUTYPE} == "athlon-fx" || ${CPUTYPE} == "k8" MACHINE_CPU = k8 3dnow . elif ${CPUTYPE} == "core-avx2" MACHINE_CPU = avx2 avx sse42 sse41 ssse3 sse3 . elif ${CPUTYPE} == "core-avx-i" || ${CPUTYPE} == "corei7-avx" MACHINE_CPU = avx sse42 sse41 ssse3 sse3 . elif ${CPUTYPE} == "slm" || ${CPUTYPE} == "corei7" MACHINE_CPU = sse42 sse41 ssse3 sse3 . elif ${CPUTYPE} == "penryn" MACHINE_CPU = sse41 ssse3 sse3 . elif ${CPUTYPE} == "atom" || ${CPUTYPE} == "core2" MACHINE_CPU = ssse3 sse3 . elif ${CPUTYPE} == "nocona" MACHINE_CPU = sse3 . endif MACHINE_CPU += amd64 sse2 sse mmx . elif ${MACHINE_ARCH} == "powerpc" . if ${CPUTYPE} == "e500" MACHINE_CPU = booke . endif . elif ${MACHINE_ARCH} == "sparc64" . if ${CPUTYPE} == "v9" MACHINE_CPU = v9 . elif ${CPUTYPE} == "ultrasparc" MACHINE_CPU = v9 ultrasparc . elif ${CPUTYPE} == "ultrasparc3" MACHINE_CPU = v9 ultrasparc ultrasparc3 . endif . endif .endif .if ${MACHINE_CPUARCH} == "mips" CFLAGS += -G0 .endif # NB: COPTFLAGS is handled in /usr/src/sys/conf/kern.pre.mk .if !defined(NO_CPU_CFLAGS) CFLAGS += ${_CPUCFLAGS} .endif # Add in any architecture-specific CFLAGS. # These come from make.conf or the command line or the environment. CFLAGS += ${CFLAGS.${MACHINE_ARCH}} CXXFLAGS += ${CXXFLAGS.${MACHINE_ARCH}} Index: projects/clang350-import/share/mk/src.libnames.mk =================================================================== --- projects/clang350-import/share/mk/src.libnames.mk (revision 275386) +++ projects/clang350-import/share/mk/src.libnames.mk (revision 275387) @@ -1,369 +1,374 @@ # $FreeBSD$ # # The include file define library names suitable # for INTERNALLIB and PRIVATELIB definition .if !target(____) .error src.libnames.mk cannot be included directly. .endif .include ROOTSRCDIR= ${.MAKE.MAKEFILES:M*/src.libnames.mk:H:H:H} ROOTOBJDIR= ${.OBJDIR:S/${.CURDIR}//}${ROOTSRCDIR} _PRIVATELIBS= \ atf_c \ atf_cxx \ bsdstat \ heimipcc \ heimipcs \ ldns \ sqlite3 \ ssh \ ucl \ unbound _INTERNALIBS= \ amu \ bsnmptools \ cron \ + elftc \ event \ fifolog \ ipf \ lpr \ mandoc \ netbsd \ ntp \ ohash \ opts \ parse \ readline \ sl \ sm \ smdb \ smutil \ telnet \ vers _LIBRARIES= \ ${_PRIVATELIBS} \ ${_INTERNALIBS} \ alias \ archive \ asn1 \ auditd \ begemot \ bluetooth \ bsdxml \ bsm \ bsnmp \ bz2 \ c \ c_pic \ calendar \ cam \ capsicum \ casper \ com_err \ compiler_rt \ crypt \ crypto \ ctf \ cuse \ cxxrt \ devinfo \ devstat \ dialog \ dpv \ dwarf \ edit \ elf \ execinfo \ fetch \ figpar \ geom \ gnuregex \ gssapi \ gssapi_krb5 \ hdb \ heimbase \ heimntlm \ heimsqlite \ hx509 \ ipsec \ jail \ kadm5clnt \ kadm5srv \ kafs5 \ kdc \ kiconv \ krb5 \ kvm \ l \ lzma \ m \ magic \ mandoc \ md \ memstat \ mp \ nandfs \ ncursesw \ netgraph \ ngatm \ nv \ opie \ pam \ pcap \ pcsclite \ pjdlog \ pmc \ proc \ procstat \ pthread \ radius \ readline \ roken \ rpcsec_gss \ rpcsvc \ rt \ sbuf \ sdp \ sm \ smb \ ssl \ ssp_nonshared \ stdthreads \ supcplusplus \ tacplus \ termcapw \ ufs \ ugidfw \ ulog \ usb \ usbhid \ util \ vmmapi \ wind \ wrap \ xo \ y \ ypclnt \ z _DP_archive= z bz2 lzma bsdxml .if ${MK_OPENSSL} != "no" _DP_archive+= crypto .else _DP_archive+= md .endif _DP_ssl= crypto _DP_ssh= crypto crypt .if ${MK_LDNS} != "no" _DP_ssh+= ldns z .endif _DP_edit= ncursesw .if ${MK_OPENSSL} != "no" _DP_bsnmp= crypto .endif _DP_grom= bsdxml sbuf _DP_cam= sbuf _DP_casper= capsicum nv pjdlog _DP_capsicum= nv _DP_pjdlog= util _DP_opie= md _DP_usb= pthread _DP_unbound= pthread _DP_rt= pthread .if ${MK_OPENSSL} == "no" _DP_radius= md .else _DP_radius= crypto .endif _DP_procstat= kvm util elf .if ${MK_CXX} == "yes" .if ${MK_LIBCPLUSPLUS} != "no" _DP_proc= cxxrt .else _DP_proc= supcplusplus .endif .endif .if ${MK_CDDL} != "no" _DP_proc+= ctf .endif _DP_mp= crypto _DP_memstat= kvm _DP_magic= z _DP_ldns= crypto .if ${MK_OPENSSL} != "no" _DP_fetch= ssl crypto .else _DP_fetch= md .endif _DP_execinfo= elf _DP_dwarf= elf _DP_dpv= dialog figpar util _DP_dialog= ncursesw m _DP_cuse= pthread _DP_atf_cxx= atf_c _DP_devstat= kvm _DP_pam= radius tacplus opie md util .if ${MK_KERBEROS} != "no" _DP_pam+= krb5 .endif .if ${MK_OPENSSH} != "no" _DP_pam+= ssh .endif .if ${MK_NIS} != "no" _DP_pam+= ypclnt .endif _DP_krb5+= asn1 com_err crypt crypto hx509 roken wind heimbase heimipcc \ pthread _DP_gssapi_krb5+= gssapi krb5 crypto roken asn1 com_err _DP_ucl= m # Define spacial cases LDADD_supcplusplus= -lsupc++ LDADD_atf_c= -L${LIBATF_CDIR} -latf-c LDADD_atf_cxx= -L${LIBATF_CXXDIR} -latf-c++ .for _l in ${_LIBRARIES} .if ${_PRIVATELIBS:M${_l}} LDADD_${_l}_L+= -L${LIB${_l:tu}DIR} .endif .if ${_INTERNALIBS:M${_l}} LDADD_${_l}_L+= -L${LIB${_l:tu}DIR} .endif DPADD_${_l}?= ${LIB${_l:tu}} LDADD_${_l}?= ${LDADD_${_l}_L} -l${_l} .if defined(_DP_${_l}) && defined(NO_SHARED) .for _d in ${_DP_${_l}} DPADD_${_l}+= ${DPADD_${_d}} LDADD_${_l}+= ${LDADD_${_d}} .endfor .endif .endfor DPADD_sqlite3+= ${DPADD_pthread} LDADD_sqlite3+= ${LDADD_pthread} DPADD_atf_cxx+= ${DPADD_atf_c} LDADD_atf_cxx+= ${LDADD_atf_c} DPADD_ipf+= ${DPADD_kvm} LDADD_ipf+= ${LDADD_kvm} # The following depends on libraries which are using pthread DPADD_hdb+= ${DPADD_pthread} LDADD_hdb+= ${LDADD_pthread} DPADD_kadm5srv+= ${DPADD_pthread} LDADD_kadm5srv+= ${LDADD_pthread} DPADD_krb5+= ${DPADD_pthread} LDADD_krb5+= ${LDADD_pthread} DPADD_gssapi_krb5+= ${DPADD_pthread} LDADD_gssapi_krb5+= ${LDADD_pthread} .for _l in ${LIBADD} .if ${_PRIVATELIBS:M${_l}} USEPRIVATELIB+= ${_l} .endif DPADD+= ${DPADD_${_l}} LDADD+= ${LDADD_${_l}} .endfor .if defined(USEPRIVATELIB) LDFLAGS+= -rpath ${LIBPRIVATEDIR} .endif LIBATF_CDIR= ${ROOTOBJDIR}/lib/atf/libatf-c LDATF_C?= ${LIBATF_CDIR}/libatf-c.so LIBATF_C?= ${LIBATF_CDIR}/libatf-c.a LIBATF_CXXDIR= ${ROOTOBJDIR}/lib/atf/libatf-c++ LDATF_CXX?= ${LIBATF_CXXDIR}/libatf-c++.so LIBATF_CXX?= ${LIBATF_CXXDIR}/libatf-c++.a LIBBSDSTATDIR= ${ROOTOBJDIR}/lib/libbsdstat LIBBSDSTAT?= ${LIBBSDSTATDIR}/libbsdstat.a + +LIBELFTCDIR= ${ROOTOBJDIR}/lib/libelftc +LDELFTC?= ${LIBELFTCDIR}/libelftc.a +LIBELFTC?= ${LIBELFTCDIR}/libelftc.a LIBEVENTDIR= ${ROOTOBJDIR}/lib/libevent LIBEVENT?= ${LIBEVENTDIR}/libevent.a LIBHEIMIPCCDIR= ${ROOTOBJDIR}/kerberos5/lib/libheimipcc LIBHEIMIPCC?= ${LIBHEIMIPCCDIR}/libheimipcc.a LIBHEIMIPCSDIR= ${ROOTOBJDIR}/kerberos5/lib/libheimipcs LIBHEIMIPCS?= ${LIBHEIMIPCSDIR}/libheimipcs.a LIBLDNSDIR= ${ROOTOBJDIR}/lib/libldns LIBLDNS?= ${LIBLDNSDIR}/libldns.a LIBSSHDIR= ${ROOTOBJDIR}/secure/lib/libssh LIBSSH?= ${LIBSSHDIR}/libssh.a LIBUNBOUNDDIR= ${ROOTOBJDIR}/lib/libunbound LIBUNBOUND?= ${LIBUNBOUNDDIR}/libunbound.a LIBUCLDIR= ${ROOTOBJDIR}/lib/libucl LIBUCL?= ${LIBUCLDIR}/libucl.a LIBREADLINEDIR= ${ROOTOBJDIR}/gnu/lib/libreadline/readline LIBREADLINE?= ${LIBREADLINEDIR}/libreadline.a LIBOHASHDIR= ${ROOTOBJDIR}/lib/libohash LIBOHASH?= ${LIBOHASHDIR}/libohash.a LIBSQLITE3DIR= ${ROOTOBJDIR}/lib/libsqlite3 LIBSQLITE3?= ${LIBSQLITE3DIR}/libsqlite3.a LIBMANDOCDIR= ${ROOTOBJDIR}/lib/libmandoc LIBMANDOC?= ${LIBMANDOCDIR}/libmandoc.a LIBSMDIR= ${ROOTOBJDIR}/lib/libsm LIBSM?= ${LIBSMDIR}/libsm.a LIBSMDBDIR= ${ROOTOBJDIR}/lib/libsmdb LIBSMDB?= ${LIBSMDBDIR}/libsmdb.a LIBSMUTILDIR= ${ROOTOBJDIR}/lib/libsmutil LIBSMUTIL?= ${LIBSMDBDIR}/libsmutil.a LIBNETBSDDIR?= ${ROOTOBJDIR}/lib/libnetbsd LIBNETBSD?= ${LIBNETBSDDIR}/libnetbsd.a LIBVERSDIR?= ${ROOTOBJDIR}/kerberos5/lib/libvers LIBVERS?= ${LIBVERSDIR}/libvers.a LIBSLDIR= ${ROOTOBJDIR}/kerberos5/lib/libsl LIBSL?= ${LIBSLDIR}/libsl.a LIBIPFDIR= ${ROOTOBJDIR}/sbin/ipf/libipf LIBIPF?= ${LIBIPFDIR}/libipf.a LIBTELNETDIR= ${ROOTOBJDIR}/lib/libtelnet LIBTELNET?= ${LIBIPFDIR}/libtelnet.a LIBCRONDIR= ${ROOTOBJDIR}/usr.sbin/cron/lib LIBCRON?= ${LIBCRONDIR}/libcron.a LIBNTPDIR= ${ROOTOBJDIR}/usr.sbin/ntp/libntp LIBNTP?= ${LIBNTPDIR}/libntp.a LIBOPTSDIR= ${ROOTOBJDIR}/usr.sbin/ntp/libopts LIBOTPS?= ${LIBOPTSDIR}/libopts.a LIBPARSEDIR= ${ROOTOBJDIR}/usr.sbin/ntp/libparse LIBPARSE?= ${LIBOPTSDIR}/libparse.a LIBLPRDIR= ${ROOTOBJDIR}/usr.sbin/lpr/common_source LIBLPR?= ${LIBOPTSDIR}/liblpr.a LIBFIFOLOGDIR= ${ROOTOBJDIR}/usr.sbin/fifolog/lib LIBFIFOLOG?= ${LIBOPTSDIR}/libfifolog.a LIBBSNMPTOOLSDIR= ${ROOTOBJDIR}/usr.sbin/bsnmpd/tools/libbsnmptools LIBBSNMPTOOLS?= ${LIBBSNMPTOOLSDIR}/libbsnmptools.a LIBAMUDIR= ${ROOTOBJDIR}/usr.sbin/amd/libamu LIBAMU?= ${LIBAMUDIR}/libamu/libamu.a Index: projects/clang350-import/share/mk/src.opts.mk =================================================================== --- projects/clang350-import/share/mk/src.opts.mk (revision 275386) +++ projects/clang350-import/share/mk/src.opts.mk (revision 275387) @@ -1,370 +1,371 @@ # $FreeBSD$ # # Option file for FreeBSD /usr/src builds. # # Users define WITH_FOO and WITHOUT_FOO on the command line or in /etc/src.conf # and /etc/make.conf files. These translate in the build system to MK_FOO={yes,no} # with sensible (usually) defaults. # # Makefiles must include bsd.opts.mk after defining specific MK_FOO options that # are applicable for that Makefile (typically there are none, but sometimes there # are exceptions). Recursive makes usually add MK_FOO=no for options that they wish # to omit from that make. # # Makefiles must include bsd.srcpot.mk before they test the value of any MK_FOO # variable. # # Makefiles may also assume that this file is included by src.opts.mk should it # need variables defined there prior to the end of the Makefile where # bsd.{subdir,lib.bin}.mk is traditionally included. # # The old-style YES_FOO and NO_FOO are being phased out. No new instances of them # should be added. Old instances should be removed since they were just to # bridge the gap between FreeBSD 4 and FreeBSD 5. # # Makefiles should never test WITH_FOO or WITHOUT_FOO directly (although an # exception is made for _WITHOUT_SRCONF which turns off this mechanism # completely inside bsd.*.mk files). # .if !target(____) ____: .include # # Define MK_* variables (which are either "yes" or "no") for users # to set via WITH_*/WITHOUT_* in /etc/src.conf and override in the # make(1) environment. # These should be tested with `== "no"' or `!= "no"' in makefiles. # The NO_* variables should only be set by makefiles for variables # that haven't been converted over. # # These options are used by src the builds __DEFAULT_YES_OPTIONS = \ ACCT \ ACPI \ AMD \ APM \ AT \ ATM \ AUDIT \ AUTHPF \ BINUTILS \ BINUTILS_BOOTSTRAP \ BLUETOOTH \ BOOT \ BSD_CPIO \ BSNMP \ BZIP2 \ CALENDAR \ CAPSICUM \ CASPER \ CDDL \ CPP \ CROSS_COMPILER \ CRYPT \ CTM \ CUSE \ CXX \ DICT \ DMAGENT \ DYNAMICROOT \ ED_CRYPTO \ EXAMPLES \ FDT \ FLOPPY \ FMTREE \ FORTH \ FP_LIBC \ FREEBSD_UPDATE \ GAMES \ GCOV \ GDB \ GNU \ GNU_GREP_COMPAT \ GPIB \ GPIO \ GPL_DTC \ GROFF \ HTML \ HYPERV \ ICONV \ INET \ INET6 \ IPFILTER \ IPFW \ JAIL \ KDUMP \ KVM \ LDNS \ LDNS_UTILS \ LEGACY_CONSOLE \ LIB32 \ LIBPTHREAD \ LIBTHR \ LOCALES \ LOCATE \ LPR \ LS_COLORS \ LZMA_SUPPORT \ MAIL \ MAILWRAPPER \ MAKE \ NDIS \ NETCAT \ NETGRAPH \ NLS_CATALOGS \ NS_CACHING \ NTP \ OPENSSL \ PAM \ PC_SYSINSTALL \ PF \ PKGBOOTSTRAP \ PMC \ PORTSNAP \ PPP \ QUOTAS \ RCMDS \ RCS \ RESCUE \ ROUTED \ SENDMAIL \ SETUID_LOGIN \ SHAREDOCS \ SOURCELESS \ SOURCELESS_HOST \ SOURCELESS_UCODE \ SVNLITE \ SYSCALL_COMPAT \ SYSCONS \ SYSINSTALL \ TCSH \ TELNET \ TESTS \ TEXTPROC \ UNBOUND \ USB \ UTMPX \ VI \ VT \ WIRELESS \ WPA_SUPPLICANT_EAPOL \ ZFS \ ZONEINFO __DEFAULT_NO_OPTIONS = \ BSD_GREP \ CLANG_EXTRAS \ EISA \ + ELFTOOLCHAIN_TOOLS \ FMAKE \ HESIOD \ LLDB \ NAND \ OFED \ OPENLDAP \ OPENSSH_NONE_CIPHER \ SHARED_TOOLCHAIN \ SORT_THREADS \ SVN # # Default behaviour of some options depends on the architecture. Unfortunately # this means that we have to test TARGET_ARCH (the buildworld case) as well # as MACHINE_ARCH (the non-buildworld case). Normally TARGET_ARCH is not # used at all in bsd.*.mk, but we have to make an exception here if we want # to allow defaults for some things like clang to vary by target architecture. # Additional, per-target behavior should be rarely added only after much # gnashing of teeth and grinding of gears. # .if defined(TARGET_ARCH) __T=${TARGET_ARCH} .else __T=${MACHINE_ARCH} .endif .if defined(TARGET) __TT=${TARGET} .else __TT=${MACHINE} .endif # Clang is only for x86, powerpc and little-endian arm right now, by default. .if ${__T} == "amd64" || ${__T} == "i386" || ${__T:Mpowerpc*} __DEFAULT_YES_OPTIONS+=CLANG CLANG_FULL CLANG_BOOTSTRAP .elif ${__TT} == "arm" && ${__T:Marm*eb*} == "" __DEFAULT_YES_OPTIONS+=CLANG CLANG_BOOTSTRAP # GCC is unable to build the full clang on arm, disable it by default. __DEFAULT_NO_OPTIONS+=CLANG_FULL .else __DEFAULT_NO_OPTIONS+=CLANG CLANG_FULL CLANG_BOOTSTRAP .endif # Clang the default system compiler only on little-endian arm and x86. .if ${__T} == "amd64" || (${__TT} == "arm" && ${__T:Marm*eb*} == "") || \ ${__T} == "i386" __DEFAULT_YES_OPTIONS+=CLANG_IS_CC __DEFAULT_NO_OPTIONS+=GCC GCC_BOOTSTRAP GNUCXX .else # If clang is not cc, then build gcc by default __DEFAULT_NO_OPTIONS+=CLANG_IS_CC CLANG CLANG_BOOTSTRAP __DEFAULT_YES_OPTIONS+=GCC GCC_BOOTSTRAP GNUCXX .endif .include # # MK_* options that default to "yes" if the compiler is a C++11 compiler. # .include .for var in \ LIBCPLUSPLUS .if !defined(MK_${var}) .if ${COMPILER_FEATURES:Mc++11} .if defined(WITHOUT_${var}) MK_${var}:= no .else MK_${var}:= yes .endif .else .if defined(WITH_${var}) MK_${var}:= yes .else MK_${var}:= no .endif .endif .endif .endfor # # Force some options off if their dependencies are off. # Order is somewhat important. # .if ${MK_LIBPTHREAD} == "no" MK_LIBTHR:= no .endif .if ${MK_LDNS} == "no" MK_LDNS_UTILS:= no MK_UNBOUND:= no .endif .if ${MK_SOURCELESS} == "no" MK_SOURCELESS_HOST:= no MK_SOURCELESS_UCODE:= no .endif .if ${MK_CDDL} == "no" MK_ZFS:= no MK_CTF:= no .endif .if ${MK_CRYPT} == "no" MK_OPENSSL:= no MK_OPENSSH:= no MK_KERBEROS:= no .endif .if ${MK_CXX} == "no" MK_CLANG:= no MK_GROFF:= no MK_GNUCXX:= no .endif .if ${MK_MAIL} == "no" MK_MAILWRAPPER:= no MK_SENDMAIL:= no MK_DMAGENT:= no .endif .if ${MK_NETGRAPH} == "no" MK_ATM:= no MK_BLUETOOTH:= no .endif .if ${MK_OPENSSL} == "no" MK_OPENSSH:= no MK_KERBEROS:= no .endif .if ${MK_PF} == "no" MK_AUTHPF:= no .endif .if ${MK_TEXTPROC} == "no" MK_GROFF:= no .endif .if ${MK_CROSS_COMPILER} == "no" MK_BINUTILS_BOOTSTRAP:= no MK_CLANG_BOOTSTRAP:= no MK_GCC_BOOTSTRAP:= no .endif .if ${MK_TOOLCHAIN} == "no" MK_BINUTILS:= no MK_CLANG:= no MK_GCC:= no MK_GDB:= no MK_INCLUDES:= no .endif .if ${MK_CLANG} == "no" MK_CLANG_EXTRAS:= no MK_CLANG_FULL:= no .endif # # Set defaults for the MK_*_SUPPORT variables. # # # MK_*_SUPPORT options which default to "yes" unless their corresponding # MK_* variable is set to "no". # .for var in \ BZIP2 \ GNU \ INET \ INET6 \ KERBEROS \ KVM \ NETGRAPH \ PAM \ TESTS \ WIRELESS .if defined(WITHOUT_${var}_SUPPORT) || ${MK_${var}} == "no" MK_${var}_SUPPORT:= no .else MK_${var}_SUPPORT:= yes .endif .endfor # # MK_* options whose default value depends on another option. # .for vv in \ GSSAPI/KERBEROS \ MAN_UTILS/MAN .if defined(WITH_${vv:H}) MK_${vv:H}:= yes .elif defined(WITHOUT_${vv:H}) MK_${vv:H}:= no .else MK_${vv:H}:= ${MK_${vv:T}} .endif .endfor .if !${COMPILER_FEATURES:Mc++11} MK_LLDB:= no .endif # gcc 4.8 and newer supports libc++, so suppress gnuc++ in that case. # while in theory we could build it with that, we don't want to do # that since it creates too much confusion for too little gain. .if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 40800 MK_GNUCXX:=no MK_GCC:=no .endif .endif # !target(____) Index: projects/clang350-import/share =================================================================== --- projects/clang350-import/share (revision 275386) +++ projects/clang350-import/share (revision 275387) Property changes on: projects/clang350-import/share ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share:r275364-275386 Index: projects/clang350-import/sys/arm/include/asm.h =================================================================== --- projects/clang350-import/sys/arm/include/asm.h (revision 275386) +++ projects/clang350-import/sys/arm/include/asm.h (revision 275387) @@ -1,206 +1,212 @@ /* $NetBSD: asm.h,v 1.5 2003/08/07 16:26:53 agc Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)asm.h 5.5 (Berkeley) 5/7/91 * * $FreeBSD$ */ #ifndef _MACHINE_ASM_H_ #define _MACHINE_ASM_H_ #include #define _C_LABEL(x) x #define _ASM_LABEL(x) x #ifndef _ALIGN_TEXT # define _ALIGN_TEXT .align 0 #endif #if defined(__ARM_EABI__) && !defined(_STANDALONE) #define STOP_UNWINDING .cantunwind #define _FNSTART .fnstart #define _FNEND .fnend #else #define STOP_UNWINDING #define _FNSTART #define _FNEND #endif /* * EENTRY()/EEND() mark "extra" entry/exit points from a function. * The unwind info cannot handle the concept of a nested function, or a function * with multiple .fnstart directives, but some of our assembler code is written * with multiple labels to allow entry at several points. The EENTRY() macro * defines such an extra entry point without a new .fnstart, so that it's * basically just a label that you can jump to. The EEND() macro does nothing * at all, except document the exit point associated with the same-named entry. */ #define _EENTRY(x) .globl x; .type x,_ASM_TYPE_FUNCTION; x: #define _EEND(x) /* nothing */ /* * gas/arm uses @ as a single comment character and thus cannot be used here * Instead it recognised the # instead of an @ symbols in .type directives * We define a couple of macros so that assembly code will not be dependent * on one or the other. */ #define _ASM_TYPE_FUNCTION #function #define _ASM_TYPE_OBJECT #object #define GLOBAL(X) .globl x #define _ENTRY(x) \ .text; _ALIGN_TEXT; _EENTRY(x) _FNSTART #define _END(x) .size x, . - x; _FNEND #ifdef GPROF # define _PROF_PROLOGUE \ mov ip, lr; bl __mcount #else # define _PROF_PROLOGUE #endif #define ENTRY(y) _ENTRY(_C_LABEL(y)); _PROF_PROLOGUE #define EENTRY(y) _EENTRY(_C_LABEL(y)); _PROF_PROLOGUE #define ENTRY_NP(y) _ENTRY(_C_LABEL(y)) #define EENTRY_NP(y) _EENTRY(_C_LABEL(y)) #define END(y) _END(_C_LABEL(y)) #define EEND(y) #define ASENTRY(y) _ENTRY(_ASM_LABEL(y)); _PROF_PROLOGUE #define ASEENTRY(y) _EENTRY(_ASM_LABEL(y)); _PROF_PROLOGUE #define ASENTRY_NP(y) _ENTRY(_ASM_LABEL(y)) #define ASEENTRY_NP(y) _EENTRY(_ASM_LABEL(y)) #define ASEND(y) _END(_ASM_LABEL(y)) #define ASEEND(y) #define ASMSTR .asciz #if defined(PIC) #define PLT_SYM(x) PIC_SYM(x, PLT) #define GOT_SYM(x) PIC_SYM(x, GOT) #define GOT_GET(x,got,sym) \ ldr x, sym; \ ldr x, [x, got] #define GOT_INIT(got,gotsym,pclabel) \ ldr got, gotsym; \ - add got, got, pc; \ - pclabel: + pclabel: add got, got, pc +#ifdef __thumb__ #define GOT_INITSYM(gotsym,pclabel) \ - gotsym: .word _C_LABEL(_GLOBAL_OFFSET_TABLE_) + (. - (pclabel+4)) + .align 0; \ + gotsym: .word _C_LABEL(_GLOBAL_OFFSET_TABLE_) - (pclabel+4) +#else +#define GOT_INITSYM(gotsym,pclabel) \ + .align 0; \ + gotsym: .word _C_LABEL(_GLOBAL_OFFSET_TABLE_) - (pclabel+8) +#endif #ifdef __STDC__ #define PIC_SYM(x,y) x ## ( ## y ## ) #else #define PIC_SYM(x,y) x/**/(/**/y/**/) #endif #else #define PLT_SYM(x) x #define GOT_SYM(x) x #define GOT_GET(x,got,sym) \ ldr x, sym; #define GOT_INIT(got,gotsym,pclabel) #define GOT_INITSYM(gotsym,pclabel) #define PIC_SYM(x,y) x #endif /* PIC */ #undef __FBSDID #if !defined(lint) && !defined(STRIP_FBSDID) #define __FBSDID(s) .ident s #else #define __FBSDID(s) /* nothing */ #endif #define WEAK_ALIAS(alias,sym) \ .weak alias; \ alias = sym #ifdef __STDC__ #define WARN_REFERENCES(sym,msg) \ .stabs msg ## ,30,0,0,0 ; \ .stabs __STRING(_C_LABEL(sym)) ## ,1,0,0,0 #else #define WARN_REFERENCES(sym,msg) \ .stabs msg,30,0,0,0 ; \ .stabs __STRING(sym),1,0,0,0 #endif /* __STDC__ */ /* Exactly one of the __ARM_ARCH_*__ macros will be defined by the compiler. */ /* The _ARM_ARCH_* macros are deprecated and will be removed soon. */ /* This should be moved into another header so it can be used in * both asm and C code. machine/asm.h cannot be included in C code. */ #if defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__) #define _ARM_ARCH_7 #define _HAVE_ARMv7_INSTRUCTIONS 1 #endif #if defined (_HAVE_ARMv7_INSTRUCTIONS) || defined (__ARM_ARCH_6__) || \ defined (__ARM_ARCH_6J__) || defined (__ARM_ARCH_6K__) || \ defined (__ARM_ARCH_6Z__) || defined (__ARM_ARCH_6ZK__) #define _ARM_ARCH_6 #define _HAVE_ARMv6_INSTRUCTIONS 1 #endif #if defined (_HAVE_ARMv6_INSTRUCTIONS) || defined (__ARM_ARCH_5TE__) || \ defined (__ARM_ARCH_5TEJ__) || defined (__ARM_ARCH_5E__) #define _ARM_ARCH_5E #define _HAVE_ARMv5E_INSTRUCTIONS 1 #endif #if defined (_HAVE_ARMv5E_INSTRUCTIONS) || defined (__ARM_ARCH_5__) || \ defined (__ARM_ARCH_5T__) #define _ARM_ARCH_5 #define _HAVE_ARMv5_INSTRUCTIONS 1 #endif #if defined (_HAVE_ARMv5_INSTRUCTIONS) || defined (__ARM_ARCH_4T__) #define _ARM_ARCH_4T #define _HAVE_ARMv4T_INSTRUCTIONS 1 #endif /* FreeBSD requires ARMv4, so this is always set. */ #define _HAVE_ARMv4_INSTRUCTIONS 1 #if defined (_HAVE_ARMv4T_INSTRUCTIONS) # define RET bx lr # define RETeq bxeq lr # define RETne bxne lr # define RETc(c) bx##c lr #else # define RET mov pc, lr # define RETeq moveq pc, lr # define RETne movne pc, lr # define RETc(c) mov##c pc, lr #endif #endif /* !_MACHINE_ASM_H_ */ Index: projects/clang350-import/sys/arm/ti/ti_pruss.c =================================================================== --- projects/clang350-import/sys/arm/ti/ti_pruss.c (revision 275386) +++ projects/clang350-import/sys/arm/ti/ti_pruss.c (revision 275387) @@ -1,324 +1,305 @@ /*- * Copyright (c) 2013 Rui Paulo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG #ifdef DEBUG #define DPRINTF(fmt, ...) do { \ printf("%s: ", __func__); \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(fmt, ...) #endif static device_probe_t ti_pruss_probe; static device_attach_t ti_pruss_attach; static device_detach_t ti_pruss_detach; static void ti_pruss_intr(void *); static d_open_t ti_pruss_open; -static d_close_t ti_pruss_close; static d_mmap_t ti_pruss_mmap; static void ti_pruss_kq_read_detach(struct knote *); static int ti_pruss_kq_read_event(struct knote *, long); static d_kqfilter_t ti_pruss_kqfilter; #define TI_PRUSS_IRQS 8 struct ti_pruss_softc { struct mtx sc_mtx; struct resource *sc_mem_res; struct resource *sc_irq_res[TI_PRUSS_IRQS]; void *sc_intr[TI_PRUSS_IRQS]; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; struct cdev *sc_pdev; struct selinfo sc_selinfo; - uint32_t sc_inuse; }; static struct cdevsw ti_pruss_cdevsw = { .d_version = D_VERSION, .d_name = "ti_pruss", .d_open = ti_pruss_open, - .d_close = ti_pruss_close, .d_mmap = ti_pruss_mmap, .d_kqfilter = ti_pruss_kqfilter, }; static device_method_t ti_pruss_methods[] = { DEVMETHOD(device_probe, ti_pruss_probe), DEVMETHOD(device_attach, ti_pruss_attach), DEVMETHOD(device_detach, ti_pruss_detach), DEVMETHOD_END }; static driver_t ti_pruss_driver = { "ti_pruss", ti_pruss_methods, sizeof(struct ti_pruss_softc) }; static devclass_t ti_pruss_devclass; DRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0); static struct resource_spec ti_pruss_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { SYS_RES_IRQ, 4, RF_ACTIVE }, { SYS_RES_IRQ, 5, RF_ACTIVE }, { SYS_RES_IRQ, 6, RF_ACTIVE }, { SYS_RES_IRQ, 7, RF_ACTIVE }, { -1, 0, 0 } }; static struct ti_pruss_irq_arg { int irq; struct ti_pruss_softc *sc; } ti_pruss_irq_args[TI_PRUSS_IRQS]; static __inline uint32_t ti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg) { return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static __inline void ti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val) { bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); } static int ti_pruss_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "ti,pruss-v1") || ofw_bus_is_compatible(dev, "ti,pruss-v2")) { device_set_desc(dev, "TI Programmable Realtime Unit Subsystem"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int ti_pruss_attach(device_t dev) { struct ti_pruss_softc *sc; int rid, i; if (ti_prcm_clk_enable(PRUSS_CLK) != 0) { device_printf(dev, "could not enable PRUSS clock\n"); return (ENXIO); } sc = device_get_softc(dev); rid = 0; mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF); sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_mem_res); sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) { device_printf(dev, "could not allocate interrupt resource\n"); ti_pruss_detach(dev); return (ENXIO); } for (i = 0; i < TI_PRUSS_IRQS; i++) { ti_pruss_irq_args[i].irq = i; ti_pruss_irq_args[i].sc = sc; - if (bus_setup_intr(dev, sc->sc_irq_res[i], + if (bus_setup_intr(dev, sc->sc_irq_res[i], INTR_MPSAFE | INTR_TYPE_MISC, - NULL, ti_pruss_intr, &ti_pruss_irq_args[i], + NULL, ti_pruss_intr, &ti_pruss_irq_args[i], &sc->sc_intr[i]) != 0) { - device_printf(dev, + device_printf(dev, "unable to setup the interrupt handler\n"); ti_pruss_detach(dev); return (ENXIO); } } if (ti_pruss_reg_read(sc, PRUSS_AM18XX_INTC) == PRUSS_AM18XX_REV) device_printf(dev, "AM18xx PRU-ICSS\n"); else if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV) device_printf(dev, "AM33xx PRU-ICSS\n"); sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "pruss%d", device_get_unit(dev)); sc->sc_pdev->si_drv1 = dev; return (0); } static int ti_pruss_detach(device_t dev) { struct ti_pruss_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < TI_PRUSS_IRQS; i++) { if (sc->sc_intr[i]) bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]); if (sc->sc_irq_res[i]) - bus_release_resource(dev, SYS_RES_IRQ, + bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res[i]), sc->sc_irq_res[i]); } if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); if (sc->sc_pdev) destroy_dev(sc->sc_pdev); return (0); } static void ti_pruss_intr(void *arg) { struct ti_pruss_irq_arg *iap; struct ti_pruss_softc *sc; iap = arg; sc = iap->sc; DPRINTF("interrupt %p", sc); KNOTE_UNLOCKED(&sc->sc_selinfo.si_note, iap->irq); } static int -ti_pruss_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) +ti_pruss_open(struct cdev *cdev __unused, int oflags __unused, + int devtype __unused, struct thread *td __unused) { - device_t dev = cdev->si_drv1; - struct ti_pruss_softc *sc = device_get_softc(dev); - - if (atomic_cmpset_32(&sc->sc_inuse, 0, 1) == 0) - return (EBUSY); - else - return (0); -} - -static int -ti_pruss_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) -{ - device_t dev = cdev->si_drv1; - struct ti_pruss_softc *sc = device_get_softc(dev); - - sc->sc_inuse = 0; - return (0); } static int ti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) { device_t dev = cdev->si_drv1; struct ti_pruss_softc *sc = device_get_softc(dev); if (offset > rman_get_size(sc->sc_mem_res)) return (-1); *paddr = rman_get_start(sc->sc_mem_res) + offset; return (0); } static struct filterops ti_pruss_kq_read = { .f_isfd = 1, .f_detach = ti_pruss_kq_read_detach, .f_event = ti_pruss_kq_read_event, }; static void ti_pruss_kq_read_detach(struct knote *kn) { struct ti_pruss_softc *sc = kn->kn_hook; knlist_remove(&sc->sc_selinfo.si_note, kn, 0); } static int ti_pruss_kq_read_event(struct knote *kn, long hint) { kn->kn_data = hint; return (hint); } static int ti_pruss_kqfilter(struct cdev *cdev, struct knote *kn) { device_t dev = cdev->si_drv1; struct ti_pruss_softc *sc = device_get_softc(dev); switch (kn->kn_filter) { case EVFILT_READ: kn->kn_hook = sc; kn->kn_fop = &ti_pruss_kq_read; knlist_add(&sc->sc_selinfo.si_note, kn, 1); break; default: return (EINVAL); } return (0); } Index: projects/clang350-import/sys/cam/cam_xpt.c =================================================================== --- projects/clang350-import/sys/cam/cam_xpt.c (revision 275386) +++ projects/clang350-import/sys/cam/cam_xpt.c (revision 275387) @@ -1,5302 +1,5309 @@ /*- * Implementation of the Common Access Method Transport (XPT) layer. * * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* geometry translation */ #include /* for xpt_print below */ #include "opt_cam.h" /* * This is the maximum number of high powered commands (e.g. start unit) * that can be outstanding at a particular time. */ #ifndef CAM_MAX_HIGHPOWER #define CAM_MAX_HIGHPOWER 4 #endif /* Datastructures internal to the xpt layer */ MALLOC_DEFINE(M_CAMXPT, "CAM XPT", "CAM XPT buffers"); MALLOC_DEFINE(M_CAMDEV, "CAM DEV", "CAM devices"); MALLOC_DEFINE(M_CAMCCB, "CAM CCB", "CAM CCBs"); MALLOC_DEFINE(M_CAMPATH, "CAM path", "CAM paths"); /* Object for defering XPT actions to a taskqueue */ struct xpt_task { struct task task; void *data1; uintptr_t data2; }; struct xpt_softc { /* number of high powered commands that can go through right now */ struct mtx xpt_highpower_lock; STAILQ_HEAD(highpowerlist, cam_ed) highpowerq; int num_highpower; /* queue for handling async rescan requests. */ TAILQ_HEAD(, ccb_hdr) ccb_scanq; int buses_to_config; int buses_config_done; /* Registered busses */ TAILQ_HEAD(,cam_eb) xpt_busses; u_int bus_generation; struct intr_config_hook *xpt_config_hook; int boot_delay; struct callout boot_callout; struct mtx xpt_topo_lock; struct mtx xpt_lock; struct taskqueue *xpt_taskq; }; typedef enum { DM_RET_COPY = 0x01, DM_RET_FLAG_MASK = 0x0f, DM_RET_NONE = 0x00, DM_RET_STOP = 0x10, DM_RET_DESCEND = 0x20, DM_RET_ERROR = 0x30, DM_RET_ACTION_MASK = 0xf0 } dev_match_ret; typedef enum { XPT_DEPTH_BUS, XPT_DEPTH_TARGET, XPT_DEPTH_DEVICE, XPT_DEPTH_PERIPH } xpt_traverse_depth; struct xpt_traverse_config { xpt_traverse_depth depth; void *tr_func; void *tr_arg; }; typedef int xpt_busfunc_t (struct cam_eb *bus, void *arg); typedef int xpt_targetfunc_t (struct cam_et *target, void *arg); typedef int xpt_devicefunc_t (struct cam_ed *device, void *arg); typedef int xpt_periphfunc_t (struct cam_periph *periph, void *arg); typedef int xpt_pdrvfunc_t (struct periph_driver **pdrv, void *arg); /* Transport layer configuration information */ static struct xpt_softc xsoftc; SYSCTL_INT(_kern_cam, OID_AUTO, boot_delay, CTLFLAG_RDTUN, &xsoftc.boot_delay, 0, "Bus registration wait time"); struct cam_doneq { struct mtx_padalign cam_doneq_mtx; STAILQ_HEAD(, ccb_hdr) cam_doneq; int cam_doneq_sleep; }; static struct cam_doneq cam_doneqs[MAXCPU]; static int cam_num_doneqs; static struct proc *cam_proc; SYSCTL_INT(_kern_cam, OID_AUTO, num_doneqs, CTLFLAG_RDTUN, &cam_num_doneqs, 0, "Number of completion queues/threads"); struct cam_periph *xpt_periph; static periph_init_t xpt_periph_init; static struct periph_driver xpt_driver = { xpt_periph_init, "xpt", TAILQ_HEAD_INITIALIZER(xpt_driver.units), /* generation */ 0, CAM_PERIPH_DRV_EARLY }; PERIPHDRIVER_DECLARE(xpt, xpt_driver); static d_open_t xptopen; static d_close_t xptclose; static d_ioctl_t xptioctl; static d_ioctl_t xptdoioctl; static struct cdevsw xpt_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_open = xptopen, .d_close = xptclose, .d_ioctl = xptioctl, .d_name = "xpt", }; /* Storage for debugging datastructures */ struct cam_path *cam_dpath; u_int32_t cam_dflags = CAM_DEBUG_FLAGS; SYSCTL_UINT(_kern_cam, OID_AUTO, dflags, CTLFLAG_RWTUN, &cam_dflags, 0, "Enabled debug flags"); u_int32_t cam_debug_delay = CAM_DEBUG_DELAY; SYSCTL_UINT(_kern_cam, OID_AUTO, debug_delay, CTLFLAG_RWTUN, &cam_debug_delay, 0, "Delay in us after each debug message"); /* Our boot-time initialization hook */ static int cam_module_event_handler(module_t, int /*modeventtype_t*/, void *); static moduledata_t cam_moduledata = { "cam", cam_module_event_handler, NULL }; static int xpt_init(void *); DECLARE_MODULE(cam, cam_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND); MODULE_VERSION(cam, 1); static void xpt_async_bcast(struct async_list *async_head, u_int32_t async_code, struct cam_path *path, void *async_arg); static path_id_t xptnextfreepathid(void); static path_id_t xptpathid(const char *sim_name, int sim_unit, int sim_bus); static union ccb *xpt_get_ccb(struct cam_periph *periph); static union ccb *xpt_get_ccb_nowait(struct cam_periph *periph); static void xpt_run_allocq(struct cam_periph *periph, int sleep); static void xpt_run_allocq_task(void *context, int pending); static void xpt_run_devq(struct cam_devq *devq); static timeout_t xpt_release_devq_timeout; static void xpt_release_simq_timeout(void *arg) __unused; static void xpt_acquire_bus(struct cam_eb *bus); static void xpt_release_bus(struct cam_eb *bus); static uint32_t xpt_freeze_devq_device(struct cam_ed *dev, u_int count); static int xpt_release_devq_device(struct cam_ed *dev, u_int count, int run_queue); static struct cam_et* xpt_alloc_target(struct cam_eb *bus, target_id_t target_id); static void xpt_acquire_target(struct cam_et *target); static void xpt_release_target(struct cam_et *target); static struct cam_eb* xpt_find_bus(path_id_t path_id); static struct cam_et* xpt_find_target(struct cam_eb *bus, target_id_t target_id); static struct cam_ed* xpt_find_device(struct cam_et *target, lun_id_t lun_id); static void xpt_config(void *arg); static int xpt_schedule_dev(struct camq *queue, cam_pinfo *dev_pinfo, u_int32_t new_priority); static xpt_devicefunc_t xptpassannouncefunc; static void xptaction(struct cam_sim *sim, union ccb *work_ccb); static void xptpoll(struct cam_sim *sim); static void camisr_runqueue(void); static void xpt_done_process(struct ccb_hdr *ccb_h); static void xpt_done_td(void *); static dev_match_ret xptbusmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_eb *bus); static dev_match_ret xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_ed *device); static dev_match_ret xptperiphmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_periph *periph); static xpt_busfunc_t xptedtbusfunc; static xpt_targetfunc_t xptedttargetfunc; static xpt_devicefunc_t xptedtdevicefunc; static xpt_periphfunc_t xptedtperiphfunc; static xpt_pdrvfunc_t xptplistpdrvfunc; static xpt_periphfunc_t xptplistperiphfunc; static int xptedtmatch(struct ccb_dev_match *cdm); static int xptperiphlistmatch(struct ccb_dev_match *cdm); static int xptbustraverse(struct cam_eb *start_bus, xpt_busfunc_t *tr_func, void *arg); static int xpttargettraverse(struct cam_eb *bus, struct cam_et *start_target, xpt_targetfunc_t *tr_func, void *arg); static int xptdevicetraverse(struct cam_et *target, struct cam_ed *start_device, xpt_devicefunc_t *tr_func, void *arg); static int xptperiphtraverse(struct cam_ed *device, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg); static int xptpdrvtraverse(struct periph_driver **start_pdrv, xpt_pdrvfunc_t *tr_func, void *arg); static int xptpdperiphtraverse(struct periph_driver **pdrv, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg); static xpt_busfunc_t xptdefbusfunc; static xpt_targetfunc_t xptdeftargetfunc; static xpt_devicefunc_t xptdefdevicefunc; static xpt_periphfunc_t xptdefperiphfunc; static void xpt_finishconfig_task(void *context, int pending); static void xpt_dev_async_default(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg); static struct cam_ed * xpt_alloc_device_default(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); static xpt_devicefunc_t xptsetasyncfunc; static xpt_busfunc_t xptsetasyncbusfunc; static cam_status xptregister(struct cam_periph *periph, void *arg); static __inline int device_is_queued(struct cam_ed *device); static __inline int xpt_schedule_devq(struct cam_devq *devq, struct cam_ed *dev) { int retval; mtx_assert(&devq->send_mtx, MA_OWNED); if ((dev->ccbq.queue.entries > 0) && (dev->ccbq.dev_openings > 0) && (dev->ccbq.queue.qfrozen_cnt == 0)) { /* * The priority of a device waiting for controller * resources is that of the highest priority CCB * enqueued. */ retval = xpt_schedule_dev(&devq->send_queue, &dev->devq_entry, CAMQ_GET_PRIO(&dev->ccbq.queue)); } else { retval = 0; } return (retval); } static __inline int device_is_queued(struct cam_ed *device) { return (device->devq_entry.index != CAM_UNQUEUED_INDEX); } static void xpt_periph_init() { make_dev(&xpt_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "xpt0"); } static int xptopen(struct cdev *dev, int flags, int fmt, struct thread *td) { /* * Only allow read-write access. */ if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) return(EPERM); /* * We don't allow nonblocking access. */ if ((flags & O_NONBLOCK) != 0) { printf("%s: can't do nonblocking access\n", devtoname(dev)); return(ENODEV); } return(0); } static int xptclose(struct cdev *dev, int flag, int fmt, struct thread *td) { return(0); } /* * Don't automatically grab the xpt softc lock here even though this is going * through the xpt device. The xpt device is really just a back door for * accessing other devices and SIMs, so the right thing to do is to grab * the appropriate SIM lock once the bus/SIM is located. */ static int xptioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { int error; if ((error = xptdoioctl(dev, cmd, addr, flag, td)) == ENOTTY) { error = cam_compat_ioctl(dev, cmd, addr, flag, td, xptdoioctl); } return (error); } static int xptdoioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { int error; error = 0; switch(cmd) { /* * For the transport layer CAMIOCOMMAND ioctl, we really only want * to accept CCB types that don't quite make sense to send through a * passthrough driver. XPT_PATH_INQ is an exception to this, as stated * in the CAM spec. */ case CAMIOCOMMAND: { union ccb *ccb; union ccb *inccb; struct cam_eb *bus; inccb = (union ccb *)addr; bus = xpt_find_bus(inccb->ccb_h.path_id); if (bus == NULL) return (EINVAL); switch (inccb->ccb_h.func_code) { case XPT_SCAN_BUS: case XPT_RESET_BUS: if (inccb->ccb_h.target_id != CAM_TARGET_WILDCARD || inccb->ccb_h.target_lun != CAM_LUN_WILDCARD) { xpt_release_bus(bus); return (EINVAL); } break; case XPT_SCAN_TGT: if (inccb->ccb_h.target_id == CAM_TARGET_WILDCARD || inccb->ccb_h.target_lun != CAM_LUN_WILDCARD) { xpt_release_bus(bus); return (EINVAL); } break; default: break; } switch(inccb->ccb_h.func_code) { case XPT_SCAN_BUS: case XPT_RESET_BUS: case XPT_PATH_INQ: case XPT_ENG_INQ: case XPT_SCAN_LUN: case XPT_SCAN_TGT: ccb = xpt_alloc_ccb(); /* * Create a path using the bus, target, and lun the * user passed in. */ if (xpt_create_path(&ccb->ccb_h.path, NULL, inccb->ccb_h.path_id, inccb->ccb_h.target_id, inccb->ccb_h.target_lun) != CAM_REQ_CMP){ error = EINVAL; xpt_free_ccb(ccb); break; } /* Ensure all of our fields are correct */ xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, inccb->ccb_h.pinfo.priority); xpt_merge_ccb(ccb, inccb); xpt_path_lock(ccb->ccb_h.path); cam_periph_runccb(ccb, NULL, 0, 0, NULL); xpt_path_unlock(ccb->ccb_h.path); bcopy(ccb, inccb, sizeof(union ccb)); xpt_free_path(ccb->ccb_h.path); xpt_free_ccb(ccb); break; case XPT_DEBUG: { union ccb ccb; /* * This is an immediate CCB, so it's okay to * allocate it on the stack. */ /* * Create a path using the bus, target, and lun the * user passed in. */ if (xpt_create_path(&ccb.ccb_h.path, NULL, inccb->ccb_h.path_id, inccb->ccb_h.target_id, inccb->ccb_h.target_lun) != CAM_REQ_CMP){ error = EINVAL; break; } /* Ensure all of our fields are correct */ xpt_setup_ccb(&ccb.ccb_h, ccb.ccb_h.path, inccb->ccb_h.pinfo.priority); xpt_merge_ccb(&ccb, inccb); xpt_action(&ccb); bcopy(&ccb, inccb, sizeof(union ccb)); xpt_free_path(ccb.ccb_h.path); break; } case XPT_DEV_MATCH: { struct cam_periph_map_info mapinfo; struct cam_path *old_path; /* * We can't deal with physical addresses for this * type of transaction. */ if ((inccb->ccb_h.flags & CAM_DATA_MASK) != CAM_DATA_VADDR) { error = EINVAL; break; } /* * Save this in case the caller had it set to * something in particular. */ old_path = inccb->ccb_h.path; /* * We really don't need a path for the matching * code. The path is needed because of the * debugging statements in xpt_action(). They * assume that the CCB has a valid path. */ inccb->ccb_h.path = xpt_periph->path; bzero(&mapinfo, sizeof(mapinfo)); /* * Map the pattern and match buffers into kernel * virtual address space. */ error = cam_periph_mapmem(inccb, &mapinfo); if (error) { inccb->ccb_h.path = old_path; break; } /* * This is an immediate CCB, we can send it on directly. */ xpt_action(inccb); /* * Map the buffers back into user space. */ cam_periph_unmapmem(inccb, &mapinfo); inccb->ccb_h.path = old_path; error = 0; break; } default: error = ENOTSUP; break; } xpt_release_bus(bus); break; } /* * This is the getpassthru ioctl. It takes a XPT_GDEVLIST ccb as input, * with the periphal driver name and unit name filled in. The other * fields don't really matter as input. The passthrough driver name * ("pass"), and unit number are passed back in the ccb. The current * device generation number, and the index into the device peripheral * driver list, and the status are also passed back. Note that * since we do everything in one pass, unlike the XPT_GDEVLIST ccb, * we never return a status of CAM_GDEVLIST_LIST_CHANGED. It is * (or rather should be) impossible for the device peripheral driver * list to change since we look at the whole thing in one pass, and * we do it with lock protection. * */ case CAMGETPASSTHRU: { union ccb *ccb; struct cam_periph *periph; struct periph_driver **p_drv; char *name; u_int unit; int base_periph_found; ccb = (union ccb *)addr; unit = ccb->cgdl.unit_number; name = ccb->cgdl.periph_name; base_periph_found = 0; /* * Sanity check -- make sure we don't get a null peripheral * driver name. */ if (*ccb->cgdl.periph_name == '\0') { error = EINVAL; break; } /* Keep the list from changing while we traverse it */ xpt_lock_buses(); /* first find our driver in the list of drivers */ for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) if (strcmp((*p_drv)->driver_name, name) == 0) break; if (*p_drv == NULL) { xpt_unlock_buses(); ccb->ccb_h.status = CAM_REQ_CMP_ERR; ccb->cgdl.status = CAM_GDEVLIST_ERROR; *ccb->cgdl.periph_name = '\0'; ccb->cgdl.unit_number = 0; error = ENOENT; break; } /* * Run through every peripheral instance of this driver * and check to see whether it matches the unit passed * in by the user. If it does, get out of the loops and * find the passthrough driver associated with that * peripheral driver. */ for (periph = TAILQ_FIRST(&(*p_drv)->units); periph != NULL; periph = TAILQ_NEXT(periph, unit_links)) { if (periph->unit_number == unit) break; } /* * If we found the peripheral driver that the user passed * in, go through all of the peripheral drivers for that * particular device and look for a passthrough driver. */ if (periph != NULL) { struct cam_ed *device; int i; base_periph_found = 1; device = periph->path->device; for (i = 0, periph = SLIST_FIRST(&device->periphs); periph != NULL; periph = SLIST_NEXT(periph, periph_links), i++) { /* * Check to see whether we have a * passthrough device or not. */ if (strcmp(periph->periph_name, "pass") == 0) { /* * Fill in the getdevlist fields. */ strcpy(ccb->cgdl.periph_name, periph->periph_name); ccb->cgdl.unit_number = periph->unit_number; if (SLIST_NEXT(periph, periph_links)) ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; else ccb->cgdl.status = CAM_GDEVLIST_LAST_DEVICE; ccb->cgdl.generation = device->generation; ccb->cgdl.index = i; /* * Fill in some CCB header fields * that the user may want. */ ccb->ccb_h.path_id = periph->path->bus->path_id; ccb->ccb_h.target_id = periph->path->target->target_id; ccb->ccb_h.target_lun = periph->path->device->lun_id; ccb->ccb_h.status = CAM_REQ_CMP; break; } } } /* * If the periph is null here, one of two things has * happened. The first possibility is that we couldn't * find the unit number of the particular peripheral driver * that the user is asking about. e.g. the user asks for * the passthrough driver for "da11". We find the list of * "da" peripherals all right, but there is no unit 11. * The other possibility is that we went through the list * of peripheral drivers attached to the device structure, * but didn't find one with the name "pass". Either way, * we return ENOENT, since we couldn't find something. */ if (periph == NULL) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; ccb->cgdl.status = CAM_GDEVLIST_ERROR; *ccb->cgdl.periph_name = '\0'; ccb->cgdl.unit_number = 0; error = ENOENT; /* * It is unfortunate that this is even necessary, * but there are many, many clueless users out there. * If this is true, the user is looking for the * passthrough driver, but doesn't have one in his * kernel. */ if (base_periph_found == 1) { printf("xptioctl: pass driver is not in the " "kernel\n"); printf("xptioctl: put \"device pass\" in " "your kernel config file\n"); } } xpt_unlock_buses(); break; } default: error = ENOTTY; break; } return(error); } static int cam_module_event_handler(module_t mod, int what, void *arg) { int error; switch (what) { case MOD_LOAD: if ((error = xpt_init(NULL)) != 0) return (error); break; case MOD_UNLOAD: return EBUSY; default: return EOPNOTSUPP; } return 0; } static void xpt_rescan_done(struct cam_periph *periph, union ccb *done_ccb) { if (done_ccb->ccb_h.ppriv_ptr1 == NULL) { xpt_free_path(done_ccb->ccb_h.path); xpt_free_ccb(done_ccb); } else { done_ccb->ccb_h.cbfcnp = done_ccb->ccb_h.ppriv_ptr1; (*done_ccb->ccb_h.cbfcnp)(periph, done_ccb); } xpt_release_boot(); } /* thread to handle bus rescans */ static void xpt_scanner_thread(void *dummy) { union ccb *ccb; struct cam_path path; xpt_lock_buses(); for (;;) { if (TAILQ_EMPTY(&xsoftc.ccb_scanq)) msleep(&xsoftc.ccb_scanq, &xsoftc.xpt_topo_lock, PRIBIO, "-", 0); if ((ccb = (union ccb *)TAILQ_FIRST(&xsoftc.ccb_scanq)) != NULL) { TAILQ_REMOVE(&xsoftc.ccb_scanq, &ccb->ccb_h, sim_links.tqe); xpt_unlock_buses(); /* * Since lock can be dropped inside and path freed * by completion callback even before return here, * take our own path copy for reference. */ xpt_copy_path(&path, ccb->ccb_h.path); xpt_path_lock(&path); xpt_action(ccb); xpt_path_unlock(&path); xpt_release_path(&path); xpt_lock_buses(); } } } void xpt_rescan(union ccb *ccb) { struct ccb_hdr *hdr; /* Prepare request */ if (ccb->ccb_h.path->target->target_id == CAM_TARGET_WILDCARD && ccb->ccb_h.path->device->lun_id == CAM_LUN_WILDCARD) ccb->ccb_h.func_code = XPT_SCAN_BUS; else if (ccb->ccb_h.path->target->target_id != CAM_TARGET_WILDCARD && ccb->ccb_h.path->device->lun_id == CAM_LUN_WILDCARD) ccb->ccb_h.func_code = XPT_SCAN_TGT; else if (ccb->ccb_h.path->target->target_id != CAM_TARGET_WILDCARD && ccb->ccb_h.path->device->lun_id != CAM_LUN_WILDCARD) ccb->ccb_h.func_code = XPT_SCAN_LUN; else { xpt_print(ccb->ccb_h.path, "illegal scan path\n"); xpt_free_path(ccb->ccb_h.path); xpt_free_ccb(ccb); return; } ccb->ccb_h.ppriv_ptr1 = ccb->ccb_h.cbfcnp; ccb->ccb_h.cbfcnp = xpt_rescan_done; xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, CAM_PRIORITY_XPT); /* Don't make duplicate entries for the same paths. */ xpt_lock_buses(); if (ccb->ccb_h.ppriv_ptr1 == NULL) { TAILQ_FOREACH(hdr, &xsoftc.ccb_scanq, sim_links.tqe) { if (xpt_path_comp(hdr->path, ccb->ccb_h.path) == 0) { wakeup(&xsoftc.ccb_scanq); xpt_unlock_buses(); xpt_print(ccb->ccb_h.path, "rescan already queued\n"); xpt_free_path(ccb->ccb_h.path); xpt_free_ccb(ccb); return; } } } TAILQ_INSERT_TAIL(&xsoftc.ccb_scanq, &ccb->ccb_h, sim_links.tqe); xsoftc.buses_to_config++; wakeup(&xsoftc.ccb_scanq); xpt_unlock_buses(); } /* Functions accessed by the peripheral drivers */ static int xpt_init(void *dummy) { struct cam_sim *xpt_sim; struct cam_path *path; struct cam_devq *devq; cam_status status; int error, i; TAILQ_INIT(&xsoftc.xpt_busses); TAILQ_INIT(&xsoftc.ccb_scanq); STAILQ_INIT(&xsoftc.highpowerq); xsoftc.num_highpower = CAM_MAX_HIGHPOWER; mtx_init(&xsoftc.xpt_lock, "XPT lock", NULL, MTX_DEF); mtx_init(&xsoftc.xpt_highpower_lock, "XPT highpower lock", NULL, MTX_DEF); mtx_init(&xsoftc.xpt_topo_lock, "XPT topology lock", NULL, MTX_DEF); xsoftc.xpt_taskq = taskqueue_create("CAM XPT task", M_WAITOK, taskqueue_thread_enqueue, /*context*/&xsoftc.xpt_taskq); #ifdef CAM_BOOT_DELAY /* * Override this value at compile time to assist our users * who don't use loader to boot a kernel. */ xsoftc.boot_delay = CAM_BOOT_DELAY; #endif /* * The xpt layer is, itself, the equivelent of a SIM. * Allow 16 ccbs in the ccb pool for it. This should * give decent parallelism when we probe busses and * perform other XPT functions. */ devq = cam_simq_alloc(16); xpt_sim = cam_sim_alloc(xptaction, xptpoll, "xpt", /*softc*/NULL, /*unit*/0, /*mtx*/&xsoftc.xpt_lock, /*max_dev_transactions*/0, /*max_tagged_dev_transactions*/0, devq); if (xpt_sim == NULL) return (ENOMEM); mtx_lock(&xsoftc.xpt_lock); if ((status = xpt_bus_register(xpt_sim, NULL, 0)) != CAM_SUCCESS) { mtx_unlock(&xsoftc.xpt_lock); printf("xpt_init: xpt_bus_register failed with status %#x," " failing attach\n", status); return (EINVAL); } mtx_unlock(&xsoftc.xpt_lock); /* * Looking at the XPT from the SIM layer, the XPT is * the equivelent of a peripheral driver. Allocate * a peripheral driver entry for us. */ if ((status = xpt_create_path(&path, NULL, CAM_XPT_PATH_ID, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD)) != CAM_REQ_CMP) { mtx_unlock(&xsoftc.xpt_lock); printf("xpt_init: xpt_create_path failed with status %#x," " failing attach\n", status); return (EINVAL); } xpt_path_lock(path); cam_periph_alloc(xptregister, NULL, NULL, NULL, "xpt", CAM_PERIPH_BIO, path, NULL, 0, xpt_sim); xpt_path_unlock(path); xpt_free_path(path); if (cam_num_doneqs < 1) cam_num_doneqs = 1 + mp_ncpus / 6; else if (cam_num_doneqs > MAXCPU) cam_num_doneqs = MAXCPU; for (i = 0; i < cam_num_doneqs; i++) { mtx_init(&cam_doneqs[i].cam_doneq_mtx, "CAM doneq", NULL, MTX_DEF); STAILQ_INIT(&cam_doneqs[i].cam_doneq); error = kproc_kthread_add(xpt_done_td, &cam_doneqs[i], &cam_proc, NULL, 0, 0, "cam", "doneq%d", i); if (error != 0) { cam_num_doneqs = i; break; } } if (cam_num_doneqs < 1) { printf("xpt_init: Cannot init completion queues " "- failing attach\n"); return (ENOMEM); } /* * Register a callback for when interrupts are enabled. */ xsoftc.xpt_config_hook = (struct intr_config_hook *)malloc(sizeof(struct intr_config_hook), M_CAMXPT, M_NOWAIT | M_ZERO); if (xsoftc.xpt_config_hook == NULL) { printf("xpt_init: Cannot malloc config hook " "- failing attach\n"); return (ENOMEM); } xsoftc.xpt_config_hook->ich_func = xpt_config; if (config_intrhook_establish(xsoftc.xpt_config_hook) != 0) { free (xsoftc.xpt_config_hook, M_CAMXPT); printf("xpt_init: config_intrhook_establish failed " "- failing attach\n"); } return (0); } static cam_status xptregister(struct cam_periph *periph, void *arg) { struct cam_sim *xpt_sim; if (periph == NULL) { printf("xptregister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } xpt_sim = (struct cam_sim *)arg; xpt_sim->softc = periph; xpt_periph = periph; periph->softc = NULL; return(CAM_REQ_CMP); } int32_t xpt_add_periph(struct cam_periph *periph) { struct cam_ed *device; int32_t status; TASK_INIT(&periph->periph_run_task, 0, xpt_run_allocq_task, periph); device = periph->path->device; status = CAM_REQ_CMP; if (device != NULL) { mtx_lock(&device->target->bus->eb_mtx); device->generation++; SLIST_INSERT_HEAD(&device->periphs, periph, periph_links); mtx_unlock(&device->target->bus->eb_mtx); } return (status); } void xpt_remove_periph(struct cam_periph *periph) { struct cam_ed *device; device = periph->path->device; if (device != NULL) { mtx_lock(&device->target->bus->eb_mtx); device->generation++; SLIST_REMOVE(&device->periphs, periph, cam_periph, periph_links); mtx_unlock(&device->target->bus->eb_mtx); } } void xpt_announce_periph(struct cam_periph *periph, char *announce_string) { struct cam_path *path = periph->path; cam_periph_assert(periph, MA_OWNED); periph->flags |= CAM_PERIPH_ANNOUNCED; printf("%s%d at %s%d bus %d scbus%d target %d lun %jx\n", periph->periph_name, periph->unit_number, path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id, path->bus->path_id, path->target->target_id, (uintmax_t)path->device->lun_id); printf("%s%d: ", periph->periph_name, periph->unit_number); if (path->device->protocol == PROTO_SCSI) scsi_print_inquiry(&path->device->inq_data); else if (path->device->protocol == PROTO_ATA || path->device->protocol == PROTO_SATAPM) ata_print_ident(&path->device->ident_data); else if (path->device->protocol == PROTO_SEMB) semb_print_ident( (struct sep_identify_data *)&path->device->ident_data); else printf("Unknown protocol device\n"); if (path->device->serial_num_len > 0) { /* Don't wrap the screen - print only the first 60 chars */ printf("%s%d: Serial Number %.60s\n", periph->periph_name, periph->unit_number, path->device->serial_num); } /* Announce transport details. */ (*(path->bus->xport->announce))(periph); /* Announce command queueing. */ if (path->device->inq_flags & SID_CmdQue || path->device->flags & CAM_DEV_TAG_AFTER_COUNT) { printf("%s%d: Command Queueing enabled\n", periph->periph_name, periph->unit_number); } /* Announce caller's details if they've passed in. */ if (announce_string != NULL) printf("%s%d: %s\n", periph->periph_name, periph->unit_number, announce_string); } void xpt_announce_quirks(struct cam_periph *periph, int quirks, char *bit_string) { if (quirks != 0) { printf("%s%d: quirks=0x%b\n", periph->periph_name, periph->unit_number, quirks, bit_string); } } void xpt_denounce_periph(struct cam_periph *periph) { struct cam_path *path = periph->path; cam_periph_assert(periph, MA_OWNED); printf("%s%d at %s%d bus %d scbus%d target %d lun %jx\n", periph->periph_name, periph->unit_number, path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id, path->bus->path_id, path->target->target_id, (uintmax_t)path->device->lun_id); printf("%s%d: ", periph->periph_name, periph->unit_number); if (path->device->protocol == PROTO_SCSI) scsi_print_inquiry_short(&path->device->inq_data); else if (path->device->protocol == PROTO_ATA || path->device->protocol == PROTO_SATAPM) ata_print_ident_short(&path->device->ident_data); else if (path->device->protocol == PROTO_SEMB) semb_print_ident_short( (struct sep_identify_data *)&path->device->ident_data); else printf("Unknown protocol device"); if (path->device->serial_num_len > 0) printf(" s/n %.60s", path->device->serial_num); printf(" detached\n"); } int xpt_getattr(char *buf, size_t len, const char *attr, struct cam_path *path) { int ret = -1, l; struct ccb_dev_advinfo cdai; struct scsi_vpd_id_descriptor *idd; xpt_path_assert(path, MA_OWNED); memset(&cdai, 0, sizeof(cdai)); xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); cdai.ccb_h.func_code = XPT_DEV_ADVINFO; cdai.bufsiz = len; if (!strcmp(attr, "GEOM::ident")) cdai.buftype = CDAI_TYPE_SERIAL_NUM; else if (!strcmp(attr, "GEOM::physpath")) cdai.buftype = CDAI_TYPE_PHYS_PATH; else if (strcmp(attr, "GEOM::lunid") == 0 || strcmp(attr, "GEOM::lunname") == 0) { cdai.buftype = CDAI_TYPE_SCSI_DEVID; cdai.bufsiz = CAM_SCSI_DEVID_MAXLEN; } else goto out; cdai.buf = malloc(cdai.bufsiz, M_CAMXPT, M_NOWAIT|M_ZERO); if (cdai.buf == NULL) { ret = ENOMEM; goto out; } xpt_action((union ccb *)&cdai); /* can only be synchronous */ if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); if (cdai.provsiz == 0) goto out; if (cdai.buftype == CDAI_TYPE_SCSI_DEVID) { if (strcmp(attr, "GEOM::lunid") == 0) { idd = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf, cdai.provsiz, scsi_devid_is_lun_naa); if (idd == NULL) idd = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf, cdai.provsiz, scsi_devid_is_lun_eui64); } else idd = NULL; if (idd == NULL) idd = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf, cdai.provsiz, scsi_devid_is_lun_t10); if (idd == NULL) idd = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf, cdai.provsiz, scsi_devid_is_lun_name); if (idd == NULL) goto out; ret = 0; - if ((idd->proto_codeset & SVPD_ID_CODESET_MASK) == SVPD_ID_CODESET_ASCII || - (idd->proto_codeset & SVPD_ID_CODESET_MASK) == SVPD_ID_CODESET_UTF8) { + if ((idd->proto_codeset & SVPD_ID_CODESET_MASK) == SVPD_ID_CODESET_ASCII) { + if (idd->length < len) { + for (l = 0; l < idd->length; l++) + buf[l] = idd->identifier[l] ? + idd->identifier[l] : ' '; + buf[l] = 0; + } else + ret = EFAULT; + } else if ((idd->proto_codeset & SVPD_ID_CODESET_MASK) == SVPD_ID_CODESET_UTF8) { l = strnlen(idd->identifier, idd->length); if (l < len) { bcopy(idd->identifier, buf, l); buf[l] = 0; } else ret = EFAULT; } else { if (idd->length * 2 < len) { for (l = 0; l < idd->length; l++) sprintf(buf + l * 2, "%02x", idd->identifier[l]); } else ret = EFAULT; } } else { ret = 0; if (strlcpy(buf, cdai.buf, len) >= len) ret = EFAULT; } out: if (cdai.buf != NULL) free(cdai.buf, M_CAMXPT); return ret; } static dev_match_ret xptbusmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_eb *bus) { dev_match_ret retval; int i; retval = DM_RET_NONE; /* * If we aren't given something to match against, that's an error. */ if (bus == NULL) return(DM_RET_ERROR); /* * If there are no match entries, then this bus matches no * matter what. */ if ((patterns == NULL) || (num_patterns == 0)) return(DM_RET_DESCEND | DM_RET_COPY); for (i = 0; i < num_patterns; i++) { struct bus_match_pattern *cur_pattern; /* * If the pattern in question isn't for a bus node, we * aren't interested. However, we do indicate to the * calling routine that we should continue descending the * tree, since the user wants to match against lower-level * EDT elements. */ if (patterns[i].type != DEV_MATCH_BUS) { if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) retval |= DM_RET_DESCEND; continue; } cur_pattern = &patterns[i].pattern.bus_pattern; /* * If they want to match any bus node, we give them any * device node. */ if (cur_pattern->flags == BUS_MATCH_ANY) { /* set the copy flag */ retval |= DM_RET_COPY; /* * If we've already decided on an action, go ahead * and return. */ if ((retval & DM_RET_ACTION_MASK) != DM_RET_NONE) return(retval); } /* * Not sure why someone would do this... */ if (cur_pattern->flags == BUS_MATCH_NONE) continue; if (((cur_pattern->flags & BUS_MATCH_PATH) != 0) && (cur_pattern->path_id != bus->path_id)) continue; if (((cur_pattern->flags & BUS_MATCH_BUS_ID) != 0) && (cur_pattern->bus_id != bus->sim->bus_id)) continue; if (((cur_pattern->flags & BUS_MATCH_UNIT) != 0) && (cur_pattern->unit_number != bus->sim->unit_number)) continue; if (((cur_pattern->flags & BUS_MATCH_NAME) != 0) && (strncmp(cur_pattern->dev_name, bus->sim->sim_name, DEV_IDLEN) != 0)) continue; /* * If we get to this point, the user definitely wants * information on this bus. So tell the caller to copy the * data out. */ retval |= DM_RET_COPY; /* * If the return action has been set to descend, then we * know that we've already seen a non-bus matching * expression, therefore we need to further descend the tree. * This won't change by continuing around the loop, so we * go ahead and return. If we haven't seen a non-bus * matching expression, we keep going around the loop until * we exhaust the matching expressions. We'll set the stop * flag once we fall out of the loop. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_DESCEND) return(retval); } /* * If the return action hasn't been set to descend yet, that means * we haven't seen anything other than bus matching patterns. So * tell the caller to stop descending the tree -- the user doesn't * want to match against lower level tree elements. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) retval |= DM_RET_STOP; return(retval); } static dev_match_ret xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_ed *device) { dev_match_ret retval; int i; retval = DM_RET_NONE; /* * If we aren't given something to match against, that's an error. */ if (device == NULL) return(DM_RET_ERROR); /* * If there are no match entries, then this device matches no * matter what. */ if ((patterns == NULL) || (num_patterns == 0)) return(DM_RET_DESCEND | DM_RET_COPY); for (i = 0; i < num_patterns; i++) { struct device_match_pattern *cur_pattern; struct scsi_vpd_device_id *device_id_page; /* * If the pattern in question isn't for a device node, we * aren't interested. */ if (patterns[i].type != DEV_MATCH_DEVICE) { if ((patterns[i].type == DEV_MATCH_PERIPH) && ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE)) retval |= DM_RET_DESCEND; continue; } cur_pattern = &patterns[i].pattern.device_pattern; /* Error out if mutually exclusive options are specified. */ if ((cur_pattern->flags & (DEV_MATCH_INQUIRY|DEV_MATCH_DEVID)) == (DEV_MATCH_INQUIRY|DEV_MATCH_DEVID)) return(DM_RET_ERROR); /* * If they want to match any device node, we give them any * device node. */ if (cur_pattern->flags == DEV_MATCH_ANY) goto copy_dev_node; /* * Not sure why someone would do this... */ if (cur_pattern->flags == DEV_MATCH_NONE) continue; if (((cur_pattern->flags & DEV_MATCH_PATH) != 0) && (cur_pattern->path_id != device->target->bus->path_id)) continue; if (((cur_pattern->flags & DEV_MATCH_TARGET) != 0) && (cur_pattern->target_id != device->target->target_id)) continue; if (((cur_pattern->flags & DEV_MATCH_LUN) != 0) && (cur_pattern->target_lun != device->lun_id)) continue; if (((cur_pattern->flags & DEV_MATCH_INQUIRY) != 0) && (cam_quirkmatch((caddr_t)&device->inq_data, (caddr_t)&cur_pattern->data.inq_pat, 1, sizeof(cur_pattern->data.inq_pat), scsi_static_inquiry_match) == NULL)) continue; device_id_page = (struct scsi_vpd_device_id *)device->device_id; if (((cur_pattern->flags & DEV_MATCH_DEVID) != 0) && (device->device_id_len < SVPD_DEVICE_ID_HDR_LEN || scsi_devid_match((uint8_t *)device_id_page->desc_list, device->device_id_len - SVPD_DEVICE_ID_HDR_LEN, cur_pattern->data.devid_pat.id, cur_pattern->data.devid_pat.id_len) != 0)) continue; copy_dev_node: /* * If we get to this point, the user definitely wants * information on this device. So tell the caller to copy * the data out. */ retval |= DM_RET_COPY; /* * If the return action has been set to descend, then we * know that we've already seen a peripheral matching * expression, therefore we need to further descend the tree. * This won't change by continuing around the loop, so we * go ahead and return. If we haven't seen a peripheral * matching expression, we keep going around the loop until * we exhaust the matching expressions. We'll set the stop * flag once we fall out of the loop. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_DESCEND) return(retval); } /* * If the return action hasn't been set to descend yet, that means * we haven't seen any peripheral matching patterns. So tell the * caller to stop descending the tree -- the user doesn't want to * match against lower level tree elements. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) retval |= DM_RET_STOP; return(retval); } /* * Match a single peripheral against any number of match patterns. */ static dev_match_ret xptperiphmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_periph *periph) { dev_match_ret retval; int i; /* * If we aren't given something to match against, that's an error. */ if (periph == NULL) return(DM_RET_ERROR); /* * If there are no match entries, then this peripheral matches no * matter what. */ if ((patterns == NULL) || (num_patterns == 0)) return(DM_RET_STOP | DM_RET_COPY); /* * There aren't any nodes below a peripheral node, so there's no * reason to descend the tree any further. */ retval = DM_RET_STOP; for (i = 0; i < num_patterns; i++) { struct periph_match_pattern *cur_pattern; /* * If the pattern in question isn't for a peripheral, we * aren't interested. */ if (patterns[i].type != DEV_MATCH_PERIPH) continue; cur_pattern = &patterns[i].pattern.periph_pattern; /* * If they want to match on anything, then we will do so. */ if (cur_pattern->flags == PERIPH_MATCH_ANY) { /* set the copy flag */ retval |= DM_RET_COPY; /* * We've already set the return action to stop, * since there are no nodes below peripherals in * the tree. */ return(retval); } /* * Not sure why someone would do this... */ if (cur_pattern->flags == PERIPH_MATCH_NONE) continue; if (((cur_pattern->flags & PERIPH_MATCH_PATH) != 0) && (cur_pattern->path_id != periph->path->bus->path_id)) continue; /* * For the target and lun id's, we have to make sure the * target and lun pointers aren't NULL. The xpt peripheral * has a wildcard target and device. */ if (((cur_pattern->flags & PERIPH_MATCH_TARGET) != 0) && ((periph->path->target == NULL) ||(cur_pattern->target_id != periph->path->target->target_id))) continue; if (((cur_pattern->flags & PERIPH_MATCH_LUN) != 0) && ((periph->path->device == NULL) || (cur_pattern->target_lun != periph->path->device->lun_id))) continue; if (((cur_pattern->flags & PERIPH_MATCH_UNIT) != 0) && (cur_pattern->unit_number != periph->unit_number)) continue; if (((cur_pattern->flags & PERIPH_MATCH_NAME) != 0) && (strncmp(cur_pattern->periph_name, periph->periph_name, DEV_IDLEN) != 0)) continue; /* * If we get to this point, the user definitely wants * information on this peripheral. So tell the caller to * copy the data out. */ retval |= DM_RET_COPY; /* * The return action has already been set to stop, since * peripherals don't have any nodes below them in the EDT. */ return(retval); } /* * If we get to this point, the peripheral that was passed in * doesn't match any of the patterns. */ return(retval); } static int xptedtbusfunc(struct cam_eb *bus, void *arg) { struct ccb_dev_match *cdm; struct cam_et *target; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; /* * If our position is for something deeper in the tree, that means * that we've already seen this node. So, we keep going down. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target != NULL)) retval = DM_RET_DESCEND; else retval = xptbusmatch(cdm->patterns, cdm->num_patterns, bus); /* * If we got an error, bail out of the search. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this bus out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_EDT | CAM_DEV_POS_BUS; cdm->pos.cookie.bus = bus; cdm->pos.generations[CAM_BUS_GENERATION]= xsoftc.bus_generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_BUS; cdm->matches[j].result.bus_result.path_id = bus->path_id; cdm->matches[j].result.bus_result.bus_id = bus->sim->bus_id; cdm->matches[j].result.bus_result.unit_number = bus->sim->unit_number; strncpy(cdm->matches[j].result.bus_result.dev_name, bus->sim->sim_name, DEV_IDLEN); } /* * If the user is only interested in busses, there's no * reason to descend to the next level in the tree. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_STOP) return(1); /* * If there is a target generation recorded, check it to * make sure the target list hasn't changed. */ mtx_lock(&bus->eb_mtx); if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target != NULL)) { if ((cdm->pos.generations[CAM_TARGET_GENERATION] != bus->generation)) { mtx_unlock(&bus->eb_mtx); cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return (0); } target = (struct cam_et *)cdm->pos.cookie.target; target->refcount++; } else target = NULL; mtx_unlock(&bus->eb_mtx); return (xpttargettraverse(bus, target, xptedttargetfunc, arg)); } static int xptedttargetfunc(struct cam_et *target, void *arg) { struct ccb_dev_match *cdm; struct cam_eb *bus; struct cam_ed *device; cdm = (struct ccb_dev_match *)arg; bus = target->bus; /* * If there is a device list generation recorded, check it to * make sure the device list hasn't changed. */ mtx_lock(&bus->eb_mtx); if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target == target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.cookie.device != NULL)) { if (cdm->pos.generations[CAM_DEV_GENERATION] != target->generation) { mtx_unlock(&bus->eb_mtx); cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } device = (struct cam_ed *)cdm->pos.cookie.device; device->refcount++; } else device = NULL; mtx_unlock(&bus->eb_mtx); return (xptdevicetraverse(target, device, xptedtdevicefunc, arg)); } static int xptedtdevicefunc(struct cam_ed *device, void *arg) { struct cam_eb *bus; struct cam_periph *periph; struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; bus = device->target->bus; /* * If our position is for something deeper in the tree, that means * that we've already seen this node. So, we keep going down. */ if ((cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.cookie.device == device) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.cookie.periph != NULL)) retval = DM_RET_DESCEND; else retval = xptdevicematch(cdm->patterns, cdm->num_patterns, device); if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this device out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_EDT | CAM_DEV_POS_BUS | CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE; cdm->pos.cookie.bus = device->target->bus; cdm->pos.generations[CAM_BUS_GENERATION]= xsoftc.bus_generation; cdm->pos.cookie.target = device->target; cdm->pos.generations[CAM_TARGET_GENERATION] = device->target->bus->generation; cdm->pos.cookie.device = device; cdm->pos.generations[CAM_DEV_GENERATION] = device->target->generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_DEVICE; cdm->matches[j].result.device_result.path_id = device->target->bus->path_id; cdm->matches[j].result.device_result.target_id = device->target->target_id; cdm->matches[j].result.device_result.target_lun = device->lun_id; cdm->matches[j].result.device_result.protocol = device->protocol; bcopy(&device->inq_data, &cdm->matches[j].result.device_result.inq_data, sizeof(struct scsi_inquiry_data)); bcopy(&device->ident_data, &cdm->matches[j].result.device_result.ident_data, sizeof(struct ata_params)); /* Let the user know whether this device is unconfigured */ if (device->flags & CAM_DEV_UNCONFIGURED) cdm->matches[j].result.device_result.flags = DEV_RESULT_UNCONFIGURED; else cdm->matches[j].result.device_result.flags = DEV_RESULT_NOFLAG; } /* * If the user isn't interested in peripherals, don't descend * the tree any further. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_STOP) return(1); /* * If there is a peripheral list generation recorded, make sure * it hasn't changed. */ xpt_lock_buses(); mtx_lock(&bus->eb_mtx); if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target == device->target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.cookie.device == device) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.cookie.periph != NULL)) { if (cdm->pos.generations[CAM_PERIPH_GENERATION] != device->generation) { mtx_unlock(&bus->eb_mtx); xpt_unlock_buses(); cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } periph = (struct cam_periph *)cdm->pos.cookie.periph; periph->refcount++; } else periph = NULL; mtx_unlock(&bus->eb_mtx); xpt_unlock_buses(); return (xptperiphtraverse(device, periph, xptedtperiphfunc, arg)); } static int xptedtperiphfunc(struct cam_periph *periph, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; retval = xptperiphmatch(cdm->patterns, cdm->num_patterns, periph); if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this peripheral out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_EDT | CAM_DEV_POS_BUS | CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE | CAM_DEV_POS_PERIPH; cdm->pos.cookie.bus = periph->path->bus; cdm->pos.generations[CAM_BUS_GENERATION]= xsoftc.bus_generation; cdm->pos.cookie.target = periph->path->target; cdm->pos.generations[CAM_TARGET_GENERATION] = periph->path->bus->generation; cdm->pos.cookie.device = periph->path->device; cdm->pos.generations[CAM_DEV_GENERATION] = periph->path->target->generation; cdm->pos.cookie.periph = periph; cdm->pos.generations[CAM_PERIPH_GENERATION] = periph->path->device->generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_PERIPH; cdm->matches[j].result.periph_result.path_id = periph->path->bus->path_id; cdm->matches[j].result.periph_result.target_id = periph->path->target->target_id; cdm->matches[j].result.periph_result.target_lun = periph->path->device->lun_id; cdm->matches[j].result.periph_result.unit_number = periph->unit_number; strncpy(cdm->matches[j].result.periph_result.periph_name, periph->periph_name, DEV_IDLEN); } return(1); } static int xptedtmatch(struct ccb_dev_match *cdm) { struct cam_eb *bus; int ret; cdm->num_matches = 0; /* * Check the bus list generation. If it has changed, the user * needs to reset everything and start over. */ xpt_lock_buses(); if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus != NULL)) { if (cdm->pos.generations[CAM_BUS_GENERATION] != xsoftc.bus_generation) { xpt_unlock_buses(); cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } bus = (struct cam_eb *)cdm->pos.cookie.bus; bus->refcount++; } else bus = NULL; xpt_unlock_buses(); ret = xptbustraverse(bus, xptedtbusfunc, cdm); /* * If we get back 0, that means that we had to stop before fully * traversing the EDT. It also means that one of the subroutines * has set the status field to the proper value. If we get back 1, * we've fully traversed the EDT and copied out any matching entries. */ if (ret == 1) cdm->status = CAM_DEV_MATCH_LAST; return(ret); } static int xptplistpdrvfunc(struct periph_driver **pdrv, void *arg) { struct cam_periph *periph; struct ccb_dev_match *cdm; cdm = (struct ccb_dev_match *)arg; xpt_lock_buses(); if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) && (cdm->pos.cookie.pdrv == pdrv) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.cookie.periph != NULL)) { if (cdm->pos.generations[CAM_PERIPH_GENERATION] != (*pdrv)->generation) { xpt_unlock_buses(); cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } periph = (struct cam_periph *)cdm->pos.cookie.periph; periph->refcount++; } else periph = NULL; xpt_unlock_buses(); return (xptpdperiphtraverse(pdrv, periph, xptplistperiphfunc, arg)); } static int xptplistperiphfunc(struct cam_periph *periph, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; retval = xptperiphmatch(cdm->patterns, cdm->num_patterns, periph); if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this peripheral out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { struct periph_driver **pdrv; pdrv = NULL; bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_PDRV | CAM_DEV_POS_PDPTR | CAM_DEV_POS_PERIPH; /* * This may look a bit non-sensical, but it is * actually quite logical. There are very few * peripheral drivers, and bloating every peripheral * structure with a pointer back to its parent * peripheral driver linker set entry would cost * more in the long run than doing this quick lookup. */ for (pdrv = periph_drivers; *pdrv != NULL; pdrv++) { if (strcmp((*pdrv)->driver_name, periph->periph_name) == 0) break; } if (*pdrv == NULL) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } cdm->pos.cookie.pdrv = pdrv; /* * The periph generation slot does double duty, as * does the periph pointer slot. They are used for * both edt and pdrv lookups and positioning. */ cdm->pos.cookie.periph = periph; cdm->pos.generations[CAM_PERIPH_GENERATION] = (*pdrv)->generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_PERIPH; cdm->matches[j].result.periph_result.path_id = periph->path->bus->path_id; /* * The transport layer peripheral doesn't have a target or * lun. */ if (periph->path->target) cdm->matches[j].result.periph_result.target_id = periph->path->target->target_id; else cdm->matches[j].result.periph_result.target_id = CAM_TARGET_WILDCARD; if (periph->path->device) cdm->matches[j].result.periph_result.target_lun = periph->path->device->lun_id; else cdm->matches[j].result.periph_result.target_lun = CAM_LUN_WILDCARD; cdm->matches[j].result.periph_result.unit_number = periph->unit_number; strncpy(cdm->matches[j].result.periph_result.periph_name, periph->periph_name, DEV_IDLEN); } return(1); } static int xptperiphlistmatch(struct ccb_dev_match *cdm) { int ret; cdm->num_matches = 0; /* * At this point in the edt traversal function, we check the bus * list generation to make sure that no busses have been added or * removed since the user last sent a XPT_DEV_MATCH ccb through. * For the peripheral driver list traversal function, however, we * don't have to worry about new peripheral driver types coming or * going; they're in a linker set, and therefore can't change * without a recompile. */ if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) && (cdm->pos.cookie.pdrv != NULL)) ret = xptpdrvtraverse( (struct periph_driver **)cdm->pos.cookie.pdrv, xptplistpdrvfunc, cdm); else ret = xptpdrvtraverse(NULL, xptplistpdrvfunc, cdm); /* * If we get back 0, that means that we had to stop before fully * traversing the peripheral driver tree. It also means that one of * the subroutines has set the status field to the proper value. If * we get back 1, we've fully traversed the EDT and copied out any * matching entries. */ if (ret == 1) cdm->status = CAM_DEV_MATCH_LAST; return(ret); } static int xptbustraverse(struct cam_eb *start_bus, xpt_busfunc_t *tr_func, void *arg) { struct cam_eb *bus, *next_bus; int retval; retval = 1; if (start_bus) bus = start_bus; else { xpt_lock_buses(); bus = TAILQ_FIRST(&xsoftc.xpt_busses); if (bus == NULL) { xpt_unlock_buses(); return (retval); } bus->refcount++; xpt_unlock_buses(); } for (; bus != NULL; bus = next_bus) { retval = tr_func(bus, arg); if (retval == 0) { xpt_release_bus(bus); break; } xpt_lock_buses(); next_bus = TAILQ_NEXT(bus, links); if (next_bus) next_bus->refcount++; xpt_unlock_buses(); xpt_release_bus(bus); } return(retval); } static int xpttargettraverse(struct cam_eb *bus, struct cam_et *start_target, xpt_targetfunc_t *tr_func, void *arg) { struct cam_et *target, *next_target; int retval; retval = 1; if (start_target) target = start_target; else { mtx_lock(&bus->eb_mtx); target = TAILQ_FIRST(&bus->et_entries); if (target == NULL) { mtx_unlock(&bus->eb_mtx); return (retval); } target->refcount++; mtx_unlock(&bus->eb_mtx); } for (; target != NULL; target = next_target) { retval = tr_func(target, arg); if (retval == 0) { xpt_release_target(target); break; } mtx_lock(&bus->eb_mtx); next_target = TAILQ_NEXT(target, links); if (next_target) next_target->refcount++; mtx_unlock(&bus->eb_mtx); xpt_release_target(target); } return(retval); } static int xptdevicetraverse(struct cam_et *target, struct cam_ed *start_device, xpt_devicefunc_t *tr_func, void *arg) { struct cam_eb *bus; struct cam_ed *device, *next_device; int retval; retval = 1; bus = target->bus; if (start_device) device = start_device; else { mtx_lock(&bus->eb_mtx); device = TAILQ_FIRST(&target->ed_entries); if (device == NULL) { mtx_unlock(&bus->eb_mtx); return (retval); } device->refcount++; mtx_unlock(&bus->eb_mtx); } for (; device != NULL; device = next_device) { mtx_lock(&device->device_mtx); retval = tr_func(device, arg); mtx_unlock(&device->device_mtx); if (retval == 0) { xpt_release_device(device); break; } mtx_lock(&bus->eb_mtx); next_device = TAILQ_NEXT(device, links); if (next_device) next_device->refcount++; mtx_unlock(&bus->eb_mtx); xpt_release_device(device); } return(retval); } static int xptperiphtraverse(struct cam_ed *device, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg) { struct cam_eb *bus; struct cam_periph *periph, *next_periph; int retval; retval = 1; bus = device->target->bus; if (start_periph) periph = start_periph; else { xpt_lock_buses(); mtx_lock(&bus->eb_mtx); periph = SLIST_FIRST(&device->periphs); while (periph != NULL && (periph->flags & CAM_PERIPH_FREE) != 0) periph = SLIST_NEXT(periph, periph_links); if (periph == NULL) { mtx_unlock(&bus->eb_mtx); xpt_unlock_buses(); return (retval); } periph->refcount++; mtx_unlock(&bus->eb_mtx); xpt_unlock_buses(); } for (; periph != NULL; periph = next_periph) { retval = tr_func(periph, arg); if (retval == 0) { cam_periph_release_locked(periph); break; } xpt_lock_buses(); mtx_lock(&bus->eb_mtx); next_periph = SLIST_NEXT(periph, periph_links); while (next_periph != NULL && (next_periph->flags & CAM_PERIPH_FREE) != 0) next_periph = SLIST_NEXT(next_periph, periph_links); if (next_periph) next_periph->refcount++; mtx_unlock(&bus->eb_mtx); xpt_unlock_buses(); cam_periph_release_locked(periph); } return(retval); } static int xptpdrvtraverse(struct periph_driver **start_pdrv, xpt_pdrvfunc_t *tr_func, void *arg) { struct periph_driver **pdrv; int retval; retval = 1; /* * We don't traverse the peripheral driver list like we do the * other lists, because it is a linker set, and therefore cannot be * changed during runtime. If the peripheral driver list is ever * re-done to be something other than a linker set (i.e. it can * change while the system is running), the list traversal should * be modified to work like the other traversal functions. */ for (pdrv = (start_pdrv ? start_pdrv : periph_drivers); *pdrv != NULL; pdrv++) { retval = tr_func(pdrv, arg); if (retval == 0) return(retval); } return(retval); } static int xptpdperiphtraverse(struct periph_driver **pdrv, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg) { struct cam_periph *periph, *next_periph; int retval; retval = 1; if (start_periph) periph = start_periph; else { xpt_lock_buses(); periph = TAILQ_FIRST(&(*pdrv)->units); while (periph != NULL && (periph->flags & CAM_PERIPH_FREE) != 0) periph = TAILQ_NEXT(periph, unit_links); if (periph == NULL) { xpt_unlock_buses(); return (retval); } periph->refcount++; xpt_unlock_buses(); } for (; periph != NULL; periph = next_periph) { cam_periph_lock(periph); retval = tr_func(periph, arg); cam_periph_unlock(periph); if (retval == 0) { cam_periph_release(periph); break; } xpt_lock_buses(); next_periph = TAILQ_NEXT(periph, unit_links); while (next_periph != NULL && (next_periph->flags & CAM_PERIPH_FREE) != 0) next_periph = TAILQ_NEXT(next_periph, unit_links); if (next_periph) next_periph->refcount++; xpt_unlock_buses(); cam_periph_release(periph); } return(retval); } static int xptdefbusfunc(struct cam_eb *bus, void *arg) { struct xpt_traverse_config *tr_config; tr_config = (struct xpt_traverse_config *)arg; if (tr_config->depth == XPT_DEPTH_BUS) { xpt_busfunc_t *tr_func; tr_func = (xpt_busfunc_t *)tr_config->tr_func; return(tr_func(bus, tr_config->tr_arg)); } else return(xpttargettraverse(bus, NULL, xptdeftargetfunc, arg)); } static int xptdeftargetfunc(struct cam_et *target, void *arg) { struct xpt_traverse_config *tr_config; tr_config = (struct xpt_traverse_config *)arg; if (tr_config->depth == XPT_DEPTH_TARGET) { xpt_targetfunc_t *tr_func; tr_func = (xpt_targetfunc_t *)tr_config->tr_func; return(tr_func(target, tr_config->tr_arg)); } else return(xptdevicetraverse(target, NULL, xptdefdevicefunc, arg)); } static int xptdefdevicefunc(struct cam_ed *device, void *arg) { struct xpt_traverse_config *tr_config; tr_config = (struct xpt_traverse_config *)arg; if (tr_config->depth == XPT_DEPTH_DEVICE) { xpt_devicefunc_t *tr_func; tr_func = (xpt_devicefunc_t *)tr_config->tr_func; return(tr_func(device, tr_config->tr_arg)); } else return(xptperiphtraverse(device, NULL, xptdefperiphfunc, arg)); } static int xptdefperiphfunc(struct cam_periph *periph, void *arg) { struct xpt_traverse_config *tr_config; xpt_periphfunc_t *tr_func; tr_config = (struct xpt_traverse_config *)arg; tr_func = (xpt_periphfunc_t *)tr_config->tr_func; /* * Unlike the other default functions, we don't check for depth * here. The peripheral driver level is the last level in the EDT, * so if we're here, we should execute the function in question. */ return(tr_func(periph, tr_config->tr_arg)); } /* * Execute the given function for every bus in the EDT. */ static int xpt_for_all_busses(xpt_busfunc_t *tr_func, void *arg) { struct xpt_traverse_config tr_config; tr_config.depth = XPT_DEPTH_BUS; tr_config.tr_func = tr_func; tr_config.tr_arg = arg; return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); } /* * Execute the given function for every device in the EDT. */ static int xpt_for_all_devices(xpt_devicefunc_t *tr_func, void *arg) { struct xpt_traverse_config tr_config; tr_config.depth = XPT_DEPTH_DEVICE; tr_config.tr_func = tr_func; tr_config.tr_arg = arg; return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); } static int xptsetasyncfunc(struct cam_ed *device, void *arg) { struct cam_path path; struct ccb_getdev cgd; struct ccb_setasync *csa = (struct ccb_setasync *)arg; /* * Don't report unconfigured devices (Wildcard devs, * devices only for target mode, device instances * that have been invalidated but are waiting for * their last reference count to be released). */ if ((device->flags & CAM_DEV_UNCONFIGURED) != 0) return (1); xpt_compile_path(&path, NULL, device->target->bus->path_id, device->target->target_id, device->lun_id); xpt_setup_ccb(&cgd.ccb_h, &path, CAM_PRIORITY_NORMAL); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); csa->callback(csa->callback_arg, AC_FOUND_DEVICE, &path, &cgd); xpt_release_path(&path); return(1); } static int xptsetasyncbusfunc(struct cam_eb *bus, void *arg) { struct cam_path path; struct ccb_pathinq cpi; struct ccb_setasync *csa = (struct ccb_setasync *)arg; xpt_compile_path(&path, /*periph*/NULL, bus->path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); xpt_path_lock(&path); xpt_setup_ccb(&cpi.ccb_h, &path, CAM_PRIORITY_NORMAL); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); csa->callback(csa->callback_arg, AC_PATH_REGISTERED, &path, &cpi); xpt_path_unlock(&path); xpt_release_path(&path); return(1); } void xpt_action(union ccb *start_ccb) { CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_action\n")); start_ccb->ccb_h.status = CAM_REQ_INPROG; (*(start_ccb->ccb_h.path->bus->xport->action))(start_ccb); } void xpt_action_default(union ccb *start_ccb) { struct cam_path *path; struct cam_sim *sim; int lock; path = start_ccb->ccb_h.path; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_action_default\n")); switch (start_ccb->ccb_h.func_code) { case XPT_SCSI_IO: { struct cam_ed *device; /* * For the sake of compatibility with SCSI-1 * devices that may not understand the identify * message, we include lun information in the * second byte of all commands. SCSI-1 specifies * that luns are a 3 bit value and reserves only 3 * bits for lun information in the CDB. Later * revisions of the SCSI spec allow for more than 8 * luns, but have deprecated lun information in the * CDB. So, if the lun won't fit, we must omit. * * Also be aware that during initial probing for devices, * the inquiry information is unknown but initialized to 0. * This means that this code will be exercised while probing * devices with an ANSI revision greater than 2. */ device = path->device; if (device->protocol_version <= SCSI_REV_2 && start_ccb->ccb_h.target_lun < 8 && (start_ccb->ccb_h.flags & CAM_CDB_POINTER) == 0) { start_ccb->csio.cdb_io.cdb_bytes[1] |= start_ccb->ccb_h.target_lun << 5; } start_ccb->csio.scsi_status = SCSI_STATUS_OK; } /* FALLTHROUGH */ case XPT_TARGET_IO: case XPT_CONT_TARGET_IO: start_ccb->csio.sense_resid = 0; start_ccb->csio.resid = 0; /* FALLTHROUGH */ case XPT_ATA_IO: if (start_ccb->ccb_h.func_code == XPT_ATA_IO) start_ccb->ataio.resid = 0; /* FALLTHROUGH */ case XPT_RESET_DEV: case XPT_ENG_EXEC: case XPT_SMP_IO: { struct cam_devq *devq; devq = path->bus->sim->devq; mtx_lock(&devq->send_mtx); cam_ccbq_insert_ccb(&path->device->ccbq, start_ccb); if (xpt_schedule_devq(devq, path->device) != 0) xpt_run_devq(devq); mtx_unlock(&devq->send_mtx); break; } case XPT_CALC_GEOMETRY: /* Filter out garbage */ if (start_ccb->ccg.block_size == 0 || start_ccb->ccg.volume_size == 0) { start_ccb->ccg.cylinders = 0; start_ccb->ccg.heads = 0; start_ccb->ccg.secs_per_track = 0; start_ccb->ccb_h.status = CAM_REQ_CMP; break; } #if defined(PC98) || defined(__sparc64__) /* * In a PC-98 system, geometry translation depens on * the "real" device geometry obtained from mode page 4. * SCSI geometry translation is performed in the * initialization routine of the SCSI BIOS and the result * stored in host memory. If the translation is available * in host memory, use it. If not, rely on the default * translation the device driver performs. * For sparc64, we may need adjust the geometry of large * disks in order to fit the limitations of the 16-bit * fields of the VTOC8 disk label. */ if (scsi_da_bios_params(&start_ccb->ccg) != 0) { start_ccb->ccb_h.status = CAM_REQ_CMP; break; } #endif goto call_sim; case XPT_ABORT: { union ccb* abort_ccb; abort_ccb = start_ccb->cab.abort_ccb; if (XPT_FC_IS_DEV_QUEUED(abort_ccb)) { if (abort_ccb->ccb_h.pinfo.index >= 0) { struct cam_ccbq *ccbq; struct cam_ed *device; device = abort_ccb->ccb_h.path->device; ccbq = &device->ccbq; cam_ccbq_remove_ccb(ccbq, abort_ccb); abort_ccb->ccb_h.status = CAM_REQ_ABORTED|CAM_DEV_QFRZN; xpt_freeze_devq(abort_ccb->ccb_h.path, 1); xpt_done(abort_ccb); start_ccb->ccb_h.status = CAM_REQ_CMP; break; } if (abort_ccb->ccb_h.pinfo.index == CAM_UNQUEUED_INDEX && (abort_ccb->ccb_h.status & CAM_SIM_QUEUED) == 0) { /* * We've caught this ccb en route to * the SIM. Flag it for abort and the * SIM will do so just before starting * real work on the CCB. */ abort_ccb->ccb_h.status = CAM_REQ_ABORTED|CAM_DEV_QFRZN; xpt_freeze_devq(abort_ccb->ccb_h.path, 1); start_ccb->ccb_h.status = CAM_REQ_CMP; break; } } if (XPT_FC_IS_QUEUED(abort_ccb) && (abort_ccb->ccb_h.pinfo.index == CAM_DONEQ_INDEX)) { /* * It's already completed but waiting * for our SWI to get to it. */ start_ccb->ccb_h.status = CAM_UA_ABORT; break; } /* * If we weren't able to take care of the abort request * in the XPT, pass the request down to the SIM for processing. */ } /* FALLTHROUGH */ case XPT_ACCEPT_TARGET_IO: case XPT_EN_LUN: case XPT_IMMED_NOTIFY: case XPT_NOTIFY_ACK: case XPT_RESET_BUS: case XPT_IMMEDIATE_NOTIFY: case XPT_NOTIFY_ACKNOWLEDGE: case XPT_GET_SIM_KNOB: case XPT_SET_SIM_KNOB: case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: case XPT_PATH_INQ: call_sim: sim = path->bus->sim; lock = (mtx_owned(sim->mtx) == 0); if (lock) CAM_SIM_LOCK(sim); (*(sim->sim_action))(sim, start_ccb); if (lock) CAM_SIM_UNLOCK(sim); break; case XPT_PATH_STATS: start_ccb->cpis.last_reset = path->bus->last_reset; start_ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_GDEV_TYPE: { struct cam_ed *dev; dev = path->device; if ((dev->flags & CAM_DEV_UNCONFIGURED) != 0) { start_ccb->ccb_h.status = CAM_DEV_NOT_THERE; } else { struct ccb_getdev *cgd; cgd = &start_ccb->cgd; cgd->protocol = dev->protocol; cgd->inq_data = dev->inq_data; cgd->ident_data = dev->ident_data; cgd->inq_flags = dev->inq_flags; cgd->ccb_h.status = CAM_REQ_CMP; cgd->serial_num_len = dev->serial_num_len; if ((dev->serial_num_len > 0) && (dev->serial_num != NULL)) bcopy(dev->serial_num, cgd->serial_num, dev->serial_num_len); } break; } case XPT_GDEV_STATS: { struct cam_ed *dev; dev = path->device; if ((dev->flags & CAM_DEV_UNCONFIGURED) != 0) { start_ccb->ccb_h.status = CAM_DEV_NOT_THERE; } else { struct ccb_getdevstats *cgds; struct cam_eb *bus; struct cam_et *tar; struct cam_devq *devq; cgds = &start_ccb->cgds; bus = path->bus; tar = path->target; devq = bus->sim->devq; mtx_lock(&devq->send_mtx); cgds->dev_openings = dev->ccbq.dev_openings; cgds->dev_active = dev->ccbq.dev_active; cgds->allocated = dev->ccbq.allocated; cgds->queued = cam_ccbq_pending_ccb_count(&dev->ccbq); cgds->held = cgds->allocated - cgds->dev_active - cgds->queued; cgds->last_reset = tar->last_reset; cgds->maxtags = dev->maxtags; cgds->mintags = dev->mintags; if (timevalcmp(&tar->last_reset, &bus->last_reset, <)) cgds->last_reset = bus->last_reset; mtx_unlock(&devq->send_mtx); cgds->ccb_h.status = CAM_REQ_CMP; } break; } case XPT_GDEVLIST: { struct cam_periph *nperiph; struct periph_list *periph_head; struct ccb_getdevlist *cgdl; u_int i; struct cam_ed *device; int found; found = 0; /* * Don't want anyone mucking with our data. */ device = path->device; periph_head = &device->periphs; cgdl = &start_ccb->cgdl; /* * Check and see if the list has changed since the user * last requested a list member. If so, tell them that the * list has changed, and therefore they need to start over * from the beginning. */ if ((cgdl->index != 0) && (cgdl->generation != device->generation)) { cgdl->status = CAM_GDEVLIST_LIST_CHANGED; break; } /* * Traverse the list of peripherals and attempt to find * the requested peripheral. */ for (nperiph = SLIST_FIRST(periph_head), i = 0; (nperiph != NULL) && (i <= cgdl->index); nperiph = SLIST_NEXT(nperiph, periph_links), i++) { if (i == cgdl->index) { strncpy(cgdl->periph_name, nperiph->periph_name, DEV_IDLEN); cgdl->unit_number = nperiph->unit_number; found = 1; } } if (found == 0) { cgdl->status = CAM_GDEVLIST_ERROR; break; } if (nperiph == NULL) cgdl->status = CAM_GDEVLIST_LAST_DEVICE; else cgdl->status = CAM_GDEVLIST_MORE_DEVS; cgdl->index++; cgdl->generation = device->generation; cgdl->ccb_h.status = CAM_REQ_CMP; break; } case XPT_DEV_MATCH: { dev_pos_type position_type; struct ccb_dev_match *cdm; cdm = &start_ccb->cdm; /* * There are two ways of getting at information in the EDT. * The first way is via the primary EDT tree. It starts * with a list of busses, then a list of targets on a bus, * then devices/luns on a target, and then peripherals on a * device/lun. The "other" way is by the peripheral driver * lists. The peripheral driver lists are organized by * peripheral driver. (obviously) So it makes sense to * use the peripheral driver list if the user is looking * for something like "da1", or all "da" devices. If the * user is looking for something on a particular bus/target * or lun, it's generally better to go through the EDT tree. */ if (cdm->pos.position_type != CAM_DEV_POS_NONE) position_type = cdm->pos.position_type; else { u_int i; position_type = CAM_DEV_POS_NONE; for (i = 0; i < cdm->num_patterns; i++) { if ((cdm->patterns[i].type == DEV_MATCH_BUS) ||(cdm->patterns[i].type == DEV_MATCH_DEVICE)){ position_type = CAM_DEV_POS_EDT; break; } } if (cdm->num_patterns == 0) position_type = CAM_DEV_POS_EDT; else if (position_type == CAM_DEV_POS_NONE) position_type = CAM_DEV_POS_PDRV; } switch(position_type & CAM_DEV_POS_TYPEMASK) { case CAM_DEV_POS_EDT: xptedtmatch(cdm); break; case CAM_DEV_POS_PDRV: xptperiphlistmatch(cdm); break; default: cdm->status = CAM_DEV_MATCH_ERROR; break; } if (cdm->status == CAM_DEV_MATCH_ERROR) start_ccb->ccb_h.status = CAM_REQ_CMP_ERR; else start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_SASYNC_CB: { struct ccb_setasync *csa; struct async_node *cur_entry; struct async_list *async_head; u_int32_t added; csa = &start_ccb->csa; added = csa->event_enable; async_head = &path->device->asyncs; /* * If there is already an entry for us, simply * update it. */ cur_entry = SLIST_FIRST(async_head); while (cur_entry != NULL) { if ((cur_entry->callback_arg == csa->callback_arg) && (cur_entry->callback == csa->callback)) break; cur_entry = SLIST_NEXT(cur_entry, links); } if (cur_entry != NULL) { /* * If the request has no flags set, * remove the entry. */ added &= ~cur_entry->event_enable; if (csa->event_enable == 0) { SLIST_REMOVE(async_head, cur_entry, async_node, links); xpt_release_device(path->device); free(cur_entry, M_CAMXPT); } else { cur_entry->event_enable = csa->event_enable; } csa->event_enable = added; } else { cur_entry = malloc(sizeof(*cur_entry), M_CAMXPT, M_NOWAIT); if (cur_entry == NULL) { csa->ccb_h.status = CAM_RESRC_UNAVAIL; break; } cur_entry->event_enable = csa->event_enable; cur_entry->event_lock = mtx_owned(path->bus->sim->mtx) ? 1 : 0; cur_entry->callback_arg = csa->callback_arg; cur_entry->callback = csa->callback; SLIST_INSERT_HEAD(async_head, cur_entry, links); xpt_acquire_device(path->device); } start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_REL_SIMQ: { struct ccb_relsim *crs; struct cam_ed *dev; crs = &start_ccb->crs; dev = path->device; if (dev == NULL) { crs->ccb_h.status = CAM_DEV_NOT_THERE; break; } if ((crs->release_flags & RELSIM_ADJUST_OPENINGS) != 0) { /* Don't ever go below one opening */ if (crs->openings > 0) { xpt_dev_ccbq_resize(path, crs->openings); if (bootverbose) { xpt_print(path, "number of openings is now %d\n", crs->openings); } } } mtx_lock(&dev->sim->devq->send_mtx); if ((crs->release_flags & RELSIM_RELEASE_AFTER_TIMEOUT) != 0) { if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) { /* * Just extend the old timeout and decrement * the freeze count so that a single timeout * is sufficient for releasing the queue. */ start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; callout_stop(&dev->callout); } else { start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } callout_reset_sbt(&dev->callout, SBT_1MS * crs->release_timeout, 0, xpt_release_devq_timeout, dev, 0); dev->flags |= CAM_DEV_REL_TIMEOUT_PENDING; } if ((crs->release_flags & RELSIM_RELEASE_AFTER_CMDCMPLT) != 0) { if ((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0) { /* * Decrement the freeze count so that a single * completion is still sufficient to unfreeze * the queue. */ start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; } else { dev->flags |= CAM_DEV_REL_ON_COMPLETE; start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } } if ((crs->release_flags & RELSIM_RELEASE_AFTER_QEMPTY) != 0) { if ((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0 || (dev->ccbq.dev_active == 0)) { start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; } else { dev->flags |= CAM_DEV_REL_ON_QUEUE_EMPTY; start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } } mtx_unlock(&dev->sim->devq->send_mtx); if ((start_ccb->ccb_h.flags & CAM_DEV_QFREEZE) == 0) xpt_release_devq(path, /*count*/1, /*run_queue*/TRUE); start_ccb->crs.qfrozen_cnt = dev->ccbq.queue.qfrozen_cnt; start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_DEBUG: { struct cam_path *oldpath; /* Check that all request bits are supported. */ if (start_ccb->cdbg.flags & ~(CAM_DEBUG_COMPILE)) { start_ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; } cam_dflags = CAM_DEBUG_NONE; if (cam_dpath != NULL) { oldpath = cam_dpath; cam_dpath = NULL; xpt_free_path(oldpath); } if (start_ccb->cdbg.flags != CAM_DEBUG_NONE) { if (xpt_create_path(&cam_dpath, NULL, start_ccb->ccb_h.path_id, start_ccb->ccb_h.target_id, start_ccb->ccb_h.target_lun) != CAM_REQ_CMP) { start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; } else { cam_dflags = start_ccb->cdbg.flags; start_ccb->ccb_h.status = CAM_REQ_CMP; xpt_print(cam_dpath, "debugging flags now %x\n", cam_dflags); } } else start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_NOOP: if ((start_ccb->ccb_h.flags & CAM_DEV_QFREEZE) != 0) xpt_freeze_devq(path, 1); start_ccb->ccb_h.status = CAM_REQ_CMP; break; default: case XPT_SDEV_TYPE: case XPT_TERM_IO: case XPT_ENG_INQ: /* XXX Implement */ printf("%s: CCB type %#x not supported\n", __func__, start_ccb->ccb_h.func_code); start_ccb->ccb_h.status = CAM_PROVIDE_FAIL; if (start_ccb->ccb_h.func_code & XPT_FC_DEV_QUEUED) { xpt_done(start_ccb); } break; } } void xpt_polled_action(union ccb *start_ccb) { u_int32_t timeout; struct cam_sim *sim; struct cam_devq *devq; struct cam_ed *dev; timeout = start_ccb->ccb_h.timeout * 10; sim = start_ccb->ccb_h.path->bus->sim; devq = sim->devq; dev = start_ccb->ccb_h.path->device; mtx_unlock(&dev->device_mtx); /* * Steal an opening so that no other queued requests * can get it before us while we simulate interrupts. */ mtx_lock(&devq->send_mtx); dev->ccbq.dev_openings--; while((devq->send_openings <= 0 || dev->ccbq.dev_openings < 0) && (--timeout > 0)) { mtx_unlock(&devq->send_mtx); DELAY(100); CAM_SIM_LOCK(sim); (*(sim->sim_poll))(sim); CAM_SIM_UNLOCK(sim); camisr_runqueue(); mtx_lock(&devq->send_mtx); } dev->ccbq.dev_openings++; mtx_unlock(&devq->send_mtx); if (timeout != 0) { xpt_action(start_ccb); while(--timeout > 0) { CAM_SIM_LOCK(sim); (*(sim->sim_poll))(sim); CAM_SIM_UNLOCK(sim); camisr_runqueue(); if ((start_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) break; DELAY(100); } if (timeout == 0) { /* * XXX Is it worth adding a sim_timeout entry * point so we can attempt recovery? If * this is only used for dumps, I don't think * it is. */ start_ccb->ccb_h.status = CAM_CMD_TIMEOUT; } } else { start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; } mtx_lock(&dev->device_mtx); } /* * Schedule a peripheral driver to receive a ccb when its * target device has space for more transactions. */ void xpt_schedule(struct cam_periph *periph, u_int32_t new_priority) { CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("xpt_schedule\n")); cam_periph_assert(periph, MA_OWNED); if (new_priority < periph->scheduled_priority) { periph->scheduled_priority = new_priority; xpt_run_allocq(periph, 0); } } /* * Schedule a device to run on a given queue. * If the device was inserted as a new entry on the queue, * return 1 meaning the device queue should be run. If we * were already queued, implying someone else has already * started the queue, return 0 so the caller doesn't attempt * to run the queue. */ static int xpt_schedule_dev(struct camq *queue, cam_pinfo *pinfo, u_int32_t new_priority) { int retval; u_int32_t old_priority; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_schedule_dev\n")); old_priority = pinfo->priority; /* * Are we already queued? */ if (pinfo->index != CAM_UNQUEUED_INDEX) { /* Simply reorder based on new priority */ if (new_priority < old_priority) { camq_change_priority(queue, pinfo->index, new_priority); CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("changed priority to %d\n", new_priority)); retval = 1; } else retval = 0; } else { /* New entry on the queue */ if (new_priority < old_priority) pinfo->priority = new_priority; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("Inserting onto queue\n")); pinfo->generation = ++queue->generation; camq_insert(queue, pinfo); retval = 1; } return (retval); } static void xpt_run_allocq_task(void *context, int pending) { struct cam_periph *periph = context; cam_periph_lock(periph); periph->flags &= ~CAM_PERIPH_RUN_TASK; xpt_run_allocq(periph, 1); cam_periph_unlock(periph); cam_periph_release(periph); } static void xpt_run_allocq(struct cam_periph *periph, int sleep) { struct cam_ed *device; union ccb *ccb; uint32_t prio; cam_periph_assert(periph, MA_OWNED); if (periph->periph_allocating) return; periph->periph_allocating = 1; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_run_allocq(%p)\n", periph)); device = periph->path->device; ccb = NULL; restart: while ((prio = min(periph->scheduled_priority, periph->immediate_priority)) != CAM_PRIORITY_NONE && (periph->periph_allocated - (ccb != NULL ? 1 : 0) < device->ccbq.total_openings || prio <= CAM_PRIORITY_OOB)) { if (ccb == NULL && (ccb = xpt_get_ccb_nowait(periph)) == NULL) { if (sleep) { ccb = xpt_get_ccb(periph); goto restart; } if (periph->flags & CAM_PERIPH_RUN_TASK) break; cam_periph_doacquire(periph); periph->flags |= CAM_PERIPH_RUN_TASK; taskqueue_enqueue(xsoftc.xpt_taskq, &periph->periph_run_task); break; } xpt_setup_ccb(&ccb->ccb_h, periph->path, prio); if (prio == periph->immediate_priority) { periph->immediate_priority = CAM_PRIORITY_NONE; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("waking cam_periph_getccb()\n")); SLIST_INSERT_HEAD(&periph->ccb_list, &ccb->ccb_h, periph_links.sle); wakeup(&periph->ccb_list); } else { periph->scheduled_priority = CAM_PRIORITY_NONE; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("calling periph_start()\n")); periph->periph_start(periph, ccb); } ccb = NULL; } if (ccb != NULL) xpt_release_ccb(ccb); periph->periph_allocating = 0; } static void xpt_run_devq(struct cam_devq *devq) { char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1]; int lock; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_run_devq\n")); devq->send_queue.qfrozen_cnt++; while ((devq->send_queue.entries > 0) && (devq->send_openings > 0) && (devq->send_queue.qfrozen_cnt <= 1)) { struct cam_ed *device; union ccb *work_ccb; struct cam_sim *sim; device = (struct cam_ed *)camq_remove(&devq->send_queue, CAMQ_HEAD); CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("running device %p\n", device)); work_ccb = cam_ccbq_peek_ccb(&device->ccbq, CAMQ_HEAD); if (work_ccb == NULL) { printf("device on run queue with no ccbs???\n"); continue; } if ((work_ccb->ccb_h.flags & CAM_HIGH_POWER) != 0) { mtx_lock(&xsoftc.xpt_highpower_lock); if (xsoftc.num_highpower <= 0) { /* * We got a high power command, but we * don't have any available slots. Freeze * the device queue until we have a slot * available. */ xpt_freeze_devq_device(device, 1); STAILQ_INSERT_TAIL(&xsoftc.highpowerq, device, highpowerq_entry); mtx_unlock(&xsoftc.xpt_highpower_lock); continue; } else { /* * Consume a high power slot while * this ccb runs. */ xsoftc.num_highpower--; } mtx_unlock(&xsoftc.xpt_highpower_lock); } cam_ccbq_remove_ccb(&device->ccbq, work_ccb); cam_ccbq_send_ccb(&device->ccbq, work_ccb); devq->send_openings--; devq->send_active++; xpt_schedule_devq(devq, device); mtx_unlock(&devq->send_mtx); if ((work_ccb->ccb_h.flags & CAM_DEV_QFREEZE) != 0) { /* * The client wants to freeze the queue * after this CCB is sent. */ xpt_freeze_devq(work_ccb->ccb_h.path, 1); } /* In Target mode, the peripheral driver knows best... */ if (work_ccb->ccb_h.func_code == XPT_SCSI_IO) { if ((device->inq_flags & SID_CmdQue) != 0 && work_ccb->csio.tag_action != CAM_TAG_ACTION_NONE) work_ccb->ccb_h.flags |= CAM_TAG_ACTION_VALID; else /* * Clear this in case of a retried CCB that * failed due to a rejected tag. */ work_ccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; } switch (work_ccb->ccb_h.func_code) { case XPT_SCSI_IO: CAM_DEBUG(work_ccb->ccb_h.path, CAM_DEBUG_CDB,("%s. CDB: %s\n", scsi_op_desc(work_ccb->csio.cdb_io.cdb_bytes[0], &device->inq_data), scsi_cdb_string(work_ccb->csio.cdb_io.cdb_bytes, cdb_str, sizeof(cdb_str)))); break; case XPT_ATA_IO: CAM_DEBUG(work_ccb->ccb_h.path, CAM_DEBUG_CDB,("%s. ACB: %s\n", ata_op_string(&work_ccb->ataio.cmd), ata_cmd_string(&work_ccb->ataio.cmd, cdb_str, sizeof(cdb_str)))); break; default: break; } /* * Device queues can be shared among multiple SIM instances * that reside on different busses. Use the SIM from the * queued device, rather than the one from the calling bus. */ sim = device->sim; lock = (mtx_owned(sim->mtx) == 0); if (lock) CAM_SIM_LOCK(sim); (*(sim->sim_action))(sim, work_ccb); if (lock) CAM_SIM_UNLOCK(sim); mtx_lock(&devq->send_mtx); } devq->send_queue.qfrozen_cnt--; } /* * This function merges stuff from the slave ccb into the master ccb, while * keeping important fields in the master ccb constant. */ void xpt_merge_ccb(union ccb *master_ccb, union ccb *slave_ccb) { /* * Pull fields that are valid for peripheral drivers to set * into the master CCB along with the CCB "payload". */ master_ccb->ccb_h.retry_count = slave_ccb->ccb_h.retry_count; master_ccb->ccb_h.func_code = slave_ccb->ccb_h.func_code; master_ccb->ccb_h.timeout = slave_ccb->ccb_h.timeout; master_ccb->ccb_h.flags = slave_ccb->ccb_h.flags; bcopy(&(&slave_ccb->ccb_h)[1], &(&master_ccb->ccb_h)[1], sizeof(union ccb) - sizeof(struct ccb_hdr)); } void xpt_setup_ccb(struct ccb_hdr *ccb_h, struct cam_path *path, u_int32_t priority) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_setup_ccb\n")); ccb_h->pinfo.priority = priority; ccb_h->path = path; ccb_h->path_id = path->bus->path_id; if (path->target) ccb_h->target_id = path->target->target_id; else ccb_h->target_id = CAM_TARGET_WILDCARD; if (path->device) { ccb_h->target_lun = path->device->lun_id; ccb_h->pinfo.generation = ++path->device->ccbq.queue.generation; } else { ccb_h->target_lun = CAM_TARGET_WILDCARD; } ccb_h->pinfo.index = CAM_UNQUEUED_INDEX; ccb_h->flags = 0; ccb_h->xflags = 0; } /* Path manipulation functions */ cam_status xpt_create_path(struct cam_path **new_path_ptr, struct cam_periph *perph, path_id_t path_id, target_id_t target_id, lun_id_t lun_id) { struct cam_path *path; cam_status status; path = (struct cam_path *)malloc(sizeof(*path), M_CAMPATH, M_NOWAIT); if (path == NULL) { status = CAM_RESRC_UNAVAIL; return(status); } status = xpt_compile_path(path, perph, path_id, target_id, lun_id); if (status != CAM_REQ_CMP) { free(path, M_CAMPATH); path = NULL; } *new_path_ptr = path; return (status); } cam_status xpt_create_path_unlocked(struct cam_path **new_path_ptr, struct cam_periph *periph, path_id_t path_id, target_id_t target_id, lun_id_t lun_id) { return (xpt_create_path(new_path_ptr, periph, path_id, target_id, lun_id)); } cam_status xpt_compile_path(struct cam_path *new_path, struct cam_periph *perph, path_id_t path_id, target_id_t target_id, lun_id_t lun_id) { struct cam_eb *bus; struct cam_et *target; struct cam_ed *device; cam_status status; status = CAM_REQ_CMP; /* Completed without error */ target = NULL; /* Wildcarded */ device = NULL; /* Wildcarded */ /* * We will potentially modify the EDT, so block interrupts * that may attempt to create cam paths. */ bus = xpt_find_bus(path_id); if (bus == NULL) { status = CAM_PATH_INVALID; } else { xpt_lock_buses(); mtx_lock(&bus->eb_mtx); target = xpt_find_target(bus, target_id); if (target == NULL) { /* Create one */ struct cam_et *new_target; new_target = xpt_alloc_target(bus, target_id); if (new_target == NULL) { status = CAM_RESRC_UNAVAIL; } else { target = new_target; } } xpt_unlock_buses(); if (target != NULL) { device = xpt_find_device(target, lun_id); if (device == NULL) { /* Create one */ struct cam_ed *new_device; new_device = (*(bus->xport->alloc_device))(bus, target, lun_id); if (new_device == NULL) { status = CAM_RESRC_UNAVAIL; } else { device = new_device; } } } mtx_unlock(&bus->eb_mtx); } /* * Only touch the user's data if we are successful. */ if (status == CAM_REQ_CMP) { new_path->periph = perph; new_path->bus = bus; new_path->target = target; new_path->device = device; CAM_DEBUG(new_path, CAM_DEBUG_TRACE, ("xpt_compile_path\n")); } else { if (device != NULL) xpt_release_device(device); if (target != NULL) xpt_release_target(target); if (bus != NULL) xpt_release_bus(bus); } return (status); } cam_status xpt_clone_path(struct cam_path **new_path_ptr, struct cam_path *path) { struct cam_path *new_path; new_path = (struct cam_path *)malloc(sizeof(*path), M_CAMPATH, M_NOWAIT); if (new_path == NULL) return(CAM_RESRC_UNAVAIL); xpt_copy_path(new_path, path); *new_path_ptr = new_path; return (CAM_REQ_CMP); } void xpt_copy_path(struct cam_path *new_path, struct cam_path *path) { *new_path = *path; if (path->bus != NULL) xpt_acquire_bus(path->bus); if (path->target != NULL) xpt_acquire_target(path->target); if (path->device != NULL) xpt_acquire_device(path->device); } void xpt_release_path(struct cam_path *path) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_release_path\n")); if (path->device != NULL) { xpt_release_device(path->device); path->device = NULL; } if (path->target != NULL) { xpt_release_target(path->target); path->target = NULL; } if (path->bus != NULL) { xpt_release_bus(path->bus); path->bus = NULL; } } void xpt_free_path(struct cam_path *path) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_free_path\n")); xpt_release_path(path); free(path, M_CAMPATH); } void xpt_path_counts(struct cam_path *path, uint32_t *bus_ref, uint32_t *periph_ref, uint32_t *target_ref, uint32_t *device_ref) { xpt_lock_buses(); if (bus_ref) { if (path->bus) *bus_ref = path->bus->refcount; else *bus_ref = 0; } if (periph_ref) { if (path->periph) *periph_ref = path->periph->refcount; else *periph_ref = 0; } xpt_unlock_buses(); if (target_ref) { if (path->target) *target_ref = path->target->refcount; else *target_ref = 0; } if (device_ref) { if (path->device) *device_ref = path->device->refcount; else *device_ref = 0; } } /* * Return -1 for failure, 0 for exact match, 1 for match with wildcards * in path1, 2 for match with wildcards in path2. */ int xpt_path_comp(struct cam_path *path1, struct cam_path *path2) { int retval = 0; if (path1->bus != path2->bus) { if (path1->bus->path_id == CAM_BUS_WILDCARD) retval = 1; else if (path2->bus->path_id == CAM_BUS_WILDCARD) retval = 2; else return (-1); } if (path1->target != path2->target) { if (path1->target->target_id == CAM_TARGET_WILDCARD) { if (retval == 0) retval = 1; } else if (path2->target->target_id == CAM_TARGET_WILDCARD) retval = 2; else return (-1); } if (path1->device != path2->device) { if (path1->device->lun_id == CAM_LUN_WILDCARD) { if (retval == 0) retval = 1; } else if (path2->device->lun_id == CAM_LUN_WILDCARD) retval = 2; else return (-1); } return (retval); } int xpt_path_comp_dev(struct cam_path *path, struct cam_ed *dev) { int retval = 0; if (path->bus != dev->target->bus) { if (path->bus->path_id == CAM_BUS_WILDCARD) retval = 1; else if (dev->target->bus->path_id == CAM_BUS_WILDCARD) retval = 2; else return (-1); } if (path->target != dev->target) { if (path->target->target_id == CAM_TARGET_WILDCARD) { if (retval == 0) retval = 1; } else if (dev->target->target_id == CAM_TARGET_WILDCARD) retval = 2; else return (-1); } if (path->device != dev) { if (path->device->lun_id == CAM_LUN_WILDCARD) { if (retval == 0) retval = 1; } else if (dev->lun_id == CAM_LUN_WILDCARD) retval = 2; else return (-1); } return (retval); } void xpt_print_path(struct cam_path *path) { if (path == NULL) printf("(nopath): "); else { if (path->periph != NULL) printf("(%s%d:", path->periph->periph_name, path->periph->unit_number); else printf("(noperiph:"); if (path->bus != NULL) printf("%s%d:%d:", path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id); else printf("nobus:"); if (path->target != NULL) printf("%d:", path->target->target_id); else printf("X:"); if (path->device != NULL) printf("%jx): ", (uintmax_t)path->device->lun_id); else printf("X): "); } } void xpt_print_device(struct cam_ed *device) { if (device == NULL) printf("(nopath): "); else { printf("(noperiph:%s%d:%d:%d:%jx): ", device->sim->sim_name, device->sim->unit_number, device->sim->bus_id, device->target->target_id, (uintmax_t)device->lun_id); } } void xpt_print(struct cam_path *path, const char *fmt, ...) { va_list ap; xpt_print_path(path); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } int xpt_path_string(struct cam_path *path, char *str, size_t str_len) { struct sbuf sb; sbuf_new(&sb, str, str_len, 0); if (path == NULL) sbuf_printf(&sb, "(nopath): "); else { if (path->periph != NULL) sbuf_printf(&sb, "(%s%d:", path->periph->periph_name, path->periph->unit_number); else sbuf_printf(&sb, "(noperiph:"); if (path->bus != NULL) sbuf_printf(&sb, "%s%d:%d:", path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id); else sbuf_printf(&sb, "nobus:"); if (path->target != NULL) sbuf_printf(&sb, "%d:", path->target->target_id); else sbuf_printf(&sb, "X:"); if (path->device != NULL) sbuf_printf(&sb, "%jx): ", (uintmax_t)path->device->lun_id); else sbuf_printf(&sb, "X): "); } sbuf_finish(&sb); return(sbuf_len(&sb)); } path_id_t xpt_path_path_id(struct cam_path *path) { return(path->bus->path_id); } target_id_t xpt_path_target_id(struct cam_path *path) { if (path->target != NULL) return (path->target->target_id); else return (CAM_TARGET_WILDCARD); } lun_id_t xpt_path_lun_id(struct cam_path *path) { if (path->device != NULL) return (path->device->lun_id); else return (CAM_LUN_WILDCARD); } struct cam_sim * xpt_path_sim(struct cam_path *path) { return (path->bus->sim); } struct cam_periph* xpt_path_periph(struct cam_path *path) { return (path->periph); } int xpt_path_legacy_ata_id(struct cam_path *path) { struct cam_eb *bus; int bus_id; if ((strcmp(path->bus->sim->sim_name, "ata") != 0) && strcmp(path->bus->sim->sim_name, "ahcich") != 0 && strcmp(path->bus->sim->sim_name, "mvsch") != 0 && strcmp(path->bus->sim->sim_name, "siisch") != 0) return (-1); if (strcmp(path->bus->sim->sim_name, "ata") == 0 && path->bus->sim->unit_number < 2) { bus_id = path->bus->sim->unit_number; } else { bus_id = 2; xpt_lock_buses(); TAILQ_FOREACH(bus, &xsoftc.xpt_busses, links) { if (bus == path->bus) break; if ((strcmp(bus->sim->sim_name, "ata") == 0 && bus->sim->unit_number >= 2) || strcmp(bus->sim->sim_name, "ahcich") == 0 || strcmp(bus->sim->sim_name, "mvsch") == 0 || strcmp(bus->sim->sim_name, "siisch") == 0) bus_id++; } xpt_unlock_buses(); } if (path->target != NULL) { if (path->target->target_id < 2) return (bus_id * 2 + path->target->target_id); else return (-1); } else return (bus_id * 2); } /* * Release a CAM control block for the caller. Remit the cost of the structure * to the device referenced by the path. If the this device had no 'credits' * and peripheral drivers have registered async callbacks for this notification * call them now. */ void xpt_release_ccb(union ccb *free_ccb) { struct cam_ed *device; struct cam_periph *periph; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_release_ccb\n")); xpt_path_assert(free_ccb->ccb_h.path, MA_OWNED); device = free_ccb->ccb_h.path->device; periph = free_ccb->ccb_h.path->periph; xpt_free_ccb(free_ccb); periph->periph_allocated--; cam_ccbq_release_opening(&device->ccbq); xpt_run_allocq(periph, 0); } /* Functions accessed by SIM drivers */ static struct xpt_xport xport_default = { .alloc_device = xpt_alloc_device_default, .action = xpt_action_default, .async = xpt_dev_async_default, }; /* * A sim structure, listing the SIM entry points and instance * identification info is passed to xpt_bus_register to hook the SIM * into the CAM framework. xpt_bus_register creates a cam_eb entry * for this new bus and places it in the array of busses and assigns * it a path_id. The path_id may be influenced by "hard wiring" * information specified by the user. Once interrupt services are * available, the bus will be probed. */ int32_t xpt_bus_register(struct cam_sim *sim, device_t parent, u_int32_t bus) { struct cam_eb *new_bus; struct cam_eb *old_bus; struct ccb_pathinq cpi; struct cam_path *path; cam_status status; mtx_assert(sim->mtx, MA_OWNED); sim->bus_id = bus; new_bus = (struct cam_eb *)malloc(sizeof(*new_bus), M_CAMXPT, M_NOWAIT|M_ZERO); if (new_bus == NULL) { /* Couldn't satisfy request */ return (CAM_RESRC_UNAVAIL); } mtx_init(&new_bus->eb_mtx, "CAM bus lock", NULL, MTX_DEF); TAILQ_INIT(&new_bus->et_entries); cam_sim_hold(sim); new_bus->sim = sim; timevalclear(&new_bus->last_reset); new_bus->flags = 0; new_bus->refcount = 1; /* Held until a bus_deregister event */ new_bus->generation = 0; xpt_lock_buses(); sim->path_id = new_bus->path_id = xptpathid(sim->sim_name, sim->unit_number, sim->bus_id); old_bus = TAILQ_FIRST(&xsoftc.xpt_busses); while (old_bus != NULL && old_bus->path_id < new_bus->path_id) old_bus = TAILQ_NEXT(old_bus, links); if (old_bus != NULL) TAILQ_INSERT_BEFORE(old_bus, new_bus, links); else TAILQ_INSERT_TAIL(&xsoftc.xpt_busses, new_bus, links); xsoftc.bus_generation++; xpt_unlock_buses(); /* * Set a default transport so that a PATH_INQ can be issued to * the SIM. This will then allow for probing and attaching of * a more appropriate transport. */ new_bus->xport = &xport_default; status = xpt_create_path(&path, /*periph*/NULL, sim->path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status != CAM_REQ_CMP) { xpt_release_bus(new_bus); free(path, M_CAMXPT); return (CAM_RESRC_UNAVAIL); } xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status == CAM_REQ_CMP) { switch (cpi.transport) { case XPORT_SPI: case XPORT_SAS: case XPORT_FC: case XPORT_USB: case XPORT_ISCSI: case XPORT_SRP: case XPORT_PPB: new_bus->xport = scsi_get_xport(); break; case XPORT_ATA: case XPORT_SATA: new_bus->xport = ata_get_xport(); break; default: new_bus->xport = &xport_default; break; } } /* Notify interested parties */ if (sim->path_id != CAM_XPT_PATH_ID) { xpt_async(AC_PATH_REGISTERED, path, &cpi); if ((cpi.hba_misc & PIM_NOSCAN) == 0) { union ccb *scan_ccb; /* Initiate bus rescan. */ scan_ccb = xpt_alloc_ccb_nowait(); if (scan_ccb != NULL) { scan_ccb->ccb_h.path = path; scan_ccb->ccb_h.func_code = XPT_SCAN_BUS; scan_ccb->crcn.flags = 0; xpt_rescan(scan_ccb); } else { xpt_print(path, "Can't allocate CCB to scan bus\n"); xpt_free_path(path); } } else xpt_free_path(path); } else xpt_free_path(path); return (CAM_SUCCESS); } int32_t xpt_bus_deregister(path_id_t pathid) { struct cam_path bus_path; cam_status status; status = xpt_compile_path(&bus_path, NULL, pathid, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status != CAM_REQ_CMP) return (status); xpt_async(AC_LOST_DEVICE, &bus_path, NULL); xpt_async(AC_PATH_DEREGISTERED, &bus_path, NULL); /* Release the reference count held while registered. */ xpt_release_bus(bus_path.bus); xpt_release_path(&bus_path); return (CAM_REQ_CMP); } static path_id_t xptnextfreepathid(void) { struct cam_eb *bus; path_id_t pathid; const char *strval; mtx_assert(&xsoftc.xpt_topo_lock, MA_OWNED); pathid = 0; bus = TAILQ_FIRST(&xsoftc.xpt_busses); retry: /* Find an unoccupied pathid */ while (bus != NULL && bus->path_id <= pathid) { if (bus->path_id == pathid) pathid++; bus = TAILQ_NEXT(bus, links); } /* * Ensure that this pathid is not reserved for * a bus that may be registered in the future. */ if (resource_string_value("scbus", pathid, "at", &strval) == 0) { ++pathid; /* Start the search over */ goto retry; } return (pathid); } static path_id_t xptpathid(const char *sim_name, int sim_unit, int sim_bus) { path_id_t pathid; int i, dunit, val; char buf[32]; const char *dname; pathid = CAM_XPT_PATH_ID; snprintf(buf, sizeof(buf), "%s%d", sim_name, sim_unit); if (strcmp(buf, "xpt0") == 0 && sim_bus == 0) return (pathid); i = 0; while ((resource_find_match(&i, &dname, &dunit, "at", buf)) == 0) { if (strcmp(dname, "scbus")) { /* Avoid a bit of foot shooting. */ continue; } if (dunit < 0) /* unwired?! */ continue; if (resource_int_value("scbus", dunit, "bus", &val) == 0) { if (sim_bus == val) { pathid = dunit; break; } } else if (sim_bus == 0) { /* Unspecified matches bus 0 */ pathid = dunit; break; } else { printf("Ambiguous scbus configuration for %s%d " "bus %d, cannot wire down. The kernel " "config entry for scbus%d should " "specify a controller bus.\n" "Scbus will be assigned dynamically.\n", sim_name, sim_unit, sim_bus, dunit); break; } } if (pathid == CAM_XPT_PATH_ID) pathid = xptnextfreepathid(); return (pathid); } static const char * xpt_async_string(u_int32_t async_code) { switch (async_code) { case AC_BUS_RESET: return ("AC_BUS_RESET"); case AC_UNSOL_RESEL: return ("AC_UNSOL_RESEL"); case AC_SCSI_AEN: return ("AC_SCSI_AEN"); case AC_SENT_BDR: return ("AC_SENT_BDR"); case AC_PATH_REGISTERED: return ("AC_PATH_REGISTERED"); case AC_PATH_DEREGISTERED: return ("AC_PATH_DEREGISTERED"); case AC_FOUND_DEVICE: return ("AC_FOUND_DEVICE"); case AC_LOST_DEVICE: return ("AC_LOST_DEVICE"); case AC_TRANSFER_NEG: return ("AC_TRANSFER_NEG"); case AC_INQ_CHANGED: return ("AC_INQ_CHANGED"); case AC_GETDEV_CHANGED: return ("AC_GETDEV_CHANGED"); case AC_CONTRACT: return ("AC_CONTRACT"); case AC_ADVINFO_CHANGED: return ("AC_ADVINFO_CHANGED"); case AC_UNIT_ATTENTION: return ("AC_UNIT_ATTENTION"); } return ("AC_UNKNOWN"); } static int xpt_async_size(u_int32_t async_code) { switch (async_code) { case AC_BUS_RESET: return (0); case AC_UNSOL_RESEL: return (0); case AC_SCSI_AEN: return (0); case AC_SENT_BDR: return (0); case AC_PATH_REGISTERED: return (sizeof(struct ccb_pathinq)); case AC_PATH_DEREGISTERED: return (0); case AC_FOUND_DEVICE: return (sizeof(struct ccb_getdev)); case AC_LOST_DEVICE: return (0); case AC_TRANSFER_NEG: return (sizeof(struct ccb_trans_settings)); case AC_INQ_CHANGED: return (0); case AC_GETDEV_CHANGED: return (0); case AC_CONTRACT: return (sizeof(struct ac_contract)); case AC_ADVINFO_CHANGED: return (-1); case AC_UNIT_ATTENTION: return (sizeof(struct ccb_scsiio)); } return (0); } static int xpt_async_process_dev(struct cam_ed *device, void *arg) { union ccb *ccb = arg; struct cam_path *path = ccb->ccb_h.path; void *async_arg = ccb->casync.async_arg_ptr; u_int32_t async_code = ccb->casync.async_code; int relock; if (path->device != device && path->device->lun_id != CAM_LUN_WILDCARD && device->lun_id != CAM_LUN_WILDCARD) return (1); /* * The async callback could free the device. * If it is a broadcast async, it doesn't hold * device reference, so take our own reference. */ xpt_acquire_device(device); /* * If async for specific device is to be delivered to * the wildcard client, take the specific device lock. * XXX: We may need a way for client to specify it. */ if ((device->lun_id == CAM_LUN_WILDCARD && path->device->lun_id != CAM_LUN_WILDCARD) || (device->target->target_id == CAM_TARGET_WILDCARD && path->target->target_id != CAM_TARGET_WILDCARD) || (device->target->bus->path_id == CAM_BUS_WILDCARD && path->target->bus->path_id != CAM_BUS_WILDCARD)) { mtx_unlock(&device->device_mtx); xpt_path_lock(path); relock = 1; } else relock = 0; (*(device->target->bus->xport->async))(async_code, device->target->bus, device->target, device, async_arg); xpt_async_bcast(&device->asyncs, async_code, path, async_arg); if (relock) { xpt_path_unlock(path); mtx_lock(&device->device_mtx); } xpt_release_device(device); return (1); } static int xpt_async_process_tgt(struct cam_et *target, void *arg) { union ccb *ccb = arg; struct cam_path *path = ccb->ccb_h.path; if (path->target != target && path->target->target_id != CAM_TARGET_WILDCARD && target->target_id != CAM_TARGET_WILDCARD) return (1); if (ccb->casync.async_code == AC_SENT_BDR) { /* Update our notion of when the last reset occurred */ microtime(&target->last_reset); } return (xptdevicetraverse(target, NULL, xpt_async_process_dev, ccb)); } static void xpt_async_process(struct cam_periph *periph, union ccb *ccb) { struct cam_eb *bus; struct cam_path *path; void *async_arg; u_int32_t async_code; path = ccb->ccb_h.path; async_code = ccb->casync.async_code; async_arg = ccb->casync.async_arg_ptr; CAM_DEBUG(path, CAM_DEBUG_TRACE | CAM_DEBUG_INFO, ("xpt_async(%s)\n", xpt_async_string(async_code))); bus = path->bus; if (async_code == AC_BUS_RESET) { /* Update our notion of when the last reset occurred */ microtime(&bus->last_reset); } xpttargettraverse(bus, NULL, xpt_async_process_tgt, ccb); /* * If this wasn't a fully wildcarded async, tell all * clients that want all async events. */ if (bus != xpt_periph->path->bus) { xpt_path_lock(xpt_periph->path); xpt_async_process_dev(xpt_periph->path->device, ccb); xpt_path_unlock(xpt_periph->path); } if (path->device != NULL && path->device->lun_id != CAM_LUN_WILDCARD) xpt_release_devq(path, 1, TRUE); else xpt_release_simq(path->bus->sim, TRUE); if (ccb->casync.async_arg_size > 0) free(async_arg, M_CAMXPT); xpt_free_path(path); xpt_free_ccb(ccb); } static void xpt_async_bcast(struct async_list *async_head, u_int32_t async_code, struct cam_path *path, void *async_arg) { struct async_node *cur_entry; int lock; cur_entry = SLIST_FIRST(async_head); while (cur_entry != NULL) { struct async_node *next_entry; /* * Grab the next list entry before we call the current * entry's callback. This is because the callback function * can delete its async callback entry. */ next_entry = SLIST_NEXT(cur_entry, links); if ((cur_entry->event_enable & async_code) != 0) { lock = cur_entry->event_lock; if (lock) CAM_SIM_LOCK(path->device->sim); cur_entry->callback(cur_entry->callback_arg, async_code, path, async_arg); if (lock) CAM_SIM_UNLOCK(path->device->sim); } cur_entry = next_entry; } } void xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg) { union ccb *ccb; int size; ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { xpt_print(path, "Can't allocate CCB to send %s\n", xpt_async_string(async_code)); return; } if (xpt_clone_path(&ccb->ccb_h.path, path) != CAM_REQ_CMP) { xpt_print(path, "Can't allocate path to send %s\n", xpt_async_string(async_code)); xpt_free_ccb(ccb); return; } ccb->ccb_h.path->periph = NULL; ccb->ccb_h.func_code = XPT_ASYNC; ccb->ccb_h.cbfcnp = xpt_async_process; ccb->ccb_h.flags |= CAM_UNLOCKED; ccb->casync.async_code = async_code; ccb->casync.async_arg_size = 0; size = xpt_async_size(async_code); if (size > 0 && async_arg != NULL) { ccb->casync.async_arg_ptr = malloc(size, M_CAMXPT, M_NOWAIT); if (ccb->casync.async_arg_ptr == NULL) { xpt_print(path, "Can't allocate argument to send %s\n", xpt_async_string(async_code)); xpt_free_path(ccb->ccb_h.path); xpt_free_ccb(ccb); return; } memcpy(ccb->casync.async_arg_ptr, async_arg, size); ccb->casync.async_arg_size = size; } else if (size < 0) ccb->casync.async_arg_size = size; if (path->device != NULL && path->device->lun_id != CAM_LUN_WILDCARD) xpt_freeze_devq(path, 1); else xpt_freeze_simq(path->bus->sim, 1); xpt_done(ccb); } static void xpt_dev_async_default(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg) { /* * We only need to handle events for real devices. */ if (target->target_id == CAM_TARGET_WILDCARD || device->lun_id == CAM_LUN_WILDCARD) return; printf("%s called\n", __func__); } static uint32_t xpt_freeze_devq_device(struct cam_ed *dev, u_int count) { struct cam_devq *devq; uint32_t freeze; devq = dev->sim->devq; mtx_assert(&devq->send_mtx, MA_OWNED); CAM_DEBUG_DEV(dev, CAM_DEBUG_TRACE, ("xpt_freeze_devq_device(%d) %u->%u\n", count, dev->ccbq.queue.qfrozen_cnt, dev->ccbq.queue.qfrozen_cnt + count)); freeze = (dev->ccbq.queue.qfrozen_cnt += count); /* Remove frozen device from sendq. */ if (device_is_queued(dev)) camq_remove(&devq->send_queue, dev->devq_entry.index); return (freeze); } u_int32_t xpt_freeze_devq(struct cam_path *path, u_int count) { struct cam_ed *dev = path->device; struct cam_devq *devq; uint32_t freeze; devq = dev->sim->devq; mtx_lock(&devq->send_mtx); CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_freeze_devq(%d)\n", count)); freeze = xpt_freeze_devq_device(dev, count); mtx_unlock(&devq->send_mtx); return (freeze); } u_int32_t xpt_freeze_simq(struct cam_sim *sim, u_int count) { struct cam_devq *devq; uint32_t freeze; devq = sim->devq; mtx_lock(&devq->send_mtx); freeze = (devq->send_queue.qfrozen_cnt += count); mtx_unlock(&devq->send_mtx); return (freeze); } static void xpt_release_devq_timeout(void *arg) { struct cam_ed *dev; struct cam_devq *devq; dev = (struct cam_ed *)arg; CAM_DEBUG_DEV(dev, CAM_DEBUG_TRACE, ("xpt_release_devq_timeout\n")); devq = dev->sim->devq; mtx_assert(&devq->send_mtx, MA_OWNED); if (xpt_release_devq_device(dev, /*count*/1, /*run_queue*/TRUE)) xpt_run_devq(devq); } void xpt_release_devq(struct cam_path *path, u_int count, int run_queue) { struct cam_ed *dev; struct cam_devq *devq; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_release_devq(%d, %d)\n", count, run_queue)); dev = path->device; devq = dev->sim->devq; mtx_lock(&devq->send_mtx); if (xpt_release_devq_device(dev, count, run_queue)) xpt_run_devq(dev->sim->devq); mtx_unlock(&devq->send_mtx); } static int xpt_release_devq_device(struct cam_ed *dev, u_int count, int run_queue) { mtx_assert(&dev->sim->devq->send_mtx, MA_OWNED); CAM_DEBUG_DEV(dev, CAM_DEBUG_TRACE, ("xpt_release_devq_device(%d, %d) %u->%u\n", count, run_queue, dev->ccbq.queue.qfrozen_cnt, dev->ccbq.queue.qfrozen_cnt - count)); if (count > dev->ccbq.queue.qfrozen_cnt) { #ifdef INVARIANTS printf("xpt_release_devq(): requested %u > present %u\n", count, dev->ccbq.queue.qfrozen_cnt); #endif count = dev->ccbq.queue.qfrozen_cnt; } dev->ccbq.queue.qfrozen_cnt -= count; if (dev->ccbq.queue.qfrozen_cnt == 0) { /* * No longer need to wait for a successful * command completion. */ dev->flags &= ~CAM_DEV_REL_ON_COMPLETE; /* * Remove any timeouts that might be scheduled * to release this queue. */ if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) { callout_stop(&dev->callout); dev->flags &= ~CAM_DEV_REL_TIMEOUT_PENDING; } /* * Now that we are unfrozen schedule the * device so any pending transactions are * run. */ xpt_schedule_devq(dev->sim->devq, dev); } else run_queue = 0; return (run_queue); } void xpt_release_simq(struct cam_sim *sim, int run_queue) { struct cam_devq *devq; devq = sim->devq; mtx_lock(&devq->send_mtx); if (devq->send_queue.qfrozen_cnt <= 0) { #ifdef INVARIANTS printf("xpt_release_simq: requested 1 > present %u\n", devq->send_queue.qfrozen_cnt); #endif } else devq->send_queue.qfrozen_cnt--; if (devq->send_queue.qfrozen_cnt == 0) { /* * If there is a timeout scheduled to release this * sim queue, remove it. The queue frozen count is * already at 0. */ if ((sim->flags & CAM_SIM_REL_TIMEOUT_PENDING) != 0){ callout_stop(&sim->callout); sim->flags &= ~CAM_SIM_REL_TIMEOUT_PENDING; } if (run_queue) { /* * Now that we are unfrozen run the send queue. */ xpt_run_devq(sim->devq); } } mtx_unlock(&devq->send_mtx); } /* * XXX Appears to be unused. */ static void xpt_release_simq_timeout(void *arg) { struct cam_sim *sim; sim = (struct cam_sim *)arg; xpt_release_simq(sim, /* run_queue */ TRUE); } void xpt_done(union ccb *done_ccb) { struct cam_doneq *queue; int run, hash; CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_done\n")); if ((done_ccb->ccb_h.func_code & XPT_FC_QUEUED) == 0) return; hash = (done_ccb->ccb_h.path_id + done_ccb->ccb_h.target_id + done_ccb->ccb_h.target_lun) % cam_num_doneqs; queue = &cam_doneqs[hash]; mtx_lock(&queue->cam_doneq_mtx); run = (queue->cam_doneq_sleep && STAILQ_EMPTY(&queue->cam_doneq)); STAILQ_INSERT_TAIL(&queue->cam_doneq, &done_ccb->ccb_h, sim_links.stqe); done_ccb->ccb_h.pinfo.index = CAM_DONEQ_INDEX; mtx_unlock(&queue->cam_doneq_mtx); if (run) wakeup(&queue->cam_doneq); } void xpt_done_direct(union ccb *done_ccb) { CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_done_direct\n")); if ((done_ccb->ccb_h.func_code & XPT_FC_QUEUED) == 0) return; xpt_done_process(&done_ccb->ccb_h); } union ccb * xpt_alloc_ccb() { union ccb *new_ccb; new_ccb = malloc(sizeof(*new_ccb), M_CAMCCB, M_ZERO|M_WAITOK); return (new_ccb); } union ccb * xpt_alloc_ccb_nowait() { union ccb *new_ccb; new_ccb = malloc(sizeof(*new_ccb), M_CAMCCB, M_ZERO|M_NOWAIT); return (new_ccb); } void xpt_free_ccb(union ccb *free_ccb) { free(free_ccb, M_CAMCCB); } /* Private XPT functions */ /* * Get a CAM control block for the caller. Charge the structure to the device * referenced by the path. If we don't have sufficient resources to allocate * more ccbs, we return NULL. */ static union ccb * xpt_get_ccb_nowait(struct cam_periph *periph) { union ccb *new_ccb; new_ccb = malloc(sizeof(*new_ccb), M_CAMCCB, M_NOWAIT); if (new_ccb == NULL) return (NULL); periph->periph_allocated++; cam_ccbq_take_opening(&periph->path->device->ccbq); return (new_ccb); } static union ccb * xpt_get_ccb(struct cam_periph *periph) { union ccb *new_ccb; cam_periph_unlock(periph); new_ccb = malloc(sizeof(*new_ccb), M_CAMCCB, M_WAITOK); cam_periph_lock(periph); periph->periph_allocated++; cam_ccbq_take_opening(&periph->path->device->ccbq); return (new_ccb); } union ccb * cam_periph_getccb(struct cam_periph *periph, u_int32_t priority) { struct ccb_hdr *ccb_h; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("cam_periph_getccb\n")); cam_periph_assert(periph, MA_OWNED); while ((ccb_h = SLIST_FIRST(&periph->ccb_list)) == NULL || ccb_h->pinfo.priority != priority) { if (priority < periph->immediate_priority) { periph->immediate_priority = priority; xpt_run_allocq(periph, 0); } else cam_periph_sleep(periph, &periph->ccb_list, PRIBIO, "cgticb", 0); } SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle); return ((union ccb *)ccb_h); } static void xpt_acquire_bus(struct cam_eb *bus) { xpt_lock_buses(); bus->refcount++; xpt_unlock_buses(); } static void xpt_release_bus(struct cam_eb *bus) { xpt_lock_buses(); KASSERT(bus->refcount >= 1, ("bus->refcount >= 1")); if (--bus->refcount > 0) { xpt_unlock_buses(); return; } TAILQ_REMOVE(&xsoftc.xpt_busses, bus, links); xsoftc.bus_generation++; xpt_unlock_buses(); KASSERT(TAILQ_EMPTY(&bus->et_entries), ("destroying bus, but target list is not empty")); cam_sim_release(bus->sim); mtx_destroy(&bus->eb_mtx); free(bus, M_CAMXPT); } static struct cam_et * xpt_alloc_target(struct cam_eb *bus, target_id_t target_id) { struct cam_et *cur_target, *target; mtx_assert(&xsoftc.xpt_topo_lock, MA_OWNED); mtx_assert(&bus->eb_mtx, MA_OWNED); target = (struct cam_et *)malloc(sizeof(*target), M_CAMXPT, M_NOWAIT|M_ZERO); if (target == NULL) return (NULL); TAILQ_INIT(&target->ed_entries); target->bus = bus; target->target_id = target_id; target->refcount = 1; target->generation = 0; target->luns = NULL; mtx_init(&target->luns_mtx, "CAM LUNs lock", NULL, MTX_DEF); timevalclear(&target->last_reset); /* * Hold a reference to our parent bus so it * will not go away before we do. */ bus->refcount++; /* Insertion sort into our bus's target list */ cur_target = TAILQ_FIRST(&bus->et_entries); while (cur_target != NULL && cur_target->target_id < target_id) cur_target = TAILQ_NEXT(cur_target, links); if (cur_target != NULL) { TAILQ_INSERT_BEFORE(cur_target, target, links); } else { TAILQ_INSERT_TAIL(&bus->et_entries, target, links); } bus->generation++; return (target); } static void xpt_acquire_target(struct cam_et *target) { struct cam_eb *bus = target->bus; mtx_lock(&bus->eb_mtx); target->refcount++; mtx_unlock(&bus->eb_mtx); } static void xpt_release_target(struct cam_et *target) { struct cam_eb *bus = target->bus; mtx_lock(&bus->eb_mtx); if (--target->refcount > 0) { mtx_unlock(&bus->eb_mtx); return; } TAILQ_REMOVE(&bus->et_entries, target, links); bus->generation++; mtx_unlock(&bus->eb_mtx); KASSERT(TAILQ_EMPTY(&target->ed_entries), ("destroying target, but device list is not empty")); xpt_release_bus(bus); mtx_destroy(&target->luns_mtx); if (target->luns) free(target->luns, M_CAMXPT); free(target, M_CAMXPT); } static struct cam_ed * xpt_alloc_device_default(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) { struct cam_ed *device; device = xpt_alloc_device(bus, target, lun_id); if (device == NULL) return (NULL); device->mintags = 1; device->maxtags = 1; return (device); } static void xpt_destroy_device(void *context, int pending) { struct cam_ed *device = context; mtx_lock(&device->device_mtx); mtx_destroy(&device->device_mtx); free(device, M_CAMDEV); } struct cam_ed * xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) { struct cam_ed *cur_device, *device; struct cam_devq *devq; cam_status status; mtx_assert(&bus->eb_mtx, MA_OWNED); /* Make space for us in the device queue on our bus */ devq = bus->sim->devq; mtx_lock(&devq->send_mtx); status = cam_devq_resize(devq, devq->send_queue.array_size + 1); mtx_unlock(&devq->send_mtx); if (status != CAM_REQ_CMP) return (NULL); device = (struct cam_ed *)malloc(sizeof(*device), M_CAMDEV, M_NOWAIT|M_ZERO); if (device == NULL) return (NULL); cam_init_pinfo(&device->devq_entry); device->target = target; device->lun_id = lun_id; device->sim = bus->sim; if (cam_ccbq_init(&device->ccbq, bus->sim->max_dev_openings) != 0) { free(device, M_CAMDEV); return (NULL); } SLIST_INIT(&device->asyncs); SLIST_INIT(&device->periphs); device->generation = 0; device->flags = CAM_DEV_UNCONFIGURED; device->tag_delay_count = 0; device->tag_saved_openings = 0; device->refcount = 1; mtx_init(&device->device_mtx, "CAM device lock", NULL, MTX_DEF); callout_init_mtx(&device->callout, &devq->send_mtx, 0); TASK_INIT(&device->device_destroy_task, 0, xpt_destroy_device, device); /* * Hold a reference to our parent bus so it * will not go away before we do. */ target->refcount++; cur_device = TAILQ_FIRST(&target->ed_entries); while (cur_device != NULL && cur_device->lun_id < lun_id) cur_device = TAILQ_NEXT(cur_device, links); if (cur_device != NULL) TAILQ_INSERT_BEFORE(cur_device, device, links); else TAILQ_INSERT_TAIL(&target->ed_entries, device, links); target->generation++; return (device); } void xpt_acquire_device(struct cam_ed *device) { struct cam_eb *bus = device->target->bus; mtx_lock(&bus->eb_mtx); device->refcount++; mtx_unlock(&bus->eb_mtx); } void xpt_release_device(struct cam_ed *device) { struct cam_eb *bus = device->target->bus; struct cam_devq *devq; mtx_lock(&bus->eb_mtx); if (--device->refcount > 0) { mtx_unlock(&bus->eb_mtx); return; } TAILQ_REMOVE(&device->target->ed_entries, device,links); device->target->generation++; mtx_unlock(&bus->eb_mtx); /* Release our slot in the devq */ devq = bus->sim->devq; mtx_lock(&devq->send_mtx); cam_devq_resize(devq, devq->send_queue.array_size - 1); mtx_unlock(&devq->send_mtx); KASSERT(SLIST_EMPTY(&device->periphs), ("destroying device, but periphs list is not empty")); KASSERT(device->devq_entry.index == CAM_UNQUEUED_INDEX, ("destroying device while still queued for ccbs")); if ((device->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) callout_stop(&device->callout); xpt_release_target(device->target); cam_ccbq_fini(&device->ccbq); /* * Free allocated memory. free(9) does nothing if the * supplied pointer is NULL, so it is safe to call without * checking. */ free(device->supported_vpds, M_CAMXPT); free(device->device_id, M_CAMXPT); free(device->physpath, M_CAMXPT); free(device->rcap_buf, M_CAMXPT); free(device->serial_num, M_CAMXPT); taskqueue_enqueue(xsoftc.xpt_taskq, &device->device_destroy_task); } u_int32_t xpt_dev_ccbq_resize(struct cam_path *path, int newopenings) { int result; struct cam_ed *dev; dev = path->device; mtx_lock(&dev->sim->devq->send_mtx); result = cam_ccbq_resize(&dev->ccbq, newopenings); mtx_unlock(&dev->sim->devq->send_mtx); if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || (dev->inq_flags & SID_CmdQue) != 0) dev->tag_saved_openings = newopenings; return (result); } static struct cam_eb * xpt_find_bus(path_id_t path_id) { struct cam_eb *bus; xpt_lock_buses(); for (bus = TAILQ_FIRST(&xsoftc.xpt_busses); bus != NULL; bus = TAILQ_NEXT(bus, links)) { if (bus->path_id == path_id) { bus->refcount++; break; } } xpt_unlock_buses(); return (bus); } static struct cam_et * xpt_find_target(struct cam_eb *bus, target_id_t target_id) { struct cam_et *target; mtx_assert(&bus->eb_mtx, MA_OWNED); for (target = TAILQ_FIRST(&bus->et_entries); target != NULL; target = TAILQ_NEXT(target, links)) { if (target->target_id == target_id) { target->refcount++; break; } } return (target); } static struct cam_ed * xpt_find_device(struct cam_et *target, lun_id_t lun_id) { struct cam_ed *device; mtx_assert(&target->bus->eb_mtx, MA_OWNED); for (device = TAILQ_FIRST(&target->ed_entries); device != NULL; device = TAILQ_NEXT(device, links)) { if (device->lun_id == lun_id) { device->refcount++; break; } } return (device); } void xpt_start_tags(struct cam_path *path) { struct ccb_relsim crs; struct cam_ed *device; struct cam_sim *sim; int newopenings; device = path->device; sim = path->bus->sim; device->flags &= ~CAM_DEV_TAG_AFTER_COUNT; xpt_freeze_devq(path, /*count*/1); device->inq_flags |= SID_CmdQue; if (device->tag_saved_openings != 0) newopenings = device->tag_saved_openings; else newopenings = min(device->maxtags, sim->max_tagged_dev_openings); xpt_dev_ccbq_resize(path, newopenings); xpt_async(AC_GETDEV_CHANGED, path, NULL); xpt_setup_ccb(&crs.ccb_h, path, CAM_PRIORITY_NORMAL); crs.ccb_h.func_code = XPT_REL_SIMQ; crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; crs.openings = crs.release_timeout = crs.qfrozen_cnt = 0; xpt_action((union ccb *)&crs); } void xpt_stop_tags(struct cam_path *path) { struct ccb_relsim crs; struct cam_ed *device; struct cam_sim *sim; device = path->device; sim = path->bus->sim; device->flags &= ~CAM_DEV_TAG_AFTER_COUNT; device->tag_delay_count = 0; xpt_freeze_devq(path, /*count*/1); device->inq_flags &= ~SID_CmdQue; xpt_dev_ccbq_resize(path, sim->max_dev_openings); xpt_async(AC_GETDEV_CHANGED, path, NULL); xpt_setup_ccb(&crs.ccb_h, path, CAM_PRIORITY_NORMAL); crs.ccb_h.func_code = XPT_REL_SIMQ; crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; crs.openings = crs.release_timeout = crs.qfrozen_cnt = 0; xpt_action((union ccb *)&crs); } static void xpt_boot_delay(void *arg) { xpt_release_boot(); } static void xpt_config(void *arg) { /* * Now that interrupts are enabled, go find our devices */ if (taskqueue_start_threads(&xsoftc.xpt_taskq, 1, PRIBIO, "CAM taskq")) printf("xpt_config: failed to create taskqueue thread.\n"); /* Setup debugging path */ if (cam_dflags != CAM_DEBUG_NONE) { if (xpt_create_path(&cam_dpath, NULL, CAM_DEBUG_BUS, CAM_DEBUG_TARGET, CAM_DEBUG_LUN) != CAM_REQ_CMP) { printf("xpt_config: xpt_create_path() failed for debug" " target %d:%d:%d, debugging disabled\n", CAM_DEBUG_BUS, CAM_DEBUG_TARGET, CAM_DEBUG_LUN); cam_dflags = CAM_DEBUG_NONE; } } else cam_dpath = NULL; periphdriver_init(1); xpt_hold_boot(); callout_init(&xsoftc.boot_callout, 1); callout_reset_sbt(&xsoftc.boot_callout, SBT_1MS * xsoftc.boot_delay, 0, xpt_boot_delay, NULL, 0); /* Fire up rescan thread. */ if (kproc_kthread_add(xpt_scanner_thread, NULL, &cam_proc, NULL, 0, 0, "cam", "scanner")) { printf("xpt_config: failed to create rescan thread.\n"); } } void xpt_hold_boot(void) { xpt_lock_buses(); xsoftc.buses_to_config++; xpt_unlock_buses(); } void xpt_release_boot(void) { xpt_lock_buses(); xsoftc.buses_to_config--; if (xsoftc.buses_to_config == 0 && xsoftc.buses_config_done == 0) { struct xpt_task *task; xsoftc.buses_config_done = 1; xpt_unlock_buses(); /* Call manually because we don't have any busses */ task = malloc(sizeof(struct xpt_task), M_CAMXPT, M_NOWAIT); if (task != NULL) { TASK_INIT(&task->task, 0, xpt_finishconfig_task, task); taskqueue_enqueue(taskqueue_thread, &task->task); } } else xpt_unlock_buses(); } /* * If the given device only has one peripheral attached to it, and if that * peripheral is the passthrough driver, announce it. This insures that the * user sees some sort of announcement for every peripheral in their system. */ static int xptpassannouncefunc(struct cam_ed *device, void *arg) { struct cam_periph *periph; int i; for (periph = SLIST_FIRST(&device->periphs), i = 0; periph != NULL; periph = SLIST_NEXT(periph, periph_links), i++); periph = SLIST_FIRST(&device->periphs); if ((i == 1) && (strncmp(periph->periph_name, "pass", 4) == 0)) xpt_announce_periph(periph, NULL); return(1); } static void xpt_finishconfig_task(void *context, int pending) { periphdriver_init(2); /* * Check for devices with no "standard" peripheral driver * attached. For any devices like that, announce the * passthrough driver so the user will see something. */ if (!bootverbose) xpt_for_all_devices(xptpassannouncefunc, NULL); /* Release our hook so that the boot can continue. */ config_intrhook_disestablish(xsoftc.xpt_config_hook); free(xsoftc.xpt_config_hook, M_CAMXPT); xsoftc.xpt_config_hook = NULL; free(context, M_CAMXPT); } cam_status xpt_register_async(int event, ac_callback_t *cbfunc, void *cbarg, struct cam_path *path) { struct ccb_setasync csa; cam_status status; int xptpath = 0; if (path == NULL) { status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status != CAM_REQ_CMP) return (status); xpt_path_lock(path); xptpath = 1; } xpt_setup_ccb(&csa.ccb_h, path, CAM_PRIORITY_NORMAL); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = event; csa.callback = cbfunc; csa.callback_arg = cbarg; xpt_action((union ccb *)&csa); status = csa.ccb_h.status; if (xptpath) { xpt_path_unlock(path); xpt_free_path(path); } if ((status == CAM_REQ_CMP) && (csa.event_enable & AC_FOUND_DEVICE)) { /* * Get this peripheral up to date with all * the currently existing devices. */ xpt_for_all_devices(xptsetasyncfunc, &csa); } if ((status == CAM_REQ_CMP) && (csa.event_enable & AC_PATH_REGISTERED)) { /* * Get this peripheral up to date with all * the currently existing busses. */ xpt_for_all_busses(xptsetasyncbusfunc, &csa); } return (status); } static void xptaction(struct cam_sim *sim, union ccb *work_ccb) { CAM_DEBUG(work_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xptaction\n")); switch (work_ccb->ccb_h.func_code) { /* Common cases first */ case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi; cpi = &work_ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = 0; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = 0; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "", HBA_IDLEN); strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); cpi->unit_number = sim->unit_number; cpi->bus_id = sim->bus_id; cpi->base_transfer_speed = 0; cpi->protocol = PROTO_UNSPECIFIED; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->transport = XPORT_UNSPECIFIED; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(work_ccb); break; } default: work_ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(work_ccb); break; } } /* * The xpt as a "controller" has no interrupt sources, so polling * is a no-op. */ static void xptpoll(struct cam_sim *sim) { } void xpt_lock_buses(void) { mtx_lock(&xsoftc.xpt_topo_lock); } void xpt_unlock_buses(void) { mtx_unlock(&xsoftc.xpt_topo_lock); } struct mtx * xpt_path_mtx(struct cam_path *path) { return (&path->device->device_mtx); } static void xpt_done_process(struct ccb_hdr *ccb_h) { struct cam_sim *sim; struct cam_devq *devq; struct mtx *mtx = NULL; if (ccb_h->flags & CAM_HIGH_POWER) { struct highpowerlist *hphead; struct cam_ed *device; mtx_lock(&xsoftc.xpt_highpower_lock); hphead = &xsoftc.highpowerq; device = STAILQ_FIRST(hphead); /* * Increment the count since this command is done. */ xsoftc.num_highpower++; /* * Any high powered commands queued up? */ if (device != NULL) { STAILQ_REMOVE_HEAD(hphead, highpowerq_entry); mtx_unlock(&xsoftc.xpt_highpower_lock); mtx_lock(&device->sim->devq->send_mtx); xpt_release_devq_device(device, /*count*/1, /*runqueue*/TRUE); mtx_unlock(&device->sim->devq->send_mtx); } else mtx_unlock(&xsoftc.xpt_highpower_lock); } sim = ccb_h->path->bus->sim; if (ccb_h->status & CAM_RELEASE_SIMQ) { xpt_release_simq(sim, /*run_queue*/FALSE); ccb_h->status &= ~CAM_RELEASE_SIMQ; } if ((ccb_h->flags & CAM_DEV_QFRZDIS) && (ccb_h->status & CAM_DEV_QFRZN)) { xpt_release_devq(ccb_h->path, /*count*/1, /*run_queue*/TRUE); ccb_h->status &= ~CAM_DEV_QFRZN; } devq = sim->devq; if ((ccb_h->func_code & XPT_FC_USER_CCB) == 0) { struct cam_ed *dev = ccb_h->path->device; mtx_lock(&devq->send_mtx); devq->send_active--; devq->send_openings++; cam_ccbq_ccb_done(&dev->ccbq, (union ccb *)ccb_h); if (((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0 && (dev->ccbq.dev_active == 0))) { dev->flags &= ~CAM_DEV_REL_ON_QUEUE_EMPTY; xpt_release_devq_device(dev, /*count*/1, /*run_queue*/FALSE); } if (((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0 && (ccb_h->status&CAM_STATUS_MASK) != CAM_REQUEUE_REQ)) { dev->flags &= ~CAM_DEV_REL_ON_COMPLETE; xpt_release_devq_device(dev, /*count*/1, /*run_queue*/FALSE); } if (!device_is_queued(dev)) (void)xpt_schedule_devq(devq, dev); xpt_run_devq(devq); mtx_unlock(&devq->send_mtx); if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0) { mtx = xpt_path_mtx(ccb_h->path); mtx_lock(mtx); if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 && (--dev->tag_delay_count == 0)) xpt_start_tags(ccb_h->path); } } if ((ccb_h->flags & CAM_UNLOCKED) == 0) { if (mtx == NULL) { mtx = xpt_path_mtx(ccb_h->path); mtx_lock(mtx); } } else { if (mtx != NULL) { mtx_unlock(mtx); mtx = NULL; } } /* Call the peripheral driver's callback */ ccb_h->pinfo.index = CAM_UNQUEUED_INDEX; (*ccb_h->cbfcnp)(ccb_h->path->periph, (union ccb *)ccb_h); if (mtx != NULL) mtx_unlock(mtx); } void xpt_done_td(void *arg) { struct cam_doneq *queue = arg; struct ccb_hdr *ccb_h; STAILQ_HEAD(, ccb_hdr) doneq; STAILQ_INIT(&doneq); mtx_lock(&queue->cam_doneq_mtx); while (1) { while (STAILQ_EMPTY(&queue->cam_doneq)) { queue->cam_doneq_sleep = 1; msleep(&queue->cam_doneq, &queue->cam_doneq_mtx, PRIBIO, "-", 0); queue->cam_doneq_sleep = 0; } STAILQ_CONCAT(&doneq, &queue->cam_doneq); mtx_unlock(&queue->cam_doneq_mtx); THREAD_NO_SLEEPING(); while ((ccb_h = STAILQ_FIRST(&doneq)) != NULL) { STAILQ_REMOVE_HEAD(&doneq, sim_links.stqe); xpt_done_process(ccb_h); } THREAD_SLEEPING_OK(); mtx_lock(&queue->cam_doneq_mtx); } } static void camisr_runqueue(void) { struct ccb_hdr *ccb_h; struct cam_doneq *queue; int i; /* Process global queues. */ for (i = 0; i < cam_num_doneqs; i++) { queue = &cam_doneqs[i]; mtx_lock(&queue->cam_doneq_mtx); while ((ccb_h = STAILQ_FIRST(&queue->cam_doneq)) != NULL) { STAILQ_REMOVE_HEAD(&queue->cam_doneq, sim_links.stqe); mtx_unlock(&queue->cam_doneq_mtx); xpt_done_process(ccb_h); mtx_lock(&queue->cam_doneq_mtx); } mtx_unlock(&queue->cam_doneq_mtx); } } Index: projects/clang350-import/sys/kern/kern_mutex.c =================================================================== --- projects/clang350-import/sys/kern/kern_mutex.c (revision 275386) +++ projects/clang350-import/sys/kern/kern_mutex.c (revision 275387) @@ -1,1019 +1,1019 @@ /*- * Copyright (c) 1998 Berkeley Software Design, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Berkeley Software Design Inc's name may not be used to endorse or * promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from BSDI $Id: mutex_witness.c,v 1.1.2.20 2000/04/27 03:10:27 cp Exp $ * and BSDI $Id: synch_machdep.c,v 2.3.2.39 2000/04/27 03:10:25 cp Exp $ */ /* * Machine independent bits of mutex implementation. */ #include __FBSDID("$FreeBSD$"); #include "opt_adaptive_mutexes.h" #include "opt_ddb.h" #include "opt_hwpmc_hooks.h" #include "opt_sched.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(SMP) && !defined(NO_ADAPTIVE_MUTEXES) #define ADAPTIVE_MUTEXES #endif #ifdef HWPMC_HOOKS #include PMC_SOFT_DEFINE( , , lock, failed); #endif /* * Return the mutex address when the lock cookie address is provided. * This functionality assumes that struct mtx* have a member named mtx_lock. */ #define mtxlock2mtx(c) (__containerof(c, struct mtx, mtx_lock)) /* * Internal utility macros. */ #define mtx_unowned(m) ((m)->mtx_lock == MTX_UNOWNED) #define mtx_destroyed(m) ((m)->mtx_lock == MTX_DESTROYED) #define mtx_owner(m) ((struct thread *)((m)->mtx_lock & ~MTX_FLAGMASK)) static void assert_mtx(const struct lock_object *lock, int what); #ifdef DDB static void db_show_mtx(const struct lock_object *lock); #endif static void lock_mtx(struct lock_object *lock, uintptr_t how); static void lock_spin(struct lock_object *lock, uintptr_t how); #ifdef KDTRACE_HOOKS static int owner_mtx(const struct lock_object *lock, struct thread **owner); #endif static uintptr_t unlock_mtx(struct lock_object *lock); static uintptr_t unlock_spin(struct lock_object *lock); /* * Lock classes for sleep and spin mutexes. */ struct lock_class lock_class_mtx_sleep = { .lc_name = "sleep mutex", .lc_flags = LC_SLEEPLOCK | LC_RECURSABLE, .lc_assert = assert_mtx, #ifdef DDB .lc_ddb_show = db_show_mtx, #endif .lc_lock = lock_mtx, .lc_unlock = unlock_mtx, #ifdef KDTRACE_HOOKS .lc_owner = owner_mtx, #endif }; struct lock_class lock_class_mtx_spin = { .lc_name = "spin mutex", .lc_flags = LC_SPINLOCK | LC_RECURSABLE, .lc_assert = assert_mtx, #ifdef DDB .lc_ddb_show = db_show_mtx, #endif .lc_lock = lock_spin, .lc_unlock = unlock_spin, #ifdef KDTRACE_HOOKS .lc_owner = owner_mtx, #endif }; /* * System-wide mutexes */ struct mtx blocked_lock; struct mtx Giant; void assert_mtx(const struct lock_object *lock, int what) { mtx_assert((const struct mtx *)lock, what); } void lock_mtx(struct lock_object *lock, uintptr_t how) { mtx_lock((struct mtx *)lock); } void lock_spin(struct lock_object *lock, uintptr_t how) { panic("spin locks can only use msleep_spin"); } uintptr_t unlock_mtx(struct lock_object *lock) { struct mtx *m; m = (struct mtx *)lock; mtx_assert(m, MA_OWNED | MA_NOTRECURSED); mtx_unlock(m); return (0); } uintptr_t unlock_spin(struct lock_object *lock) { panic("spin locks can only use msleep_spin"); } #ifdef KDTRACE_HOOKS int owner_mtx(const struct lock_object *lock, struct thread **owner) { const struct mtx *m = (const struct mtx *)lock; *owner = mtx_owner(m); return (mtx_unowned(m) == 0); } #endif /* * Function versions of the inlined __mtx_* macros. These are used by * modules and can also be called from assembly language if needed. */ void __mtx_lock_flags(volatile uintptr_t *c, int opts, const char *file, int line) { struct mtx *m; if (SCHEDULER_STOPPED()) return; m = mtxlock2mtx(c); KASSERT(kdb_active != 0 || !TD_IS_IDLETHREAD(curthread), ("mtx_lock() by idle thread %p on sleep mutex %s @ %s:%d", curthread, m->lock_object.lo_name, file, line)); KASSERT(m->mtx_lock != MTX_DESTROYED, ("mtx_lock() of destroyed mutex @ %s:%d", file, line)); KASSERT(LOCK_CLASS(&m->lock_object) == &lock_class_mtx_sleep, ("mtx_lock() of spin mutex %s @ %s:%d", m->lock_object.lo_name, file, line)); WITNESS_CHECKORDER(&m->lock_object, (opts & ~MTX_RECURSE) | LOP_NEWORDER | LOP_EXCLUSIVE, file, line, NULL); __mtx_lock(m, curthread, opts, file, line); LOCK_LOG_LOCK("LOCK", &m->lock_object, opts, m->mtx_recurse, file, line); WITNESS_LOCK(&m->lock_object, (opts & ~MTX_RECURSE) | LOP_EXCLUSIVE, file, line); curthread->td_locks++; } void __mtx_unlock_flags(volatile uintptr_t *c, int opts, const char *file, int line) { struct mtx *m; if (SCHEDULER_STOPPED()) return; m = mtxlock2mtx(c); KASSERT(m->mtx_lock != MTX_DESTROYED, ("mtx_unlock() of destroyed mutex @ %s:%d", file, line)); KASSERT(LOCK_CLASS(&m->lock_object) == &lock_class_mtx_sleep, ("mtx_unlock() of spin mutex %s @ %s:%d", m->lock_object.lo_name, file, line)); WITNESS_UNLOCK(&m->lock_object, opts | LOP_EXCLUSIVE, file, line); LOCK_LOG_LOCK("UNLOCK", &m->lock_object, opts, m->mtx_recurse, file, line); mtx_assert(m, MA_OWNED); __mtx_unlock(m, curthread, opts, file, line); curthread->td_locks--; } void __mtx_lock_spin_flags(volatile uintptr_t *c, int opts, const char *file, int line) { struct mtx *m; if (SCHEDULER_STOPPED()) return; m = mtxlock2mtx(c); KASSERT(m->mtx_lock != MTX_DESTROYED, ("mtx_lock_spin() of destroyed mutex @ %s:%d", file, line)); KASSERT(LOCK_CLASS(&m->lock_object) == &lock_class_mtx_spin, ("mtx_lock_spin() of sleep mutex %s @ %s:%d", m->lock_object.lo_name, file, line)); if (mtx_owned(m)) KASSERT((m->lock_object.lo_flags & LO_RECURSABLE) != 0 || (opts & MTX_RECURSE) != 0, ("mtx_lock_spin: recursed on non-recursive mutex %s @ %s:%d\n", m->lock_object.lo_name, file, line)); opts &= ~MTX_RECURSE; WITNESS_CHECKORDER(&m->lock_object, opts | LOP_NEWORDER | LOP_EXCLUSIVE, file, line, NULL); __mtx_lock_spin(m, curthread, opts, file, line); LOCK_LOG_LOCK("LOCK", &m->lock_object, opts, m->mtx_recurse, file, line); WITNESS_LOCK(&m->lock_object, opts | LOP_EXCLUSIVE, file, line); } void __mtx_unlock_spin_flags(volatile uintptr_t *c, int opts, const char *file, int line) { struct mtx *m; if (SCHEDULER_STOPPED()) return; m = mtxlock2mtx(c); KASSERT(m->mtx_lock != MTX_DESTROYED, ("mtx_unlock_spin() of destroyed mutex @ %s:%d", file, line)); KASSERT(LOCK_CLASS(&m->lock_object) == &lock_class_mtx_spin, ("mtx_unlock_spin() of sleep mutex %s @ %s:%d", m->lock_object.lo_name, file, line)); WITNESS_UNLOCK(&m->lock_object, opts | LOP_EXCLUSIVE, file, line); LOCK_LOG_LOCK("UNLOCK", &m->lock_object, opts, m->mtx_recurse, file, line); mtx_assert(m, MA_OWNED); __mtx_unlock_spin(m); } /* * The important part of mtx_trylock{,_flags}() * Tries to acquire lock `m.' If this function is called on a mutex that * is already owned, it will recursively acquire the lock. */ int _mtx_trylock_flags_(volatile uintptr_t *c, int opts, const char *file, int line) { struct mtx *m; #ifdef LOCK_PROFILING uint64_t waittime = 0; int contested = 0; #endif int rval; if (SCHEDULER_STOPPED()) return (1); m = mtxlock2mtx(c); KASSERT(kdb_active != 0 || !TD_IS_IDLETHREAD(curthread), ("mtx_trylock() by idle thread %p on sleep mutex %s @ %s:%d", curthread, m->lock_object.lo_name, file, line)); KASSERT(m->mtx_lock != MTX_DESTROYED, ("mtx_trylock() of destroyed mutex @ %s:%d", file, line)); KASSERT(LOCK_CLASS(&m->lock_object) == &lock_class_mtx_sleep, ("mtx_trylock() of spin mutex %s @ %s:%d", m->lock_object.lo_name, file, line)); if (mtx_owned(m) && ((m->lock_object.lo_flags & LO_RECURSABLE) != 0 || (opts & MTX_RECURSE) != 0)) { m->mtx_recurse++; atomic_set_ptr(&m->mtx_lock, MTX_RECURSED); rval = 1; } else rval = _mtx_obtain_lock(m, (uintptr_t)curthread); opts &= ~MTX_RECURSE; LOCK_LOG_TRY("LOCK", &m->lock_object, opts, rval, file, line); if (rval) { WITNESS_LOCK(&m->lock_object, opts | LOP_EXCLUSIVE | LOP_TRYLOCK, file, line); curthread->td_locks++; if (m->mtx_recurse == 0) LOCKSTAT_PROFILE_OBTAIN_LOCK_SUCCESS(LS_MTX_LOCK_ACQUIRE, m, contested, waittime, file, line); } return (rval); } /* * __mtx_lock_sleep: the tougher part of acquiring an MTX_DEF lock. * * We call this if the lock is either contested (i.e. we need to go to * sleep waiting for it), or if we need to recurse on it. */ void __mtx_lock_sleep(volatile uintptr_t *c, uintptr_t tid, int opts, const char *file, int line) { struct mtx *m; struct turnstile *ts; uintptr_t v; #ifdef ADAPTIVE_MUTEXES volatile struct thread *owner; #endif #ifdef KTR int cont_logged = 0; #endif #ifdef LOCK_PROFILING int contested = 0; uint64_t waittime = 0; #endif #ifdef KDTRACE_HOOKS uint64_t spin_cnt = 0; uint64_t sleep_cnt = 0; int64_t sleep_time = 0; #endif if (SCHEDULER_STOPPED()) return; m = mtxlock2mtx(c); if (mtx_owned(m)) { KASSERT((m->lock_object.lo_flags & LO_RECURSABLE) != 0 || (opts & MTX_RECURSE) != 0, ("_mtx_lock_sleep: recursed on non-recursive mutex %s @ %s:%d\n", m->lock_object.lo_name, file, line)); opts &= ~MTX_RECURSE; m->mtx_recurse++; atomic_set_ptr(&m->mtx_lock, MTX_RECURSED); if (LOCK_LOG_TEST(&m->lock_object, opts)) CTR1(KTR_LOCK, "_mtx_lock_sleep: %p recursing", m); return; } opts &= ~MTX_RECURSE; #ifdef HWPMC_HOOKS PMC_SOFT_CALL( , , lock, failed); #endif lock_profile_obtain_lock_failed(&m->lock_object, &contested, &waittime); if (LOCK_LOG_TEST(&m->lock_object, opts)) CTR4(KTR_LOCK, "_mtx_lock_sleep: %s contested (lock=%p) at %s:%d", m->lock_object.lo_name, (void *)m->mtx_lock, file, line); while (!_mtx_obtain_lock(m, tid)) { #ifdef KDTRACE_HOOKS spin_cnt++; #endif #ifdef ADAPTIVE_MUTEXES /* * If the owner is running on another CPU, spin until the * owner stops running or the state of the lock changes. */ v = m->mtx_lock; if (v != MTX_UNOWNED) { owner = (struct thread *)(v & ~MTX_FLAGMASK); if (TD_IS_RUNNING(owner)) { if (LOCK_LOG_TEST(&m->lock_object, 0)) CTR3(KTR_LOCK, "%s: spinning on %p held by %p", __func__, m, owner); KTR_STATE1(KTR_SCHED, "thread", sched_tdname((struct thread *)tid), "spinning", "lockname:\"%s\"", m->lock_object.lo_name); while (mtx_owner(m) == owner && TD_IS_RUNNING(owner)) { cpu_spinwait(); #ifdef KDTRACE_HOOKS spin_cnt++; #endif } KTR_STATE0(KTR_SCHED, "thread", sched_tdname((struct thread *)tid), "running"); continue; } } #endif ts = turnstile_trywait(&m->lock_object); v = m->mtx_lock; /* * Check if the lock has been released while spinning for * the turnstile chain lock. */ if (v == MTX_UNOWNED) { turnstile_cancel(ts); continue; } #ifdef ADAPTIVE_MUTEXES /* * The current lock owner might have started executing * on another CPU (or the lock could have changed * owners) while we were waiting on the turnstile * chain lock. If so, drop the turnstile lock and try * again. */ owner = (struct thread *)(v & ~MTX_FLAGMASK); if (TD_IS_RUNNING(owner)) { turnstile_cancel(ts); continue; } #endif /* * If the mutex isn't already contested and a failure occurs * setting the contested bit, the mutex was either released * or the state of the MTX_RECURSED bit changed. */ if ((v & MTX_CONTESTED) == 0 && !atomic_cmpset_ptr(&m->mtx_lock, v, v | MTX_CONTESTED)) { turnstile_cancel(ts); continue; } /* * We definitely must sleep for this lock. */ mtx_assert(m, MA_NOTOWNED); #ifdef KTR if (!cont_logged) { CTR6(KTR_CONTENTION, "contention: %p at %s:%d wants %s, taken by %s:%d", (void *)tid, file, line, m->lock_object.lo_name, WITNESS_FILE(&m->lock_object), WITNESS_LINE(&m->lock_object)); cont_logged = 1; } #endif /* * Block on the turnstile. */ #ifdef KDTRACE_HOOKS sleep_time -= lockstat_nsecs(); #endif turnstile_wait(ts, mtx_owner(m), TS_EXCLUSIVE_QUEUE); #ifdef KDTRACE_HOOKS sleep_time += lockstat_nsecs(); sleep_cnt++; #endif } #ifdef KTR if (cont_logged) { CTR4(KTR_CONTENTION, "contention end: %s acquired by %p at %s:%d", m->lock_object.lo_name, (void *)tid, file, line); } #endif LOCKSTAT_PROFILE_OBTAIN_LOCK_SUCCESS(LS_MTX_LOCK_ACQUIRE, m, contested, waittime, file, line); #ifdef KDTRACE_HOOKS if (sleep_time) LOCKSTAT_RECORD1(LS_MTX_LOCK_BLOCK, m, sleep_time); /* * Only record the loops spinning and not sleeping. */ if (spin_cnt > sleep_cnt) LOCKSTAT_RECORD1(LS_MTX_LOCK_SPIN, m, (spin_cnt - sleep_cnt)); #endif } static void _mtx_lock_spin_failed(struct mtx *m) { struct thread *td; td = mtx_owner(m); /* If the mutex is unlocked, try again. */ if (td == NULL) return; printf( "spin lock %p (%s) held by %p (tid %d) too long\n", m, m->lock_object.lo_name, td, td->td_tid); #ifdef WITNESS witness_display_spinlock(&m->lock_object, td, printf); #endif panic("spin lock held too long"); } #ifdef SMP /* * _mtx_lock_spin_cookie: the tougher part of acquiring an MTX_SPIN lock. * * This is only called if we need to actually spin for the lock. Recursion * is handled inline. */ void _mtx_lock_spin_cookie(volatile uintptr_t *c, uintptr_t tid, int opts, const char *file, int line) { struct mtx *m; int i = 0; #ifdef LOCK_PROFILING int contested = 0; uint64_t waittime = 0; #endif if (SCHEDULER_STOPPED()) return; m = mtxlock2mtx(c); if (LOCK_LOG_TEST(&m->lock_object, opts)) CTR1(KTR_LOCK, "_mtx_lock_spin: %p spinning", m); KTR_STATE1(KTR_SCHED, "thread", sched_tdname((struct thread *)tid), "spinning", "lockname:\"%s\"", m->lock_object.lo_name); #ifdef HWPMC_HOOKS PMC_SOFT_CALL( , , lock, failed); #endif lock_profile_obtain_lock_failed(&m->lock_object, &contested, &waittime); while (!_mtx_obtain_lock(m, tid)) { /* Give interrupts a chance while we spin. */ spinlock_exit(); while (m->mtx_lock != MTX_UNOWNED) { if (i++ < 10000000) { cpu_spinwait(); continue; } if (i < 60000000 || kdb_active || panicstr != NULL) DELAY(1); else _mtx_lock_spin_failed(m); cpu_spinwait(); } spinlock_enter(); } if (LOCK_LOG_TEST(&m->lock_object, opts)) CTR1(KTR_LOCK, "_mtx_lock_spin: %p spin done", m); KTR_STATE0(KTR_SCHED, "thread", sched_tdname((struct thread *)tid), "running"); LOCKSTAT_PROFILE_OBTAIN_LOCK_SUCCESS(LS_MTX_SPIN_LOCK_ACQUIRE, m, contested, waittime, (file), (line)); LOCKSTAT_RECORD1(LS_MTX_SPIN_LOCK_SPIN, m, i); } #endif /* SMP */ void thread_lock_flags_(struct thread *td, int opts, const char *file, int line) { struct mtx *m; uintptr_t tid; int i; #ifdef LOCK_PROFILING int contested = 0; uint64_t waittime = 0; #endif #ifdef KDTRACE_HOOKS uint64_t spin_cnt = 0; #endif i = 0; tid = (uintptr_t)curthread; if (SCHEDULER_STOPPED()) return; for (;;) { retry: spinlock_enter(); m = td->td_lock; KASSERT(m->mtx_lock != MTX_DESTROYED, ("thread_lock() of destroyed mutex @ %s:%d", file, line)); KASSERT(LOCK_CLASS(&m->lock_object) == &lock_class_mtx_spin, ("thread_lock() of sleep mutex %s @ %s:%d", m->lock_object.lo_name, file, line)); if (mtx_owned(m)) KASSERT((m->lock_object.lo_flags & LO_RECURSABLE) != 0, ("thread_lock: recursed on non-recursive mutex %s @ %s:%d\n", m->lock_object.lo_name, file, line)); WITNESS_CHECKORDER(&m->lock_object, opts | LOP_NEWORDER | LOP_EXCLUSIVE, file, line, NULL); while (!_mtx_obtain_lock(m, tid)) { #ifdef KDTRACE_HOOKS spin_cnt++; #endif if (m->mtx_lock == tid) { m->mtx_recurse++; break; } #ifdef HWPMC_HOOKS PMC_SOFT_CALL( , , lock, failed); #endif lock_profile_obtain_lock_failed(&m->lock_object, &contested, &waittime); /* Give interrupts a chance while we spin. */ spinlock_exit(); while (m->mtx_lock != MTX_UNOWNED) { if (i++ < 10000000) cpu_spinwait(); else if (i < 60000000 || kdb_active || panicstr != NULL) DELAY(1); else _mtx_lock_spin_failed(m); cpu_spinwait(); if (m != td->td_lock) goto retry; } spinlock_enter(); } if (m == td->td_lock) break; __mtx_unlock_spin(m); /* does spinlock_exit() */ #ifdef KDTRACE_HOOKS spin_cnt++; #endif } if (m->mtx_recurse == 0) LOCKSTAT_PROFILE_OBTAIN_LOCK_SUCCESS(LS_MTX_SPIN_LOCK_ACQUIRE, m, contested, waittime, (file), (line)); LOCK_LOG_LOCK("LOCK", &m->lock_object, opts, m->mtx_recurse, file, line); WITNESS_LOCK(&m->lock_object, opts | LOP_EXCLUSIVE, file, line); LOCKSTAT_RECORD1(LS_THREAD_LOCK_SPIN, m, spin_cnt); } struct mtx * thread_lock_block(struct thread *td) { struct mtx *lock; THREAD_LOCK_ASSERT(td, MA_OWNED); lock = td->td_lock; td->td_lock = &blocked_lock; mtx_unlock_spin(lock); return (lock); } void thread_lock_unblock(struct thread *td, struct mtx *new) { mtx_assert(new, MA_OWNED); MPASS(td->td_lock == &blocked_lock); atomic_store_rel_ptr((volatile void *)&td->td_lock, (uintptr_t)new); } void thread_lock_set(struct thread *td, struct mtx *new) { struct mtx *lock; mtx_assert(new, MA_OWNED); THREAD_LOCK_ASSERT(td, MA_OWNED); lock = td->td_lock; td->td_lock = new; mtx_unlock_spin(lock); } /* * __mtx_unlock_sleep: the tougher part of releasing an MTX_DEF lock. * * We are only called here if the lock is recursed or contested (i.e. we * need to wake up a blocked thread). */ void __mtx_unlock_sleep(volatile uintptr_t *c, int opts, const char *file, int line) { struct mtx *m; struct turnstile *ts; if (SCHEDULER_STOPPED()) return; m = mtxlock2mtx(c); if (mtx_recursed(m)) { if (--(m->mtx_recurse) == 0) atomic_clear_ptr(&m->mtx_lock, MTX_RECURSED); if (LOCK_LOG_TEST(&m->lock_object, opts)) CTR1(KTR_LOCK, "_mtx_unlock_sleep: %p unrecurse", m); return; } /* * We have to lock the chain before the turnstile so this turnstile * can be removed from the hash list if it is empty. */ turnstile_chain_lock(&m->lock_object); ts = turnstile_lookup(&m->lock_object); if (LOCK_LOG_TEST(&m->lock_object, opts)) CTR1(KTR_LOCK, "_mtx_unlock_sleep: %p contested", m); MPASS(ts != NULL); turnstile_broadcast(ts, TS_EXCLUSIVE_QUEUE); _mtx_release_lock_quick(m); /* * This turnstile is now no longer associated with the mutex. We can * unlock the chain lock so a new turnstile may take it's place. */ turnstile_unpend(ts, TS_EXCLUSIVE_LOCK); turnstile_chain_unlock(&m->lock_object); } /* * All the unlocking of MTX_SPIN locks is done inline. * See the __mtx_unlock_spin() macro for the details. */ /* * The backing function for the INVARIANTS-enabled mtx_assert() */ #ifdef INVARIANT_SUPPORT void __mtx_assert(const volatile uintptr_t *c, int what, const char *file, int line) { const struct mtx *m; if (panicstr != NULL || dumping) return; m = mtxlock2mtx(c); switch (what) { case MA_OWNED: case MA_OWNED | MA_RECURSED: case MA_OWNED | MA_NOTRECURSED: if (!mtx_owned(m)) panic("mutex %s not owned at %s:%d", m->lock_object.lo_name, file, line); if (mtx_recursed(m)) { if ((what & MA_NOTRECURSED) != 0) panic("mutex %s recursed at %s:%d", m->lock_object.lo_name, file, line); } else if ((what & MA_RECURSED) != 0) { panic("mutex %s unrecursed at %s:%d", m->lock_object.lo_name, file, line); } break; case MA_NOTOWNED: if (mtx_owned(m)) panic("mutex %s owned at %s:%d", m->lock_object.lo_name, file, line); break; default: panic("unknown mtx_assert at %s:%d", file, line); } } #endif /* * The MUTEX_DEBUG-enabled mtx_validate() * * Most of these checks have been moved off into the LO_INITIALIZED flag * maintained by the witness code. */ #ifdef MUTEX_DEBUG void mtx_validate(struct mtx *); void mtx_validate(struct mtx *m) { /* * XXX: When kernacc() does not require Giant we can reenable this check */ #ifdef notyet /* * Can't call kernacc() from early init386(), especially when * initializing Giant mutex, because some stuff in kernacc() * requires Giant itself. */ if (!cold) if (!kernacc((caddr_t)m, sizeof(m), VM_PROT_READ | VM_PROT_WRITE)) panic("Can't read and write to mutex %p", m); #endif } #endif /* * General init routine used by the MTX_SYSINIT() macro. */ void mtx_sysinit(void *arg) { struct mtx_args *margs = arg; mtx_init((struct mtx *)margs->ma_mtx, margs->ma_desc, NULL, margs->ma_opts); } /* * Mutex initialization routine; initialize lock `m' of type contained in * `opts' with options contained in `opts' and name `name.' The optional * lock type `type' is used as a general lock category name for use with * witness. */ void _mtx_init(volatile uintptr_t *c, const char *name, const char *type, int opts) { struct mtx *m; struct lock_class *class; int flags; m = mtxlock2mtx(c); MPASS((opts & ~(MTX_SPIN | MTX_QUIET | MTX_RECURSE | MTX_NOWITNESS | MTX_DUPOK | MTX_NOPROFILE)) == 0); ASSERT_ATOMIC_LOAD_PTR(m->mtx_lock, ("%s: mtx_lock not aligned for %s: %p", __func__, name, &m->mtx_lock)); #ifdef MUTEX_DEBUG /* Diagnostic and error correction */ mtx_validate(m); #endif /* Determine lock class and lock flags. */ if (opts & MTX_SPIN) class = &lock_class_mtx_spin; else class = &lock_class_mtx_sleep; flags = 0; if (opts & MTX_QUIET) flags |= LO_QUIET; if (opts & MTX_RECURSE) flags |= LO_RECURSABLE; if ((opts & MTX_NOWITNESS) == 0) flags |= LO_WITNESS; if (opts & MTX_DUPOK) flags |= LO_DUPOK; if (opts & MTX_NOPROFILE) flags |= LO_NOPROFILE; /* Initialize mutex. */ lock_init(&m->lock_object, class, name, type, flags); m->mtx_lock = MTX_UNOWNED; m->mtx_recurse = 0; } /* * Remove lock `m' from all_mtx queue. We don't allow MTX_QUIET to be * passed in as a flag here because if the corresponding mtx_init() was * called with MTX_QUIET set, then it will already be set in the mutex's * flags. */ void _mtx_destroy(volatile uintptr_t *c) { struct mtx *m; m = mtxlock2mtx(c); if (!mtx_owned(m)) MPASS(mtx_unowned(m)); else { MPASS((m->mtx_lock & (MTX_RECURSED|MTX_CONTESTED)) == 0); /* Perform the non-mtx related part of mtx_unlock_spin(). */ if (LOCK_CLASS(&m->lock_object) == &lock_class_mtx_spin) spinlock_exit(); else curthread->td_locks--; lock_profile_release_lock(&m->lock_object); /* Tell witness this isn't locked to make it happy. */ WITNESS_UNLOCK(&m->lock_object, LOP_EXCLUSIVE, __FILE__, __LINE__); } m->mtx_lock = MTX_DESTROYED; lock_destroy(&m->lock_object); } /* * Intialize the mutex code and system mutexes. This is called from the MD * startup code prior to mi_startup(). The per-CPU data space needs to be * setup before this is called. */ void mutex_init(void) { /* Setup turnstiles so that sleep mutexes work. */ init_turnstiles(); /* * Initialize mutexes. */ mtx_init(&Giant, "Giant", NULL, MTX_DEF | MTX_RECURSE); mtx_init(&blocked_lock, "blocked lock", NULL, MTX_SPIN); blocked_lock.mtx_lock = 0xdeadc0de; /* Always blocked. */ mtx_init(&proc0.p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK); - mtx_init(&proc0.p_slock, "process slock", NULL, MTX_SPIN | MTX_RECURSE); + mtx_init(&proc0.p_slock, "process slock", NULL, MTX_SPIN); mtx_init(&proc0.p_statmtx, "pstatl", NULL, MTX_SPIN); mtx_init(&proc0.p_itimmtx, "pitiml", NULL, MTX_SPIN); mtx_init(&proc0.p_profmtx, "pprofl", NULL, MTX_SPIN); mtx_init(&devmtx, "cdev", NULL, MTX_DEF); mtx_lock(&Giant); } #ifdef DDB void db_show_mtx(const struct lock_object *lock) { struct thread *td; const struct mtx *m; m = (const struct mtx *)lock; db_printf(" flags: {"); if (LOCK_CLASS(lock) == &lock_class_mtx_spin) db_printf("SPIN"); else db_printf("DEF"); if (m->lock_object.lo_flags & LO_RECURSABLE) db_printf(", RECURSE"); if (m->lock_object.lo_flags & LO_DUPOK) db_printf(", DUPOK"); db_printf("}\n"); db_printf(" state: {"); if (mtx_unowned(m)) db_printf("UNOWNED"); else if (mtx_destroyed(m)) db_printf("DESTROYED"); else { db_printf("OWNED"); if (m->mtx_lock & MTX_CONTESTED) db_printf(", CONTESTED"); if (m->mtx_lock & MTX_RECURSED) db_printf(", RECURSED"); } db_printf("}\n"); if (!mtx_unowned(m) && !mtx_destroyed(m)) { td = mtx_owner(m); db_printf(" owner: %p (tid %d, pid %d, \"%s\")\n", td, td->td_tid, td->td_proc->p_pid, td->td_name); if (mtx_recursed(m)) db_printf(" recursed: %d\n", m->mtx_recurse); } } #endif Index: projects/clang350-import/sys/kern/kern_proc.c =================================================================== --- projects/clang350-import/sys/kern/kern_proc.c (revision 275386) +++ projects/clang350-import/sys/kern/kern_proc.c (revision 275387) @@ -1,2898 +1,2898 @@ /*- * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)kern_proc.c 8.7 (Berkeley) 2/14/95 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_ddb.h" #include "opt_ktrace.h" #include "opt_kstack_pages.h" #include "opt_stack.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif #include #include #include #include #include #include #include #include #ifdef COMPAT_FREEBSD32 #include #include #endif SDT_PROVIDER_DEFINE(proc); SDT_PROBE_DEFINE4(proc, kernel, ctor, entry, "struct proc *", "int", "void *", "int"); SDT_PROBE_DEFINE4(proc, kernel, ctor, return, "struct proc *", "int", "void *", "int"); SDT_PROBE_DEFINE4(proc, kernel, dtor, entry, "struct proc *", "int", "void *", "struct thread *"); SDT_PROBE_DEFINE3(proc, kernel, dtor, return, "struct proc *", "int", "void *"); SDT_PROBE_DEFINE3(proc, kernel, init, entry, "struct proc *", "int", "int"); SDT_PROBE_DEFINE3(proc, kernel, init, return, "struct proc *", "int", "int"); MALLOC_DEFINE(M_PGRP, "pgrp", "process group header"); MALLOC_DEFINE(M_SESSION, "session", "session header"); static MALLOC_DEFINE(M_PROC, "proc", "Proc structures"); MALLOC_DEFINE(M_SUBPROC, "subproc", "Proc sub-structures"); static void doenterpgrp(struct proc *, struct pgrp *); static void orphanpg(struct pgrp *pg); static void fill_kinfo_aggregate(struct proc *p, struct kinfo_proc *kp); static void fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp); static void fill_kinfo_thread(struct thread *td, struct kinfo_proc *kp, int preferthread); static void pgadjustjobc(struct pgrp *pgrp, int entering); static void pgdelete(struct pgrp *); static int proc_ctor(void *mem, int size, void *arg, int flags); static void proc_dtor(void *mem, int size, void *arg); static int proc_init(void *mem, int size, int flags); static void proc_fini(void *mem, int size); static void pargs_free(struct pargs *pa); static struct proc *zpfind_locked(pid_t pid); /* * Other process lists */ struct pidhashhead *pidhashtbl; u_long pidhash; struct pgrphashhead *pgrphashtbl; u_long pgrphash; struct proclist allproc; struct proclist zombproc; struct sx allproc_lock; struct sx proctree_lock; struct mtx ppeers_lock; uma_zone_t proc_zone; int kstack_pages = KSTACK_PAGES; SYSCTL_INT(_kern, OID_AUTO, kstack_pages, CTLFLAG_RD, &kstack_pages, 0, "Kernel stack size in pages"); static int vmmap_skip_res_cnt = 0; SYSCTL_INT(_kern, OID_AUTO, proc_vmmap_skip_resident_count, CTLFLAG_RW, &vmmap_skip_res_cnt, 0, "Skip calculation of the pages resident count in kern.proc.vmmap"); CTASSERT(sizeof(struct kinfo_proc) == KINFO_PROC_SIZE); #ifdef COMPAT_FREEBSD32 CTASSERT(sizeof(struct kinfo_proc32) == KINFO_PROC32_SIZE); #endif /* * Initialize global process hashing structures. */ void procinit() { sx_init(&allproc_lock, "allproc"); sx_init(&proctree_lock, "proctree"); mtx_init(&ppeers_lock, "p_peers", NULL, MTX_DEF); LIST_INIT(&allproc); LIST_INIT(&zombproc); pidhashtbl = hashinit(maxproc / 4, M_PROC, &pidhash); pgrphashtbl = hashinit(maxproc / 4, M_PROC, &pgrphash); proc_zone = uma_zcreate("PROC", sched_sizeof_proc(), proc_ctor, proc_dtor, proc_init, proc_fini, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); uihashinit(); } /* * Prepare a proc for use. */ static int proc_ctor(void *mem, int size, void *arg, int flags) { struct proc *p; p = (struct proc *)mem; SDT_PROBE(proc, kernel, ctor , entry, p, size, arg, flags, 0); EVENTHANDLER_INVOKE(process_ctor, p); SDT_PROBE(proc, kernel, ctor , return, p, size, arg, flags, 0); return (0); } /* * Reclaim a proc after use. */ static void proc_dtor(void *mem, int size, void *arg) { struct proc *p; struct thread *td; /* INVARIANTS checks go here */ p = (struct proc *)mem; td = FIRST_THREAD_IN_PROC(p); SDT_PROBE(proc, kernel, dtor, entry, p, size, arg, td, 0); if (td != NULL) { #ifdef INVARIANTS KASSERT((p->p_numthreads == 1), ("bad number of threads in exiting process")); KASSERT(STAILQ_EMPTY(&p->p_ktr), ("proc_dtor: non-empty p_ktr")); #endif /* Free all OSD associated to this thread. */ osd_thread_exit(td); } EVENTHANDLER_INVOKE(process_dtor, p); if (p->p_ksi != NULL) KASSERT(! KSI_ONQ(p->p_ksi), ("SIGCHLD queue")); SDT_PROBE(proc, kernel, dtor, return, p, size, arg, 0, 0); } /* * Initialize type-stable parts of a proc (when newly created). */ static int proc_init(void *mem, int size, int flags) { struct proc *p; p = (struct proc *)mem; SDT_PROBE(proc, kernel, init, entry, p, size, flags, 0, 0); p->p_sched = (struct p_sched *)&p[1]; bzero(&p->p_mtx, sizeof(struct mtx)); mtx_init(&p->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK); - mtx_init(&p->p_slock, "process slock", NULL, MTX_SPIN | MTX_RECURSE); + mtx_init(&p->p_slock, "process slock", NULL, MTX_SPIN); mtx_init(&p->p_statmtx, "pstatl", NULL, MTX_SPIN); mtx_init(&p->p_itimmtx, "pitiml", NULL, MTX_SPIN); mtx_init(&p->p_profmtx, "pprofl", NULL, MTX_SPIN); cv_init(&p->p_pwait, "ppwait"); cv_init(&p->p_dbgwait, "dbgwait"); TAILQ_INIT(&p->p_threads); /* all threads in proc */ EVENTHANDLER_INVOKE(process_init, p); p->p_stats = pstats_alloc(); SDT_PROBE(proc, kernel, init, return, p, size, flags, 0, 0); return (0); } /* * UMA should ensure that this function is never called. * Freeing a proc structure would violate type stability. */ static void proc_fini(void *mem, int size) { #ifdef notnow struct proc *p; p = (struct proc *)mem; EVENTHANDLER_INVOKE(process_fini, p); pstats_free(p->p_stats); thread_free(FIRST_THREAD_IN_PROC(p)); mtx_destroy(&p->p_mtx); if (p->p_ksi != NULL) ksiginfo_free(p->p_ksi); #else panic("proc reclaimed"); #endif } /* * Is p an inferior of the current process? */ int inferior(struct proc *p) { sx_assert(&proctree_lock, SX_LOCKED); PROC_LOCK_ASSERT(p, MA_OWNED); for (; p != curproc; p = proc_realparent(p)) { if (p->p_pid == 0) return (0); } return (1); } struct proc * pfind_locked(pid_t pid) { struct proc *p; sx_assert(&allproc_lock, SX_LOCKED); LIST_FOREACH(p, PIDHASH(pid), p_hash) { if (p->p_pid == pid) { PROC_LOCK(p); if (p->p_state == PRS_NEW) { PROC_UNLOCK(p); p = NULL; } break; } } return (p); } /* * Locate a process by number; return only "live" processes -- i.e., neither * zombies nor newly born but incompletely initialized processes. By not * returning processes in the PRS_NEW state, we allow callers to avoid * testing for that condition to avoid dereferencing p_ucred, et al. */ struct proc * pfind(pid_t pid) { struct proc *p; sx_slock(&allproc_lock); p = pfind_locked(pid); sx_sunlock(&allproc_lock); return (p); } static struct proc * pfind_tid_locked(pid_t tid) { struct proc *p; struct thread *td; sx_assert(&allproc_lock, SX_LOCKED); FOREACH_PROC_IN_SYSTEM(p) { PROC_LOCK(p); if (p->p_state == PRS_NEW) { PROC_UNLOCK(p); continue; } FOREACH_THREAD_IN_PROC(p, td) { if (td->td_tid == tid) goto found; } PROC_UNLOCK(p); } found: return (p); } /* * Locate a process group by number. * The caller must hold proctree_lock. */ struct pgrp * pgfind(pgid) register pid_t pgid; { register struct pgrp *pgrp; sx_assert(&proctree_lock, SX_LOCKED); LIST_FOREACH(pgrp, PGRPHASH(pgid), pg_hash) { if (pgrp->pg_id == pgid) { PGRP_LOCK(pgrp); return (pgrp); } } return (NULL); } /* * Locate process and do additional manipulations, depending on flags. */ int pget(pid_t pid, int flags, struct proc **pp) { struct proc *p; int error; sx_slock(&allproc_lock); if (pid <= PID_MAX) { p = pfind_locked(pid); if (p == NULL && (flags & PGET_NOTWEXIT) == 0) p = zpfind_locked(pid); } else if ((flags & PGET_NOTID) == 0) { p = pfind_tid_locked(pid); } else { p = NULL; } sx_sunlock(&allproc_lock); if (p == NULL) return (ESRCH); if ((flags & PGET_CANSEE) != 0) { error = p_cansee(curthread, p); if (error != 0) goto errout; } if ((flags & PGET_CANDEBUG) != 0) { error = p_candebug(curthread, p); if (error != 0) goto errout; } if ((flags & PGET_ISCURRENT) != 0 && curproc != p) { error = EPERM; goto errout; } if ((flags & PGET_NOTWEXIT) != 0 && (p->p_flag & P_WEXIT) != 0) { error = ESRCH; goto errout; } if ((flags & PGET_NOTINEXEC) != 0 && (p->p_flag & P_INEXEC) != 0) { /* * XXXRW: Not clear ESRCH is the right error during proc * execve(). */ error = ESRCH; goto errout; } if ((flags & PGET_HOLD) != 0) { _PHOLD(p); PROC_UNLOCK(p); } *pp = p; return (0); errout: PROC_UNLOCK(p); return (error); } /* * Create a new process group. * pgid must be equal to the pid of p. * Begin a new session if required. */ int enterpgrp(p, pgid, pgrp, sess) register struct proc *p; pid_t pgid; struct pgrp *pgrp; struct session *sess; { sx_assert(&proctree_lock, SX_XLOCKED); KASSERT(pgrp != NULL, ("enterpgrp: pgrp == NULL")); KASSERT(p->p_pid == pgid, ("enterpgrp: new pgrp and pid != pgid")); KASSERT(pgfind(pgid) == NULL, ("enterpgrp: pgrp with pgid exists")); KASSERT(!SESS_LEADER(p), ("enterpgrp: session leader attempted setpgrp")); mtx_init(&pgrp->pg_mtx, "process group", NULL, MTX_DEF | MTX_DUPOK); if (sess != NULL) { /* * new session */ mtx_init(&sess->s_mtx, "session", NULL, MTX_DEF); PROC_LOCK(p); p->p_flag &= ~P_CONTROLT; PROC_UNLOCK(p); PGRP_LOCK(pgrp); sess->s_leader = p; sess->s_sid = p->p_pid; refcount_init(&sess->s_count, 1); sess->s_ttyvp = NULL; sess->s_ttydp = NULL; sess->s_ttyp = NULL; bcopy(p->p_session->s_login, sess->s_login, sizeof(sess->s_login)); pgrp->pg_session = sess; KASSERT(p == curproc, ("enterpgrp: mksession and p != curproc")); } else { pgrp->pg_session = p->p_session; sess_hold(pgrp->pg_session); PGRP_LOCK(pgrp); } pgrp->pg_id = pgid; LIST_INIT(&pgrp->pg_members); /* * As we have an exclusive lock of proctree_lock, * this should not deadlock. */ LIST_INSERT_HEAD(PGRPHASH(pgid), pgrp, pg_hash); pgrp->pg_jobc = 0; SLIST_INIT(&pgrp->pg_sigiolst); PGRP_UNLOCK(pgrp); doenterpgrp(p, pgrp); return (0); } /* * Move p to an existing process group */ int enterthispgrp(p, pgrp) register struct proc *p; struct pgrp *pgrp; { sx_assert(&proctree_lock, SX_XLOCKED); PROC_LOCK_ASSERT(p, MA_NOTOWNED); PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); PGRP_LOCK_ASSERT(p->p_pgrp, MA_NOTOWNED); SESS_LOCK_ASSERT(p->p_session, MA_NOTOWNED); KASSERT(pgrp->pg_session == p->p_session, ("%s: pgrp's session %p, p->p_session %p.\n", __func__, pgrp->pg_session, p->p_session)); KASSERT(pgrp != p->p_pgrp, ("%s: p belongs to pgrp.", __func__)); doenterpgrp(p, pgrp); return (0); } /* * Move p to a process group */ static void doenterpgrp(p, pgrp) struct proc *p; struct pgrp *pgrp; { struct pgrp *savepgrp; sx_assert(&proctree_lock, SX_XLOCKED); PROC_LOCK_ASSERT(p, MA_NOTOWNED); PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); PGRP_LOCK_ASSERT(p->p_pgrp, MA_NOTOWNED); SESS_LOCK_ASSERT(p->p_session, MA_NOTOWNED); savepgrp = p->p_pgrp; /* * Adjust eligibility of affected pgrps to participate in job control. * Increment eligibility counts before decrementing, otherwise we * could reach 0 spuriously during the first call. */ fixjobc(p, pgrp, 1); fixjobc(p, p->p_pgrp, 0); PGRP_LOCK(pgrp); PGRP_LOCK(savepgrp); PROC_LOCK(p); LIST_REMOVE(p, p_pglist); p->p_pgrp = pgrp; PROC_UNLOCK(p); LIST_INSERT_HEAD(&pgrp->pg_members, p, p_pglist); PGRP_UNLOCK(savepgrp); PGRP_UNLOCK(pgrp); if (LIST_EMPTY(&savepgrp->pg_members)) pgdelete(savepgrp); } /* * remove process from process group */ int leavepgrp(p) register struct proc *p; { struct pgrp *savepgrp; sx_assert(&proctree_lock, SX_XLOCKED); savepgrp = p->p_pgrp; PGRP_LOCK(savepgrp); PROC_LOCK(p); LIST_REMOVE(p, p_pglist); p->p_pgrp = NULL; PROC_UNLOCK(p); PGRP_UNLOCK(savepgrp); if (LIST_EMPTY(&savepgrp->pg_members)) pgdelete(savepgrp); return (0); } /* * delete a process group */ static void pgdelete(pgrp) register struct pgrp *pgrp; { struct session *savesess; struct tty *tp; sx_assert(&proctree_lock, SX_XLOCKED); PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); SESS_LOCK_ASSERT(pgrp->pg_session, MA_NOTOWNED); /* * Reset any sigio structures pointing to us as a result of * F_SETOWN with our pgid. */ funsetownlst(&pgrp->pg_sigiolst); PGRP_LOCK(pgrp); tp = pgrp->pg_session->s_ttyp; LIST_REMOVE(pgrp, pg_hash); savesess = pgrp->pg_session; PGRP_UNLOCK(pgrp); /* Remove the reference to the pgrp before deallocating it. */ if (tp != NULL) { tty_lock(tp); tty_rel_pgrp(tp, pgrp); } mtx_destroy(&pgrp->pg_mtx); free(pgrp, M_PGRP); sess_release(savesess); } static void pgadjustjobc(pgrp, entering) struct pgrp *pgrp; int entering; { PGRP_LOCK(pgrp); if (entering) pgrp->pg_jobc++; else { --pgrp->pg_jobc; if (pgrp->pg_jobc == 0) orphanpg(pgrp); } PGRP_UNLOCK(pgrp); } /* * Adjust pgrp jobc counters when specified process changes process group. * We count the number of processes in each process group that "qualify" * the group for terminal job control (those with a parent in a different * process group of the same session). If that count reaches zero, the * process group becomes orphaned. Check both the specified process' * process group and that of its children. * entering == 0 => p is leaving specified group. * entering == 1 => p is entering specified group. */ void fixjobc(p, pgrp, entering) register struct proc *p; register struct pgrp *pgrp; int entering; { register struct pgrp *hispgrp; register struct session *mysession; sx_assert(&proctree_lock, SX_LOCKED); PROC_LOCK_ASSERT(p, MA_NOTOWNED); PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); SESS_LOCK_ASSERT(pgrp->pg_session, MA_NOTOWNED); /* * Check p's parent to see whether p qualifies its own process * group; if so, adjust count for p's process group. */ mysession = pgrp->pg_session; if ((hispgrp = p->p_pptr->p_pgrp) != pgrp && hispgrp->pg_session == mysession) pgadjustjobc(pgrp, entering); /* * Check this process' children to see whether they qualify * their process groups; if so, adjust counts for children's * process groups. */ LIST_FOREACH(p, &p->p_children, p_sibling) { hispgrp = p->p_pgrp; if (hispgrp == pgrp || hispgrp->pg_session != mysession) continue; PROC_LOCK(p); if (p->p_state == PRS_ZOMBIE) { PROC_UNLOCK(p); continue; } PROC_UNLOCK(p); pgadjustjobc(hispgrp, entering); } } /* * A process group has become orphaned; * if there are any stopped processes in the group, * hang-up all process in that group. */ static void orphanpg(pg) struct pgrp *pg; { register struct proc *p; PGRP_LOCK_ASSERT(pg, MA_OWNED); LIST_FOREACH(p, &pg->pg_members, p_pglist) { PROC_LOCK(p); if (P_SHOULDSTOP(p)) { PROC_UNLOCK(p); LIST_FOREACH(p, &pg->pg_members, p_pglist) { PROC_LOCK(p); kern_psignal(p, SIGHUP); kern_psignal(p, SIGCONT); PROC_UNLOCK(p); } return; } PROC_UNLOCK(p); } } void sess_hold(struct session *s) { refcount_acquire(&s->s_count); } void sess_release(struct session *s) { if (refcount_release(&s->s_count)) { if (s->s_ttyp != NULL) { tty_lock(s->s_ttyp); tty_rel_sess(s->s_ttyp, s); } mtx_destroy(&s->s_mtx); free(s, M_SESSION); } } #ifdef DDB DB_SHOW_COMMAND(pgrpdump, pgrpdump) { register struct pgrp *pgrp; register struct proc *p; register int i; for (i = 0; i <= pgrphash; i++) { if (!LIST_EMPTY(&pgrphashtbl[i])) { printf("\tindx %d\n", i); LIST_FOREACH(pgrp, &pgrphashtbl[i], pg_hash) { printf( "\tpgrp %p, pgid %ld, sess %p, sesscnt %d, mem %p\n", (void *)pgrp, (long)pgrp->pg_id, (void *)pgrp->pg_session, pgrp->pg_session->s_count, (void *)LIST_FIRST(&pgrp->pg_members)); LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { printf("\t\tpid %ld addr %p pgrp %p\n", (long)p->p_pid, (void *)p, (void *)p->p_pgrp); } } } } } #endif /* DDB */ /* * Calculate the kinfo_proc members which contain process-wide * informations. * Must be called with the target process locked. */ static void fill_kinfo_aggregate(struct proc *p, struct kinfo_proc *kp) { struct thread *td; PROC_LOCK_ASSERT(p, MA_OWNED); kp->ki_estcpu = 0; kp->ki_pctcpu = 0; FOREACH_THREAD_IN_PROC(p, td) { thread_lock(td); kp->ki_pctcpu += sched_pctcpu(td); kp->ki_estcpu += td->td_estcpu; thread_unlock(td); } } /* * Clear kinfo_proc and fill in any information that is common * to all threads in the process. * Must be called with the target process locked. */ static void fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp) { struct thread *td0; struct tty *tp; struct session *sp; struct ucred *cred; struct sigacts *ps; /* For proc_realparent. */ sx_assert(&proctree_lock, SX_LOCKED); PROC_LOCK_ASSERT(p, MA_OWNED); bzero(kp, sizeof(*kp)); kp->ki_structsize = sizeof(*kp); kp->ki_paddr = p; kp->ki_addr =/* p->p_addr; */0; /* XXX */ kp->ki_args = p->p_args; kp->ki_textvp = p->p_textvp; #ifdef KTRACE kp->ki_tracep = p->p_tracevp; kp->ki_traceflag = p->p_traceflag; #endif kp->ki_fd = p->p_fd; kp->ki_vmspace = p->p_vmspace; kp->ki_flag = p->p_flag; kp->ki_flag2 = p->p_flag2; cred = p->p_ucred; if (cred) { kp->ki_uid = cred->cr_uid; kp->ki_ruid = cred->cr_ruid; kp->ki_svuid = cred->cr_svuid; kp->ki_cr_flags = 0; if (cred->cr_flags & CRED_FLAG_CAPMODE) kp->ki_cr_flags |= KI_CRF_CAPABILITY_MODE; /* XXX bde doesn't like KI_NGROUPS */ if (cred->cr_ngroups > KI_NGROUPS) { kp->ki_ngroups = KI_NGROUPS; kp->ki_cr_flags |= KI_CRF_GRP_OVERFLOW; } else kp->ki_ngroups = cred->cr_ngroups; bcopy(cred->cr_groups, kp->ki_groups, kp->ki_ngroups * sizeof(gid_t)); kp->ki_rgid = cred->cr_rgid; kp->ki_svgid = cred->cr_svgid; /* If jailed(cred), emulate the old P_JAILED flag. */ if (jailed(cred)) { kp->ki_flag |= P_JAILED; /* If inside the jail, use 0 as a jail ID. */ if (cred->cr_prison != curthread->td_ucred->cr_prison) kp->ki_jid = cred->cr_prison->pr_id; } strlcpy(kp->ki_loginclass, cred->cr_loginclass->lc_name, sizeof(kp->ki_loginclass)); } ps = p->p_sigacts; if (ps) { mtx_lock(&ps->ps_mtx); kp->ki_sigignore = ps->ps_sigignore; kp->ki_sigcatch = ps->ps_sigcatch; mtx_unlock(&ps->ps_mtx); } if (p->p_state != PRS_NEW && p->p_state != PRS_ZOMBIE && p->p_vmspace != NULL) { struct vmspace *vm = p->p_vmspace; kp->ki_size = vm->vm_map.size; kp->ki_rssize = vmspace_resident_count(vm); /*XXX*/ FOREACH_THREAD_IN_PROC(p, td0) { if (!TD_IS_SWAPPED(td0)) kp->ki_rssize += td0->td_kstack_pages; } kp->ki_swrss = vm->vm_swrss; kp->ki_tsize = vm->vm_tsize; kp->ki_dsize = vm->vm_dsize; kp->ki_ssize = vm->vm_ssize; } else if (p->p_state == PRS_ZOMBIE) kp->ki_stat = SZOMB; if (kp->ki_flag & P_INMEM) kp->ki_sflag = PS_INMEM; else kp->ki_sflag = 0; /* Calculate legacy swtime as seconds since 'swtick'. */ kp->ki_swtime = (ticks - p->p_swtick) / hz; kp->ki_pid = p->p_pid; kp->ki_nice = p->p_nice; kp->ki_fibnum = p->p_fibnum; kp->ki_start = p->p_stats->p_start; timevaladd(&kp->ki_start, &boottime); PROC_STATLOCK(p); rufetch(p, &kp->ki_rusage); kp->ki_runtime = cputick2usec(p->p_rux.rux_runtime); calcru(p, &kp->ki_rusage.ru_utime, &kp->ki_rusage.ru_stime); PROC_STATUNLOCK(p); calccru(p, &kp->ki_childutime, &kp->ki_childstime); /* Some callers want child times in a single value. */ kp->ki_childtime = kp->ki_childstime; timevaladd(&kp->ki_childtime, &kp->ki_childutime); FOREACH_THREAD_IN_PROC(p, td0) kp->ki_cow += td0->td_cow; tp = NULL; if (p->p_pgrp) { kp->ki_pgid = p->p_pgrp->pg_id; kp->ki_jobc = p->p_pgrp->pg_jobc; sp = p->p_pgrp->pg_session; if (sp != NULL) { kp->ki_sid = sp->s_sid; SESS_LOCK(sp); strlcpy(kp->ki_login, sp->s_login, sizeof(kp->ki_login)); if (sp->s_ttyvp) kp->ki_kiflag |= KI_CTTY; if (SESS_LEADER(p)) kp->ki_kiflag |= KI_SLEADER; /* XXX proctree_lock */ tp = sp->s_ttyp; SESS_UNLOCK(sp); } } if ((p->p_flag & P_CONTROLT) && tp != NULL) { kp->ki_tdev = tty_udev(tp); kp->ki_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; if (tp->t_session) kp->ki_tsid = tp->t_session->s_sid; } else kp->ki_tdev = NODEV; if (p->p_comm[0] != '\0') strlcpy(kp->ki_comm, p->p_comm, sizeof(kp->ki_comm)); if (p->p_sysent && p->p_sysent->sv_name != NULL && p->p_sysent->sv_name[0] != '\0') strlcpy(kp->ki_emul, p->p_sysent->sv_name, sizeof(kp->ki_emul)); kp->ki_siglist = p->p_siglist; kp->ki_xstat = p->p_xstat; kp->ki_acflag = p->p_acflag; kp->ki_lock = p->p_lock; if (p->p_pptr) { kp->ki_ppid = proc_realparent(p)->p_pid; if (p->p_flag & P_TRACED) kp->ki_tracer = p->p_pptr->p_pid; } } /* * Fill in information that is thread specific. Must be called with * target process locked. If 'preferthread' is set, overwrite certain * process-related fields that are maintained for both threads and * processes. */ static void fill_kinfo_thread(struct thread *td, struct kinfo_proc *kp, int preferthread) { struct proc *p; p = td->td_proc; kp->ki_tdaddr = td; PROC_LOCK_ASSERT(p, MA_OWNED); if (preferthread) PROC_STATLOCK(p); thread_lock(td); if (td->td_wmesg != NULL) strlcpy(kp->ki_wmesg, td->td_wmesg, sizeof(kp->ki_wmesg)); else bzero(kp->ki_wmesg, sizeof(kp->ki_wmesg)); strlcpy(kp->ki_tdname, td->td_name, sizeof(kp->ki_tdname)); if (TD_ON_LOCK(td)) { kp->ki_kiflag |= KI_LOCKBLOCK; strlcpy(kp->ki_lockname, td->td_lockname, sizeof(kp->ki_lockname)); } else { kp->ki_kiflag &= ~KI_LOCKBLOCK; bzero(kp->ki_lockname, sizeof(kp->ki_lockname)); } if (p->p_state == PRS_NORMAL) { /* approximate. */ if (TD_ON_RUNQ(td) || TD_CAN_RUN(td) || TD_IS_RUNNING(td)) { kp->ki_stat = SRUN; } else if (P_SHOULDSTOP(p)) { kp->ki_stat = SSTOP; } else if (TD_IS_SLEEPING(td)) { kp->ki_stat = SSLEEP; } else if (TD_ON_LOCK(td)) { kp->ki_stat = SLOCK; } else { kp->ki_stat = SWAIT; } } else if (p->p_state == PRS_ZOMBIE) { kp->ki_stat = SZOMB; } else { kp->ki_stat = SIDL; } /* Things in the thread */ kp->ki_wchan = td->td_wchan; kp->ki_pri.pri_level = td->td_priority; kp->ki_pri.pri_native = td->td_base_pri; /* * Note: legacy fields; clamp at the old NOCPU value and/or * the maximum u_char CPU value. */ if (td->td_lastcpu == NOCPU) kp->ki_lastcpu_old = NOCPU_OLD; else if (td->td_lastcpu > MAXCPU_OLD) kp->ki_lastcpu_old = MAXCPU_OLD; else kp->ki_lastcpu_old = td->td_lastcpu; if (td->td_oncpu == NOCPU) kp->ki_oncpu_old = NOCPU_OLD; else if (td->td_oncpu > MAXCPU_OLD) kp->ki_oncpu_old = MAXCPU_OLD; else kp->ki_oncpu_old = td->td_oncpu; kp->ki_lastcpu = td->td_lastcpu; kp->ki_oncpu = td->td_oncpu; kp->ki_tdflags = td->td_flags; kp->ki_tid = td->td_tid; kp->ki_numthreads = p->p_numthreads; kp->ki_pcb = td->td_pcb; kp->ki_kstack = (void *)td->td_kstack; kp->ki_slptime = (ticks - td->td_slptick) / hz; kp->ki_pri.pri_class = td->td_pri_class; kp->ki_pri.pri_user = td->td_user_pri; if (preferthread) { rufetchtd(td, &kp->ki_rusage); kp->ki_runtime = cputick2usec(td->td_rux.rux_runtime); kp->ki_pctcpu = sched_pctcpu(td); kp->ki_estcpu = td->td_estcpu; kp->ki_cow = td->td_cow; } /* We can't get this anymore but ps etc never used it anyway. */ kp->ki_rqindex = 0; if (preferthread) kp->ki_siglist = td->td_siglist; kp->ki_sigmask = td->td_sigmask; thread_unlock(td); if (preferthread) PROC_STATUNLOCK(p); } /* * Fill in a kinfo_proc structure for the specified process. * Must be called with the target process locked. */ void fill_kinfo_proc(struct proc *p, struct kinfo_proc *kp) { MPASS(FIRST_THREAD_IN_PROC(p) != NULL); fill_kinfo_proc_only(p, kp); fill_kinfo_thread(FIRST_THREAD_IN_PROC(p), kp, 0); fill_kinfo_aggregate(p, kp); } struct pstats * pstats_alloc(void) { return (malloc(sizeof(struct pstats), M_SUBPROC, M_ZERO|M_WAITOK)); } /* * Copy parts of p_stats; zero the rest of p_stats (statistics). */ void pstats_fork(struct pstats *src, struct pstats *dst) { bzero(&dst->pstat_startzero, __rangeof(struct pstats, pstat_startzero, pstat_endzero)); bcopy(&src->pstat_startcopy, &dst->pstat_startcopy, __rangeof(struct pstats, pstat_startcopy, pstat_endcopy)); } void pstats_free(struct pstats *ps) { free(ps, M_SUBPROC); } static struct proc * zpfind_locked(pid_t pid) { struct proc *p; sx_assert(&allproc_lock, SX_LOCKED); LIST_FOREACH(p, &zombproc, p_list) { if (p->p_pid == pid) { PROC_LOCK(p); break; } } return (p); } /* * Locate a zombie process by number */ struct proc * zpfind(pid_t pid) { struct proc *p; sx_slock(&allproc_lock); p = zpfind_locked(pid); sx_sunlock(&allproc_lock); return (p); } #ifdef COMPAT_FREEBSD32 /* * This function is typically used to copy out the kernel address, so * it can be replaced by assignment of zero. */ static inline uint32_t ptr32_trim(void *ptr) { uintptr_t uptr; uptr = (uintptr_t)ptr; return ((uptr > UINT_MAX) ? 0 : uptr); } #define PTRTRIM_CP(src,dst,fld) \ do { (dst).fld = ptr32_trim((src).fld); } while (0) static void freebsd32_kinfo_proc_out(const struct kinfo_proc *ki, struct kinfo_proc32 *ki32) { int i; bzero(ki32, sizeof(struct kinfo_proc32)); ki32->ki_structsize = sizeof(struct kinfo_proc32); CP(*ki, *ki32, ki_layout); PTRTRIM_CP(*ki, *ki32, ki_args); PTRTRIM_CP(*ki, *ki32, ki_paddr); PTRTRIM_CP(*ki, *ki32, ki_addr); PTRTRIM_CP(*ki, *ki32, ki_tracep); PTRTRIM_CP(*ki, *ki32, ki_textvp); PTRTRIM_CP(*ki, *ki32, ki_fd); PTRTRIM_CP(*ki, *ki32, ki_vmspace); PTRTRIM_CP(*ki, *ki32, ki_wchan); CP(*ki, *ki32, ki_pid); CP(*ki, *ki32, ki_ppid); CP(*ki, *ki32, ki_pgid); CP(*ki, *ki32, ki_tpgid); CP(*ki, *ki32, ki_sid); CP(*ki, *ki32, ki_tsid); CP(*ki, *ki32, ki_jobc); CP(*ki, *ki32, ki_tdev); CP(*ki, *ki32, ki_siglist); CP(*ki, *ki32, ki_sigmask); CP(*ki, *ki32, ki_sigignore); CP(*ki, *ki32, ki_sigcatch); CP(*ki, *ki32, ki_uid); CP(*ki, *ki32, ki_ruid); CP(*ki, *ki32, ki_svuid); CP(*ki, *ki32, ki_rgid); CP(*ki, *ki32, ki_svgid); CP(*ki, *ki32, ki_ngroups); for (i = 0; i < KI_NGROUPS; i++) CP(*ki, *ki32, ki_groups[i]); CP(*ki, *ki32, ki_size); CP(*ki, *ki32, ki_rssize); CP(*ki, *ki32, ki_swrss); CP(*ki, *ki32, ki_tsize); CP(*ki, *ki32, ki_dsize); CP(*ki, *ki32, ki_ssize); CP(*ki, *ki32, ki_xstat); CP(*ki, *ki32, ki_acflag); CP(*ki, *ki32, ki_pctcpu); CP(*ki, *ki32, ki_estcpu); CP(*ki, *ki32, ki_slptime); CP(*ki, *ki32, ki_swtime); CP(*ki, *ki32, ki_cow); CP(*ki, *ki32, ki_runtime); TV_CP(*ki, *ki32, ki_start); TV_CP(*ki, *ki32, ki_childtime); CP(*ki, *ki32, ki_flag); CP(*ki, *ki32, ki_kiflag); CP(*ki, *ki32, ki_traceflag); CP(*ki, *ki32, ki_stat); CP(*ki, *ki32, ki_nice); CP(*ki, *ki32, ki_lock); CP(*ki, *ki32, ki_rqindex); CP(*ki, *ki32, ki_oncpu); CP(*ki, *ki32, ki_lastcpu); /* XXX TODO: wrap cpu value as appropriate */ CP(*ki, *ki32, ki_oncpu_old); CP(*ki, *ki32, ki_lastcpu_old); bcopy(ki->ki_tdname, ki32->ki_tdname, TDNAMLEN + 1); bcopy(ki->ki_wmesg, ki32->ki_wmesg, WMESGLEN + 1); bcopy(ki->ki_login, ki32->ki_login, LOGNAMELEN + 1); bcopy(ki->ki_lockname, ki32->ki_lockname, LOCKNAMELEN + 1); bcopy(ki->ki_comm, ki32->ki_comm, COMMLEN + 1); bcopy(ki->ki_emul, ki32->ki_emul, KI_EMULNAMELEN + 1); bcopy(ki->ki_loginclass, ki32->ki_loginclass, LOGINCLASSLEN + 1); CP(*ki, *ki32, ki_tracer); CP(*ki, *ki32, ki_flag2); CP(*ki, *ki32, ki_fibnum); CP(*ki, *ki32, ki_cr_flags); CP(*ki, *ki32, ki_jid); CP(*ki, *ki32, ki_numthreads); CP(*ki, *ki32, ki_tid); CP(*ki, *ki32, ki_pri); freebsd32_rusage_out(&ki->ki_rusage, &ki32->ki_rusage); freebsd32_rusage_out(&ki->ki_rusage_ch, &ki32->ki_rusage_ch); PTRTRIM_CP(*ki, *ki32, ki_pcb); PTRTRIM_CP(*ki, *ki32, ki_kstack); PTRTRIM_CP(*ki, *ki32, ki_udata); CP(*ki, *ki32, ki_sflag); CP(*ki, *ki32, ki_tdflags); } #endif int kern_proc_out(struct proc *p, struct sbuf *sb, int flags) { struct thread *td; struct kinfo_proc ki; #ifdef COMPAT_FREEBSD32 struct kinfo_proc32 ki32; #endif int error; PROC_LOCK_ASSERT(p, MA_OWNED); MPASS(FIRST_THREAD_IN_PROC(p) != NULL); error = 0; fill_kinfo_proc(p, &ki); if ((flags & KERN_PROC_NOTHREADS) != 0) { #ifdef COMPAT_FREEBSD32 if ((flags & KERN_PROC_MASK32) != 0) { freebsd32_kinfo_proc_out(&ki, &ki32); if (sbuf_bcat(sb, &ki32, sizeof(ki32)) != 0) error = ENOMEM; } else #endif if (sbuf_bcat(sb, &ki, sizeof(ki)) != 0) error = ENOMEM; } else { FOREACH_THREAD_IN_PROC(p, td) { fill_kinfo_thread(td, &ki, 1); #ifdef COMPAT_FREEBSD32 if ((flags & KERN_PROC_MASK32) != 0) { freebsd32_kinfo_proc_out(&ki, &ki32); if (sbuf_bcat(sb, &ki32, sizeof(ki32)) != 0) error = ENOMEM; } else #endif if (sbuf_bcat(sb, &ki, sizeof(ki)) != 0) error = ENOMEM; if (error != 0) break; } } PROC_UNLOCK(p); return (error); } static int sysctl_out_proc(struct proc *p, struct sysctl_req *req, int flags, int doingzomb) { struct sbuf sb; struct kinfo_proc ki; struct proc *np; int error, error2; pid_t pid; pid = p->p_pid; sbuf_new_for_sysctl(&sb, (char *)&ki, sizeof(ki), req); error = kern_proc_out(p, &sb, flags); error2 = sbuf_finish(&sb); sbuf_delete(&sb); if (error != 0) return (error); else if (error2 != 0) return (error2); if (doingzomb) np = zpfind(pid); else { if (pid == 0) return (0); np = pfind(pid); } if (np == NULL) return (ESRCH); if (np != p) { PROC_UNLOCK(np); return (ESRCH); } PROC_UNLOCK(np); return (0); } static int sysctl_kern_proc(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; struct proc *p; int flags, doingzomb, oid_number; int error = 0; oid_number = oidp->oid_number; if (oid_number != KERN_PROC_ALL && (oid_number & KERN_PROC_INC_THREAD) == 0) flags = KERN_PROC_NOTHREADS; else { flags = 0; oid_number &= ~KERN_PROC_INC_THREAD; } #ifdef COMPAT_FREEBSD32 if (req->flags & SCTL_MASK32) flags |= KERN_PROC_MASK32; #endif if (oid_number == KERN_PROC_PID) { if (namelen != 1) return (EINVAL); error = sysctl_wire_old_buffer(req, 0); if (error) return (error); sx_slock(&proctree_lock); error = pget((pid_t)name[0], PGET_CANSEE, &p); if (error == 0) error = sysctl_out_proc(p, req, flags, 0); sx_sunlock(&proctree_lock); return (error); } switch (oid_number) { case KERN_PROC_ALL: if (namelen != 0) return (EINVAL); break; case KERN_PROC_PROC: if (namelen != 0 && namelen != 1) return (EINVAL); break; default: if (namelen != 1) return (EINVAL); break; } if (!req->oldptr) { /* overestimate by 5 procs */ error = SYSCTL_OUT(req, 0, sizeof (struct kinfo_proc) * 5); if (error) return (error); } error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); sx_slock(&proctree_lock); sx_slock(&allproc_lock); for (doingzomb=0 ; doingzomb < 2 ; doingzomb++) { if (!doingzomb) p = LIST_FIRST(&allproc); else p = LIST_FIRST(&zombproc); for (; p != 0; p = LIST_NEXT(p, p_list)) { /* * Skip embryonic processes. */ PROC_LOCK(p); if (p->p_state == PRS_NEW) { PROC_UNLOCK(p); continue; } KASSERT(p->p_ucred != NULL, ("process credential is NULL for non-NEW proc")); /* * Show a user only appropriate processes. */ if (p_cansee(curthread, p)) { PROC_UNLOCK(p); continue; } /* * TODO - make more efficient (see notes below). * do by session. */ switch (oid_number) { case KERN_PROC_GID: if (p->p_ucred->cr_gid != (gid_t)name[0]) { PROC_UNLOCK(p); continue; } break; case KERN_PROC_PGRP: /* could do this by traversing pgrp */ if (p->p_pgrp == NULL || p->p_pgrp->pg_id != (pid_t)name[0]) { PROC_UNLOCK(p); continue; } break; case KERN_PROC_RGID: if (p->p_ucred->cr_rgid != (gid_t)name[0]) { PROC_UNLOCK(p); continue; } break; case KERN_PROC_SESSION: if (p->p_session == NULL || p->p_session->s_sid != (pid_t)name[0]) { PROC_UNLOCK(p); continue; } break; case KERN_PROC_TTY: if ((p->p_flag & P_CONTROLT) == 0 || p->p_session == NULL) { PROC_UNLOCK(p); continue; } /* XXX proctree_lock */ SESS_LOCK(p->p_session); if (p->p_session->s_ttyp == NULL || tty_udev(p->p_session->s_ttyp) != (dev_t)name[0]) { SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); continue; } SESS_UNLOCK(p->p_session); break; case KERN_PROC_UID: if (p->p_ucred->cr_uid != (uid_t)name[0]) { PROC_UNLOCK(p); continue; } break; case KERN_PROC_RUID: if (p->p_ucred->cr_ruid != (uid_t)name[0]) { PROC_UNLOCK(p); continue; } break; case KERN_PROC_PROC: break; default: break; } error = sysctl_out_proc(p, req, flags, doingzomb); if (error) { sx_sunlock(&allproc_lock); sx_sunlock(&proctree_lock); return (error); } } } sx_sunlock(&allproc_lock); sx_sunlock(&proctree_lock); return (0); } struct pargs * pargs_alloc(int len) { struct pargs *pa; pa = malloc(sizeof(struct pargs) + len, M_PARGS, M_WAITOK); refcount_init(&pa->ar_ref, 1); pa->ar_length = len; return (pa); } static void pargs_free(struct pargs *pa) { free(pa, M_PARGS); } void pargs_hold(struct pargs *pa) { if (pa == NULL) return; refcount_acquire(&pa->ar_ref); } void pargs_drop(struct pargs *pa) { if (pa == NULL) return; if (refcount_release(&pa->ar_ref)) pargs_free(pa); } static int proc_read_mem(struct thread *td, struct proc *p, vm_offset_t offset, void* buf, size_t len) { struct iovec iov; struct uio uio; iov.iov_base = (caddr_t)buf; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = offset; uio.uio_resid = (ssize_t)len; uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = UIO_READ; uio.uio_td = td; return (proc_rwmem(p, &uio)); } static int proc_read_string(struct thread *td, struct proc *p, const char *sptr, char *buf, size_t len) { size_t i; int error; error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, len); /* * Reading the chunk may validly return EFAULT if the string is shorter * than the chunk and is aligned at the end of the page, assuming the * next page is not mapped. So if EFAULT is returned do a fallback to * one byte read loop. */ if (error == EFAULT) { for (i = 0; i < len; i++, buf++, sptr++) { error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, 1); if (error != 0) return (error); if (*buf == '\0') break; } error = 0; } return (error); } #define PROC_AUXV_MAX 256 /* Safety limit on auxv size. */ enum proc_vector_type { PROC_ARG, PROC_ENV, PROC_AUX, }; #ifdef COMPAT_FREEBSD32 static int get_proc_vector32(struct thread *td, struct proc *p, char ***proc_vectorp, size_t *vsizep, enum proc_vector_type type) { struct freebsd32_ps_strings pss; Elf32_Auxinfo aux; vm_offset_t vptr, ptr; uint32_t *proc_vector32; char **proc_vector; size_t vsize, size; int i, error; error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings), &pss, sizeof(pss)); if (error != 0) return (error); switch (type) { case PROC_ARG: vptr = (vm_offset_t)PTRIN(pss.ps_argvstr); vsize = pss.ps_nargvstr; if (vsize > ARG_MAX) return (ENOEXEC); size = vsize * sizeof(int32_t); break; case PROC_ENV: vptr = (vm_offset_t)PTRIN(pss.ps_envstr); vsize = pss.ps_nenvstr; if (vsize > ARG_MAX) return (ENOEXEC); size = vsize * sizeof(int32_t); break; case PROC_AUX: vptr = (vm_offset_t)PTRIN(pss.ps_envstr) + (pss.ps_nenvstr + 1) * sizeof(int32_t); if (vptr % 4 != 0) return (ENOEXEC); for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) { error = proc_read_mem(td, p, ptr, &aux, sizeof(aux)); if (error != 0) return (error); if (aux.a_type == AT_NULL) break; ptr += sizeof(aux); } if (aux.a_type != AT_NULL) return (ENOEXEC); vsize = i + 1; size = vsize * sizeof(aux); break; default: KASSERT(0, ("Wrong proc vector type: %d", type)); return (EINVAL); } proc_vector32 = malloc(size, M_TEMP, M_WAITOK); error = proc_read_mem(td, p, vptr, proc_vector32, size); if (error != 0) goto done; if (type == PROC_AUX) { *proc_vectorp = (char **)proc_vector32; *vsizep = vsize; return (0); } proc_vector = malloc(vsize * sizeof(char *), M_TEMP, M_WAITOK); for (i = 0; i < (int)vsize; i++) proc_vector[i] = PTRIN(proc_vector32[i]); *proc_vectorp = proc_vector; *vsizep = vsize; done: free(proc_vector32, M_TEMP); return (error); } #endif static int get_proc_vector(struct thread *td, struct proc *p, char ***proc_vectorp, size_t *vsizep, enum proc_vector_type type) { struct ps_strings pss; Elf_Auxinfo aux; vm_offset_t vptr, ptr; char **proc_vector; size_t vsize, size; int error, i; #ifdef COMPAT_FREEBSD32 if (SV_PROC_FLAG(p, SV_ILP32) != 0) return (get_proc_vector32(td, p, proc_vectorp, vsizep, type)); #endif error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings), &pss, sizeof(pss)); if (error != 0) return (error); switch (type) { case PROC_ARG: vptr = (vm_offset_t)pss.ps_argvstr; vsize = pss.ps_nargvstr; if (vsize > ARG_MAX) return (ENOEXEC); size = vsize * sizeof(char *); break; case PROC_ENV: vptr = (vm_offset_t)pss.ps_envstr; vsize = pss.ps_nenvstr; if (vsize > ARG_MAX) return (ENOEXEC); size = vsize * sizeof(char *); break; case PROC_AUX: /* * The aux array is just above env array on the stack. Check * that the address is naturally aligned. */ vptr = (vm_offset_t)pss.ps_envstr + (pss.ps_nenvstr + 1) * sizeof(char *); #if __ELF_WORD_SIZE == 64 if (vptr % sizeof(uint64_t) != 0) #else if (vptr % sizeof(uint32_t) != 0) #endif return (ENOEXEC); /* * We count the array size reading the aux vectors from the * stack until AT_NULL vector is returned. So (to keep the code * simple) we read the process stack twice: the first time here * to find the size and the second time when copying the vectors * to the allocated proc_vector. */ for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) { error = proc_read_mem(td, p, ptr, &aux, sizeof(aux)); if (error != 0) return (error); if (aux.a_type == AT_NULL) break; ptr += sizeof(aux); } /* * If the PROC_AUXV_MAX entries are iterated over, and we have * not reached AT_NULL, it is most likely we are reading wrong * data: either the process doesn't have auxv array or data has * been modified. Return the error in this case. */ if (aux.a_type != AT_NULL) return (ENOEXEC); vsize = i + 1; size = vsize * sizeof(aux); break; default: KASSERT(0, ("Wrong proc vector type: %d", type)); return (EINVAL); /* In case we are built without INVARIANTS. */ } proc_vector = malloc(size, M_TEMP, M_WAITOK); if (proc_vector == NULL) return (ENOMEM); error = proc_read_mem(td, p, vptr, proc_vector, size); if (error != 0) { free(proc_vector, M_TEMP); return (error); } *proc_vectorp = proc_vector; *vsizep = vsize; return (0); } #define GET_PS_STRINGS_CHUNK_SZ 256 /* Chunk size (bytes) for ps_strings operations. */ static int get_ps_strings(struct thread *td, struct proc *p, struct sbuf *sb, enum proc_vector_type type) { size_t done, len, nchr, vsize; int error, i; char **proc_vector, *sptr; char pss_string[GET_PS_STRINGS_CHUNK_SZ]; PROC_ASSERT_HELD(p); /* * We are not going to read more than 2 * (PATH_MAX + ARG_MAX) bytes. */ nchr = 2 * (PATH_MAX + ARG_MAX); error = get_proc_vector(td, p, &proc_vector, &vsize, type); if (error != 0) return (error); for (done = 0, i = 0; i < (int)vsize && done < nchr; i++) { /* * The program may have scribbled into its argv array, e.g. to * remove some arguments. If that has happened, break out * before trying to read from NULL. */ if (proc_vector[i] == NULL) break; for (sptr = proc_vector[i]; ; sptr += GET_PS_STRINGS_CHUNK_SZ) { error = proc_read_string(td, p, sptr, pss_string, sizeof(pss_string)); if (error != 0) goto done; len = strnlen(pss_string, GET_PS_STRINGS_CHUNK_SZ); if (done + len >= nchr) len = nchr - done - 1; sbuf_bcat(sb, pss_string, len); if (len != GET_PS_STRINGS_CHUNK_SZ) break; done += GET_PS_STRINGS_CHUNK_SZ; } sbuf_bcat(sb, "", 1); done += len + 1; } done: free(proc_vector, M_TEMP); return (error); } int proc_getargv(struct thread *td, struct proc *p, struct sbuf *sb) { return (get_ps_strings(curthread, p, sb, PROC_ARG)); } int proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb) { return (get_ps_strings(curthread, p, sb, PROC_ENV)); } int proc_getauxv(struct thread *td, struct proc *p, struct sbuf *sb) { size_t vsize, size; char **auxv; int error; error = get_proc_vector(td, p, &auxv, &vsize, PROC_AUX); if (error == 0) { #ifdef COMPAT_FREEBSD32 if (SV_PROC_FLAG(p, SV_ILP32) != 0) size = vsize * sizeof(Elf32_Auxinfo); else #endif size = vsize * sizeof(Elf_Auxinfo); if (sbuf_bcat(sb, auxv, size) != 0) error = ENOMEM; free(auxv, M_TEMP); } return (error); } /* * This sysctl allows a process to retrieve the argument list or process * title for another process without groping around in the address space * of the other process. It also allow a process to set its own "process * title to a string of its own choice. */ static int sysctl_kern_proc_args(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; struct pargs *newpa, *pa; struct proc *p; struct sbuf sb; int flags, error = 0, error2; if (namelen != 1) return (EINVAL); flags = PGET_CANSEE; if (req->newptr != NULL) flags |= PGET_ISCURRENT; error = pget((pid_t)name[0], flags, &p); if (error) return (error); pa = p->p_args; if (pa != NULL) { pargs_hold(pa); PROC_UNLOCK(p); error = SYSCTL_OUT(req, pa->ar_args, pa->ar_length); pargs_drop(pa); } else if ((p->p_flag & (P_WEXIT | P_SYSTEM)) == 0) { _PHOLD(p); PROC_UNLOCK(p); sbuf_new_for_sysctl(&sb, NULL, GET_PS_STRINGS_CHUNK_SZ, req); error = proc_getargv(curthread, p, &sb); error2 = sbuf_finish(&sb); PRELE(p); sbuf_delete(&sb); if (error == 0 && error2 != 0) error = error2; } else { PROC_UNLOCK(p); } if (error != 0 || req->newptr == NULL) return (error); if (req->newlen + sizeof(struct pargs) > ps_arg_cache_limit) return (ENOMEM); newpa = pargs_alloc(req->newlen); error = SYSCTL_IN(req, newpa->ar_args, req->newlen); if (error != 0) { pargs_free(newpa); return (error); } PROC_LOCK(p); pa = p->p_args; p->p_args = newpa; PROC_UNLOCK(p); pargs_drop(pa); return (0); } /* * This sysctl allows a process to retrieve environment of another process. */ static int sysctl_kern_proc_env(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; struct proc *p; struct sbuf sb; int error, error2; if (namelen != 1) return (EINVAL); error = pget((pid_t)name[0], PGET_WANTREAD, &p); if (error != 0) return (error); if ((p->p_flag & P_SYSTEM) != 0) { PRELE(p); return (0); } sbuf_new_for_sysctl(&sb, NULL, GET_PS_STRINGS_CHUNK_SZ, req); error = proc_getenvv(curthread, p, &sb); error2 = sbuf_finish(&sb); PRELE(p); sbuf_delete(&sb); return (error != 0 ? error : error2); } /* * This sysctl allows a process to retrieve ELF auxiliary vector of * another process. */ static int sysctl_kern_proc_auxv(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; struct proc *p; struct sbuf sb; int error, error2; if (namelen != 1) return (EINVAL); error = pget((pid_t)name[0], PGET_WANTREAD, &p); if (error != 0) return (error); if ((p->p_flag & P_SYSTEM) != 0) { PRELE(p); return (0); } sbuf_new_for_sysctl(&sb, NULL, GET_PS_STRINGS_CHUNK_SZ, req); error = proc_getauxv(curthread, p, &sb); error2 = sbuf_finish(&sb); PRELE(p); sbuf_delete(&sb); return (error != 0 ? error : error2); } /* * This sysctl allows a process to retrieve the path of the executable for * itself or another process. */ static int sysctl_kern_proc_pathname(SYSCTL_HANDLER_ARGS) { pid_t *pidp = (pid_t *)arg1; unsigned int arglen = arg2; struct proc *p; struct vnode *vp; char *retbuf, *freebuf; int error; if (arglen != 1) return (EINVAL); if (*pidp == -1) { /* -1 means this process */ p = req->td->td_proc; } else { error = pget(*pidp, PGET_CANSEE, &p); if (error != 0) return (error); } vp = p->p_textvp; if (vp == NULL) { if (*pidp != -1) PROC_UNLOCK(p); return (0); } vref(vp); if (*pidp != -1) PROC_UNLOCK(p); error = vn_fullpath(req->td, vp, &retbuf, &freebuf); vrele(vp); if (error) return (error); error = SYSCTL_OUT(req, retbuf, strlen(retbuf) + 1); free(freebuf, M_TEMP); return (error); } static int sysctl_kern_proc_sv_name(SYSCTL_HANDLER_ARGS) { struct proc *p; char *sv_name; int *name; int namelen; int error; namelen = arg2; if (namelen != 1) return (EINVAL); name = (int *)arg1; error = pget((pid_t)name[0], PGET_CANSEE, &p); if (error != 0) return (error); sv_name = p->p_sysent->sv_name; PROC_UNLOCK(p); return (sysctl_handle_string(oidp, sv_name, 0, req)); } #ifdef KINFO_OVMENTRY_SIZE CTASSERT(sizeof(struct kinfo_ovmentry) == KINFO_OVMENTRY_SIZE); #endif #ifdef COMPAT_FREEBSD7 static int sysctl_kern_proc_ovmmap(SYSCTL_HANDLER_ARGS) { vm_map_entry_t entry, tmp_entry; unsigned int last_timestamp; char *fullpath, *freepath; struct kinfo_ovmentry *kve; struct vattr va; struct ucred *cred; int error, *name; struct vnode *vp; struct proc *p; vm_map_t map; struct vmspace *vm; name = (int *)arg1; error = pget((pid_t)name[0], PGET_WANTREAD, &p); if (error != 0) return (error); vm = vmspace_acquire_ref(p); if (vm == NULL) { PRELE(p); return (ESRCH); } kve = malloc(sizeof(*kve), M_TEMP, M_WAITOK); map = &vm->vm_map; vm_map_lock_read(map); for (entry = map->header.next; entry != &map->header; entry = entry->next) { vm_object_t obj, tobj, lobj; vm_offset_t addr; if (entry->eflags & MAP_ENTRY_IS_SUB_MAP) continue; bzero(kve, sizeof(*kve)); kve->kve_structsize = sizeof(*kve); kve->kve_private_resident = 0; obj = entry->object.vm_object; if (obj != NULL) { VM_OBJECT_RLOCK(obj); if (obj->shadow_count == 1) kve->kve_private_resident = obj->resident_page_count; } kve->kve_resident = 0; addr = entry->start; while (addr < entry->end) { if (pmap_extract(map->pmap, addr)) kve->kve_resident++; addr += PAGE_SIZE; } for (lobj = tobj = obj; tobj; tobj = tobj->backing_object) { if (tobj != obj) VM_OBJECT_RLOCK(tobj); if (lobj != obj) VM_OBJECT_RUNLOCK(lobj); lobj = tobj; } kve->kve_start = (void*)entry->start; kve->kve_end = (void*)entry->end; kve->kve_offset = (off_t)entry->offset; if (entry->protection & VM_PROT_READ) kve->kve_protection |= KVME_PROT_READ; if (entry->protection & VM_PROT_WRITE) kve->kve_protection |= KVME_PROT_WRITE; if (entry->protection & VM_PROT_EXECUTE) kve->kve_protection |= KVME_PROT_EXEC; if (entry->eflags & MAP_ENTRY_COW) kve->kve_flags |= KVME_FLAG_COW; if (entry->eflags & MAP_ENTRY_NEEDS_COPY) kve->kve_flags |= KVME_FLAG_NEEDS_COPY; if (entry->eflags & MAP_ENTRY_NOCOREDUMP) kve->kve_flags |= KVME_FLAG_NOCOREDUMP; last_timestamp = map->timestamp; vm_map_unlock_read(map); kve->kve_fileid = 0; kve->kve_fsid = 0; freepath = NULL; fullpath = ""; if (lobj) { vp = NULL; switch (lobj->type) { case OBJT_DEFAULT: kve->kve_type = KVME_TYPE_DEFAULT; break; case OBJT_VNODE: kve->kve_type = KVME_TYPE_VNODE; vp = lobj->handle; vref(vp); break; case OBJT_SWAP: kve->kve_type = KVME_TYPE_SWAP; break; case OBJT_DEVICE: kve->kve_type = KVME_TYPE_DEVICE; break; case OBJT_PHYS: kve->kve_type = KVME_TYPE_PHYS; break; case OBJT_DEAD: kve->kve_type = KVME_TYPE_DEAD; break; case OBJT_SG: kve->kve_type = KVME_TYPE_SG; break; default: kve->kve_type = KVME_TYPE_UNKNOWN; break; } if (lobj != obj) VM_OBJECT_RUNLOCK(lobj); kve->kve_ref_count = obj->ref_count; kve->kve_shadow_count = obj->shadow_count; VM_OBJECT_RUNLOCK(obj); if (vp != NULL) { vn_fullpath(curthread, vp, &fullpath, &freepath); cred = curthread->td_ucred; vn_lock(vp, LK_SHARED | LK_RETRY); if (VOP_GETATTR(vp, &va, cred) == 0) { kve->kve_fileid = va.va_fileid; kve->kve_fsid = va.va_fsid; } vput(vp); } } else { kve->kve_type = KVME_TYPE_NONE; kve->kve_ref_count = 0; kve->kve_shadow_count = 0; } strlcpy(kve->kve_path, fullpath, sizeof(kve->kve_path)); if (freepath != NULL) free(freepath, M_TEMP); error = SYSCTL_OUT(req, kve, sizeof(*kve)); vm_map_lock_read(map); if (error) break; if (last_timestamp != map->timestamp) { vm_map_lookup_entry(map, addr - 1, &tmp_entry); entry = tmp_entry; } } vm_map_unlock_read(map); vmspace_free(vm); PRELE(p); free(kve, M_TEMP); return (error); } #endif /* COMPAT_FREEBSD7 */ #ifdef KINFO_VMENTRY_SIZE CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); #endif static void kern_proc_vmmap_resident(vm_map_t map, vm_map_entry_t entry, struct kinfo_vmentry *kve) { vm_object_t obj, tobj; vm_page_t m, m_adv; vm_offset_t addr; vm_paddr_t locked_pa; vm_pindex_t pi, pi_adv, pindex; locked_pa = 0; obj = entry->object.vm_object; addr = entry->start; m_adv = NULL; pi = OFF_TO_IDX(entry->offset); for (; addr < entry->end; addr += IDX_TO_OFF(pi_adv), pi += pi_adv) { if (m_adv != NULL) { m = m_adv; } else { pi_adv = OFF_TO_IDX(entry->end - addr); pindex = pi; for (tobj = obj;; tobj = tobj->backing_object) { m = vm_page_find_least(tobj, pindex); if (m != NULL) { if (m->pindex == pindex) break; if (pi_adv > m->pindex - pindex) { pi_adv = m->pindex - pindex; m_adv = m; } } if (tobj->backing_object == NULL) goto next; pindex += OFF_TO_IDX(tobj-> backing_object_offset); } } m_adv = NULL; if (m->psind != 0 && addr + pagesizes[1] <= entry->end && (addr & (pagesizes[1] - 1)) == 0 && (pmap_mincore(map->pmap, addr, &locked_pa) & MINCORE_SUPER) != 0) { kve->kve_flags |= KVME_FLAG_SUPER; pi_adv = OFF_TO_IDX(pagesizes[1]); } else { /* * We do not test the found page on validity. * Either the page is busy and being paged in, * or it was invalidated. The first case * should be counted as resident, the second * is not so clear; we do account both. */ pi_adv = 1; } kve->kve_resident += pi_adv; next:; } PA_UNLOCK_COND(locked_pa); } /* * Must be called with the process locked and will return unlocked. */ int kern_proc_vmmap_out(struct proc *p, struct sbuf *sb) { vm_map_entry_t entry, tmp_entry; struct vattr va; vm_map_t map; vm_object_t obj, tobj, lobj; char *fullpath, *freepath; struct kinfo_vmentry *kve; struct ucred *cred; struct vnode *vp; struct vmspace *vm; vm_offset_t addr; unsigned int last_timestamp; int error; PROC_LOCK_ASSERT(p, MA_OWNED); _PHOLD(p); PROC_UNLOCK(p); vm = vmspace_acquire_ref(p); if (vm == NULL) { PRELE(p); return (ESRCH); } kve = malloc(sizeof(*kve), M_TEMP, M_WAITOK); error = 0; map = &vm->vm_map; vm_map_lock_read(map); for (entry = map->header.next; entry != &map->header; entry = entry->next) { if (entry->eflags & MAP_ENTRY_IS_SUB_MAP) continue; addr = entry->end; bzero(kve, sizeof(*kve)); obj = entry->object.vm_object; if (obj != NULL) { for (tobj = obj; tobj != NULL; tobj = tobj->backing_object) { VM_OBJECT_RLOCK(tobj); lobj = tobj; } if (obj->backing_object == NULL) kve->kve_private_resident = obj->resident_page_count; if (!vmmap_skip_res_cnt) kern_proc_vmmap_resident(map, entry, kve); for (tobj = obj; tobj != NULL; tobj = tobj->backing_object) { if (tobj != obj && tobj != lobj) VM_OBJECT_RUNLOCK(tobj); } } else { lobj = NULL; } kve->kve_start = entry->start; kve->kve_end = entry->end; kve->kve_offset = entry->offset; if (entry->protection & VM_PROT_READ) kve->kve_protection |= KVME_PROT_READ; if (entry->protection & VM_PROT_WRITE) kve->kve_protection |= KVME_PROT_WRITE; if (entry->protection & VM_PROT_EXECUTE) kve->kve_protection |= KVME_PROT_EXEC; if (entry->eflags & MAP_ENTRY_COW) kve->kve_flags |= KVME_FLAG_COW; if (entry->eflags & MAP_ENTRY_NEEDS_COPY) kve->kve_flags |= KVME_FLAG_NEEDS_COPY; if (entry->eflags & MAP_ENTRY_NOCOREDUMP) kve->kve_flags |= KVME_FLAG_NOCOREDUMP; if (entry->eflags & MAP_ENTRY_GROWS_UP) kve->kve_flags |= KVME_FLAG_GROWS_UP; if (entry->eflags & MAP_ENTRY_GROWS_DOWN) kve->kve_flags |= KVME_FLAG_GROWS_DOWN; last_timestamp = map->timestamp; vm_map_unlock_read(map); freepath = NULL; fullpath = ""; if (lobj != NULL) { vp = NULL; switch (lobj->type) { case OBJT_DEFAULT: kve->kve_type = KVME_TYPE_DEFAULT; break; case OBJT_VNODE: kve->kve_type = KVME_TYPE_VNODE; vp = lobj->handle; vref(vp); break; case OBJT_SWAP: kve->kve_type = KVME_TYPE_SWAP; break; case OBJT_DEVICE: kve->kve_type = KVME_TYPE_DEVICE; break; case OBJT_PHYS: kve->kve_type = KVME_TYPE_PHYS; break; case OBJT_DEAD: kve->kve_type = KVME_TYPE_DEAD; break; case OBJT_SG: kve->kve_type = KVME_TYPE_SG; break; case OBJT_MGTDEVICE: kve->kve_type = KVME_TYPE_MGTDEVICE; break; default: kve->kve_type = KVME_TYPE_UNKNOWN; break; } if (lobj != obj) VM_OBJECT_RUNLOCK(lobj); kve->kve_ref_count = obj->ref_count; kve->kve_shadow_count = obj->shadow_count; VM_OBJECT_RUNLOCK(obj); if (vp != NULL) { vn_fullpath(curthread, vp, &fullpath, &freepath); kve->kve_vn_type = vntype_to_kinfo(vp->v_type); cred = curthread->td_ucred; vn_lock(vp, LK_SHARED | LK_RETRY); if (VOP_GETATTR(vp, &va, cred) == 0) { kve->kve_vn_fileid = va.va_fileid; kve->kve_vn_fsid = va.va_fsid; kve->kve_vn_mode = MAKEIMODE(va.va_type, va.va_mode); kve->kve_vn_size = va.va_size; kve->kve_vn_rdev = va.va_rdev; kve->kve_status = KF_ATTR_VALID; } vput(vp); } } else { kve->kve_type = KVME_TYPE_NONE; kve->kve_ref_count = 0; kve->kve_shadow_count = 0; } strlcpy(kve->kve_path, fullpath, sizeof(kve->kve_path)); if (freepath != NULL) free(freepath, M_TEMP); /* Pack record size down */ kve->kve_structsize = offsetof(struct kinfo_vmentry, kve_path) + strlen(kve->kve_path) + 1; kve->kve_structsize = roundup(kve->kve_structsize, sizeof(uint64_t)); if (sbuf_bcat(sb, kve, kve->kve_structsize) != 0) error = ENOMEM; vm_map_lock_read(map); if (error != 0) break; if (last_timestamp != map->timestamp) { vm_map_lookup_entry(map, addr - 1, &tmp_entry); entry = tmp_entry; } } vm_map_unlock_read(map); vmspace_free(vm); PRELE(p); free(kve, M_TEMP); return (error); } static int sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) { struct proc *p; struct sbuf sb; int error, error2, *name; name = (int *)arg1; sbuf_new_for_sysctl(&sb, NULL, sizeof(struct kinfo_vmentry), req); error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); if (error != 0) { sbuf_delete(&sb); return (error); } error = kern_proc_vmmap_out(p, &sb); error2 = sbuf_finish(&sb); sbuf_delete(&sb); return (error != 0 ? error : error2); } #if defined(STACK) || defined(DDB) static int sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS) { struct kinfo_kstack *kkstp; int error, i, *name, numthreads; lwpid_t *lwpidarray; struct thread *td; struct stack *st; struct sbuf sb; struct proc *p; name = (int *)arg1; error = pget((pid_t)name[0], PGET_NOTINEXEC | PGET_WANTREAD, &p); if (error != 0) return (error); kkstp = malloc(sizeof(*kkstp), M_TEMP, M_WAITOK); st = stack_create(); lwpidarray = NULL; numthreads = 0; PROC_LOCK(p); repeat: if (numthreads < p->p_numthreads) { if (lwpidarray != NULL) { free(lwpidarray, M_TEMP); lwpidarray = NULL; } numthreads = p->p_numthreads; PROC_UNLOCK(p); lwpidarray = malloc(sizeof(*lwpidarray) * numthreads, M_TEMP, M_WAITOK | M_ZERO); PROC_LOCK(p); goto repeat; } i = 0; /* * XXXRW: During the below loop, execve(2) and countless other sorts * of changes could have taken place. Should we check to see if the * vmspace has been replaced, or the like, in order to prevent * giving a snapshot that spans, say, execve(2), with some threads * before and some after? Among other things, the credentials could * have changed, in which case the right to extract debug info might * no longer be assured. */ FOREACH_THREAD_IN_PROC(p, td) { KASSERT(i < numthreads, ("sysctl_kern_proc_kstack: numthreads")); lwpidarray[i] = td->td_tid; i++; } numthreads = i; for (i = 0; i < numthreads; i++) { td = thread_find(p, lwpidarray[i]); if (td == NULL) { continue; } bzero(kkstp, sizeof(*kkstp)); (void)sbuf_new(&sb, kkstp->kkst_trace, sizeof(kkstp->kkst_trace), SBUF_FIXEDLEN); thread_lock(td); kkstp->kkst_tid = td->td_tid; if (TD_IS_SWAPPED(td)) kkstp->kkst_state = KKST_STATE_SWAPPED; else if (TD_IS_RUNNING(td)) kkstp->kkst_state = KKST_STATE_RUNNING; else { kkstp->kkst_state = KKST_STATE_STACKOK; stack_save_td(st, td); } thread_unlock(td); PROC_UNLOCK(p); stack_sbuf_print(&sb, st); sbuf_finish(&sb); sbuf_delete(&sb); error = SYSCTL_OUT(req, kkstp, sizeof(*kkstp)); PROC_LOCK(p); if (error) break; } _PRELE(p); PROC_UNLOCK(p); if (lwpidarray != NULL) free(lwpidarray, M_TEMP); stack_destroy(st); free(kkstp, M_TEMP); return (error); } #endif /* * This sysctl allows a process to retrieve the full list of groups from * itself or another process. */ static int sysctl_kern_proc_groups(SYSCTL_HANDLER_ARGS) { pid_t *pidp = (pid_t *)arg1; unsigned int arglen = arg2; struct proc *p; struct ucred *cred; int error; if (arglen != 1) return (EINVAL); if (*pidp == -1) { /* -1 means this process */ p = req->td->td_proc; PROC_LOCK(p); } else { error = pget(*pidp, PGET_CANSEE, &p); if (error != 0) return (error); } cred = crhold(p->p_ucred); PROC_UNLOCK(p); error = SYSCTL_OUT(req, cred->cr_groups, cred->cr_ngroups * sizeof(gid_t)); crfree(cred); return (error); } /* * This sysctl allows a process to retrieve or/and set the resource limit for * another process. */ static int sysctl_kern_proc_rlimit(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; struct rlimit rlim; struct proc *p; u_int which; int flags, error; if (namelen != 2) return (EINVAL); which = (u_int)name[1]; if (which >= RLIM_NLIMITS) return (EINVAL); if (req->newptr != NULL && req->newlen != sizeof(rlim)) return (EINVAL); flags = PGET_HOLD | PGET_NOTWEXIT; if (req->newptr != NULL) flags |= PGET_CANDEBUG; else flags |= PGET_CANSEE; error = pget((pid_t)name[0], flags, &p); if (error != 0) return (error); /* * Retrieve limit. */ if (req->oldptr != NULL) { PROC_LOCK(p); lim_rlimit(p, which, &rlim); PROC_UNLOCK(p); } error = SYSCTL_OUT(req, &rlim, sizeof(rlim)); if (error != 0) goto errout; /* * Set limit. */ if (req->newptr != NULL) { error = SYSCTL_IN(req, &rlim, sizeof(rlim)); if (error == 0) error = kern_proc_setrlimit(curthread, p, which, &rlim); } errout: PRELE(p); return (error); } /* * This sysctl allows a process to retrieve ps_strings structure location of * another process. */ static int sysctl_kern_proc_ps_strings(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; struct proc *p; vm_offset_t ps_strings; int error; #ifdef COMPAT_FREEBSD32 uint32_t ps_strings32; #endif if (namelen != 1) return (EINVAL); error = pget((pid_t)name[0], PGET_CANDEBUG, &p); if (error != 0) return (error); #ifdef COMPAT_FREEBSD32 if ((req->flags & SCTL_MASK32) != 0) { /* * We return 0 if the 32 bit emulation request is for a 64 bit * process. */ ps_strings32 = SV_PROC_FLAG(p, SV_ILP32) != 0 ? PTROUT(p->p_sysent->sv_psstrings) : 0; PROC_UNLOCK(p); error = SYSCTL_OUT(req, &ps_strings32, sizeof(ps_strings32)); return (error); } #endif ps_strings = p->p_sysent->sv_psstrings; PROC_UNLOCK(p); error = SYSCTL_OUT(req, &ps_strings, sizeof(ps_strings)); return (error); } /* * This sysctl allows a process to retrieve umask of another process. */ static int sysctl_kern_proc_umask(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; struct proc *p; int error; u_short fd_cmask; if (namelen != 1) return (EINVAL); error = pget((pid_t)name[0], PGET_WANTREAD, &p); if (error != 0) return (error); FILEDESC_SLOCK(p->p_fd); fd_cmask = p->p_fd->fd_cmask; FILEDESC_SUNLOCK(p->p_fd); PRELE(p); error = SYSCTL_OUT(req, &fd_cmask, sizeof(fd_cmask)); return (error); } /* * This sysctl allows a process to set and retrieve binary osreldate of * another process. */ static int sysctl_kern_proc_osrel(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; struct proc *p; int flags, error, osrel; if (namelen != 1) return (EINVAL); if (req->newptr != NULL && req->newlen != sizeof(osrel)) return (EINVAL); flags = PGET_HOLD | PGET_NOTWEXIT; if (req->newptr != NULL) flags |= PGET_CANDEBUG; else flags |= PGET_CANSEE; error = pget((pid_t)name[0], flags, &p); if (error != 0) return (error); error = SYSCTL_OUT(req, &p->p_osrel, sizeof(p->p_osrel)); if (error != 0) goto errout; if (req->newptr != NULL) { error = SYSCTL_IN(req, &osrel, sizeof(osrel)); if (error != 0) goto errout; if (osrel < 0) { error = EINVAL; goto errout; } p->p_osrel = osrel; } errout: PRELE(p); return (error); } static int sysctl_kern_proc_sigtramp(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; struct proc *p; struct kinfo_sigtramp kst; const struct sysentvec *sv; int error; #ifdef COMPAT_FREEBSD32 struct kinfo_sigtramp32 kst32; #endif if (namelen != 1) return (EINVAL); error = pget((pid_t)name[0], PGET_CANDEBUG, &p); if (error != 0) return (error); sv = p->p_sysent; #ifdef COMPAT_FREEBSD32 if ((req->flags & SCTL_MASK32) != 0) { bzero(&kst32, sizeof(kst32)); if (SV_PROC_FLAG(p, SV_ILP32)) { if (sv->sv_sigcode_base != 0) { kst32.ksigtramp_start = sv->sv_sigcode_base; kst32.ksigtramp_end = sv->sv_sigcode_base + *sv->sv_szsigcode; } else { kst32.ksigtramp_start = sv->sv_psstrings - *sv->sv_szsigcode; kst32.ksigtramp_end = sv->sv_psstrings; } } PROC_UNLOCK(p); error = SYSCTL_OUT(req, &kst32, sizeof(kst32)); return (error); } #endif bzero(&kst, sizeof(kst)); if (sv->sv_sigcode_base != 0) { kst.ksigtramp_start = (char *)sv->sv_sigcode_base; kst.ksigtramp_end = (char *)sv->sv_sigcode_base + *sv->sv_szsigcode; } else { kst.ksigtramp_start = (char *)sv->sv_psstrings - *sv->sv_szsigcode; kst.ksigtramp_end = (char *)sv->sv_psstrings; } PROC_UNLOCK(p); error = SYSCTL_OUT(req, &kst, sizeof(kst)); return (error); } SYSCTL_NODE(_kern, KERN_PROC, proc, CTLFLAG_RD, 0, "Process table"); SYSCTL_PROC(_kern_proc, KERN_PROC_ALL, all, CTLFLAG_RD|CTLTYPE_STRUCT| CTLFLAG_MPSAFE, 0, 0, sysctl_kern_proc, "S,proc", "Return entire process table"); static SYSCTL_NODE(_kern_proc, KERN_PROC_GID, gid, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, KERN_PROC_PGRP, pgrp, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, KERN_PROC_RGID, rgid, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, KERN_PROC_SESSION, sid, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, KERN_PROC_TTY, tty, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, KERN_PROC_UID, uid, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, KERN_PROC_RUID, ruid, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, KERN_PROC_PID, pid, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, KERN_PROC_PROC, proc, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Return process table, no threads"); static SYSCTL_NODE(_kern_proc, KERN_PROC_ARGS, args, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, sysctl_kern_proc_args, "Process argument list"); static SYSCTL_NODE(_kern_proc, KERN_PROC_ENV, env, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_env, "Process environment"); static SYSCTL_NODE(_kern_proc, KERN_PROC_AUXV, auxv, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_auxv, "Process ELF auxiliary vector"); static SYSCTL_NODE(_kern_proc, KERN_PROC_PATHNAME, pathname, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_pathname, "Process executable path"); static SYSCTL_NODE(_kern_proc, KERN_PROC_SV_NAME, sv_name, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_sv_name, "Process syscall vector name (ABI type)"); static SYSCTL_NODE(_kern_proc, (KERN_PROC_GID | KERN_PROC_INC_THREAD), gid_td, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, (KERN_PROC_PGRP | KERN_PROC_INC_THREAD), pgrp_td, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, (KERN_PROC_RGID | KERN_PROC_INC_THREAD), rgid_td, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, (KERN_PROC_SESSION | KERN_PROC_INC_THREAD), sid_td, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, (KERN_PROC_TTY | KERN_PROC_INC_THREAD), tty_td, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, (KERN_PROC_UID | KERN_PROC_INC_THREAD), uid_td, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, (KERN_PROC_RUID | KERN_PROC_INC_THREAD), ruid_td, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, (KERN_PROC_PID | KERN_PROC_INC_THREAD), pid_td, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Process table"); static SYSCTL_NODE(_kern_proc, (KERN_PROC_PROC | KERN_PROC_INC_THREAD), proc_td, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc, "Return process table, no threads"); #ifdef COMPAT_FREEBSD7 static SYSCTL_NODE(_kern_proc, KERN_PROC_OVMMAP, ovmmap, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_ovmmap, "Old Process vm map entries"); #endif static SYSCTL_NODE(_kern_proc, KERN_PROC_VMMAP, vmmap, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_vmmap, "Process vm map entries"); #if defined(STACK) || defined(DDB) static SYSCTL_NODE(_kern_proc, KERN_PROC_KSTACK, kstack, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_kstack, "Process kernel stacks"); #endif static SYSCTL_NODE(_kern_proc, KERN_PROC_GROUPS, groups, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_groups, "Process groups"); static SYSCTL_NODE(_kern_proc, KERN_PROC_RLIMIT, rlimit, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, sysctl_kern_proc_rlimit, "Process resource limits"); static SYSCTL_NODE(_kern_proc, KERN_PROC_PS_STRINGS, ps_strings, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_ps_strings, "Process ps_strings location"); static SYSCTL_NODE(_kern_proc, KERN_PROC_UMASK, umask, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_umask, "Process umask"); static SYSCTL_NODE(_kern_proc, KERN_PROC_OSREL, osrel, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, sysctl_kern_proc_osrel, "Process binary osreldate"); static SYSCTL_NODE(_kern_proc, KERN_PROC_SIGTRAMP, sigtramp, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_sigtramp, "Process signal trampoline location"); Index: projects/clang350-import/sys =================================================================== --- projects/clang350-import/sys (revision 275386) +++ projects/clang350-import/sys (revision 275387) Property changes on: projects/clang350-import/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r275367-275386 Index: projects/clang350-import/tools/build/options/WITH_ELFTOOLCHAIN_TOOLS =================================================================== --- projects/clang350-import/tools/build/options/WITH_ELFTOOLCHAIN_TOOLS (nonexistent) +++ projects/clang350-import/tools/build/options/WITH_ELFTOOLCHAIN_TOOLS (revision 275387) @@ -0,0 +1,9 @@ +.\" $FreeBSD$ +Set to use +.Xr addr2line 1 , +.Xr nm 1 , +.Xr size 1 , +.Xr strings 1 , +and +.Xr strip 1 +from the elftoolchain project instead of GNU binutils. Property changes on: projects/clang350-import/tools/build/options/WITH_ELFTOOLCHAIN_TOOLS ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang350-import/tools/tools/nanobsd/fill_pkg.sh =================================================================== --- projects/clang350-import/tools/tools/nanobsd/fill_pkg.sh (revision 275386) +++ projects/clang350-import/tools/tools/nanobsd/fill_pkg.sh (revision 275387) @@ -1,96 +1,96 @@ #!/bin/sh # # Copyright (c) 2009 Poul-Henning Kamp. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # # Usage: # $0 PACKAGE_DUMP NANO_PACKAGE_DIR /usr/ports/foo/bar ... # # Will symlink the packages listed, including their runtime dependencies, # from the PACKAGE_DUMP to the NANO_PACKAGE_DIR. # NANO_PKG_DUMP=$1 shift; if [ ! -d $NANO_PKG_DUMP ] ; then echo "$NANO_PKG_DUMP not a directory" 1>&2 exit 1 fi NANO_PACKAGE_DIR=$1 shift; ports_recurse() ( of=$1 shift for d do if [ ! -d $d ] ; then echo "Missing port $d" 1>&2 exit 2 fi if grep -q "^$d\$" $of ; then true else ( cd $d - rd=`make -V RUN_DEPENDS` - ld=`make -V LIB_DEPENDS` + rd=`make -V RUN_DEPENDS ${PORTS_OPTS}` + ld=`make -V LIB_DEPENDS ${PORTS_OPTS}` for x in $rd $ld do ports_recurse $of `echo $x | sed 's/^[^:]*:\([^:]*\).*$/\1/'` done ) echo $d >> $of fi done ) rm -rf $NANO_PACKAGE_DIR mkdir -p $NANO_PACKAGE_DIR PL=$NANO_PACKAGE_DIR/_list true > $PL for i do ports_recurse `pwd`/$PL $i done for i in `cat $PL` do p=`(cd $i && make -V PKGNAME)` - if [ -f $NANO_PKG_DUMP/$p.tbz ] ; then - ln -s $NANO_PKG_DUMP/$p.tbz $NANO_PACKAGE_DIR + if [ -f $NANO_PKG_DUMP/$p.t[bx]z ] ; then + ln -s $NANO_PKG_DUMP/$p.t[bx]z $NANO_PACKAGE_DIR else echo "Package $p misssing in $NANO_PKG_DUMP" 1>&2 exit 1 fi done rm -f $PL exit 0 Index: projects/clang350-import/usr.bin/Makefile =================================================================== --- projects/clang350-import/usr.bin/Makefile (revision 275386) +++ projects/clang350-import/usr.bin/Makefile (revision 275387) @@ -1,393 +1,406 @@ # From: @(#)Makefile 8.3 (Berkeley) 1/7/94 # $FreeBSD$ .include # XXX MISSING: deroff diction graph learn plot # spell spline struct xsend # XXX Use GNU versions: diff ld patch # Moved to secure: bdes # -SUBDIR= alias \ +SUBDIR= ${_addr2line} \ + alias \ apply \ asa \ awk \ banner \ basename \ brandelf \ bsdiff \ bzip2 \ bzip2recover \ cap_mkdb \ chat \ chpass \ cksum \ ${_clang} \ cmp \ col \ colldef \ colrm \ column \ comm \ compress \ cpuset \ csplit \ ctlstat \ cut \ demandoc \ dirname \ dpv \ du \ ee \ elf2aout \ + ${_elfcopy} \ elfdump \ enigma \ env \ expand \ false \ fetch \ file \ find \ finger \ fmt \ fold \ fstat \ fsync \ ftp \ gcore \ gencat \ getconf \ getent \ getopt \ grep \ gzip \ head \ hexdump \ ${_iconv} \ id \ ipcrm \ ipcs \ iscsictl \ join \ jot \ ${_kdump} \ keylogin \ keylogout \ killall \ ktrace \ ktrdump \ lam \ lastcomm \ ldd \ leave \ less \ lessecho \ lesskey \ limits \ locale \ lock \ lockf \ logger \ login \ logins \ logname \ look \ lorder \ lsvfs \ lzmainfo \ m4 \ ${_makewhatis} \ ${_man} \ mandoc \ mesg \ minigzip \ ministat \ ${_mkcsmapper} \ mkdep \ ${_mkesdb} \ mkfifo \ mkimg \ mklocale \ mktemp \ mkulzma \ mkuzip \ mt \ ncal \ netstat \ newgrp \ nfsstat \ nice \ nl \ + ${_nm} \ nohup \ opieinfo \ opiekey \ opiepasswd \ pagesize \ passwd \ paste \ patch \ pathchk \ perror \ pr \ preconv \ printenv \ printf \ procstat \ protect \ rctl \ renice \ rev \ revoke \ rpcinfo \ rs \ rup \ rusers \ rwall \ script \ sed \ send-pr \ seq \ shar \ showmount \ + ${_size} \ sockstat \ soeliminate \ sort \ split \ stat \ stdbuf \ + ${_strings} \ su \ systat \ tabs \ tail \ talk \ tar \ tcopy \ tee \ ${_tests} \ tftp \ time \ timeout \ tip \ top \ touch \ tput \ tr \ true \ truncate \ ${_truss} \ tset \ tsort \ tty \ uname \ unexpand \ uniq \ unzip \ units \ unvis \ uudecode \ uuencode \ vis \ vmstat \ w \ wall \ wc \ what \ whereis \ which \ whois \ write \ xargs \ xinstall \ ${_xlint} \ xo \ ${_xstr} \ xz \ xzdec \ ${_yacc} \ yes \ ${_ypcat} \ ${_ypmatch} \ ${_ypwhich} # NB: keep these sorted by MK_* knobs .if ${MK_AT} != "no" SUBDIR+= at .endif .if ${MK_ATM} != "no" SUBDIR+= atm .endif .if ${MK_BLUETOOTH} != "no" SUBDIR+= bluetooth .endif .if ${MK_BSD_CPIO} != "no" SUBDIR+= cpio .endif .if ${MK_CALENDAR} != "no" SUBDIR+= calendar .endif .if ${MK_CLANG} != "no" _clang= clang +.endif + +.if ${MK_ELFTOOLCHAIN_TOOLS} != "no" +_addr2line= addr2line +_elfcopy= elfcopy +_nm= nm +_size= size +_strings= strings .endif .if ${MK_FMAKE} != "no" SUBDIR+= make .endif .if ${MK_GPL_DTC} != "yes" SUBDIR+= dtc .endif .if ${MK_GROFF} != "no" SUBDIR+= vgrind .endif .if ${MK_HESIOD} != "no" SUBDIR+= hesinfo .endif .if ${MK_ICONV} != "no" _iconv= iconv _mkcsmapper= mkcsmapper _mkesdb= mkesdb .endif .if ${MK_KDUMP} != "no" SUBDIR+= kdump SUBDIR+= truss .endif .if ${MK_KERBEROS_SUPPORT} != "no" SUBDIR+= compile_et .endif .if ${MK_LDNS_UTILS} != "no" SUBDIR+= drill SUBDIR+= host .endif .if ${MK_LOCATE} != "no" SUBDIR+= locate .endif # XXX msgs? .if ${MK_MAIL} != "no" SUBDIR+= biff SUBDIR+= from SUBDIR+= mail SUBDIR+= msgs .endif .if ${MK_MAKE} != "no" SUBDIR+= bmake .endif .if ${MK_MAN_UTILS} != "no" SUBDIR+= catman _makewhatis= makewhatis _man= man .endif .if ${MK_NETCAT} != "no" SUBDIR+= nc .endif .if ${MK_NIS} != "no" SUBDIR+= ypcat SUBDIR+= ypmatch SUBDIR+= ypwhich .endif .if ${MK_OPENSSH} != "no" SUBDIR+= ssh-copy-id .endif .if ${MK_OPENSSL} != "no" SUBDIR+= bc SUBDIR+= chkey SUBDIR+= dc SUBDIR+= newkey .endif .if ${MK_QUOTAS} != "no" SUBDIR+= quota .endif .if ${MK_RCMDS} != "no" SUBDIR+= rlogin SUBDIR+= rsh SUBDIR+= ruptime SUBDIR+= rwho .endif .if ${MK_SENDMAIL} != "no" SUBDIR+= vacation .endif .if ${MK_TELNET} != "no" SUBDIR+= telnet .endif .if ${MK_TESTS} != "no" _tests= tests .endif .if ${MK_TEXTPROC} != "no" SUBDIR+= checknr SUBDIR+= colcrt SUBDIR+= ul .endif .if ${MK_TOOLCHAIN} != "no" SUBDIR+= ar SUBDIR+= c89 SUBDIR+= c99 SUBDIR+= ctags SUBDIR+= file2c SUBDIR+= gprof SUBDIR+= indent SUBDIR+= lex SUBDIR+= mkstr SUBDIR+= rpcgen SUBDIR+= unifdef SUBDIR+= xlint SUBDIR+= xstr SUBDIR+= yacc .endif .if ${MK_VI} != "no" SUBDIR+= vi .endif .if ${MK_VT} != "no" SUBDIR+= vtfontcvt .endif .if ${MK_USB} != "no" SUBDIR+= usbhidaction SUBDIR+= usbhidctl .endif .if ${MK_UTMPX} != "no" SUBDIR+= last SUBDIR+= users SUBDIR+= who .endif .if ${MK_SVN} == "yes" || ${MK_SVNLITE} == "yes" SUBDIR+= svn .endif .include SUBDIR:= ${SUBDIR:O} SUBDIR_PARALLEL= .include Index: projects/clang350-import/usr.bin/addr2line/Makefile =================================================================== --- projects/clang350-import/usr.bin/addr2line/Makefile (nonexistent) +++ projects/clang350-import/usr.bin/addr2line/Makefile (revision 275387) @@ -0,0 +1,16 @@ +# $FreeBSD$ + +.include + +ELFTCDIR= ${.CURDIR}/../../contrib/elftoolchain +ADDR2LINEDIR= ${ELFTCDIR}/addr2line + +.PATH: ${ADDR2LINEDIR} + +PROG= addr2line + +LIBADD= elftc dwarf elf + +CFLAGS+=-I${ELFTCDIR}/libelftc -I${ELFTCDIR}/common + +.include Property changes on: projects/clang350-import/usr.bin/addr2line/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang350-import/usr.bin/elfcopy/Makefile =================================================================== --- projects/clang350-import/usr.bin/elfcopy/Makefile (nonexistent) +++ projects/clang350-import/usr.bin/elfcopy/Makefile (revision 275387) @@ -0,0 +1,24 @@ +# $FreeBSD$ + +.include + +ELFTCDIR= ${.CURDIR}/../../contrib/elftoolchain +ELFCOPYDIR= ${ELFTCDIR}/elfcopy + +.PATH: ${ELFCOPYDIR} + +PROG= elfcopy + +SRCS= archive.c ascii.c binary.c main.c sections.c segments.c symbols.c + +WARNS?= 5 + +LIBADD= archive elftc elf + +CFLAGS+=-I${ELFTCDIR}/libelftc -I${ELFTCDIR}/common + +MAN= elfcopy.1 strip.1 + +LINKS= ${BINDIR}/elfcopy ${BINDIR}/strip + +.include Property changes on: projects/clang350-import/usr.bin/elfcopy/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang350-import/usr.bin/nm/Makefile =================================================================== --- projects/clang350-import/usr.bin/nm/Makefile (nonexistent) +++ projects/clang350-import/usr.bin/nm/Makefile (revision 275387) @@ -0,0 +1,16 @@ +# $FreeBSD$ + +.include + +ELFTCDIR= ${.CURDIR}/../../contrib/elftoolchain +NMDIR= ${ELFTCDIR}/nm + +.PATH: ${NMDIR} + +PROG= nm + +LIBADD= dwarf elftc elf + +CFLAGS+=-I${ELFTCDIR}/libelftc -I${ELFTCDIR}/common + +.include Property changes on: projects/clang350-import/usr.bin/nm/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang350-import/usr.bin/size/Makefile =================================================================== --- projects/clang350-import/usr.bin/size/Makefile (nonexistent) +++ projects/clang350-import/usr.bin/size/Makefile (revision 275387) @@ -0,0 +1,16 @@ +# $FreeBSD$ + +.include + +ELFTCDIR= ${.CURDIR}/../../contrib/elftoolchain +SIZEDIR= ${ELFTCDIR}/size + +.PATH: ${SIZEDIR} + +PROG= size + +LIBADD= elftc elf + +CFLAGS+=-I${ELFTCDIR}/libelftc -I${ELFTCDIR}/common + +.include Property changes on: projects/clang350-import/usr.bin/size/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang350-import/usr.bin/strings/Makefile =================================================================== --- projects/clang350-import/usr.bin/strings/Makefile (nonexistent) +++ projects/clang350-import/usr.bin/strings/Makefile (revision 275387) @@ -0,0 +1,15 @@ +# $FreeBSD$ + +.include + +ELFTCDIR= ${.CURDIR}/../../contrib/elftoolchain + +.PATH: ${ELFTCDIR}/strings + +PROG= strings + +LIBADD= elftc elf + +CFLAGS+=-I${ELFTCDIR}/libelftc -I${ELFTCDIR}/common + +.include Property changes on: projects/clang350-import/usr.bin/strings/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang350-import =================================================================== --- projects/clang350-import (revision 275386) +++ projects/clang350-import (revision 275387) Property changes on: projects/clang350-import ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r275367-275386