root/usr/src/cmd/sgs/liblddbg/common/sections.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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
#include        <stdio.h>
#include        "msg.h"
#include        "_debug.h"
#include        "libld.h"
#include        "_string_table.h"

/*
 * Format an input section descriptor name for output, in the format
 *      [ndx]name
 * If possible, a user supplied fixed size buffer is used. Failing that,
 * dynamic memory is allocated, which must be freed by the caller.
 *
 * entry:
 *      [dbg_fmt_isec_name2]: name, scnndx  - Name and section index
 *      [dbg_fmt_isec_name]: isp - Input section descriptor giving name
 *              and index.
 *
 *      buf - Caller supplied buffer
 *      alloc_mem - Address of pointer to be set to address of allocated
 *              memory, or NULL if no memory is allocated.
 *
 * exit:
 *      A pointer to the formatted string is returned. If the supplied buffer
 *      was sufficient, *alloc_mem is set to NULL. If memory was allocated,
 *      *alloc_mem references it. The caller must free this memory after use.
 */
const char *
dbg_fmt_isec_name2(const char *name, Word scnndx, dbg_isec_name_buf_t buf,
    char **alloc_mem)
{
        int     cnt;

        /*
         * If the section index is 0, it's not a real section.
         * Just use the name as is.
         */
        if (scnndx == 0) {
                *alloc_mem = NULL;
                return (name);
        }

        /* Format into the fixed buffer */
        cnt = snprintf(buf, sizeof (dbg_isec_name_buf_t),
            MSG_ORIG(MSG_FMT_ISEC_NAME), EC_WORD(scnndx), name);

        /*
         * If the name was too long, try to allocate a dynamic buffer.
         * Failing that, fall through and use the clipped one already
         * formatted into buf, as that's better than nothing.
         */
        if ((cnt >= sizeof (dbg_isec_name_buf_t)) &&
            ((*alloc_mem = malloc(cnt + 1)) != NULL)) {
                (void) snprintf(*alloc_mem, cnt + 1,
                    MSG_ORIG(MSG_FMT_ISEC_NAME), EC_WORD(scnndx), name);
                return (*alloc_mem);
        }

        /* Return the caller supplied buffer */
        *alloc_mem = NULL;
        return (buf);
}
const char *
dbg_fmt_isec_name(Is_desc *isp, dbg_isec_name_buf_t buf, char **alloc_mem)
{
        return (dbg_fmt_isec_name2(isp->is_name, isp->is_scnndx, buf,
            alloc_mem));
}

void
Dbg_sec_strtab(Lm_list *lml, Os_desc *osp, Str_tbl *stp)
{
        uint_t  cnt;

        if (DBG_NOTCLASS(DBG_C_STRTAB))
                return;

        if (!osp)
                return;

        Dbg_util_nl(lml, DBG_NL_STD);
        if (stp->st_flags & FLG_STTAB_COMPRESS)
                dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_COMP), osp->os_name,
                    EC_XWORD(stp->st_fullstrsize), EC_XWORD(stp->st_strsize));
        else
                dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_STND), osp->os_name,
                    EC_XWORD(stp->st_fullstrsize));

        if ((DBG_NOTDETAIL()) ||
            ((stp->st_flags & FLG_STTAB_COMPRESS) == 0))
                return;

        dbg_print(lml, MSG_ORIG(MSG_STR_EMPTY));
        dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_HD), osp->os_name,
            stp->st_hbckcnt);

        for (cnt = 0; cnt < stp->st_hbckcnt; cnt++) {
                Str_hash        *strhash = stp->st_hashbcks[cnt];

                if (strhash == NULL)
                        continue;

                dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_BCKT), cnt);

                while (strhash) {
                        size_t  stroff = strhash->hi_mstr->sm_strlen -
                            strhash->hi_strlen;

                        if (stroff == 0) {
                                dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_MSTR),
                                    EC_XWORD(strhash->hi_refcnt),
                                    strhash->hi_mstr->sm_str);
                        } else {
                                dbg_print(lml, MSG_INTL(MSG_SEC_STRTAB_SUFSTR),
                                    EC_XWORD(strhash->hi_refcnt),
                                    &strhash->hi_mstr->sm_str[stroff],
                                    strhash->hi_mstr->sm_str);
                        }

                        strhash = strhash->hi_next;
                }
        }
}

