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