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