void
Dbg_sec_genstr_compress(Lm_list *lml, const char *os_name,
    Xword raw_size, Xword merge_size)
{
        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;

        dbg_print(lml, MSG_INTL(MSG_SEC_GENSTR_COMP), os_name,
            EC_XWORD(raw_size), EC_XWORD(merge_size));
}

void
Dbg_sec_unsup_strmerge(Lm_list *lml, Is_desc *isp)
{
        dbg_isec_name_buf_t     buf;
        char                    *alloc_mem;
        const char              *str;

        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;

        /*
         * We can only merge string table sections with single byte
         * (char) characters. For any other (wide) character types,
         * issue a message so the user will understand why these
         * sections are not being picked up.
         */
        if ((isp->is_shdr->sh_entsize > 1) ||
            (isp->is_shdr->sh_addralign > 1)) {
                str = (isp->is_file != NULL) ? isp->is_file->ifl_name :
                    MSG_INTL(MSG_STR_NULL);
                dbg_print(lml, MSG_INTL(MSG_SEC_STRMERGE_UNSUP),
                    dbg_fmt_isec_name(isp, buf, &alloc_mem), str,
                    EC_XWORD(isp->is_shdr->sh_addralign),
                    EC_XWORD(isp->is_shdr->sh_entsize));
                if (alloc_mem != NULL)
                        free(alloc_mem);
        }
}

void
Dbg_sec_backing(Lm_list *lml)
{
        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;

        Dbg_util_nl(lml, DBG_NL_STD);
        dbg_print(lml, MSG_INTL(MSG_SEC_BACKING));
}

void
Dbg_sec_in(Lm_list *lml, Is_desc *isp)
{
        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;

        if (isp->is_flags & FLG_IS_GNSTRMRG) {
                /*
                 * This section was generated because we have 1 or
                 * more SHF_MERGE|SHF_STRINGS input sections that we
                 * wish to merge. This new section will ultimately
                 * end up replacing those sections once it has been filled
                 * with their strings (merged and compressed) and relocations
                 * have been redirected.
                 */
                dbg_print(lml, MSG_INTL(MSG_SEC_INPUT_GENSTR), isp->is_name);
        } else if (isp->is_file == NULL) {
                /* Generated input section */
                dbg_print(lml, MSG_INTL(MSG_SEC_INPUT_GEN), isp->is_name);
        } else {
                /* Standard input section */
                dbg_isec_name_buf_t     buf;
                char                    *alloc_mem;

                dbg_print(lml, MSG_INTL(MSG_SEC_INPUT),
                    dbg_fmt_isec_name(isp, buf, &alloc_mem),
                    isp->is_file->ifl_name);
                if (alloc_mem != NULL)
                        free(alloc_mem);
        }
}

void
Dbg_sec_added(Lm_list *lml, Os_desc *osp, Sg_desc *sgp)
{
        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;

        dbg_print(lml, MSG_INTL(MSG_SEC_ADDED), osp->os_name,
            (sgp->sg_name ? sgp->sg_name : MSG_INTL(MSG_STR_NULL)));
}

void
Dbg_sec_created(Lm_list *lml, Os_desc *osp, Sg_desc *sgp)
{
        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;

        dbg_print(lml, MSG_INTL(MSG_SEC_CREATED), osp->os_name,
            (sgp->sg_name ? sgp->sg_name : MSG_INTL(MSG_STR_NULL)));
}

