/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Messaging support. To minimize ld.so.1's overhead, messaging support isn't * enabled until we need to contruct a message - Note that we don't rely on the * application to signify whether messaging is applicable, as many message * conditions (such as relocations) are generated before the application gains * control. * * This code implements a very trimmed down version of the capabilities found * via setlocale(3c), textdomain(3i) and gettext(3i). Dragging in the original * routines from libc/libintl isn't possible as they cause all i18n support to * be included which is far too expensive for ld.so.1. */ #include #include #include #include #include #include #include #include #include #include #include "_rtld.h" #include "msg.h" /* * A message object file (as generated by msgfmt(1)) consists of a message * header, followed by a message list, followed by the msgid strings and then * the msgstr strings. None of this is defined in any OSNET available headers * so we have our own local definitions :-( */ typedef struct { int hdr_midlst; /* middle message no. */ int hdr_lstcnt; /* total no. of message in the file */ int hdr_msgidsz; /* size of msgids (in bytes) */ int hdr_msgstrsz; /* size of msgstrs (in bytes) */ int hdr_lstsz; /* size of message list (in bytes) */ } Msghdr; typedef struct { int lst_less; int lst_more; int lst_idoff; int lst_stroff; } Msglst; #define LEAFINDICATOR -99 #define OLD_MSG_STRUCT_SIZE 20 #define NEW_MSG_STRUCT_SIZE (sizeof (Msglst)) /* * Define a local structure for maintaining the domains we care about. */ typedef struct { const char *dom_name; const Msghdr *dom_msghdr; size_t dom_msgsz; } Domain; /* * Perform a binary search of a message file (described by the Msghdr) for a * msgid (string). Given a match return the associated msgstr, otherwise * return the original msgid. */ static const char * msgid_to_msgstr(const Msghdr *msghdr, const char *msgid) { const Msglst *list, *_list; const char *ids, *strs, *_msgid; int off, var; /* * Establish pointers to the message list (we actually start the search * in the middle of this list (hdr->midlst), the msgid strings (ids) * and the msgstr strings (strs). */ list = (const Msglst *)&msghdr[1]; ids = (const char *)&list[msghdr->hdr_lstcnt]; strs = (const char *)&ids[msghdr->hdr_msgidsz]; off = msghdr->hdr_midlst; do { _list = list + off; _msgid = ids + _list->lst_idoff; if ((var = strcmp(_msgid, msgid)) == 0) return (strs + _list->lst_stroff); if (var < 0) { if ((off = _list->lst_less) == LEAFINDICATOR) return (msgid); } else { if ((off = _list->lst_more) == LEAFINDICATOR) return (msgid); } } while (off); /* NOTREACHED */ return (NULL); /* keep gcc happy */ } /* * Open a message file. Following the model of setlocale(3c) we obtain the * message file for the specified locale. Normally this is: * * /usr/lib/locale/`locale'/LC_MESSAGES/`domain'.mo * * The locale was determined during initial environment processing (see * readenv()), which was determined from an LC_ALL, LC_MESSAGES or LANG * setting. If no locale has been specified, or any file processing errors * occur, internationalization is basically disabled. */ static void open_mofile(Domain * dom) { const char *domain = dom->dom_name; char path[PATH_MAX]; int fd; rtld_stat_t status; const Msghdr *msghdr; int count; size_t size_tot, size_old, size_new; dom->dom_msghdr = (Msghdr *)-1; (void) snprintf(path, PATH_MAX, MSG_ORIG(MSG_FMT_MSGFILE), glcs[CI_LCMESSAGES].lc_un.lc_ptr, domain); if ((fd = open(path, O_RDONLY, 0)) == -1) return; if ((rtld_fstat(fd, &status) == -1) || (status.st_size < sizeof (Msghdr))) { (void) close(fd); return; } /* LINTED */ if ((msghdr = (Msghdr *)mmap(0, status.st_size, PROT_READ, MAP_SHARED, fd, 0)) == (Msghdr *)-1) { (void) close(fd); return; } (void) close(fd); /* checks if opened file is msg file */ count = msghdr->hdr_lstcnt; if (((count - 1) / 2) != msghdr->hdr_midlst) { (void) munmap((caddr_t)msghdr, status.st_size); return; } size_tot = msghdr->hdr_lstsz; size_old = OLD_MSG_STRUCT_SIZE * count; size_new = (int)NEW_MSG_STRUCT_SIZE * count; if ((size_tot != size_old) && (size_tot != size_new)) { (void) munmap((caddr_t)msghdr, status.st_size); return; } size_tot = msghdr->hdr_msgidsz + msghdr->hdr_msgstrsz + (int)sizeof (Msghdr); if ((size_tot + size_old < status.st_size) && (size_tot + size_new < status.st_size)) { (void) munmap((caddr_t)msghdr, status.st_size); return; } /* * We have a good message file, initialize the Domain information. */ dom->dom_msghdr = msghdr; dom->dom_msgsz = status.st_size; } /* * Two interfaces are established to support our internationalization. * gettext(3i) calls originate from all link-editor libraries, and thus the * SUNW_OST_SGS domain is assumed. dgettext() calls originate from * dependencies such as libelf and libc. * * Presently we support two domains (libc's strerror() uses SUNW_OST_OSLIB). * If ld.so.1's dependencies evolve to require more then the `domain' array * maintained below can be enlarged or made more dynamic in nature. */ char * dgettext(const char *domain, const char *msgid) { static int domaincnt = 0; static Domain *domains; Domain *_domain; int cnt; if (glcs[CI_LCMESSAGES].lc_un.lc_val == 0) return ((char *)msgid); /* * Determine if we've initialized any domains yet. */ if (domaincnt == 0) { if ((domains = calloc(sizeof (Domain), 2)) == NULL) return ((char *)msgid); domains[0].dom_name = MSG_ORIG(MSG_SUNW_OST_SGS); domains[1].dom_name = MSG_ORIG(MSG_SUNW_OST_OSLIB); domaincnt = 2; } /* * If this is a new locale make sure we clean up any old ones. */ if (rtld_flags & RT_FL_NEWLOCALE) { cnt = 0; for (_domain = domains; cnt < domaincnt; _domain++, cnt++) { if (_domain->dom_msghdr == 0) continue; if (_domain->dom_msghdr != (Msghdr *)-1) (void) munmap((caddr_t)_domain->dom_msghdr, _domain->dom_msgsz); _domain->dom_msghdr = 0; } rtld_flags &= ~RT_FL_NEWLOCALE; } /* * Determine which domain we need. */ for (cnt = 0, _domain = domains; cnt < domaincnt; _domain++, cnt++) { if (_domain->dom_name == domain) break; if (strcmp(_domain->dom_name, domain) == 0) break; } if (cnt == domaincnt) return ((char *)msgid); /* * Determine if the domain has been initialized yet. */ if (_domain->dom_msghdr == 0) open_mofile(_domain); if (_domain->dom_msghdr == (Msghdr *)-1) return ((char *)msgid); return ((char *)msgid_to_msgstr(_domain->dom_msghdr, msgid)); } /* * This satisfies any dependencies of code dragged in from libc, as we don't * want libc's gettext implementation in ld.so.1. This routine may not be * referenced, in which case -zignore will discard it. */ char * gettext(const char *msgid) { return ((char *)dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), msgid)); } /* * The sgsmsg.1l use requires the following interface. */ const char * _rtld_msg(Msg mid) { return ((char *)dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), MSG_ORIG(mid))); }