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