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/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"@(#)error.c	1.22	08/06/03 SMI"
28
29#include	<pthread.h> /* In lieu of Solaris <thread.h> */
30#define thr_keycreate pthread_key_create /* In lieu of Solaris <thread.h> */
31#define thr_getspecific(key, pval) (*pval = pthread_getspecific( key )) /* In lieu of Solaris <thread.h> */
32#define thr_setspecific pthread_setspecific /* In lieu of Solaris <thread.h> */
33
34/* Solaris thr_main() is used to detect the possibility that libelf is being used in a
35   threaded application and is subject to re-entrancy. There is no Posix cognate.
36   For safety, always indicate threading is live. */
37#define thr_main() 0 /* In lieu of Solaris <thread.h> */
38int	__libc_threaded = 1; /* In lieu of Solaris <thread.h> */
39
40#include	<stdlib.h>
41#include	<string.h>
42#include	<stdio.h>
43#include	<libelf.h>
44#include	"msg.h"
45#undef		_elf_seterr
46
47#define EINF_NULLERROR 0
48#define EBUG_THRDKEY 0
49
50#define MSG_SUNW_OST_SGS -2
51#define MSG_FMT_ERR -1
52#define MSG_INTL(x) "libelf internal error"
53#define MSG_ORIG(x) (x == MSG_FMT_ERR ? "%s %s" : NULL)
54
55char *_dgettext(const char *x, const char *y) { return "libelf internal error"; }
56#define dgettext(x,y) _dgettext(x,y)
57#define NATIVE_BUILD 1
58
59#include	"decl.h"
60
61#define	ELFERRSHIFT	16
62#define	SYSERRMASK	0xffff
63
64
65/*
66 * _elf_err has two values encoded in it, both the _elf_err # and
67 * the system errno value (if relevant).  These values are encoded
68 * in the upper & lower 16 bits of the 4 byte integer.
69 */
70static int		_elf_err = 0;
71
72#if !defined(NATIVE_BUILD)
73
74static thread_key_t	errkey = THR_ONCE_KEY;
75static thread_key_t	bufkey = THR_ONCE_KEY;
76
77#else	/* NATIVE_BUILD */
78
79/*
80 * This code is here to enable the building of a native version
81 * of libelf.so when the build machine has not yet been upgraded
82 * to a version of libc that provides thr_keycreate_once().
83 * It should be deleted when solaris_nevada ships.
84 * The code is not MT-safe in a relaxed memory model.
85 */
86
87static thread_key_t	errkey = 0;
88static thread_key_t	bufkey = 0;
89
90int
91thr_keycreate_once(thread_key_t *keyp, void (*destructor)(void *))
92{
93	static mutex_t key_lock = DEFAULTMUTEX;
94	thread_key_t key;
95	int error;
96
97	if (*keyp == 0) {
98		mutex_lock(&key_lock);
99		if (*keyp == 0) {
100			error = thr_keycreate(&key, destructor);
101			if (error) {
102				mutex_unlock(&key_lock);
103				return (error);
104			}
105			*keyp = key;
106		}
107		mutex_unlock(&key_lock);
108	}
109
110	return (0);
111}
112
113#endif	/* NATIVE_BUILD */
114
115
116const char *
117_libelf_msg(Msg mid)
118{
119	return (dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), MSG_ORIG(mid)));
120}
121
122
123void
124_elf_seterr(Msg lib_err, int sys_err)
125{
126	/*LINTED*/
127	intptr_t encerr = ((int)lib_err << ELFERRSHIFT) |
128	    (sys_err & SYSERRMASK);
129
130#ifndef	__lock_lint
131	if (thr_main()) {
132		_elf_err = (int)encerr;
133		return;
134	}
135#endif
136	(void) thr_keycreate_once(&errkey, 0);
137	(void) thr_setspecific(errkey, (void *)encerr);
138}
139
140int
141_elf_geterr() {
142#ifndef	__lock_lint
143	if (thr_main())
144		return (_elf_err);
145#endif
146	return ((uintptr_t)pthread_getspecific(errkey));
147}
148
149const char *
150elf_errmsg(int err)
151{
152	char			*errno_str;
153	char			*elferr_str;
154	char			*buffer = 0;
155	int			syserr;
156	int			elferr;
157	static char		intbuf[MAXELFERR];
158
159	if (err == 0) {
160		if ((err = _elf_geterr()) == 0)
161			return (0);
162	} else if (err == -1) {
163		if ((err = _elf_geterr()) == 0)
164			/*LINTED*/ /* MSG_INTL(EINF_NULLERROR) */
165			err = (int)EINF_NULLERROR << ELFERRSHIFT;
166	}
167
168	if (thr_main())
169		buffer = intbuf;
170	else {
171		/*
172		 * If this is a threaded APP then we store the
173		 * errmsg buffer in Thread Specific Storage.
174		 *
175		 * Each thread has its own private buffer.
176		 */
177		if (thr_keycreate_once(&bufkey, free) != 0)
178					return (MSG_INTL(EBUG_THRDKEY));
179		buffer = pthread_getspecific(bufkey);
180
181		if (!buffer) {
182			if ((buffer = malloc(MAXELFERR)) == 0)
183				return (MSG_INTL(EMEM_ERRMSG));
184			if (thr_setspecific(bufkey, buffer) != 0) {
185				free(buffer);
186				return (MSG_INTL(EBUG_THRDSET));
187			}
188		}
189	}
190
191	elferr = (int)((uint_t)err >> ELFERRSHIFT);
192	syserr = err & SYSERRMASK;
193	/*LINTED*/
194	elferr_str = (char *)MSG_INTL(elferr);
195	if (syserr && (errno_str = strerror(syserr)))
196		(void) snprintf(buffer, MAXELFERR,
197		    MSG_ORIG(MSG_FMT_ERR), elferr_str, errno_str);
198	else {
199		(void) strncpy(buffer, elferr_str, MAXELFERR - 1);
200		buffer[MAXELFERR - 1] = '\0';
201	}
202
203	return (buffer);
204}
205
206int
207elf_errno()
208{
209	int	rc = _elf_geterr();
210
211	_elf_seterr(0, 0);
212	return (rc);
213}
214