void
Dbg_sec_discarded(Lm_list *lml, Is_desc *isp, Is_desc *disp)
{
        if (DBG_NOTCLASS(DBG_C_SECTIONS | DBG_C_UNUSED))
                return;

        if ((isp->is_flags & FLG_IS_INSTRMRG) &&
            (disp->is_flags & FLG_IS_GNSTRMRG)) {
                /*
                 * This SHF_MERGE|SHF_STRINGS input section is being
                 * discarded in favor of the generated merged string section.
                 */
                dbg_isec_name_buf_t     buf;
                char                    *alloc_mem;

                dbg_print(lml, MSG_INTL(MSG_SEC_STRMERGE_DISCARDED),
                    dbg_fmt_isec_name(isp, buf, &alloc_mem),
                    isp->is_file->ifl_name);
                if (alloc_mem != NULL)
                        free(alloc_mem);
        } else {
                /* Generic section discard */
                dbg_isec_name_buf_t     buf1, buf2;
                char                    *alloc_mem1, *alloc_mem2;

                dbg_print(lml, MSG_INTL(MSG_SEC_DISCARDED),
                    dbg_fmt_isec_name(isp, buf1, &alloc_mem1),
                    isp->is_file->ifl_name,
                    dbg_fmt_isec_name(disp, buf2, &alloc_mem2),
                    disp->is_file->ifl_name);
                if (alloc_mem1 != NULL)
                        free(alloc_mem1);
                if (alloc_mem2 != NULL)
                        free(alloc_mem2);
        }
}

void
Dbg_sec_group(Lm_list *lml, Is_desc *isp, Group_desc *gdp)
{
        dbg_isec_name_buf_t     buf;
        char                    *alloc_mem;
        const char              *comdat, *isp_str;

        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;

        if (gdp->gd_data[0] & GRP_COMDAT)
                comdat = MSG_ORIG(MSG_STR_COMDAT);
        else
                comdat = MSG_ORIG(MSG_STR_EMPTY);

        isp_str = dbg_fmt_isec_name(isp, buf, &alloc_mem);

        if (isp->is_shdr->sh_type == SHT_GROUP) {
                dbg_print(lml, MSG_INTL(MSG_SEC_GRP_DEFINE), isp_str,
                    isp->is_file->ifl_name, comdat, gdp->gd_name);
        } else {
                dbg_print(lml, MSG_INTL(MSG_SEC_GRP_MEMBER), isp_str,
                    isp->is_file->ifl_name, comdat, gdp->gd_name);
        }

        if (gdp->gd_oisc) {
                dbg_print(lml, MSG_INTL(MSG_SEC_GRP_DISCARDED), isp_str,
                    isp->is_file->ifl_name, gdp->gd_name,
                    gdp->gd_oisc->is_file->ifl_name);
        }

        if (alloc_mem != NULL)
                free(alloc_mem);
}

void
Dbg_sec_order_list(Ofl_desc *ofl, int flag)
{
        Os_desc         *osp;
        Is_desc         *isp1;
        Aliste          idx1;
        Lm_list         *lml = ofl->ofl_lml;
        const char      *str;

        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;
        if (DBG_NOTDETAIL())
                return;

        Dbg_util_nl(lml, DBG_NL_STD);

        /*
         * If the flag == 0, then the routine is called before sorting.
         */
        if (flag == 0)
                str = MSG_INTL(MSG_ORD_SORT_BEFORE);
        else
                str = MSG_INTL(MSG_ORD_SORT_AFTER);

        for (APLIST_TRAVERSE(ofl->ofl_ordered, idx1, osp)) {
                int             os_isdescs_idx;
                Aliste          idx2;

                Dbg_util_nl(lml, DBG_NL_STD);
                dbg_print(lml, str, osp->os_name);
                dbg_print(lml, MSG_INTL(MSG_ORD_HDR_1),
                    EC_WORD(aplist_nitems(osp->os_isdescs[OS_ISD_BEFORE])),
                    EC_WORD(aplist_nitems(osp->os_isdescs[OS_ISD_ORDERED])),
                    EC_WORD(aplist_nitems(osp->os_isdescs[OS_ISD_DEFAULT])),
                    EC_WORD(aplist_nitems(osp->os_isdescs[OS_ISD_AFTER])));

                OS_ISDESCS_TRAVERSE(os_isdescs_idx, osp, idx2, isp1) {
                        dbg_isec_name_buf_t     buf;
                        char                    *alloc_mem;
                        const char              *isp1_str;
                        Word                    link;
                        Ifl_desc                *ifl = isp1->is_file;
                        Is_desc                 *isp2;
                        const char              *msg;

                        /*
                         * An output segment that requires ordering might have
                         * as little as two sorted input sections.  For example,
                         * the crt's can provide a SHN_BEGIN and SHN_AFTER, and
                         * only these two sections must be processed.  Thus, if
                         * a input section is unordered, move on.  Diagnosing
                         * any unsorted section can produce way too much noise.
                         */
                        if ((isp1->is_flags & FLG_IS_ORDERED) == 0)
                                continue;

                        if (isp1->is_shdr->sh_flags & SHF_ORDERED) {
                                link = isp1->is_shdr->sh_info;
                                msg = MSG_ORIG(MSG_SH_INFO);
                        } else {        /* SHF_LINK_ORDER */
                                link = isp1->is_shdr->sh_link;
                                msg = MSG_ORIG(MSG_SH_LINK);
                        }

                        isp1_str = dbg_fmt_isec_name(isp1, buf, &alloc_mem);

                        if (link == SHN_BEFORE) {
                                dbg_print(lml, MSG_INTL(MSG_ORD_TITLE_1), msg,
                                    isp1_str, isp1->is_file->ifl_name);
                        } else if (link == SHN_AFTER) {
                                dbg_print(lml, MSG_INTL(MSG_ORD_TITLE_2), msg,
                                    isp1_str, isp1->is_file->ifl_name);
                        } else {
                                isp2 = ifl->ifl_isdesc[link];
                                dbg_print(lml, MSG_INTL(MSG_ORD_TITLE_3),
                                    EC_WORD(isp2->is_keyident), isp1_str,
                                    ifl->ifl_name, msg, isp2->is_name);
                        }
                        if (alloc_mem != NULL)
                                free(alloc_mem);
                }
        }
        Dbg_util_nl(lml, DBG_NL_STD);
}

