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 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 /*
  29  * Given a file containing sections with stabs data, convert the stabs data to
  30  * CTF data, and replace the stabs sections with a CTF section.
  31  */
  32 
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <unistd.h>
  36 #include <signal.h>
  37 #include <string.h>
  38 #include <fcntl.h>
  39 #include <libgen.h>
  40 #include <errno.h>
  41 #include <assert.h>
  42 
  43 #include "ctftools.h"
  44 #include "memory.h"
  45 
  46 const  char *progname;
  47 int debug_level = DEBUG_LEVEL;
  48 
  49 static const char *infile = NULL;
  50 static const char *outfile = NULL;
  51 static int dynsym;
  52 
  53 static void
  54 usage(void)
  55 {
  56         (void) fprintf(stderr,
  57             "Usage: %s [-gis] -l label | -L labelenv [-o outfile] object_file\n"
  58             "\n"
  59             "  Note: if -L labelenv is specified and labelenv is not set in\n"
  60             "  the environment, a default value is used.\n",
  61             progname);
  62 }
  63 
  64 static void
  65 terminate_cleanup(void)
  66 {
  67         if (!outfile) {
  68                 fprintf(stderr, "Removing %s\n", infile);
  69                 unlink(infile);
  70         }
  71 }
  72 
  73 static void
  74 handle_sig(int sig)
  75 {
  76         terminate("Caught signal %d - exiting\n", sig);
  77 }
  78 
  79 static int
  80 file_read(tdata_t *td, const char *filename, int ignore_non_c)
  81 {
  82         typedef int (*reader_f)(tdata_t *, Elf *, const char *);
  83         static const reader_f readers[] = {
  84                 stabs_read,
  85                 dw_read,
  86                 NULL
  87         };
  88 
  89         source_types_t source_types;
  90         Elf *elf;
  91         int i, rc, fd;
  92 
  93         if ((fd = open(filename, O_RDONLY)) < 0)
  94                 terminate("failed to open %s", filename);
  95 
  96         (void) elf_version(EV_CURRENT);
  97 
  98         if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
  99                 close(fd);
 100                 terminate("failed to read %s: %s\n", filename,
 101                     elf_errmsg(-1));
 102         }
 103 
 104         source_types = built_source_types(elf, filename);
 105 
 106         if ((source_types == SOURCE_NONE || (source_types & SOURCE_UNKNOWN)) &&
 107             ignore_non_c) {
 108                 debug(1, "Ignoring file %s from unknown sources\n", filename);
 109                 exit(0);
 110         }
 111 
 112         for (i = 0; readers[i] != NULL; i++) {
 113                 if ((rc = readers[i](td, elf, filename)) == 0)
 114                         break;
 115 
 116                 assert(rc < 0 && errno == ENOENT);
 117         }
 118 
 119         if (readers[i] == NULL) {
 120                 /*
 121                  * None of the readers found compatible type data.
 122                  */
 123 
 124                 if (findelfsecidx(elf, filename, ".debug") >= 0) {
 125                         terminate("%s: DWARF version 1 is not supported\n",
 126                             filename);
 127                 }
 128 
 129                 if (!(source_types & SOURCE_C) && ignore_non_c) {
 130                         debug(1, "Ignoring file %s not built from C sources\n",
 131                             filename);
 132                         exit(0);
 133                 }
 134 
 135                 rc = 0;
 136         } else {
 137                 rc = 1;
 138         }
 139 
 140         (void) elf_end(elf);
 141         (void) close(fd);
 142 
 143         return (rc);
 144 }
 145 
 146 int
 147 main(int argc, char **argv)
 148 {
 149         tdata_t *filetd, *mstrtd;
 150         char *label = NULL;
 151         int verbose = 0;
 152         int ignore_non_c = 0;
 153         int keep_stabs = 0;
 154         int c;
 155 
 156         sighold(SIGINT);
 157         sighold(SIGQUIT);
 158         sighold(SIGTERM);
 159 
 160         progname = basename(argv[0]);
 161 
 162         if (getenv("CTFCONVERT_DEBUG_LEVEL"))
 163                 debug_level = atoi(getenv("CTFCONVERT_DEBUG_LEVEL"));
 164         if (getenv("CTFCONVERT_DEBUG_PARSE"))
 165                 debug_parse = atoi(getenv("CTFCONVERT_DEBUG_PARSE"));
 166 
 167         while ((c = getopt(argc, argv, ":l:L:o:givs")) != EOF) {
 168                 switch (c) {
 169                 case 'l':
 170                         label = optarg;
 171                         break;
 172                 case 'L':
 173                         if ((label = getenv(optarg)) == NULL)
 174                                 label = CTF_DEFAULT_LABEL;
 175                         break;
 176                 case 'o':
 177                         outfile = optarg;
 178                         break;
 179                 case 's':
 180                         dynsym = CTF_USE_DYNSYM;
 181                         break;
 182                 case 'i':
 183                         ignore_non_c = 1;
 184                         break;
 185                 case 'g':
 186                         keep_stabs = CTF_KEEP_STABS;
 187                         break;
 188                 case 'v':
 189                         verbose = 1;
 190                         break;
 191                 default:
 192                         usage();
 193                         exit(2);
 194                 }
 195         }
 196 
 197         if (getenv("STRIPSTABS_KEEP_STABS") != NULL)
 198                 keep_stabs = CTF_KEEP_STABS;
 199 
 200         if (argc - optind != 1 || label == NULL) {
 201                 usage();
 202                 exit(2);
 203         }
 204 
 205         infile = argv[optind];
 206         if (access(infile, R_OK) != 0)
 207                 terminate("Can't access %s", infile);
 208 
 209         /*
 210          * Upon receipt of a signal, we want to clean up and exit.  Our
 211          * primary goal during cleanup is to restore the system to a state
 212          * such that a subsequent make will eventually cause this command to
 213          * be re-run.  If we remove the input file (which we do if we get a
 214          * signal and the user didn't specify a separate output file), make
 215          * will need to rebuild the input file, and will then need to re-run
 216          * ctfconvert, which is what we want.
 217          */
 218         set_terminate_cleanup(terminate_cleanup);
 219 
 220         sigset(SIGINT, handle_sig);
 221         sigset(SIGQUIT, handle_sig);
 222         sigset(SIGTERM, handle_sig);
 223 
 224         filetd = tdata_new();
 225 
 226         if (!file_read(filetd, infile, ignore_non_c))
 227                 terminate("%s doesn't have type data to convert\n", infile);
 228 
 229         if (verbose)
 230                 iidesc_stats(filetd->td_iihash);
 231 
 232         mstrtd = tdata_new();
 233         merge_into_master(filetd, mstrtd, NULL, 1);
 234 
 235         tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX);
 236 
 237         /*
 238          * If the user supplied an output file that is different from the
 239          * input file, write directly to the output file.  Otherwise, write
 240          * to a temporary file, and replace the input file when we're done.
 241          */
 242         if (outfile && strcmp(infile, outfile) != 0) {
 243                 write_ctf(mstrtd, infile, outfile, dynsym | keep_stabs);
 244         } else {
 245                 char *tmpname = mktmpname(infile, ".ctf");
 246 
 247                 write_ctf(mstrtd, infile, tmpname, dynsym | keep_stabs);
 248                 if (rename(tmpname, infile) != 0)
 249                         terminate("Couldn't rename temp file %s", tmpname);
 250                 free(tmpname);
 251         }
 252 
 253         return (0);
 254 }