root/usr/src/cmd/sgs/libelf/demo/dispsyms.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) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * dispsyms: Display Symbols
 *
 * This program demonstrates the use of the libelf interface to
 * read an ELF file.  dispsyms will open an ELF file using
 * elf_begin(ELF_C_READ) and examine search the ELF file
 * for a symbol table (SHT_SYMTAB, SHT_DYNSYM, or SHT_SUNW_LDYNSYM).
 * It will display the contents of any symbol tables it finds.
 *
 * Note:  This program also understands about the use
 *        of 'Extended ELF Section indexes' and will
 *        decode a corresponding SHT_SYMTAB_SHNDX
 *        section if required.
 */
#include <stdio.h>
#include <libelf.h>
#include <gelf.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

static const char *symbind[STB_NUM] = {
/* STB_LOCL */          "LOCL",
/* STB_GLOBAL */        "GLOB",
/* STB_WEAK */          "WEAK"
};

static const char *symtype[STT_NUM] = {
/* STT_NOTYPE */        "NOTY",
/* STT_OBJECT */        "OBJT",
/* STT_FUNC */          "FUNC",
/* STT_SECTION */       "SECT",
/* STT_FILE */          "FILE",
/* STT_COMMON */        "COMM",
/* STT_TLS */           "TLS"
};
#if STT_NUM != (STT_TLS + 1)
#error "STT_NUM has grown. Update symtype[]."
#endif

#define INTSTRLEN       32


static void
print_symtab(Elf *elf, const char *file)
{
        Elf_Scn         *scn = NULL;
        GElf_Shdr       shdr;
        GElf_Ehdr       ehdr;
        size_t          shstrndx;


        if (gelf_getehdr(elf, &ehdr) == NULL) {
                (void) fprintf(stderr, "%s: elf_getehdr() failed: %s\n",
                    file, elf_errmsg(0));
                return;
        }

        if (elf_getshdrstrndx(elf, &shstrndx) == -1) {
                (void) fprintf(stderr, "%s: elf_getshdrstrndx() failed: %s\n",
                    file, elf_errmsg(0));
                return;
        }

        while ((scn = elf_nextscn(elf, scn)) != NULL) {
                uint_t          symcnt, ndx, nosymshndx;
                Elf_Data        *symdata, *shndxdata;

                if (gelf_getshdr(scn, &shdr) == NULL) {
                        (void) fprintf(stderr,
                            "%s: elf_getshdr() failed: %s\n",
                            file, elf_errmsg(0));
                        return;
                }
                if ((shdr.sh_type != SHT_SYMTAB) &&
                    (shdr.sh_type != SHT_DYNSYM) &&
                    (shdr.sh_type != SHT_SUNW_LDYNSYM))
                        continue;

                /*
                 * Get the data associated with the Symbol
                 * section.
                 */
                if ((symdata = elf_getdata(scn, NULL)) == NULL) {
                        (void) fprintf(stderr,
                            "%s: elf_getdata() failed: %s\n",
                            file, elf_errmsg(0));
                        return;
                }

                /*
                 * Print symbol table title and header for symbol display
                 */
                (void) printf("\nSymTab: %s:%s\n", file,
                    elf_strptr(elf, shstrndx, shdr.sh_name));
                (void) printf("  index   value    size     type "
                    "bind  oth shndx name\n");

                /*
                 * We now iterate over the full symbol table printing
                 * the symbols as we go.
                 */
                shndxdata = 0;
                nosymshndx = 0;
                symcnt = shdr.sh_size / shdr.sh_entsize;
                for (ndx = 0; ndx < symcnt; ndx++) {
                        GElf_Sym        sym;
                        Elf32_Word      shndx;
                        uint_t          type, bind, specshndx;
                        char            bindbuf[INTSTRLEN];
                        char            typebuf[INTSTRLEN];
                        char            shndxbuf[INTSTRLEN];
                        const char      *bindstr, *typestr, *shndxstr;

                        /*
                         * Get a symbol entry
                         */
                        if (gelf_getsymshndx(symdata, shndxdata, ndx,
                            &sym, &shndx) == NULL) {
                                (void) fprintf(stderr,
                                    "%s: gelf_getsymshndx() failed: %s\n",
                                    file, elf_errmsg(0));
                                return;
                        }
                        /*
                         * Check to see if this symbol's st_shndx is using
                         * the 'Extended SHNDX table' for a SYMTAB.
                         *
                         * If it is - and we haven't searched before, go
                         * find the associated SHT_SYMTAB_SHNDX section.
                         */
                        if ((sym.st_shndx == SHN_XINDEX) &&
                            (shndxdata == 0) && (nosymshndx == 0)) {
                                Elf_Scn         *_scn = NULL;
                                GElf_Shdr       _shdr;
                                GElf_Word       symscnndx;

                                specshndx = 0;
                                symscnndx = elf_ndxscn(scn);

                                while ((_scn =
                                    elf_nextscn(elf, _scn)) != NULL) {
                                        if (gelf_getshdr(_scn, &_shdr) == NULL)
                                                break;

                                        /*
                                         * We've found the Symtab SHNDX table
                                         * if it's of type SHT_SYMTAB_SHNDX
                                         * and it's shdr.sh_link points to the
                                         * section index for the current symbol
                                         * table.
                                         */
                                        if ((_shdr.sh_type ==
                                            SHT_SYMTAB_SHNDX) &&
                                            (_shdr.sh_link == symscnndx) &&
                                            ((shndxdata = elf_getdata(_scn,
                                            NULL)) != NULL))
                                                break;
                                }
                                /*
                                 * Get a symbol entry
                                 */
                                if (shndxdata &&
                                    (gelf_getsymshndx(symdata, shndxdata, ndx,
                                    &sym, &shndx) == NULL)) {
                                        (void) fprintf(stderr,
                                            "%s: gelf_getsymshndx() "
                                            "failed: %s\n",
                                            file, elf_errmsg(0));
                                        return;
                                }
                                /*
                                 * No Symtab SHNDX table was found.  We could
                                 * give a fatal error here - instead we'll
                                 * just mark that fact and display as much of
                                 * the symbol table as we can.  Any symbol
                                 * displayed with a XINDX section index has
                                 * a bogus value.
                                 */
                                if (shndxdata == 0)
                                        nosymshndx = 1;
                        }

                        /*
                         * Decode the type & binding information
                         */
                        type = GELF_ST_TYPE(sym.st_info);
                        bind = GELF_ST_BIND(sym.st_info);

                        if (type < STT_NUM)
                                typestr = symtype[type];
                        else {
                                (void) snprintf(typebuf, INTSTRLEN,
                                    "%d", type);
                                typestr = typebuf;
                        }

                        if (bind < STB_NUM)
                                bindstr = symbind[bind];
                        else {
                                (void) snprintf(bindbuf, INTSTRLEN,
                                    "%d", bind);
                                bindstr = bindbuf;
                        }


                        specshndx = 0;
                        if (sym.st_shndx <  SHN_LORESERVE)
                                shndx = sym.st_shndx;
                        else if ((sym.st_shndx != SHN_XINDEX) ||
                            (shndxdata == NULL)) {
                                shndx = sym.st_shndx;
                                specshndx = 1;
                        }

                        if (shndx == SHN_UNDEF) {
                                shndxstr = (const char *)"UNDEF";

                        } else if (specshndx) {
                                if (shndx == SHN_ABS)
                                        shndxstr = (const char *)"ABS";
                                else if (shndx == SHN_COMMON)
                                        shndxstr = (const char *)"COMM";
                                else if (shndx == SHN_XINDEX)
                                        shndxstr = (const char *)"XIND";
                                else {
                                        (void) snprintf(shndxbuf, INTSTRLEN,
                                            "%ld", shndx);
                                        shndxstr = shndxbuf;
                                }
                        } else {
                                (void) snprintf(shndxbuf, INTSTRLEN,
                                    "%ld", shndx);
                                shndxstr = shndxbuf;
                        }

                        /*
                         * Display the symbol entry.
                         */
                        (void) printf("[%3d] 0x%08llx 0x%08llx %-4s "
                            "%-6s %2d %5s %s\n",
                            ndx, sym.st_value, sym.st_size,
                            typestr, bindstr, sym.st_other, shndxstr,
                            elf_strptr(elf, shdr.sh_link, sym.st_name));
                }
        }
}


