/*
* 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 <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <libintl.h>
#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;
for (;;) {
_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);
}
}
/* 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)));
}