root/usr/src/cmd/sgs/librtld/common/dynamic.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Update any dynamic entry offsets.  One issue with dynamic entries is that
 * you only know whether they refer to a value or an offset if you know each
 * type.  Thus we check for all types we know about, it a type is found that
 * we don't know about then return and error as we have no idea what to do.
 */

#include        <libelf.h>
#include        <link.h>
#include        "libld.h"
#include        "msg.h"
#include        "rtld.h"
#include        "_librtld.h"

int
update_dynamic(Cache *cache, Cache *_cache, Rt_map *lmp, int flags,
    Addr addr, Off off, const char *file, Xword null, Xword data, Xword func,
    Xword entsize, Xword checksum)
{
        Dyn             *dyn = (Dyn *)_cache->c_data->d_buf, *posdyn = 0;
        const char      *strs;
        Cache           *__cache;

        /*
         * If we're dealing with an object that might have bound to an external
         * dependency establish our string table for possible NEEDED processing.
         */
        if (flags & RTLD_REL_DEPENDS) {
                __cache = &cache[_cache->c_shdr->sh_link];
                strs = (const char *)__cache->c_data->d_buf;
        }

        /*
         * Loop through the dynamic table updating all offsets.
         */
        while (dyn->d_tag != DT_NULL) {
                Rt_map  *dlmp;

                switch ((Xword)dyn->d_tag) {
                case DT_NEEDED:
                        if (posdyn == 0)
                                break;

                        /*
                         * Determine whether this dependency has been loaded
                         * (this is the most generic way to check any alias
                         * names), and if it has been bound to, undo any
                         * lazy-loading or deferred position flag.
                         */
                        if (dlmp = is_so_loaded(LIST(lmp),
                            (strs + dyn->d_un.d_val), NULL)) {
                                Bnd_desc        *bdp;
                                Aliste          idx;

                                for (APLIST_TRAVERSE(DEPENDS(lmp), idx, bdp)) {
                                        if (dlmp != bdp->b_depend)
                                                continue;

                                        posdyn->d_un.d_val &=
                                            ~(DF_P1_LAZYLOAD | DF_P1_DEFERRED);
                                        break;
                                }
                        }
                        break;

                case DT_RELAENT:
                case DT_STRSZ:
                case DT_SYMENT:
                case DT_SONAME:
                case DT_RPATH:
                case DT_SYMBOLIC:
                case DT_RELENT:
                case DT_PLTREL:
                case DT_TEXTREL:
                case DT_VERDEFNUM:
                case DT_VERNEEDNUM:
                case DT_AUXILIARY:
                case DT_USED:
                case DT_FILTER:
                case DT_DEPRECATED_SPARC_REGISTER:
                case M_DT_REGISTER:
                case DT_BIND_NOW:
                case DT_INIT_ARRAYSZ:
                case DT_FINI_ARRAYSZ:
                case DT_RUNPATH:
                case DT_FLAGS:
                case DT_CONFIG:
                case DT_DEPAUDIT:
                case DT_AUDIT:
                case DT_SUNW_SYMSZ:
                        break;
                case DT_PLTGOT:
                case DT_HASH:
                case DT_STRTAB:
                case DT_SYMTAB:
                case DT_SUNW_SYMTAB:
                case DT_INIT:
                case DT_FINI:
                case DT_VERSYM:
                case DT_VERDEF:
                case DT_VERNEED:
                case DT_INIT_ARRAY:
                case DT_FINI_ARRAY:
                        dyn->d_un.d_ptr += addr;
                        break;

                /*
                 * If the memory image is being used, this element would have
                 * been initialized to the runtime linkers internal link-map
                 * list.  Clear it.
                 */
                case DT_DEBUG:
                        dyn->d_un.d_val = 0;
                        break;

                /*
                 * The number of relocations may have been reduced if
                 * relocations have been saved in the new image.  Thus we
                 * compute the new relocation size and start.
                 */
                case DT_RELASZ:
                case DT_RELSZ:
                        dyn->d_un.d_val = ((data + func) * entsize);
                        break;

                case DT_RELA:
                case DT_REL:
                        dyn->d_un.d_ptr = (addr + off + (null * entsize));
                        break;

                /*
                 * If relative relocations have been processed clear the count.
                 */
                case DT_RELACOUNT:
                case DT_RELCOUNT:
                        if (flags & RTLD_REL_RELATIVE)
                                dyn->d_un.d_val = 0;
                        break;

                case DT_PLTRELSZ:
                        dyn->d_un.d_val = (func * entsize);
                        break;

                case DT_JMPREL:
                        dyn->d_un.d_ptr = (addr + off +
                            ((null + data) * entsize));
                        break;

                /*
                 * Recompute the images elf checksum.
                 */
                case DT_CHECKSUM:
                        dyn->d_un.d_val = checksum;
                        break;

                /*
                 * If a flag entry is available, indicate if this image has
                 * been generated via the configuration process (crle(1)).
                 * Because we only started depositing DT_FLAGS_1 entries in all
                 * objects starting with Solaris 8, set a feature flag if it
                 * is present (these got added in Solaris 7).
                 * The runtime linker may use this flag to search for a local
                 * configuration file - this is only meaningful in executables
                 * but the flag has value for identifying images regardless.
                 *
                 * If this file is acting as a filter, and dependency
                 * relocations have been processed (a filter is thought of as a
                 * dependency in terms of symbol binding), we may have bound to
                 * the filtee, and hence carried out the relocation.  Indicate
                 * that the filtee must be preloaded, as the .plt won't get
                 * exercised to cause its normal loading.
                 */
                case DT_FLAGS_1:
                        if (flags & RTLD_CONFSET)
                                dyn->d_un.d_val |= DF_1_CONFALT;
                        if ((flags & RTLD_REL_DEPENDS) &&
                            (FLAGS1(lmp)) & MSK_RT_FILTER)
                                dyn->d_un.d_val |= DF_1_LOADFLTR;
                        break;

                case DT_FEATURE_1:
                        if (flags & RTLD_CONFSET)
                                dyn->d_un.d_val |= DTF_1_CONFEXP;
                        break;

                /*
                 * If a position flag is available save it for possible update
                 * when processing the next NEEDED tag.
                 */
                case DT_POSFLAG_1:
                        if (flags & RTLD_REL_DEPENDS) {
                                posdyn = dyn++;
                                continue;
                        }
                        break;

                /*
                 * Collect the defaults.
                 */
                default:
                        /*
                         * If d_val is used, don't touch.
                         */
                        if ((dyn->d_tag >= DT_VALRNGLO) &&
                            (dyn->d_tag <= DT_VALRNGHI))
                                break;

                        /*
                         * If d_ptr is used, adjust.  Note, some entries that
                         * fell into this range are offsets into the dynamic
                         * string table.  Although these would need modifying
                         * if the section itself were resized, there is no
                         * resizing with dldump().  Entries that correspond to
                         * offsets are picked off in the initial DT_ loop
                         * above.
                         */
                        if ((dyn->d_tag >= DT_ADDRRNGLO) &&
                            (dyn->d_tag <= DT_ADDRRNGHI)) {
                                dyn->d_un.d_ptr += addr;
                                break;
                        }

                        /*
                         * Check to see if this DT_ entry conforms
                         * to the DT_ENCODING rules.
                         */
                        if ((dyn->d_tag >= DT_ENCODING) &&
                            (dyn->d_tag <= DT_HIOS)) {
                                /*
                                 * Even tag values are ADDRESS encodings
                                 */
                                if ((dyn->d_tag % 2) == 0) {
                                        dyn->d_un.d_ptr += addr;
                                }
                                break;
                        }
                        eprintf(LIST(lmp), ERR_WARNING,
                            MSG_INTL(MSG_DT_UNKNOWN), file,
                            EC_XWORD(dyn->d_tag));
                        return (1);
                }
                posdyn = 0;
                dyn++;
        }
        return (0);
}