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