static void
process_elf(Elf *elf, char *file, int fd, int member)
{
        Elf_Cmd cmd;
        Elf     *_elf;

        switch (elf_kind(elf)) {
        case ELF_K_ELF:
                /*
                 * This is an ELF file, now attempt to find it's
                 * .comment section and to display it.
                 */
                print_symtab(elf, file);
                break;
        case ELF_K_AR:
                /*
                 * Archives contain multiple ELF files, which can each
                 * in turn be examined with libelf.
                 *
                 * The below loop will iterate over each member of the
                 * archive and recursively call process_elf().
                 */
                cmd = ELF_C_READ;
                while ((_elf = elf_begin(fd, cmd, elf)) != NULL) {
                        Elf_Arhdr       *arhdr;
                        char            buffer[1024];

                        arhdr = elf_getarhdr(_elf);

                        /*
                         * Build up file names based off of
                         * 'archivename(membername)'.
                         */
                        (void) snprintf(buffer, 1024, "%s(%s)",
                            file, arhdr->ar_name);

                        /*
                         * Recursively process the ELF members.
                         */
                        process_elf(_elf, buffer, fd, 1);
                        cmd = elf_next(_elf);
                        (void) elf_end(_elf);
                }
                break;
        default:
                if (!member)
                        (void) fprintf(stderr,
                            "%s: unexpected elf_kind(): 0x%x\n",
                            file, elf_kind(elf));
                return;
        }
}

int
main(int argc, char **argv)
{
        int     i;

        if (argc < 2) {
                (void) printf("usage: %s elf_file ...\n", argv[0]);
                return (1);
        }

        /*
         * Initialize the elf library, must be called before elf_begin()
         * can be called.
         */
        if (elf_version(EV_CURRENT) == EV_NONE) {
                (void) fprintf(stderr,
                    "elf_version() failed: %s\n", elf_errmsg(0));
                return (1);
        }

        for (i = 1; i < argc; i++) {
                int     fd;
                Elf     *elf;
                char    *elf_fname;

                elf_fname = argv[i];
                if ((fd = open(elf_fname, O_RDONLY)) == -1) {
                        perror("open");
                        continue;
                }

                /*
                 * Attempt to open an Elf descriptor Read-Only
                 * for each file.
                 */
                if ((elf = elf_begin(fd, ELF_C_READ, 0)) == NULL) {
                        (void) fprintf(stderr, "elf_begin() failed: %s\n",
                            elf_errmsg(0));
                        (void) close(fd);
                        continue;
                }

                /*
                 * Process each elf descriptor.
                 */
                process_elf(elf, elf_fname, fd, 0);
                (void) elf_end(elf);
                (void) close(fd);
        }

        return (0);
}