1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * Given a file containing sections with stabs data, convert the stabs data to
  28  * CTF data, and replace the stabs sections with a CTF section.
  29  */
  30 
  31 #include <stdio.h>
  32 #include <stdlib.h>
  33 #include <unistd.h>
  34 #include <signal.h>
  35 #include <string.h>
  36 #include <fcntl.h>
  37 #include <libgen.h>
  38 #include <errno.h>
  39 #include <assert.h>
  40 
  41 #include "ctftools.h"
  42 #include "memory.h"
  43 
  44 const  char *progname;
  45 int debug_level = DEBUG_LEVEL;
  46 
  47 static const char *infile = NULL;
  48 static const char *outfile = NULL;
  49 static int dynsym;
  50 
  51 static void
  52 usage(void)
  53 {
  54         (void) fprintf(stderr,
  55             "Usage: %s [-gis] -l label | -L labelenv [-o outfile] object_file\n"
  56             "\n"
  57             "  Note: if -L labelenv is specified and labelenv is not set in\n"
  58             "  the environment, a default value is used.\n",
  59             progname);
  60 }
  61 
  62 static void
  63 terminate_cleanup(void)
  64 {
  65         if (!outfile) {
  66                 fprintf(stderr, "Removing %s\n", infile);
  67                 unlink(infile);
  68         }
  69 }
  70 
  71 static void
  72 handle_sig(int sig)
  73 {
  74         terminate("Caught signal %d - exiting\n", sig);
  75 }
  76 
  77 static int
  78 file_read(tdata_t *td, const char *filename, int ignore_non_c)
  79 {
  80         typedef int (*reader_f)(tdata_t *, Elf *, const char *);
  81         static const reader_f readers[] = {
  82                 stabs_read,
  83                 dw_read,
  84                 NULL
  85         };
  86 
  87         source_types_t source_types;
  88         Elf *elf;
  89         int i, rc, fd;
  90 
  91         if ((fd = open(filename, O_RDONLY)) < 0)
  92                 terminate("failed to open %s", filename);
  93 
  94         (void) elf_version(EV_CURRENT);
  95 
  96         if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
  97                 close(fd);
  98                 terminate("failed to read %s: %s\n", filename,
  99                     elf_errmsg(-1));
 100         }
 101 
 102         source_types = built_source_types(elf, filename);
 103 
 104         if ((source_types == SOURCE_NONE || (source_types & SOURCE_UNKNOWN)) &&
 105             ignore_non_c) {
 106                 debug(1, "Ignoring file %s from unknown sources\n", filename);
 107                 exit(0);
 108         }
 109 
 110         for (i = 0; readers[i] != NULL; i++) {
 111                 if ((rc = readers[i](td, elf, filename)) == 0)
 112                         break;
 113 
 114                 assert(rc < 0 && errno == ENOENT);
 115         }
 116 
 117         if (readers[i] == NULL) {
 118                 /*
 119                  * None of the readers found compatible type data.
 120                  */
 121 
 122                 if (findelfsecidx(elf, filename, ".debug") >= 0) {
 123                         terminate("%s: DWARF version 1 is not supported\n",
 124                             filename);
 125                 }
 126 
 127                 if (!(source_types & SOURCE_C) && ignore_non_c) {
 128                         debug(1, "Ignoring file %s not built from C sources\n",
 129                             filename);
 130                         exit(0);
 131                 }
 132 
 133                 rc = 0;
 134         } else {
 135                 rc = 1;
 136         }
 137 
 138         (void) elf_end(elf);
 139         (void) close(fd);
 140 
 141         return (rc);
 142 }
 143 
 144 int
 145 main(int argc, char **argv)
 146 {
 147         tdata_t *filetd, *mstrtd;
 148         char *label = NULL;
 149         int verbose = 0;
 150         int ignore_non_c = 0;
 151         int c;
 152 
 153         sighold(SIGINT);
 154         sighold(SIGQUIT);
 155         sighold(SIGTERM);
 156 
 157         progname = basename(argv[0]);
 158 
 159         if (getenv("CTFCONVERT_DEBUG_LEVEL"))
 160                 debug_level = atoi(getenv("CTFCONVERT_DEBUG_LEVEL"));
 161         if (getenv("CTFCONVERT_DEBUG_PARSE"))
 162                 debug_parse = atoi(getenv("CTFCONVERT_DEBUG_PARSE"));
 163 
 164         while ((c = getopt(argc, argv, ":l:L:o:ivs")) != EOF) {
 165                 switch (c) {
 166                 case 'l':
 167                         label = optarg;
 168                         break;
 169                 case 'L':
 170                         if ((label = getenv(optarg)) == NULL)
 171                                 label = CTF_DEFAULT_LABEL;
 172                         break;
 173                 case 'o':
 174                         outfile = optarg;
 175                         break;
 176                 case 's':
 177                         dynsym = CTF_USE_DYNSYM;
 178                         break;
 179                 case 'i':
 180                         ignore_non_c = 1;
 181                         break;
 182                 case 'v':
 183                         verbose = 1;
 184                         break;
 185                 default:
 186                         usage();
 187                         exit(2);
 188                 }
 189         }
 190 
 191         if (argc - optind != 1 || label == NULL) {
 192                 usage();
 193                 exit(2);
 194         }
 195 
 196         infile = argv[optind];
 197         if (access(infile, R_OK) != 0)
 198                 terminate("Can't access %s", infile);
 199 
 200         /*
 201          * Upon receipt of a signal, we want to clean up and exit.  Our
 202          * primary goal during cleanup is to restore the system to a state
 203          * such that a subsequent make will eventually cause this command to
 204          * be re-run.  If we remove the input file (which we do if we get a
 205          * signal and the user didn't specify a separate output file), make
 206          * will need to rebuild the input file, and will then need to re-run
 207          * ctfconvert, which is what we want.
 208          */
 209         set_terminate_cleanup(terminate_cleanup);
 210 
 211         sigset(SIGINT, handle_sig);
 212         sigset(SIGQUIT, handle_sig);
 213         sigset(SIGTERM, handle_sig);
 214 
 215         filetd = tdata_new();
 216 
 217         if (!file_read(filetd, infile, ignore_non_c))
 218                 terminate("%s doesn't have type data to convert\n", infile);
 219 
 220         if (verbose)
 221                 iidesc_stats(filetd->td_iihash);
 222 
 223         mstrtd = tdata_new();
 224         merge_into_master(filetd, mstrtd, NULL, 1);
 225 
 226         tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX);
 227 
 228         /*
 229          * If the user supplied an output file that is different from the
 230          * input file, write directly to the output file.  Otherwise, write
 231          * to a temporary file, and replace the input file when we're done.
 232          */
 233         if (outfile && strcmp(infile, outfile) != 0) {
 234                 write_ctf(mstrtd, infile, outfile, dynsym);
 235         } else {
 236                 char *tmpname = mktmpname(infile, ".ctf");
 237 
 238                 write_ctf(mstrtd, infile, tmpname, dynsym);
 239                 if (rename(tmpname, infile) != 0)
 240                         terminate("Couldn't rename temp file %s", tmpname);
 241                 free(tmpname);
 242         }
 243 
 244         return (0);
 245 }