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 [-is] -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         char *altexec;
 150         int verbose = 0;
 151         int ignore_non_c = 0;
 152         int c;
 153 
 154         sighold(SIGINT);
 155         sighold(SIGQUIT);
 156         sighold(SIGTERM);
 157 
 158         progname = basename(argv[0]);
 159 
 160         ctf_altexec("CTFCONVERT_ALTEXEC", argc, argv);
 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         if ((altexec = getenv("CTFCONVERT_ALTEXEC")) != NULL) {
 168                 (void) unsetenv("CTFCONVERT_ALTEXEC");
 169                 (void) execv(altexec, argv);
 170                 (void) fprintf(stderr, "ctfconvert altexec failed to "
 171                     "run %s: %s\n", altexec, strerror(errno));
 172         }
 173 
 174         while ((c = getopt(argc, argv, ":l:L:o:givs")) != EOF) {
 175                 switch (c) {
 176                 case 'l':
 177                         label = optarg;
 178                         break;
 179                 case 'L':
 180                         if ((label = getenv(optarg)) == NULL)
 181                                 label = CTF_DEFAULT_LABEL;
 182                         break;
 183                 case 'o':
 184                         outfile = optarg;
 185                         break;
 186                 case 's':
 187                         dynsym = CTF_USE_DYNSYM;
 188                         break;
 189                 case 'i':
 190                         ignore_non_c = 1;
 191                         break;
 192                 case 'v':
 193                         verbose = 1;
 194                         break;
 195                 default:
 196                         usage();
 197                         exit(2);
 198                 }
 199         }
 200 
 201         if (argc - optind != 1 || label == NULL) {
 202                 usage();
 203                 exit(2);
 204         }
 205 
 206         infile = argv[optind];
 207         if (access(infile, R_OK) != 0)
 208                 terminate("Can't access %s", infile);
 209 
 210         /*
 211          * Upon receipt of a signal, we want to clean up and exit.  Our
 212          * primary goal during cleanup is to restore the system to a state
 213          * such that a subsequent make will eventually cause this command to
 214          * be re-run.  If we remove the input file (which we do if we get a
 215          * signal and the user didn't specify a separate output file), make
 216          * will need to rebuild the input file, and will then need to re-run
 217          * ctfconvert, which is what we want.
 218          */
 219         set_terminate_cleanup(terminate_cleanup);
 220 
 221         sigset(SIGINT, handle_sig);
 222         sigset(SIGQUIT, handle_sig);
 223         sigset(SIGTERM, handle_sig);
 224 
 225         filetd = tdata_new();
 226 
 227         if (!file_read(filetd, infile, ignore_non_c))
 228                 terminate("%s doesn't have type data to convert\n", infile);
 229 
 230         if (verbose)
 231                 iidesc_stats(filetd->td_iihash);
 232 
 233         mstrtd = tdata_new();
 234         merge_into_master(filetd, mstrtd, NULL, 1);
 235 
 236         tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX);
 237 
 238         /*
 239          * If the user supplied an output file that is different from the
 240          * input file, write directly to the output file.  Otherwise, write
 241          * to a temporary file, and replace the input file when we're done.
 242          */
 243         if (outfile && strcmp(infile, outfile) != 0) {
 244                 write_ctf(mstrtd, infile, outfile, dynsym);
 245         } else {
 246                 char *tmpname = mktmpname(infile, ".ctf");
 247 
 248                 write_ctf(mstrtd, infile, tmpname, dynsym);
 249                 if (rename(tmpname, infile) != 0)
 250                         terminate("Couldn't rename temp file %s", tmpname);
 251                 free(tmpname);
 252         }
 253 
 254         return (0);
 255 }