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 */
2110a4fa49Srie
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 */
267257d1b4Sraf
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 */
397c478bd9Sstevel@tonic-gate
407c478bd9Sstevel@tonic-gate #include <sys/types.h>
417c478bd9Sstevel@tonic-gate #include <sys/mman.h>
427c478bd9Sstevel@tonic-gate #include <sys/stat.h>
437c478bd9Sstevel@tonic-gate #include <string.h>
447c478bd9Sstevel@tonic-gate #include <stdio.h>
457c478bd9Sstevel@tonic-gate #include <stdlib.h>
467c478bd9Sstevel@tonic-gate #include <unistd.h>
477c478bd9Sstevel@tonic-gate #include <fcntl.h>
487c478bd9Sstevel@tonic-gate #include <limits.h>
497257d1b4Sraf #include <libintl.h>
507c478bd9Sstevel@tonic-gate #include "_rtld.h"
517c478bd9Sstevel@tonic-gate #include "msg.h"
527c478bd9Sstevel@tonic-gate
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;
667c478bd9Sstevel@tonic-gate
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;
737c478bd9Sstevel@tonic-gate
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))
777c478bd9Sstevel@tonic-gate
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;
867c478bd9Sstevel@tonic-gate
877c478bd9Sstevel@tonic-gate
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;
997c478bd9Sstevel@tonic-gate
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];
1087c478bd9Sstevel@tonic-gate
1097c478bd9Sstevel@tonic-gate off = msghdr->hdr_midlst;
1107c478bd9Sstevel@tonic-gate
111*9ac6ca4dSRod Evans for (;;) {
1127c478bd9Sstevel@tonic-gate _list = list + off;
1137c478bd9Sstevel@tonic-gate _msgid = ids + _list->lst_idoff;
1147c478bd9Sstevel@tonic-gate
1157c478bd9Sstevel@tonic-gate if ((var = strcmp(_msgid, msgid)) == 0)
1167c478bd9Sstevel@tonic-gate return (strs + _list->lst_stroff);
1177c478bd9Sstevel@tonic-gate
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 }
1297c478bd9Sstevel@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;
1517c478bd9Sstevel@tonic-gate
1527c478bd9Sstevel@tonic-gate dom->dom_msghdr = (Msghdr *)-1;
1537c478bd9Sstevel@tonic-gate
1547c478bd9Sstevel@tonic-gate (void) snprintf(path, PATH_MAX, MSG_ORIG(MSG_FMT_MSGFILE),
15510a4fa49Srie glcs[CI_LCMESSAGES].lc_un.lc_ptr, domain);
1567c478bd9Sstevel@tonic-gate
1577c478bd9Sstevel@tonic-gate if ((fd = open(path, O_RDONLY, 0)) == -1)
1587c478bd9Sstevel@tonic-gate return;
1597c478bd9Sstevel@tonic-gate
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 }
1657c478bd9Sstevel@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);
1737c478bd9Sstevel@tonic-gate
1747c478bd9Sstevel@tonic-gate /* checks if opened file is msg file */
1757c478bd9Sstevel@tonic-gate
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 }
1817c478bd9Sstevel@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 }
1897c478bd9Sstevel@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 }
1977c478bd9Sstevel@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 }
2047c478bd9Sstevel@tonic-gate
2057c478bd9Sstevel@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;
2237c478bd9Sstevel@tonic-gate
22410a4fa49Srie if (glcs[CI_LCMESSAGES].lc_un.lc_val == 0)
2257257d1b4Sraf return ((char *)msgid);
2267c478bd9Sstevel@tonic-gate
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 }
2377c478bd9Sstevel@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;
2437c478bd9Sstevel@tonic-gate
2447c478bd9Sstevel@tonic-gate for (_domain = domains; cnt < domaincnt; _domain++, cnt++) {
2457c478bd9Sstevel@tonic-gate if (_domain->dom_msghdr == 0)
2467c478bd9Sstevel@tonic-gate continue;
2477c478bd9Sstevel@tonic-gate
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);
2517c478bd9Sstevel@tonic-gate
2527c478bd9Sstevel@tonic-gate _domain->dom_msghdr = 0;
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate rtld_flags &= ~RT_FL_NEWLOCALE;
2557c478bd9Sstevel@tonic-gate }
2567c478bd9Sstevel@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);
2687c478bd9Sstevel@tonic-gate
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);
2767c478bd9Sstevel@tonic-gate
2777257d1b4Sraf return ((char *)msgid_to_msgstr(_domain->dom_msghdr, msgid));
2787c478bd9Sstevel@tonic-gate }
2797c478bd9Sstevel@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 }
2907c478bd9Sstevel@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 }
299