xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/locale.c (revision 10a4fa49)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*10a4fa49Srie  * Common Development and Distribution License (the "License").
6*10a4fa49Srie  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21*10a4fa49Srie 
227c478bd9Sstevel@tonic-gate /*
23*10a4fa49Srie  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * Messaging support.  To minimize ld.so.1's overhead, messaging support isn't
307c478bd9Sstevel@tonic-gate  * enabled until we need to contruct a message - Note that we don't rely on the
317c478bd9Sstevel@tonic-gate  * application to signify whether messaging is applicable, as many message
327c478bd9Sstevel@tonic-gate  * conditions (such as relocations) are generated before the application gains
337c478bd9Sstevel@tonic-gate  * control.
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  * This code implements a very trimmed down version of the capabilities found
367c478bd9Sstevel@tonic-gate  * via setlocale(3c), textdomain(3i) and gettext(3i).  Dragging in the original
377c478bd9Sstevel@tonic-gate  * routines from libc/libintl isn't possible as they cause all i18n support to
387c478bd9Sstevel@tonic-gate  * be included which is far too expensive for ld.so.1.
397c478bd9Sstevel@tonic-gate  */
407c478bd9Sstevel@tonic-gate #include	"_synonyms.h"
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #include	<sys/types.h>
437c478bd9Sstevel@tonic-gate #include	<sys/mman.h>
447c478bd9Sstevel@tonic-gate #include	<sys/stat.h>
457c478bd9Sstevel@tonic-gate #include	<string.h>
467c478bd9Sstevel@tonic-gate #include	<stdio.h>
477c478bd9Sstevel@tonic-gate #include	<stdlib.h>
487c478bd9Sstevel@tonic-gate #include	<unistd.h>
497c478bd9Sstevel@tonic-gate #include	<fcntl.h>
507c478bd9Sstevel@tonic-gate #include	<limits.h>
517c478bd9Sstevel@tonic-gate #include	"_rtld.h"
527c478bd9Sstevel@tonic-gate #include	"msg.h"
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate /*
557c478bd9Sstevel@tonic-gate  * A message object file (as generated by msgfmt(1)) consists of a message
567c478bd9Sstevel@tonic-gate  * header, followed by a message list, followed by the msgid strings and then
577c478bd9Sstevel@tonic-gate  * the msgstr strings.  None of this is defined in any OSNET available headers
587c478bd9Sstevel@tonic-gate  * so we have our own local definitions :-(
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate typedef struct {
617c478bd9Sstevel@tonic-gate 	int	hdr_midlst;		/* middle message no. */
627c478bd9Sstevel@tonic-gate 	int	hdr_lstcnt;		/* total no. of message in the file */
637c478bd9Sstevel@tonic-gate 	int	hdr_msgidsz;		/* size of msgids (in bytes) */
647c478bd9Sstevel@tonic-gate 	int	hdr_msgstrsz;		/* size of msgstrs (in bytes) */
657c478bd9Sstevel@tonic-gate 	int	hdr_lstsz;		/* size of message list (in bytes) */
667c478bd9Sstevel@tonic-gate } Msghdr;
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate typedef struct {
697c478bd9Sstevel@tonic-gate 	int	lst_less;
707c478bd9Sstevel@tonic-gate 	int	lst_more;
717c478bd9Sstevel@tonic-gate 	int	lst_idoff;
727c478bd9Sstevel@tonic-gate 	int	lst_stroff;
737c478bd9Sstevel@tonic-gate } Msglst;
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate #define	LEAFINDICATOR		-99
767c478bd9Sstevel@tonic-gate #define	OLD_MSG_STRUCT_SIZE	20
777c478bd9Sstevel@tonic-gate #define	NEW_MSG_STRUCT_SIZE	(sizeof (Msglst))
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /*
807c478bd9Sstevel@tonic-gate  * Define a local structure for maintaining the domains we care about.
817c478bd9Sstevel@tonic-gate  */
827c478bd9Sstevel@tonic-gate typedef struct {
837c478bd9Sstevel@tonic-gate 	const char	*dom_name;
847c478bd9Sstevel@tonic-gate 	const Msghdr	*dom_msghdr;
857c478bd9Sstevel@tonic-gate 	size_t		dom_msgsz;
867c478bd9Sstevel@tonic-gate } Domain;
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate /*
907c478bd9Sstevel@tonic-gate  * Perform a binary search of a message file (described by the Msghdr) for a
917c478bd9Sstevel@tonic-gate  * msgid (string).  Given a match return the associated msgstr, otherwise
927c478bd9Sstevel@tonic-gate  * return the original msgid.
937c478bd9Sstevel@tonic-gate  */
947c478bd9Sstevel@tonic-gate static const char *
957c478bd9Sstevel@tonic-gate msgid_to_msgstr(const Msghdr *msghdr, const char *msgid)
967c478bd9Sstevel@tonic-gate {
977c478bd9Sstevel@tonic-gate 	const Msglst	*list, *_list;
987c478bd9Sstevel@tonic-gate 	const char	*ids, *strs, *_msgid;
997c478bd9Sstevel@tonic-gate 	int		off, var;
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	/*
1027c478bd9Sstevel@tonic-gate 	 * Establish pointers to the message list (we actually start the search
1037c478bd9Sstevel@tonic-gate 	 * in the middle of this list (hdr->midlst), the msgid strings (ids)
1047c478bd9Sstevel@tonic-gate 	 * and the msgstr strings (strs).
1057c478bd9Sstevel@tonic-gate 	 */
1067c478bd9Sstevel@tonic-gate 	list = (const Msglst *)&msghdr[1];
1077c478bd9Sstevel@tonic-gate 	ids = (const char *)&list[msghdr->hdr_lstcnt];
1087c478bd9Sstevel@tonic-gate 	strs = (const char *)&ids[msghdr->hdr_msgidsz];
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	off = msghdr->hdr_midlst;
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	do {
1137c478bd9Sstevel@tonic-gate 		_list = list + off;
1147c478bd9Sstevel@tonic-gate 		_msgid = ids + _list->lst_idoff;
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 		if ((var = strcmp(_msgid, msgid)) == 0)
1177c478bd9Sstevel@tonic-gate 			return (strs + _list->lst_stroff);
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 		if (var < 0) {
1207c478bd9Sstevel@tonic-gate 			if ((off = _list->lst_less) == LEAFINDICATOR)
1217c478bd9Sstevel@tonic-gate 				return (msgid);
1227c478bd9Sstevel@tonic-gate 		} else {
1237c478bd9Sstevel@tonic-gate 			if ((off = _list->lst_more) == LEAFINDICATOR)
1247c478bd9Sstevel@tonic-gate 				return (msgid);
1257c478bd9Sstevel@tonic-gate 		}
1267c478bd9Sstevel@tonic-gate 	} while (off);
1277c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
1287c478bd9Sstevel@tonic-gate 	return (NULL);	/* keep gcc happy */
1297c478bd9Sstevel@tonic-gate }
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate /*
1327c478bd9Sstevel@tonic-gate  * Open a message file. Following the model of setlocale(3c) we obtain the
1337c478bd9Sstevel@tonic-gate  * message file for the specified locale.  Normally this is:
1347c478bd9Sstevel@tonic-gate  *
1357c478bd9Sstevel@tonic-gate  *	/usr/lib/locale/`locale'/LC_MESSAGES/`domain'.mo
1367c478bd9Sstevel@tonic-gate  *
1377c478bd9Sstevel@tonic-gate  * The locale was determined during initial environment processing (see
1387c478bd9Sstevel@tonic-gate  * readenv()), which was determined from an LC_ALL, LC_MESSAGES or LANG
1397c478bd9Sstevel@tonic-gate  * setting.  If no locale has been specified, or any file processing errors
1407c478bd9Sstevel@tonic-gate  * occur, internationalization is basically disabled.
1417c478bd9Sstevel@tonic-gate  */
1427c478bd9Sstevel@tonic-gate static void
1437c478bd9Sstevel@tonic-gate open_mofile(Domain * dom)
1447c478bd9Sstevel@tonic-gate {
1457c478bd9Sstevel@tonic-gate 	const char	*domain = dom->dom_name;
1467c478bd9Sstevel@tonic-gate 	char		path[PATH_MAX];
1477c478bd9Sstevel@tonic-gate 	int		fd;
1487c478bd9Sstevel@tonic-gate 	struct stat	status;
1497c478bd9Sstevel@tonic-gate 	const Msghdr	*msghdr;
1507c478bd9Sstevel@tonic-gate 	int		count;
1517c478bd9Sstevel@tonic-gate 	size_t		size_tot, size_old, size_new;
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	dom->dom_msghdr = (Msghdr *)-1;
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	(void) snprintf(path, PATH_MAX, MSG_ORIG(MSG_FMT_MSGFILE),
156*10a4fa49Srie 		glcs[CI_LCMESSAGES].lc_un.lc_ptr, domain);
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	if ((fd = open(path, O_RDONLY, 0)) == -1)
1597c478bd9Sstevel@tonic-gate 		return;
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	if ((fstat(fd, &status) == -1) ||
1627c478bd9Sstevel@tonic-gate 	    (status.st_size < sizeof (Msghdr))) {
1637c478bd9Sstevel@tonic-gate 		(void) close(fd);
1647c478bd9Sstevel@tonic-gate 		return;
1657c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	/* LINTED */
1687c478bd9Sstevel@tonic-gate 	if ((msghdr = (Msghdr *)mmap(0, status.st_size, PROT_READ, MAP_SHARED,
1697c478bd9Sstevel@tonic-gate 	    fd, 0)) == (Msghdr *)-1) {
1707c478bd9Sstevel@tonic-gate 		(void) close(fd);
1717c478bd9Sstevel@tonic-gate 		return;
1727c478bd9Sstevel@tonic-gate 	}
1737c478bd9Sstevel@tonic-gate 	(void) close(fd);
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	/* checks if opened file is msg file */
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 	count = msghdr->hdr_lstcnt;
1787c478bd9Sstevel@tonic-gate 	if (((count - 1) / 2) != msghdr->hdr_midlst) {
1797c478bd9Sstevel@tonic-gate 		(void) munmap((caddr_t)msghdr, status.st_size);
1807c478bd9Sstevel@tonic-gate 		return;
1817c478bd9Sstevel@tonic-gate 	}
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	size_tot = msghdr->hdr_lstsz;
1847c478bd9Sstevel@tonic-gate 	size_old = OLD_MSG_STRUCT_SIZE * count;
1857c478bd9Sstevel@tonic-gate 	size_new = (int)NEW_MSG_STRUCT_SIZE * count;
1867c478bd9Sstevel@tonic-gate 	if ((size_tot != size_old) && (size_tot != size_new)) {
1877c478bd9Sstevel@tonic-gate 		(void) munmap((caddr_t)msghdr, status.st_size);
1887c478bd9Sstevel@tonic-gate 		return;
1897c478bd9Sstevel@tonic-gate 	}
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	size_tot = msghdr->hdr_msgidsz + msghdr->hdr_msgstrsz +
1927c478bd9Sstevel@tonic-gate 	    (int)sizeof (Msghdr);
1937c478bd9Sstevel@tonic-gate 	if ((size_tot + size_old < status.st_size) &&
1947c478bd9Sstevel@tonic-gate 	    (size_tot + size_new < status.st_size)) {
1957c478bd9Sstevel@tonic-gate 		(void) munmap((caddr_t)msghdr, status.st_size);
1967c478bd9Sstevel@tonic-gate 		return;
1977c478bd9Sstevel@tonic-gate 	}
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	/*
2007c478bd9Sstevel@tonic-gate 	 * We have a good message file, initialize the Domain information.
2017c478bd9Sstevel@tonic-gate 	 */
2027c478bd9Sstevel@tonic-gate 	dom->dom_msghdr = msghdr;
2037c478bd9Sstevel@tonic-gate 	dom->dom_msgsz = status.st_size;
2047c478bd9Sstevel@tonic-gate }
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate /*
2087c478bd9Sstevel@tonic-gate  * Two interfaces are established to support our internationalization.
2097c478bd9Sstevel@tonic-gate  * gettext(3i) calls originate from all link-editor libraries, and thus the
2107c478bd9Sstevel@tonic-gate  * SUNW_OST_SGS domain is assumed.  _dgettext() calls originate from
2117c478bd9Sstevel@tonic-gate  * dependencies such as libelf and libc.
2127c478bd9Sstevel@tonic-gate  *
2137c478bd9Sstevel@tonic-gate  * Presently we support two domains (libc's strerror() uses SUNW_OST_OSLIB).
2147c478bd9Sstevel@tonic-gate  * If ld.so.1's dependencies evolve to require more then the `domain' array
2157c478bd9Sstevel@tonic-gate  * maintained below can be enlarged or made more dynamic in nature.
2167c478bd9Sstevel@tonic-gate  */
2177c478bd9Sstevel@tonic-gate const char *
2187c478bd9Sstevel@tonic-gate _dgettext(const char *domain, const char *msgid)
2197c478bd9Sstevel@tonic-gate {
2207c478bd9Sstevel@tonic-gate 	static int	domaincnt = 0;
2217c478bd9Sstevel@tonic-gate 	static Domain	*domains;
2227c478bd9Sstevel@tonic-gate 	Domain		*_domain;
2237c478bd9Sstevel@tonic-gate 	int		cnt;
2247c478bd9Sstevel@tonic-gate 
225*10a4fa49Srie 	if (glcs[CI_LCMESSAGES].lc_un.lc_val == 0)
2267c478bd9Sstevel@tonic-gate 		return (msgid);
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 	/*
2297c478bd9Sstevel@tonic-gate 	 * Determine if we've initialized any domains yet.
2307c478bd9Sstevel@tonic-gate 	 */
2317c478bd9Sstevel@tonic-gate 	if (domaincnt == 0) {
2327c478bd9Sstevel@tonic-gate 		if ((domains = (Domain *)calloc(sizeof (Domain), 2)) == 0)
2337c478bd9Sstevel@tonic-gate 			return (msgid);
2347c478bd9Sstevel@tonic-gate 		domains[0].dom_name = MSG_ORIG(MSG_SUNW_OST_SGS);
2357c478bd9Sstevel@tonic-gate 		domains[1].dom_name = MSG_ORIG(MSG_SUNW_OST_OSLIB);
2367c478bd9Sstevel@tonic-gate 		domaincnt = 2;
2377c478bd9Sstevel@tonic-gate 	}
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	/*
2407c478bd9Sstevel@tonic-gate 	 * If this is a new locale make sure we clean up any old ones.
2417c478bd9Sstevel@tonic-gate 	 */
2427c478bd9Sstevel@tonic-gate 	if (rtld_flags & RT_FL_NEWLOCALE) {
2437c478bd9Sstevel@tonic-gate 		cnt = 0;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 		for (_domain = domains; cnt < domaincnt; _domain++, cnt++) {
2467c478bd9Sstevel@tonic-gate 			if (_domain->dom_msghdr == 0)
2477c478bd9Sstevel@tonic-gate 				continue;
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 			if (_domain->dom_msghdr != (Msghdr *)-1)
2507c478bd9Sstevel@tonic-gate 				(void) munmap((caddr_t)_domain->dom_msghdr,
2517c478bd9Sstevel@tonic-gate 				    _domain->dom_msgsz);
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 			_domain->dom_msghdr = 0;
2547c478bd9Sstevel@tonic-gate 		}
2557c478bd9Sstevel@tonic-gate 		rtld_flags &= ~RT_FL_NEWLOCALE;
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/*
2597c478bd9Sstevel@tonic-gate 	 * Determine which domain we need.
2607c478bd9Sstevel@tonic-gate 	 */
2617c478bd9Sstevel@tonic-gate 	for (cnt = 0, _domain = domains; cnt < domaincnt; _domain++, cnt++) {
2627c478bd9Sstevel@tonic-gate 		if (_domain->dom_name == domain)
2637c478bd9Sstevel@tonic-gate 			break;
2647c478bd9Sstevel@tonic-gate 		if (strcmp(_domain->dom_name, domain) == 0)
2657c478bd9Sstevel@tonic-gate 			break;
2667c478bd9Sstevel@tonic-gate 	}
2677c478bd9Sstevel@tonic-gate 	if (cnt == domaincnt)
2687c478bd9Sstevel@tonic-gate 		return (msgid);
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	/*
2717c478bd9Sstevel@tonic-gate 	 * Determine if the domain has been initialized yet.
2727c478bd9Sstevel@tonic-gate 	 */
2737c478bd9Sstevel@tonic-gate 	if (_domain->dom_msghdr == 0)
2747c478bd9Sstevel@tonic-gate 		open_mofile(_domain);
2757c478bd9Sstevel@tonic-gate 	if (_domain->dom_msghdr == (Msghdr *)-1)
2767c478bd9Sstevel@tonic-gate 		return (msgid);
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	return (msgid_to_msgstr(_domain->dom_msghdr, msgid));
2797c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate  * This satisfies any dependencies of code dragged in from libc, as we don't
2837c478bd9Sstevel@tonic-gate  * want libc's gettext implementation in ld.so.1.  This routine may not be
2847c478bd9Sstevel@tonic-gate  * referenced, in which case -zignore will discard it.
2857c478bd9Sstevel@tonic-gate  */
2867c478bd9Sstevel@tonic-gate char *
2877c478bd9Sstevel@tonic-gate gettext(const char *msgid)
2887c478bd9Sstevel@tonic-gate {
2897c478bd9Sstevel@tonic-gate 	return ((char *)_dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), msgid));
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate /*
2937c478bd9Sstevel@tonic-gate  * The sgsmsg.1l use requires the following interface.
2947c478bd9Sstevel@tonic-gate  */
2957c478bd9Sstevel@tonic-gate const char *
2967c478bd9Sstevel@tonic-gate _rtld_msg(Msg mid)
2977c478bd9Sstevel@tonic-gate {
2987c478bd9Sstevel@tonic-gate 	return ((char *)_dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), MSG_ORIG(mid)));
2997c478bd9Sstevel@tonic-gate }
300