xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/locale.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*7c478bd9Sstevel@tonic-gate 
28*7c478bd9Sstevel@tonic-gate /*
29*7c478bd9Sstevel@tonic-gate  * Messaging support.  To minimize ld.so.1's overhead, messaging support isn't
30*7c478bd9Sstevel@tonic-gate  * enabled until we need to contruct a message - Note that we don't rely on the
31*7c478bd9Sstevel@tonic-gate  * application to signify whether messaging is applicable, as many message
32*7c478bd9Sstevel@tonic-gate  * conditions (such as relocations) are generated before the application gains
33*7c478bd9Sstevel@tonic-gate  * control.
34*7c478bd9Sstevel@tonic-gate  *
35*7c478bd9Sstevel@tonic-gate  * This code implements a very trimmed down version of the capabilities found
36*7c478bd9Sstevel@tonic-gate  * via setlocale(3c), textdomain(3i) and gettext(3i).  Dragging in the original
37*7c478bd9Sstevel@tonic-gate  * routines from libc/libintl isn't possible as they cause all i18n support to
38*7c478bd9Sstevel@tonic-gate  * be included which is far too expensive for ld.so.1.
39*7c478bd9Sstevel@tonic-gate  */
40*7c478bd9Sstevel@tonic-gate #include	"_synonyms.h"
41*7c478bd9Sstevel@tonic-gate 
42*7c478bd9Sstevel@tonic-gate #include	<sys/types.h>
43*7c478bd9Sstevel@tonic-gate #include	<sys/mman.h>
44*7c478bd9Sstevel@tonic-gate #include	<sys/stat.h>
45*7c478bd9Sstevel@tonic-gate #include	<string.h>
46*7c478bd9Sstevel@tonic-gate #include	<stdio.h>
47*7c478bd9Sstevel@tonic-gate #include	<stdlib.h>
48*7c478bd9Sstevel@tonic-gate #include	<unistd.h>
49*7c478bd9Sstevel@tonic-gate #include	<fcntl.h>
50*7c478bd9Sstevel@tonic-gate #include	<limits.h>
51*7c478bd9Sstevel@tonic-gate #include	"_rtld.h"
52*7c478bd9Sstevel@tonic-gate #include	"msg.h"
53*7c478bd9Sstevel@tonic-gate 
54*7c478bd9Sstevel@tonic-gate /*
55*7c478bd9Sstevel@tonic-gate  * A message object file (as generated by msgfmt(1)) consists of a message
56*7c478bd9Sstevel@tonic-gate  * header, followed by a message list, followed by the msgid strings and then
57*7c478bd9Sstevel@tonic-gate  * the msgstr strings.  None of this is defined in any OSNET available headers
58*7c478bd9Sstevel@tonic-gate  * so we have our own local definitions :-(
59*7c478bd9Sstevel@tonic-gate  */
60*7c478bd9Sstevel@tonic-gate typedef struct {
61*7c478bd9Sstevel@tonic-gate 	int	hdr_midlst;		/* middle message no. */
62*7c478bd9Sstevel@tonic-gate 	int	hdr_lstcnt;		/* total no. of message in the file */
63*7c478bd9Sstevel@tonic-gate 	int	hdr_msgidsz;		/* size of msgids (in bytes) */
64*7c478bd9Sstevel@tonic-gate 	int	hdr_msgstrsz;		/* size of msgstrs (in bytes) */
65*7c478bd9Sstevel@tonic-gate 	int	hdr_lstsz;		/* size of message list (in bytes) */
66*7c478bd9Sstevel@tonic-gate } Msghdr;
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate typedef struct {
69*7c478bd9Sstevel@tonic-gate 	int	lst_less;
70*7c478bd9Sstevel@tonic-gate 	int	lst_more;
71*7c478bd9Sstevel@tonic-gate 	int	lst_idoff;
72*7c478bd9Sstevel@tonic-gate 	int	lst_stroff;
73*7c478bd9Sstevel@tonic-gate } Msglst;
74*7c478bd9Sstevel@tonic-gate 
75*7c478bd9Sstevel@tonic-gate #define	LEAFINDICATOR		-99
76*7c478bd9Sstevel@tonic-gate #define	OLD_MSG_STRUCT_SIZE	20
77*7c478bd9Sstevel@tonic-gate #define	NEW_MSG_STRUCT_SIZE	(sizeof (Msglst))
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate /*
80*7c478bd9Sstevel@tonic-gate  * Define a local structure for maintaining the domains we care about.
81*7c478bd9Sstevel@tonic-gate  */
82*7c478bd9Sstevel@tonic-gate typedef struct {
83*7c478bd9Sstevel@tonic-gate 	const char	*dom_name;
84*7c478bd9Sstevel@tonic-gate 	const Msghdr	*dom_msghdr;
85*7c478bd9Sstevel@tonic-gate 	size_t		dom_msgsz;
86*7c478bd9Sstevel@tonic-gate } Domain;
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate 
89*7c478bd9Sstevel@tonic-gate /*
90*7c478bd9Sstevel@tonic-gate  * Perform a binary search of a message file (described by the Msghdr) for a
91*7c478bd9Sstevel@tonic-gate  * msgid (string).  Given a match return the associated msgstr, otherwise
92*7c478bd9Sstevel@tonic-gate  * return the original msgid.
93*7c478bd9Sstevel@tonic-gate  */
94*7c478bd9Sstevel@tonic-gate static const char *
95*7c478bd9Sstevel@tonic-gate msgid_to_msgstr(const Msghdr *msghdr, const char *msgid)
96*7c478bd9Sstevel@tonic-gate {
97*7c478bd9Sstevel@tonic-gate 	const Msglst	*list, *_list;
98*7c478bd9Sstevel@tonic-gate 	const char	*ids, *strs, *_msgid;
99*7c478bd9Sstevel@tonic-gate 	int		off, var;
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate 	/*
102*7c478bd9Sstevel@tonic-gate 	 * Establish pointers to the message list (we actually start the search
103*7c478bd9Sstevel@tonic-gate 	 * in the middle of this list (hdr->midlst), the msgid strings (ids)
104*7c478bd9Sstevel@tonic-gate 	 * and the msgstr strings (strs).
105*7c478bd9Sstevel@tonic-gate 	 */
106*7c478bd9Sstevel@tonic-gate 	list = (const Msglst *)&msghdr[1];
107*7c478bd9Sstevel@tonic-gate 	ids = (const char *)&list[msghdr->hdr_lstcnt];
108*7c478bd9Sstevel@tonic-gate 	strs = (const char *)&ids[msghdr->hdr_msgidsz];
109*7c478bd9Sstevel@tonic-gate 
110*7c478bd9Sstevel@tonic-gate 	off = msghdr->hdr_midlst;
111*7c478bd9Sstevel@tonic-gate 
112*7c478bd9Sstevel@tonic-gate 	do {
113*7c478bd9Sstevel@tonic-gate 		_list = list + off;
114*7c478bd9Sstevel@tonic-gate 		_msgid = ids + _list->lst_idoff;
115*7c478bd9Sstevel@tonic-gate 
116*7c478bd9Sstevel@tonic-gate 		if ((var = strcmp(_msgid, msgid)) == 0)
117*7c478bd9Sstevel@tonic-gate 			return (strs + _list->lst_stroff);
118*7c478bd9Sstevel@tonic-gate 
119*7c478bd9Sstevel@tonic-gate 		if (var < 0) {
120*7c478bd9Sstevel@tonic-gate 			if ((off = _list->lst_less) == LEAFINDICATOR)
121*7c478bd9Sstevel@tonic-gate 				return (msgid);
122*7c478bd9Sstevel@tonic-gate 		} else {
123*7c478bd9Sstevel@tonic-gate 			if ((off = _list->lst_more) == LEAFINDICATOR)
124*7c478bd9Sstevel@tonic-gate 				return (msgid);
125*7c478bd9Sstevel@tonic-gate 		}
126*7c478bd9Sstevel@tonic-gate 	} while (off);
127*7c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
128*7c478bd9Sstevel@tonic-gate 	return (NULL);	/* keep gcc happy */
129*7c478bd9Sstevel@tonic-gate }
130*7c478bd9Sstevel@tonic-gate 
131*7c478bd9Sstevel@tonic-gate /*
132*7c478bd9Sstevel@tonic-gate  * Open a message file. Following the model of setlocale(3c) we obtain the
133*7c478bd9Sstevel@tonic-gate  * message file for the specified locale.  Normally this is:
134*7c478bd9Sstevel@tonic-gate  *
135*7c478bd9Sstevel@tonic-gate  *	/usr/lib/locale/`locale'/LC_MESSAGES/`domain'.mo
136*7c478bd9Sstevel@tonic-gate  *
137*7c478bd9Sstevel@tonic-gate  * The locale was determined during initial environment processing (see
138*7c478bd9Sstevel@tonic-gate  * readenv()), which was determined from an LC_ALL, LC_MESSAGES or LANG
139*7c478bd9Sstevel@tonic-gate  * setting.  If no locale has been specified, or any file processing errors
140*7c478bd9Sstevel@tonic-gate  * occur, internationalization is basically disabled.
141*7c478bd9Sstevel@tonic-gate  */
142*7c478bd9Sstevel@tonic-gate static void
143*7c478bd9Sstevel@tonic-gate open_mofile(Domain * dom)
144*7c478bd9Sstevel@tonic-gate {
145*7c478bd9Sstevel@tonic-gate 	const char	*domain = dom->dom_name;
146*7c478bd9Sstevel@tonic-gate 	char		path[PATH_MAX];
147*7c478bd9Sstevel@tonic-gate 	int		fd;
148*7c478bd9Sstevel@tonic-gate 	struct stat	status;
149*7c478bd9Sstevel@tonic-gate 	const Msghdr	*msghdr;
150*7c478bd9Sstevel@tonic-gate 	int		count;
151*7c478bd9Sstevel@tonic-gate 	size_t		size_tot, size_old, size_new;
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate 	dom->dom_msghdr = (Msghdr *)-1;
154*7c478bd9Sstevel@tonic-gate 
155*7c478bd9Sstevel@tonic-gate 	(void) snprintf(path, PATH_MAX, MSG_ORIG(MSG_FMT_MSGFILE),
156*7c478bd9Sstevel@tonic-gate 		locale, domain);
157*7c478bd9Sstevel@tonic-gate 
158*7c478bd9Sstevel@tonic-gate 	if ((fd = open(path, O_RDONLY, 0)) == -1)
159*7c478bd9Sstevel@tonic-gate 		return;
160*7c478bd9Sstevel@tonic-gate 
161*7c478bd9Sstevel@tonic-gate 	if ((fstat(fd, &status) == -1) ||
162*7c478bd9Sstevel@tonic-gate 	    (status.st_size < sizeof (Msghdr))) {
163*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
164*7c478bd9Sstevel@tonic-gate 		return;
165*7c478bd9Sstevel@tonic-gate 	}
166*7c478bd9Sstevel@tonic-gate 
167*7c478bd9Sstevel@tonic-gate 	/* LINTED */
168*7c478bd9Sstevel@tonic-gate 	if ((msghdr = (Msghdr *)mmap(0, status.st_size, PROT_READ, MAP_SHARED,
169*7c478bd9Sstevel@tonic-gate 	    fd, 0)) == (Msghdr *)-1) {
170*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
171*7c478bd9Sstevel@tonic-gate 		return;
172*7c478bd9Sstevel@tonic-gate 	}
173*7c478bd9Sstevel@tonic-gate 	(void) close(fd);
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 	/* checks if opened file is msg file */
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate 	count = msghdr->hdr_lstcnt;
178*7c478bd9Sstevel@tonic-gate 	if (((count - 1) / 2) != msghdr->hdr_midlst) {
179*7c478bd9Sstevel@tonic-gate 		(void) munmap((caddr_t)msghdr, status.st_size);
180*7c478bd9Sstevel@tonic-gate 		return;
181*7c478bd9Sstevel@tonic-gate 	}
182*7c478bd9Sstevel@tonic-gate 
183*7c478bd9Sstevel@tonic-gate 	size_tot = msghdr->hdr_lstsz;
184*7c478bd9Sstevel@tonic-gate 	size_old = OLD_MSG_STRUCT_SIZE * count;
185*7c478bd9Sstevel@tonic-gate 	size_new = (int)NEW_MSG_STRUCT_SIZE * count;
186*7c478bd9Sstevel@tonic-gate 	if ((size_tot != size_old) && (size_tot != size_new)) {
187*7c478bd9Sstevel@tonic-gate 		(void) munmap((caddr_t)msghdr, status.st_size);
188*7c478bd9Sstevel@tonic-gate 		return;
189*7c478bd9Sstevel@tonic-gate 	}
190*7c478bd9Sstevel@tonic-gate 
191*7c478bd9Sstevel@tonic-gate 	size_tot = msghdr->hdr_msgidsz + msghdr->hdr_msgstrsz +
192*7c478bd9Sstevel@tonic-gate 	    (int)sizeof (Msghdr);
193*7c478bd9Sstevel@tonic-gate 	if ((size_tot + size_old < status.st_size) &&
194*7c478bd9Sstevel@tonic-gate 	    (size_tot + size_new < status.st_size)) {
195*7c478bd9Sstevel@tonic-gate 		(void) munmap((caddr_t)msghdr, status.st_size);
196*7c478bd9Sstevel@tonic-gate 		return;
197*7c478bd9Sstevel@tonic-gate 	}
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate 	/*
200*7c478bd9Sstevel@tonic-gate 	 * We have a good message file, initialize the Domain information.
201*7c478bd9Sstevel@tonic-gate 	 */
202*7c478bd9Sstevel@tonic-gate 	dom->dom_msghdr = msghdr;
203*7c478bd9Sstevel@tonic-gate 	dom->dom_msgsz = status.st_size;
204*7c478bd9Sstevel@tonic-gate }
205*7c478bd9Sstevel@tonic-gate 
206*7c478bd9Sstevel@tonic-gate 
207*7c478bd9Sstevel@tonic-gate /*
208*7c478bd9Sstevel@tonic-gate  * Two interfaces are established to support our internationalization.
209*7c478bd9Sstevel@tonic-gate  * gettext(3i) calls originate from all link-editor libraries, and thus the
210*7c478bd9Sstevel@tonic-gate  * SUNW_OST_SGS domain is assumed.  _dgettext() calls originate from
211*7c478bd9Sstevel@tonic-gate  * dependencies such as libelf and libc.
212*7c478bd9Sstevel@tonic-gate  *
213*7c478bd9Sstevel@tonic-gate  * Presently we support two domains (libc's strerror() uses SUNW_OST_OSLIB).
214*7c478bd9Sstevel@tonic-gate  * If ld.so.1's dependencies evolve to require more then the `domain' array
215*7c478bd9Sstevel@tonic-gate  * maintained below can be enlarged or made more dynamic in nature.
216*7c478bd9Sstevel@tonic-gate  */
217*7c478bd9Sstevel@tonic-gate const char *
218*7c478bd9Sstevel@tonic-gate _dgettext(const char *domain, const char *msgid)
219*7c478bd9Sstevel@tonic-gate {
220*7c478bd9Sstevel@tonic-gate 	static int	domaincnt = 0;
221*7c478bd9Sstevel@tonic-gate 	static Domain	*domains;
222*7c478bd9Sstevel@tonic-gate 	Domain		*_domain;
223*7c478bd9Sstevel@tonic-gate 	int		cnt;
224*7c478bd9Sstevel@tonic-gate 
225*7c478bd9Sstevel@tonic-gate 	if (locale == 0)
226*7c478bd9Sstevel@tonic-gate 		return (msgid);
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate 	/*
229*7c478bd9Sstevel@tonic-gate 	 * Determine if we've initialized any domains yet.
230*7c478bd9Sstevel@tonic-gate 	 */
231*7c478bd9Sstevel@tonic-gate 	if (domaincnt == 0) {
232*7c478bd9Sstevel@tonic-gate 		if ((domains = (Domain *)calloc(sizeof (Domain), 2)) == 0)
233*7c478bd9Sstevel@tonic-gate 			return (msgid);
234*7c478bd9Sstevel@tonic-gate 		domains[0].dom_name = MSG_ORIG(MSG_SUNW_OST_SGS);
235*7c478bd9Sstevel@tonic-gate 		domains[1].dom_name = MSG_ORIG(MSG_SUNW_OST_OSLIB);
236*7c478bd9Sstevel@tonic-gate 		domaincnt = 2;
237*7c478bd9Sstevel@tonic-gate 	}
238*7c478bd9Sstevel@tonic-gate 
239*7c478bd9Sstevel@tonic-gate 	/*
240*7c478bd9Sstevel@tonic-gate 	 * If this is a new locale make sure we clean up any old ones.
241*7c478bd9Sstevel@tonic-gate 	 */
242*7c478bd9Sstevel@tonic-gate 	if (rtld_flags & RT_FL_NEWLOCALE) {
243*7c478bd9Sstevel@tonic-gate 		cnt = 0;
244*7c478bd9Sstevel@tonic-gate 
245*7c478bd9Sstevel@tonic-gate 		for (_domain = domains; cnt < domaincnt; _domain++, cnt++) {
246*7c478bd9Sstevel@tonic-gate 			if (_domain->dom_msghdr == 0)
247*7c478bd9Sstevel@tonic-gate 				continue;
248*7c478bd9Sstevel@tonic-gate 
249*7c478bd9Sstevel@tonic-gate 			if (_domain->dom_msghdr != (Msghdr *)-1)
250*7c478bd9Sstevel@tonic-gate 				(void) munmap((caddr_t)_domain->dom_msghdr,
251*7c478bd9Sstevel@tonic-gate 				    _domain->dom_msgsz);
252*7c478bd9Sstevel@tonic-gate 
253*7c478bd9Sstevel@tonic-gate 			_domain->dom_msghdr = 0;
254*7c478bd9Sstevel@tonic-gate 		}
255*7c478bd9Sstevel@tonic-gate 		rtld_flags &= ~RT_FL_NEWLOCALE;
256*7c478bd9Sstevel@tonic-gate 	}
257*7c478bd9Sstevel@tonic-gate 
258*7c478bd9Sstevel@tonic-gate 	/*
259*7c478bd9Sstevel@tonic-gate 	 * Determine which domain we need.
260*7c478bd9Sstevel@tonic-gate 	 */
261*7c478bd9Sstevel@tonic-gate 	for (cnt = 0, _domain = domains; cnt < domaincnt; _domain++, cnt++) {
262*7c478bd9Sstevel@tonic-gate 		if (_domain->dom_name == domain)
263*7c478bd9Sstevel@tonic-gate 			break;
264*7c478bd9Sstevel@tonic-gate 		if (strcmp(_domain->dom_name, domain) == 0)
265*7c478bd9Sstevel@tonic-gate 			break;
266*7c478bd9Sstevel@tonic-gate 	}
267*7c478bd9Sstevel@tonic-gate 	if (cnt == domaincnt)
268*7c478bd9Sstevel@tonic-gate 		return (msgid);
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate 	/*
271*7c478bd9Sstevel@tonic-gate 	 * Determine if the domain has been initialized yet.
272*7c478bd9Sstevel@tonic-gate 	 */
273*7c478bd9Sstevel@tonic-gate 	if (_domain->dom_msghdr == 0)
274*7c478bd9Sstevel@tonic-gate 		open_mofile(_domain);
275*7c478bd9Sstevel@tonic-gate 	if (_domain->dom_msghdr == (Msghdr *)-1)
276*7c478bd9Sstevel@tonic-gate 		return (msgid);
277*7c478bd9Sstevel@tonic-gate 
278*7c478bd9Sstevel@tonic-gate 	return (msgid_to_msgstr(_domain->dom_msghdr, msgid));
279*7c478bd9Sstevel@tonic-gate }
280*7c478bd9Sstevel@tonic-gate 
281*7c478bd9Sstevel@tonic-gate /*
282*7c478bd9Sstevel@tonic-gate  * This satisfies any dependencies of code dragged in from libc, as we don't
283*7c478bd9Sstevel@tonic-gate  * want libc's gettext implementation in ld.so.1.  This routine may not be
284*7c478bd9Sstevel@tonic-gate  * referenced, in which case -zignore will discard it.
285*7c478bd9Sstevel@tonic-gate  */
286*7c478bd9Sstevel@tonic-gate char *
287*7c478bd9Sstevel@tonic-gate gettext(const char *msgid)
288*7c478bd9Sstevel@tonic-gate {
289*7c478bd9Sstevel@tonic-gate 	return ((char *)_dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), msgid));
290*7c478bd9Sstevel@tonic-gate }
291*7c478bd9Sstevel@tonic-gate 
292*7c478bd9Sstevel@tonic-gate /*
293*7c478bd9Sstevel@tonic-gate  * The sgsmsg.1l use requires the following interface.
294*7c478bd9Sstevel@tonic-gate  */
295*7c478bd9Sstevel@tonic-gate const char *
296*7c478bd9Sstevel@tonic-gate _rtld_msg(Msg mid)
297*7c478bd9Sstevel@tonic-gate {
298*7c478bd9Sstevel@tonic-gate 	return ((char *)_dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), MSG_ORIG(mid)));
299*7c478bd9Sstevel@tonic-gate }
300