/*
* 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) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <strings.h>
#include <dlfcn.h>
#include <debug.h>
#include <conv.h>
#include "msg.h"
#include "_libld.h"
/*
* dbg_setup() can be called a number of times. The typical use through
* LD_OPTIONS, results in dbg_setup() being called as the first argument to
* ld(1). It's also possible to pass debugging tokens through the compiler,
* for example -Wl,-Dlibs -Wl-Ddetail, in which case multiple dbg_setup()
* calls are made.
*
* A distinction is also made between diagnostics being requested before any
* other ld(1) options are read, or whether the debugging options occur
* between other options on the command line. In the latter case, the
* debugging options can be used to isolate diagnostics around one or more
* input files. The "phase" argument allows us to select which phase of
* dbg_setup() processing we should isolate ourselves to.
*
* dbg_print() can require the output filename for use in the diagnostics
* created. Save the address of the output filename pointer for this use.
*/
static const char **Name = NULL;
static int Phase = 0;
/* Debug file output state */
static struct {
FILE *fptr; /* File to send debug output */
int close_needed; /* True if explicitly opened stream */
} dbg_ofile = {
stderr,
0
};
/*
* If there is an explicitly opened debug file, close it and reset the state.
*/
void
dbg_cleanup(void)
{
if (dbg_ofile.close_needed) {
(void) fclose(dbg_ofile.fptr);
dbg_ofile.close_needed = 0;
dbg_ofile.fptr = stderr;
}
}
/*
* Process debug tokens. Returns True (1) on success, and False (0)
* on failure.
*/
int
dbg_setup(Ofl_desc *ofl, const char *options, int phase)
{
const char *ofile;
if (Phase == 0)
Phase = phase;
else if (Phase != phase)
return (1);
Name = &ofl->ofl_name;
/*
* Call the debugging setup routine to initialize the mask and
* debug function array.
*/
if (Dbg_setup(DBG_CALLER_LD, options, dbg_desc, &ofile) == 0)
return (0);
/*
* If output= token was used, close the old file if necessary
* and open a new one if the file name is not NULL.
*/
if (ofile) {
dbg_cleanup();
if (*ofile != '\0') {
FILE *fptr = fopen(ofile, MSG_ORIG(MSG_DBG_FOPEN_MODE));
if (fptr == NULL) {
int err = errno;
ld_eprintf(ofl, ERR_FATAL,
MSG_INTL(MSG_SYS_OPEN), ofile,
strerror(err));
return (0);
} else {
dbg_ofile.fptr = fptr;
dbg_ofile.close_needed = 1;
}
}
}
/*
* Now that the output file is established, identify the linker
* package, and generate help output if the user specified the
* debug help token.
*/
Dbg_version();
if (dbg_desc->d_extra & DBG_E_HELP)
Dbg_help();
return (1);
}
/* PRINTFLIKE2 */
void
dbg_print(Lm_list *lml, const char *format, ...)
{
static char *prestr = NULL;
va_list args;
#if defined(lint)
/*
* The lml argument is only meaningful for diagnostics sent to ld.so.1.
* Supress the lint error by making a dummy assignment.
*/
lml = NULL;
#endif
/*
* Knock off any newline indicator to signify that a diagnostic has
* been processed.
*/
dbg_desc->d_extra &= ~DBG_E_STDNL;
if (DBG_ISSNAME()) {
/*
* If the debugging options have requested each diagnostic line
* be prepended by a name create a prefix string.
*/
if ((prestr == NULL) && *Name) {
const char *name, *cls;
size_t len;
/*
* Select the fullname or basename of the output file
* being created.
*/
if (DBG_ISFNAME())
name = *Name;
else {
if ((name =
strrchr(*Name, '/')) == NULL)
name = *Name;
else
name++;
}
len = strlen(name) +
strlen(MSG_INTL(MSG_DBG_NAME_FMT)) + 1;
/*
* Add the output file class if required.
*/
if (DBG_ISCLASS()) {
#if defined(_ELF64)
len += MSG_DBG_CLS64_FMT_SIZE;
cls = MSG_ORIG(MSG_DBG_CLS64_FMT);
#else
len += MSG_DBG_CLS32_FMT_SIZE;
cls = MSG_ORIG(MSG_DBG_CLS32_FMT);
#endif
}
/*
* Allocate a string to build the prefix.
*/
if ((prestr = libld_malloc(len)) == NULL)
prestr = (char *)MSG_INTL(MSG_DBG_DFLT_FMT);
else {
(void) snprintf(prestr, len,
MSG_INTL(MSG_DBG_NAME_FMT), name);
if (DBG_ISCLASS())
(void) strcat(prestr, cls);
}
}
(void) fputs(prestr ? prestr : MSG_INTL(MSG_DBG_AOUT_FMT),
dbg_ofile.fptr);
} else
(void) fputs(MSG_INTL(MSG_DBG_DFLT_FMT), dbg_ofile.fptr);
if (DBG_ISTIME()) {
Conv_time_buf_t buf;
struct timeval new;
if (gettimeofday(&new, NULL) == 0) {
if (DBG_ISTTIME())
(void) fputs(conv_time(&DBG_TOTALTIME, &new,
&buf), stderr);
if (DBG_ISDTIME())
(void) fputs(conv_time(&DBG_DELTATIME, &new,
&buf), stderr);
DBG_DELTATIME = new;
}
}
va_start(args, format);
(void) vfprintf(dbg_ofile.fptr, format, args);
(void) fprintf(dbg_ofile.fptr, MSG_ORIG(MSG_STR_NL));
va_end(args);
}