/*
 * Error message string table.
 */
static const Msg order_errors[] = {
        MSG_ORD_ERR_INFORANGE,          /* MSG_INTL(MSG_ORD_ERR_INFORANGE) */
        MSG_ORD_ERR_ORDER,              /* MSG_INTL(MSG_ORD_ERR_ORDER) */
        MSG_ORD_ERR_LINKRANGE,          /* MSG_INTL(MSG_ORD_ERR_LINKRANGE) */
        MSG_ORD_ERR_FLAGS,              /* MSG_INTL(MSG_ORD_ERR_FLAGS) */
        MSG_ORD_ERR_CYCLIC,             /* MSG_INTL(MSG_ORD_ERR_CYCLIC) */
        MSG_ORD_ERR_LINKINV             /* MSG_INTL(MSG_ORD_ERR_LINKINV) */
};

void
Dbg_sec_order_error(Lm_list *lml, Ifl_desc *ifl, Word ndx, int error)
{
        dbg_isec_name_buf_t     buf;
        char                    *alloc_mem;

        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;
        if (DBG_NOTDETAIL())
                return;

        if (error == 0)
                return;

        dbg_print(lml, MSG_INTL(MSG_ORD_ERR_TITLE),
            dbg_fmt_isec_name(ifl->ifl_isdesc[ndx], buf, &alloc_mem),
            ifl->ifl_name);
        if (alloc_mem != NULL)
                free(alloc_mem);

        if (error)
                dbg_print(lml, MSG_INTL(order_errors[error - 1]));
}

void
Dbg_sec_redirected(Lm_list *lml, Is_desc *isp, const char *nname)
{
        dbg_isec_name_buf_t     buf;
        char                    *alloc_mem;

        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;

        dbg_print(lml, MSG_INTL(MSG_SEC_REDIRECTED),
            dbg_fmt_isec_name(isp, buf, &alloc_mem), nname);
        if (alloc_mem != NULL)
                free(alloc_mem);
}

void
Dbg_sec_gnu_comdat(Lm_list *lml, Is_desc *isp, Boolean comdat, Boolean relax)
{
        dbg_isec_name_buf_t     buf;
        char                    *alloc_mem;
        const char              *fmt;

        if (DBG_NOTCLASS(DBG_C_SECTIONS))
                return;

        if (comdat && relax)
                fmt = MSG_INTL(MSG_SEC_GNU_COMDAT_1);
        else if (comdat)
                fmt = MSG_INTL(MSG_SEC_GNU_COMDAT_2);
        else
                fmt = MSG_INTL(MSG_SEC_GNU_COMDAT_3);

        dbg_print(lml, fmt, dbg_fmt_isec_name(isp, buf, &alloc_mem));
        if (alloc_mem != NULL)
                free(alloc_mem);
}