xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/locale.c (revision 9ac6ca4d)
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
510a4fa49Srie  * Common Development and Distribution License (the "License").
610a4fa49Srie  * 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  */
227c478bd9Sstevel@tonic-gate /*
2356deab07SRod Evans  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Messaging support.  To minimize ld.so.1's overhead, messaging support isn't
297c478bd9Sstevel@tonic-gate  * enabled until we need to contruct a message - Note that we don't rely on the
307c478bd9Sstevel@tonic-gate  * application to signify whether messaging is applicable, as many message
317c478bd9Sstevel@tonic-gate  * conditions (such as relocations) are generated before the application gains
327c478bd9Sstevel@tonic-gate  * control.
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * This code implements a very trimmed down version of the capabilities found
357c478bd9Sstevel@tonic-gate  * via setlocale(3c), textdomain(3i) and gettext(3i).  Dragging in the original
367c478bd9Sstevel@tonic-gate  * routines from libc/libintl isn't possible as they cause all i18n support to
377c478bd9Sstevel@tonic-gate  * be included which is far too expensive for ld.so.1.
387c478bd9Sstevel@tonic-gate  */
407257d1b4Sraf #include <sys/types.h>
417257d1b4Sraf #include <sys/mman.h>
427257d1b4Sraf #include <sys/stat.h>
437257d1b4Sraf #include <string.h>
447257d1b4Sraf #include <stdio.h>
457257d1b4Sraf #include <stdlib.h>
467257d1b4Sraf #include <unistd.h>
477257d1b4Sraf #include <fcntl.h>
487257d1b4Sraf #include <limits.h>
497257d1b4Sraf #include <libintl.h>
507257d1b4Sraf #include "_rtld.h"
517257d1b4Sraf #include "msg.h"
537c478bd9Sstevel@tonic-gate /*
547c478bd9Sstevel@tonic-gate  * A message object file (as generated by msgfmt(1)) consists of a message
557c478bd9Sstevel@tonic-gate  * header, followed by a message list, followed by the msgid strings and then
567c478bd9Sstevel@tonic-gate  * the msgstr strings.  None of this is defined in any OSNET available headers
577c478bd9Sstevel@tonic-gate  * so we have our own local definitions :-(
587c478bd9Sstevel@tonic-gate  */
597c478bd9Sstevel@tonic-gate typedef struct {
607c478bd9Sstevel@tonic-gate 	int	hdr_midlst;		/* middle message no. */
617c478bd9Sstevel@tonic-gate 	int	hdr_lstcnt;		/* total no. of message in the file */
627c478bd9Sstevel@tonic-gate 	int	hdr_msgidsz;		/* size of msgids (in bytes) */
637c478bd9Sstevel@tonic-gate 	int	hdr_msgstrsz;		/* size of msgstrs (in bytes) */
647c478bd9Sstevel@tonic-gate 	int	hdr_lstsz;		/* size of message list (in bytes) */
657c478bd9Sstevel@tonic-gate } Msghdr;
677c478bd9Sstevel@tonic-gate typedef struct {
687c478bd9Sstevel@tonic-gate 	int	lst_less;
697c478bd9Sstevel@tonic-gate 	int	lst_more;
707c478bd9Sstevel@tonic-gate 	int	lst_idoff;
717c478bd9Sstevel@tonic-gate 	int	lst_stroff;
727c478bd9Sstevel@tonic-gate } Msglst;
747c478bd9Sstevel@tonic-gate #define	LEAFINDICATOR		-99
757c478bd9Sstevel@tonic-gate #define	OLD_MSG_STRUCT_SIZE	20
767c478bd9Sstevel@tonic-gate #define	NEW_MSG_STRUCT_SIZE	(sizeof (Msglst))
787c478bd9Sstevel@tonic-gate /*
797c478bd9Sstevel@tonic-gate  * Define a local structure for maintaining the domains we care about.
807c478bd9Sstevel@tonic-gate  */
817c478bd9Sstevel@tonic-gate typedef struct {
827c478bd9Sstevel@tonic-gate 	const char	*dom_name;
837c478bd9Sstevel@tonic-gate 	const Msghdr	*dom_msghdr;
847c478bd9Sstevel@tonic-gate 	size_t		dom_msgsz;
857c478bd9Sstevel@tonic-gate } Domain;
887c478bd9Sstevel@tonic-gate /*
897c478bd9Sstevel@tonic-gate  * Perform a binary search of a message file (described by the Msghdr) for a
907c478bd9Sstevel@tonic-gate  * msgid (string).  Given a match return the associated msgstr, otherwise
917c478bd9Sstevel@tonic-gate  * return the original msgid.
927c478bd9Sstevel@tonic-gate  */
937c478bd9Sstevel@tonic-gate static const char *
msgid_to_msgstr(const Msghdr * msghdr,const char * msgid)947c478bd9Sstevel@tonic-gate msgid_to_msgstr(const Msghdr *msghdr, const char *msgid)
957c478bd9Sstevel@tonic-gate {
967c478bd9Sstevel@tonic-gate 	const Msglst	*list, *_list;
977c478bd9Sstevel@tonic-gate 	const char	*ids, *strs, *_msgid;
987c478bd9Sstevel@tonic-gate 	int		off, var;
1007c478bd9Sstevel@tonic-gate 	/*
1017c478bd9Sstevel@tonic-gate 	 * Establish pointers to the message list (we actually start the search
1027c478bd9Sstevel@tonic-gate 	 * in the middle of this list (hdr->midlst), the msgid strings (ids)
1037c478bd9Sstevel@tonic-gate 	 * and the msgstr strings (strs).
1047c478bd9Sstevel@tonic-gate 	 */
1057c478bd9Sstevel@tonic-gate 	list = (const Msglst *)&msghdr[1];
1067c478bd9Sstevel@tonic-gate 	ids = (const char *)&list[msghdr->hdr_lstcnt];
1077c478bd9Sstevel@tonic-gate 	strs = (const char *)&ids[msghdr->hdr_msgidsz];
1097c478bd9Sstevel@tonic-gate 	off = msghdr->hdr_midlst;
111*9ac6ca4dSRod Evans 	for (;;) {
1127c478bd9Sstevel@tonic-gate 		_list = list + off;
1137c478bd9Sstevel@tonic-gate 		_msgid = ids + _list->lst_idoff;
1157c478bd9Sstevel@tonic-gate 		if ((var = strcmp(_msgid, msgid)) == 0)
1167c478bd9Sstevel@tonic-gate 			return (strs + _list->lst_stroff);
1187c478bd9Sstevel@tonic-gate 		if (var < 0) {
1197c478bd9Sstevel@tonic-gate 			if ((off = _list->lst_less) == LEAFINDICATOR)
1207c478bd9Sstevel@tonic-gate 				return (msgid);
1217c478bd9Sstevel@tonic-gate 		} else {
1227c478bd9Sstevel@tonic-gate 			if ((off = _list->lst_more) == LEAFINDICATOR)
1237c478bd9Sstevel@tonic-gate 				return (msgid);
1247c478bd9Sstevel@tonic-gate 		}
125*9ac6ca4dSRod Evans 	}
1267c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
1277c478bd9Sstevel@tonic-gate 	return (NULL);	/* keep gcc happy */
1287c478bd9Sstevel@tonic-gate }
1307c478bd9Sstevel@tonic-gate /*
1317c478bd9Sstevel@tonic-gate  * Open a message file. Following the model of setlocale(3c) we obtain the
1327c478bd9Sstevel@tonic-gate  * message file for the specified locale.  Normally this is:
1337c478bd9Sstevel@tonic-gate  *
1347c478bd9Sstevel@tonic-gate  *	/usr/lib/locale/`locale'/LC_MESSAGES/`domain'.mo
1357c478bd9Sstevel@tonic-gate  *
1367c478bd9Sstevel@tonic-gate  * The locale was determined during initial environment processing (see
1377c478bd9Sstevel@tonic-gate  * readenv()), which was determined from an LC_ALL, LC_MESSAGES or LANG
1387c478bd9Sstevel@tonic-gate  * setting.  If no locale has been specified, or any file processing errors
1397c478bd9Sstevel@tonic-gate  * occur, internationalization is basically disabled.
1407c478bd9Sstevel@tonic-gate  */
1417c478bd9Sstevel@tonic-gate static void
open_mofile(Domain * dom)1427c478bd9Sstevel@tonic-gate open_mofile(Domain * dom)
1437c478bd9Sstevel@tonic-gate {
1447c478bd9Sstevel@tonic-gate 	const char	*domain = dom->dom_name;
1457c478bd9Sstevel@tonic-gate 	char		path[PATH_MAX];
1467c478bd9Sstevel@tonic-gate 	int		fd;
147cb511613SAli Bahrami 	rtld_stat_t	status;
1487c478bd9Sstevel@tonic-gate 	const Msghdr	*msghdr;
1497c478bd9Sstevel@tonic-gate 	int		count;
1507c478bd9Sstevel@tonic-gate 	size_t		size_tot, size_old, size_new;
1527c478bd9Sstevel@tonic-gate 	dom->dom_msghdr = (Msghdr *)-1;
1547c478bd9Sstevel@tonic-gate 	(void) snprintf(path, PATH_MAX, MSG_ORIG(MSG_FMT_MSGFILE),
1557257d1b4Sraf 	    glcs[CI_LCMESSAGES].lc_un.lc_ptr, domain);
1577c478bd9Sstevel@tonic-gate 	if ((fd = open(path, O_RDONLY, 0)) == -1)
1587c478bd9Sstevel@tonic-gate 		return;
160cb511613SAli Bahrami 	if ((rtld_fstat(fd, &status) == -1) ||
1617c478bd9Sstevel@tonic-gate 	    (status.st_size < sizeof (Msghdr))) {
1627c478bd9Sstevel@tonic-gate 		(void) close(fd);
1637c478bd9Sstevel@tonic-gate 		return;
1647c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 	/* LINTED */
1677c478bd9Sstevel@tonic-gate 	if ((msghdr = (Msghdr *)mmap(0, status.st_size, PROT_READ, MAP_SHARED,
1687c478bd9Sstevel@tonic-gate 	    fd, 0)) == (Msghdr *)-1) {
1697c478bd9Sstevel@tonic-gate 		(void) close(fd);
1707c478bd9Sstevel@tonic-gate 		return;
1717c478bd9Sstevel@tonic-gate 	}
1727c478bd9Sstevel@tonic-gate 	(void) close(fd);
1747c478bd9Sstevel@tonic-gate 	/* checks if opened file is msg file */
1767c478bd9Sstevel@tonic-gate 	count = msghdr->hdr_lstcnt;
1777c478bd9Sstevel@tonic-gate 	if (((count - 1) / 2) != msghdr->hdr_midlst) {
1787c478bd9Sstevel@tonic-gate 		(void) munmap((caddr_t)msghdr, status.st_size);
1797c478bd9Sstevel@tonic-gate 		return;
1807c478bd9Sstevel@tonic-gate 	}
1827c478bd9Sstevel@tonic-gate 	size_tot = msghdr->hdr_lstsz;
1837c478bd9Sstevel@tonic-gate 	size_old = OLD_MSG_STRUCT_SIZE * count;
1847c478bd9Sstevel@tonic-gate 	size_new = (int)NEW_MSG_STRUCT_SIZE * count;
1857c478bd9Sstevel@tonic-gate 	if ((size_tot != size_old) && (size_tot != size_new)) {
1867c478bd9Sstevel@tonic-gate 		(void) munmap((caddr_t)msghdr, status.st_size);
1877c478bd9Sstevel@tonic-gate 		return;
1887c478bd9Sstevel@tonic-gate 	}
1907c478bd9Sstevel@tonic-gate 	size_tot = msghdr->hdr_msgidsz + msghdr->hdr_msgstrsz +
1917c478bd9Sstevel@tonic-gate 	    (int)sizeof (Msghdr);
1927c478bd9Sstevel@tonic-gate 	if ((size_tot + size_old < status.st_size) &&
1937c478bd9Sstevel@tonic-gate 	    (size_tot + size_new < status.st_size)) {
1947c478bd9Sstevel@tonic-gate 		(void) munmap((caddr_t)msghdr, status.st_size);
1957c478bd9Sstevel@tonic-gate 		return;
1967c478bd9Sstevel@tonic-gate 	}
1987c478bd9Sstevel@tonic-gate 	/*
1997c478bd9Sstevel@tonic-gate 	 * We have a good message file, initialize the Domain information.
2007c478bd9Sstevel@tonic-gate 	 */
2017c478bd9Sstevel@tonic-gate 	dom->dom_msghdr = msghdr;
2027c478bd9Sstevel@tonic-gate 	dom->dom_msgsz = status.st_size;
2037c478bd9Sstevel@tonic-gate }
2067c478bd9Sstevel@tonic-gate /*
2077c478bd9Sstevel@tonic-gate  * Two interfaces are established to support our internationalization.
2087c478bd9Sstevel@tonic-gate  * gettext(3i) calls originate from all link-editor libraries, and thus the
2097257d1b4Sraf  * SUNW_OST_SGS domain is assumed.  dgettext() calls originate from
2107c478bd9Sstevel@tonic-gate  * dependencies such as libelf and libc.
2117c478bd9Sstevel@tonic-gate  *
2127c478bd9Sstevel@tonic-gate  * Presently we support two domains (libc's strerror() uses SUNW_OST_OSLIB).
2137c478bd9Sstevel@tonic-gate  * If ld.so.1's dependencies evolve to require more then the `domain' array
2147c478bd9Sstevel@tonic-gate  * maintained below can be enlarged or made more dynamic in nature.
2157c478bd9Sstevel@tonic-gate  */
2167257d1b4Sraf char *
dgettext(const char * domain,const char * msgid)2177257d1b4Sraf dgettext(const char *domain, const char *msgid)
2187c478bd9Sstevel@tonic-gate {
2197c478bd9Sstevel@tonic-gate 	static int	domaincnt = 0;
2207c478bd9Sstevel@tonic-gate 	static Domain	*domains;
2217c478bd9Sstevel@tonic-gate 	Domain		*_domain;
2227c478bd9Sstevel@tonic-gate 	int		cnt;
22410a4fa49Srie 	if (glcs[CI_LCMESSAGES].lc_un.lc_val == 0)
2257257d1b4Sraf 		return ((char *)msgid);
2277c478bd9Sstevel@tonic-gate 	/*
2287c478bd9Sstevel@tonic-gate 	 * Determine if we've initialized any domains yet.
2297c478bd9Sstevel@tonic-gate 	 */
2307c478bd9Sstevel@tonic-gate 	if (domaincnt == 0) {
23156deab07SRod Evans 		if ((domains = calloc(sizeof (Domain), 2)) == NULL)
2327257d1b4Sraf 			return ((char *)msgid);
2337c478bd9Sstevel@tonic-gate 		domains[0].dom_name = MSG_ORIG(MSG_SUNW_OST_SGS);
2347c478bd9Sstevel@tonic-gate 		domains[1].dom_name = MSG_ORIG(MSG_SUNW_OST_OSLIB);
2357c478bd9Sstevel@tonic-gate 		domaincnt = 2;
2367c478bd9Sstevel@tonic-gate 	}
2387c478bd9Sstevel@tonic-gate 	/*
2397c478bd9Sstevel@tonic-gate 	 * If this is a new locale make sure we clean up any old ones.
2407c478bd9Sstevel@tonic-gate 	 */
2417c478bd9Sstevel@tonic-gate 	if (rtld_flags & RT_FL_NEWLOCALE) {
2427c478bd9Sstevel@tonic-gate 		cnt = 0;
2447c478bd9Sstevel@tonic-gate 		for (_domain = domains; cnt < domaincnt; _domain++, cnt++) {
2457c478bd9Sstevel@tonic-gate 			if (_domain->dom_msghdr == 0)
2467c478bd9Sstevel@tonic-gate 				continue;
2487c478bd9Sstevel@tonic-gate 			if (_domain->dom_msghdr != (Msghdr *)-1)
2497c478bd9Sstevel@tonic-gate 				(void) munmap((caddr_t)_domain->dom_msghdr,
2507c478bd9Sstevel@tonic-gate 				    _domain->dom_msgsz);
2527c478bd9Sstevel@tonic-gate 			_domain->dom_msghdr = 0;
2537c478bd9Sstevel@tonic-gate 		}
2547c478bd9Sstevel@tonic-gate 		rtld_flags &= ~RT_FL_NEWLOCALE;
2557c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 	/*
2587c478bd9Sstevel@tonic-gate 	 * Determine which domain we need.
2597c478bd9Sstevel@tonic-gate 	 */
2607c478bd9Sstevel@tonic-gate 	for (cnt = 0, _domain = domains; cnt < domaincnt; _domain++, cnt++) {
2617c478bd9Sstevel@tonic-gate 		if (_domain->dom_name == domain)
2627c478bd9Sstevel@tonic-gate 			break;
2637c478bd9Sstevel@tonic-gate 		if (strcmp(_domain->dom_name, domain) == 0)
2647c478bd9Sstevel@tonic-gate 			break;
2657c478bd9Sstevel@tonic-gate 	}
2667c478bd9Sstevel@tonic-gate 	if (cnt == domaincnt)
2677257d1b4Sraf 		return ((char *)msgid);
2697c478bd9Sstevel@tonic-gate 	/*
2707c478bd9Sstevel@tonic-gate 	 * Determine if the domain has been initialized yet.
2717c478bd9Sstevel@tonic-gate 	 */
2727c478bd9Sstevel@tonic-gate 	if (_domain->dom_msghdr == 0)
2737c478bd9Sstevel@tonic-gate 		open_mofile(_domain);
2747c478bd9Sstevel@tonic-gate 	if (_domain->dom_msghdr == (Msghdr *)-1)
2757257d1b4Sraf 		return ((char *)msgid);
2777257d1b4Sraf 	return ((char *)msgid_to_msgstr(_domain->dom_msghdr, msgid));
2787c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate /*
2817c478bd9Sstevel@tonic-gate  * This satisfies any dependencies of code dragged in from libc, as we don't
2827c478bd9Sstevel@tonic-gate  * want libc's gettext implementation in ld.so.1.  This routine may not be
2837c478bd9Sstevel@tonic-gate  * referenced, in which case -zignore will discard it.
2847c478bd9Sstevel@tonic-gate  */
2857c478bd9Sstevel@tonic-gate char *
gettext(const char * msgid)2867c478bd9Sstevel@tonic-gate gettext(const char *msgid)
2877c478bd9Sstevel@tonic-gate {
2887257d1b4Sraf 	return ((char *)dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), msgid));
2897c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate /*
2927c478bd9Sstevel@tonic-gate  * The sgsmsg.1l use requires the following interface.
2937c478bd9Sstevel@tonic-gate  */
2947c478bd9Sstevel@tonic-gate const char *
_rtld_msg(Msg mid)2957c478bd9Sstevel@tonic-gate _rtld_msg(Msg mid)
2967c478bd9Sstevel@tonic-gate {
2977257d1b4Sraf 	return ((char *)dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), MSG_ORIG(mid)));
2987c478bd9Sstevel@tonic-gate }