Print this page
12314 ld fatal warnings miss some guidance messages
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/sgs/libld/common/ldmain.c
+++ new/usr/src/cmd/sgs/libld/common/ldmain.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
↓ open down ↓ |
16 lines elided |
↑ open up ↑ |
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 1988 AT&T
24 24 * All Rights Reserved
25 25 *
26 26 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
27 + *
28 + * Copyright 2020 Joyent, Inc.
27 29 */
28 30
29 31 /*
30 32 * Processing of relocatable objects and shared objects.
31 33 */
32 34
33 35 /*
34 36 * ld -- link/editor main program
35 37 */
36 38 #include <sys/types.h>
37 39 #include <sys/time.h>
38 40 #include <sys/mman.h>
39 41 #include <string.h>
40 42 #include <stdio.h>
41 43 #include <locale.h>
42 44 #include <stdarg.h>
43 45 #include <debug.h>
44 46 #include "msg.h"
45 47 #include "_libld.h"
46 48
47 49 /*
48 50 * All target specific code is referenced via this global variable, which
49 51 * is initialized in ld_main(). This allows the linker to function as
50 52 * a cross linker, by vectoring to the target-specific code for the
51 53 * current target machine.
52 54 */
53 55 Target ld_targ;
54 56
55 57 /*
56 58 * A default library search path is used if one was not supplied on the command
57 59 * line. Note: these strings can not use MSG_ORIG() since they are modified as
58 60 * part of the path processing.
59 61 */
60 62 #if defined(_ELF64)
61 63 static char def_Plibpath[] = "/lib/64:/usr/lib/64";
62 64 #else
63 65 static char def_Plibpath[] = "/usr/ccs/lib:/lib:/usr/lib";
64 66 #endif
65 67
66 68 /*
67 69 * A default elf header provides for simplifying diagnostic processing.
68 70 */
69 71 static Ehdr def_ehdr = { { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
70 72 ELFCLASSNONE, ELFDATANONE }, 0, EM_NONE,
71 73 EV_CURRENT };
72 74
73 75 /*
74 76 * ld-centric wrapper on top of veprintf():
75 77 * - Accepts output descriptor rather than linkmap list
76 78 * - Sets the FLG_OF_FATAL/FLG_OF_WARN flags as necessary
77 79 */
78 80 void
79 81 ld_eprintf(Ofl_desc *ofl, Error error, const char *format, ...)
80 82 {
81 83 va_list args;
82 84
83 85 /* Set flag indicating type of error being issued */
84 86 switch (error) {
85 87 case ERR_NONE:
86 88 case ERR_WARNING_NF:
87 89 break;
88 90 case ERR_WARNING:
89 91 ofl->ofl_flags |= FLG_OF_WARN;
90 92 break;
91 93 case ERR_GUIDANCE:
92 94 if ((ofl->ofl_guideflags & FLG_OFG_ENABLE) == 0)
93 95 return;
94 96 ofl->ofl_guideflags |= FLG_OFG_ISSUED;
95 97 ofl->ofl_flags |= FLG_OF_WARN;
96 98 break;
97 99 default:
98 100 ofl->ofl_flags |= FLG_OF_FATAL;
99 101 }
100 102
101 103 /* Issue the error */
102 104 va_start(args, format);
103 105 veprintf(ofl->ofl_lml, error, format, args);
104 106 va_end(args);
105 107 }
106 108
107 109 /*
108 110 * Establish the global state necessary to link the desired machine
109 111 * target, as reflected by the ld_targ global variable.
110 112 */
111 113 int
112 114 ld_init_target(Lm_list *lml, Half mach)
113 115 {
114 116 switch (mach) {
115 117 case EM_386:
116 118 case EM_AMD64:
117 119 ld_targ = *ld_targ_init_x86();
118 120 break;
119 121
120 122 case EM_SPARC:
121 123 case EM_SPARC32PLUS:
122 124 case EM_SPARCV9:
123 125 ld_targ = *ld_targ_init_sparc();
124 126 break;
125 127
126 128 default:
127 129 {
128 130 Conv_inv_buf_t inv_buf;
129 131
130 132 eprintf(lml, ERR_FATAL, MSG_INTL(MSG_TARG_UNSUPPORTED),
131 133 conv_ehdr_mach(mach, 0, &inv_buf));
132 134 return (1);
133 135 }
134 136 }
135 137
136 138 return (0);
137 139 }
138 140
139 141
140 142 /*
141 143 * The main program
142 144 */
143 145 int
144 146 ld_main(int argc, char **argv, Half mach)
145 147 {
146 148 char *sgs_support; /* SGS_SUPPORT environment string */
147 149 Half etype;
148 150 Ofl_desc *ofl;
149 151 ofl_flag_t save_flg_of_warn;
150 152
151 153 /*
152 154 * Establish a base time. Total time diagnostics are relative to
153 155 * entering the link-editor here.
154 156 */
155 157 (void) gettimeofday(&DBG_TOTALTIME, NULL);
156 158 DBG_DELTATIME = DBG_TOTALTIME;
157 159
158 160 /* Output file descriptor */
159 161 if ((ofl = libld_calloc(1, sizeof (Ofl_desc))) == 0)
160 162 return (1);
161 163
162 164 /* Initialize target state */
163 165 if (ld_init_target(NULL, mach) != 0)
164 166 return (1);
165 167
166 168 /*
167 169 * Set up the default output ELF header to satisfy diagnostic
168 170 * requirements, and initialize the machine and class details.
169 171 */
170 172 ofl->ofl_dehdr = &def_ehdr;
171 173 def_ehdr.e_ident[EI_CLASS] = ld_targ.t_m.m_class;
172 174 def_ehdr.e_ident[EI_DATA] = ld_targ.t_m.m_data;
173 175 def_ehdr.e_machine = ld_targ.t_m.m_mach;
174 176
175 177 /*
176 178 * Build up linker version string
177 179 */
178 180 if ((ofl->ofl_sgsid = (char *)libld_calloc(MSG_SGS_ID_SIZE +
179 181 strlen(link_ver_string) + 1, 1)) == NULL)
180 182 return (1);
181 183 (void) strcpy(ofl->ofl_sgsid, MSG_ORIG(MSG_SGS_ID));
182 184 (void) strcat(ofl->ofl_sgsid, link_ver_string);
183 185
184 186 /*
185 187 * Argument pass one. Get all the input flags (skip any files) and
186 188 * check for consistency. Return from ld_process_flags() marks the
187 189 * end of mapfile processing. The entrance criteria and segment
188 190 * descriptors are complete and in their final form.
189 191 */
190 192 if (ld_process_flags(ofl, argc, argv) == S_ERROR) {
191 193 /* If any ERR_GUIDANCE messages were issued, add a summary */
192 194 if (ofl->ofl_guideflags & FLG_OFG_ISSUED)
193 195 ld_eprintf(ofl, ERR_GUIDANCE,
194 196 MSG_INTL(MSG_GUIDE_SUMMARY));
195 197 return (1);
196 198 }
197 199 if (ofl->ofl_flags & FLG_OF_FATAL) {
198 200 ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_FLAGS));
199 201 /* If any ERR_GUIDANCE messages were issued, add a summary */
200 202 if (ofl->ofl_guideflags & FLG_OFG_ISSUED)
201 203 ld_eprintf(ofl, ERR_GUIDANCE,
202 204 MSG_INTL(MSG_GUIDE_SUMMARY));
203 205 return (1);
204 206 }
205 207
206 208 /*
207 209 * At this point a call such as ld -V is considered complete.
208 210 */
209 211 if (ofl->ofl_flags1 & FLG_OF1_DONE)
210 212 return (0);
211 213
212 214 /* Initialize signal handler */
213 215 ld_init_sighandler(ofl);
214 216
215 217 /*
216 218 * Determine whether any support libraries should be loaded,
217 219 * (either through the SGS_SUPPORT environment variable and/or
218 220 * through the -S option).
219 221 */
220 222 #if defined(_LP64)
221 223 if ((sgs_support = getenv(MSG_ORIG(MSG_SGS_SUPPORT_64))) == NULL)
222 224 #else
223 225 if ((sgs_support = getenv(MSG_ORIG(MSG_SGS_SUPPORT_32))) == NULL)
224 226 #endif
225 227 sgs_support = getenv(MSG_ORIG(MSG_SGS_SUPPORT));
226 228
227 229 if (sgs_support && sgs_support[0]) {
228 230 const char *sep = MSG_ORIG(MSG_STR_COLON);
229 231 char *lib;
230 232 char *lasts;
231 233
232 234 DBG_CALL(Dbg_support_req(ofl->ofl_lml, sgs_support,
233 235 DBG_SUP_ENVIRON));
234 236 if ((lib = strtok_r(sgs_support, sep, &lasts)) != NULL) {
235 237 do {
236 238 if (ld_sup_loadso(ofl, lib) == S_ERROR)
237 239 return (ld_exit(ofl));
238 240 DBG_CALL(Dbg_util_nl(ofl->ofl_lml, DBG_NL_STD));
239 241
240 242 } while ((lib = strtok_r(NULL, sep, &lasts)) != NULL);
241 243 }
242 244 }
243 245 if (lib_support) {
244 246 Aliste idx;
245 247 char *lib;
246 248
247 249 for (APLIST_TRAVERSE(lib_support, idx, lib)) {
248 250 DBG_CALL(Dbg_support_req(ofl->ofl_lml, lib,
249 251 DBG_SUP_CMDLINE));
250 252 if (ld_sup_loadso(ofl, lib) == S_ERROR)
251 253 return (ld_exit(ofl));
252 254 DBG_CALL(Dbg_util_nl(ofl->ofl_lml, DBG_NL_STD));
253 255 }
254 256 }
255 257
256 258 DBG_CALL(Dbg_ent_print(ofl->ofl_lml,
257 259 ofl->ofl_dehdr->e_ident[EI_OSABI], ofl->ofl_dehdr->e_machine,
258 260 ofl->ofl_ents));
259 261 DBG_CALL(Dbg_seg_list(ofl->ofl_lml,
260 262 ofl->ofl_dehdr->e_ident[EI_OSABI], ofl->ofl_dehdr->e_machine,
261 263 ofl->ofl_segs));
262 264
263 265 /*
264 266 * The objscnt and soscnt variables were used to estimate the expected
265 267 * input files, and size the symbol hash buckets accordingly. Reset
266 268 * these values now, so as to gain an accurate count from pass two, for
267 269 * later statistics diagnostics.
268 270 */
269 271 ofl->ofl_objscnt = ofl->ofl_soscnt = 0;
270 272
271 273 /*
272 274 * Determine whether we can create the file before going any further.
273 275 */
274 276 if (ld_open_outfile(ofl) == S_ERROR)
275 277 return (ld_exit(ofl));
276 278
277 279 /*
278 280 * If the user didn't supply a library path supply a default. And, if
279 281 * no run-path has been specified (-R), see if the environment variable
280 282 * is in use (historic).
281 283 */
282 284 if (Plibpath == NULL)
283 285 Plibpath = def_Plibpath;
284 286
285 287 if (ofl->ofl_rpath == NULL) {
286 288 char *rpath;
287 289
288 290 if (((rpath = getenv(MSG_ORIG(MSG_LD_RUN_PATH))) != NULL) &&
289 291 rpath[0])
290 292 ofl->ofl_rpath = rpath;
291 293 }
292 294
293 295 /*
294 296 * Argument pass two. Input all libraries and objects.
295 297 */
296 298 if (ld_lib_setup(ofl) == S_ERROR)
297 299 return (ld_exit(ofl));
298 300
299 301 /*
300 302 * Call ld_start() with the etype of our output file and the
301 303 * output file name.
302 304 */
303 305 if (ofl->ofl_flags & FLG_OF_SHAROBJ)
304 306 etype = ET_DYN;
305 307 else if (ofl->ofl_flags & FLG_OF_RELOBJ)
306 308 etype = ET_REL;
307 309 else
308 310 etype = ET_EXEC;
309 311
310 312 ld_sup_start(ofl, etype, argv[0]);
311 313
312 314 /*
313 315 * Process all input files.
314 316 */
315 317 if (ld_process_files(ofl, argc, argv) == S_ERROR)
316 318 return (ld_exit(ofl));
317 319 if (ofl->ofl_flags & FLG_OF_FATAL) {
318 320 ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_FILES),
319 321 ofl->ofl_name);
320 322 return (ld_exit(ofl));
321 323 }
322 324
323 325 ld_sup_input_done(ofl);
324 326
325 327 /*
326 328 * Now that all input section processing is complete, validate and
327 329 * process any SHT_SUNW_move sections.
328 330 */
329 331 if (ofl->ofl_ismove && (ld_process_move(ofl) == S_ERROR))
330 332 return (ld_exit(ofl));
331 333
332 334 /*
333 335 * Before validating all symbols count the number of relocation entries.
334 336 * If copy relocations exist, COMMON symbols must be generated which are
335 337 * assigned to the executables .bss. During sym_validate() the actual
336 338 * size and alignment of the .bss is calculated. Doing things in this
337 339 * order reduces the number of symbol table traversals required (however
338 340 * it does take a little longer for the user to be told of any undefined
339 341 * symbol errors).
340 342 */
341 343 if (ld_reloc_init(ofl) == S_ERROR)
342 344 return (ld_exit(ofl));
343 345
344 346 /*
345 347 * We need to know if FLG_OF_WARN is currently set, in case
346 348 * we need to honor a -z fatal-warnings request. However, we also
347 349 * need to know if a warning due to symbol validation results from
348 350 * the upcoming call to ld_sym_validate() in order to issue the
349 351 * appropriate message for it. So we save the current value,
350 352 * and clear the main flag.
351 353 */
352 354 save_flg_of_warn = ofl->ofl_flags & FLG_OF_WARN;
353 355 ofl->ofl_flags &= ~FLG_OF_WARN;
354 356
355 357 if (ld_sym_validate(ofl) == S_ERROR)
356 358 return (ld_exit(ofl));
357 359
358 360 /*
359 361 * Now that all symbol processing is complete see if any undefined
360 362 * references still remain. If we observed undefined symbols the
361 363 * FLG_OF_FATAL bit will be set: If creating a static executable, or a
362 364 * dynamic executable or shared object with the -zdefs flag set, this
363 365 * condition is fatal. If creating a shared object with the -Bsymbolic
364 366 * flag set, this condition is simply a warning.
365 367 */
366 368 if (ofl->ofl_flags & FLG_OF_FATAL)
367 369 ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_SYM_FATAL),
368 370 ofl->ofl_name);
369 371 else if (ofl->ofl_flags & FLG_OF_WARN)
370 372 ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_ARG_SYM_WARN));
371 373
372 374 /*
373 375 * Guidance: Use -z defs|nodefs when building shared objects.
374 376 *
375 377 * ld_sym_validate() will mask this guidance message out unless we are
376 378 * intended to send it here, so all we need to do is use OFL_GUIDANCE()
377 379 * to decide whether to issue it or not.
378 380 */
379 381 if (OFL_GUIDANCE(ofl, FLG_OFG_NO_DEFS))
380 382 ld_eprintf(ofl, ERR_GUIDANCE, MSG_INTL(MSG_GUIDE_DEFS));
381 383
382 384 /*
383 385 * Symbol processing was the final step before we start producing the
384 386 * output object. At this time, if we've seen warnings and the
385 387 * -z fatal-warnings option is specified, promote them to fatal, which
386 388 * will cause us to exit without creating an object.
387 389 *
388 390 * We didn't do this as the warnings were reported in order to
389 391 * maximize the number of problems a given link-editor invocation
390 392 * can diagnose. This is safe, since warnings are by definition events
391 393 * one can choose to ignore.
392 394 */
393 395 if (((ofl->ofl_flags | save_flg_of_warn) &
394 396 (FLG_OF_WARN | FLG_OF_FATWARN)) ==
395 397 (FLG_OF_WARN | FLG_OF_FATWARN))
396 398 ofl->ofl_flags |= FLG_OF_FATAL;
397 399
398 400 /*
399 401 * If fatal errors occurred in symbol processing, or due to warnings
400 402 * promoted by -z fatal-warnings, this is the end of the line.
401 403 */
402 404 if (ofl->ofl_flags & FLG_OF_FATAL)
403 405 return (ld_exit(ofl));
404 406
405 407 /*
406 408 * Generate any necessary sections.
407 409 */
408 410 if (ld_make_sections(ofl) == S_ERROR)
409 411 return (ld_exit(ofl));
410 412
411 413 /*
412 414 * Now that all sections have been added to the output file, determine
413 415 * whether any mapfile section ordering was specified, and verify that
414 416 * all mapfile ordering directives have been matched. Issue a warning
415 417 * for any directives that have not been matched.
416 418 * Also, if SHF_ORDERED sections exist, set up sort key values.
417 419 */
418 420 if (ofl->ofl_flags & (FLG_OF_OS_ORDER | FLG_OF_KEY))
419 421 ld_sec_validate(ofl);
420 422
421 423 /*
422 424 * Having collected all the input data create the initial output file
423 425 * image, assign virtual addresses to the image, and generate a load
424 426 * map if the user requested one.
425 427 */
426 428 if (ld_create_outfile(ofl) == S_ERROR)
427 429 return (ld_exit(ofl));
428 430
429 431 if (ld_update_outfile(ofl) == S_ERROR)
430 432 return (ld_exit(ofl));
431 433 if (ofl->ofl_flags & FLG_OF_GENMAP)
432 434 ld_map_out(ofl);
433 435
434 436 /*
435 437 * Build relocation sections and perform any relocation updates.
436 438 */
437 439 if (ld_reloc_process(ofl) == S_ERROR)
438 440 return (ld_exit(ofl));
439 441
440 442 /*
441 443 * Fill in contents for unwind header (.eh_frame_hdr)
442 444 */
443 445 if (ld_unwind_populate_hdr(ofl) == S_ERROR)
444 446 return (ld_exit(ofl));
445 447
446 448 /*
447 449 * Finally create the files elf checksum.
448 450 */
449 451 if (ofl->ofl_checksum)
450 452 *ofl->ofl_checksum = (Xword)elf_checksum(ofl->ofl_elf);
451 453
452 454 /*
453 455 * If this is a cross link to a target with a different byte
454 456 * order than the linker, swap the data to the target byte order.
455 457 */
456 458 if (((ofl->ofl_flags1 & FLG_OF1_ENCDIFF) != 0) &&
457 459 (_elf_swap_wrimage(ofl->ofl_elf) != 0)) {
458 460 ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_SWAP_WRIMAGE),
459 461 ofl->ofl_name);
460 462 return (ld_exit(ofl));
461 463 }
462 464
463 465 /*
464 466 * We're done, so make sure the updates are flushed to the output file.
465 467 */
466 468 if ((ofl->ofl_size = elf_update(ofl->ofl_welf, ELF_C_WRITE)) == 0) {
467 469 ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_UPDATE),
468 470 ofl->ofl_name);
469 471 return (ld_exit(ofl));
470 472 }
471 473
472 474 ld_sup_atexit(ofl, 0);
↓ open down ↓ |
436 lines elided |
↑ open up ↑ |
473 475
474 476 DBG_CALL(Dbg_statistics_ld(ofl));
475 477 DBG_CALL(Dbg_basic_finish(ofl->ofl_lml));
476 478
477 479 /*
478 480 * Wrap up debug output file if one is open
479 481 */
480 482 dbg_cleanup();
481 483
482 484 /* If any ERR_GUIDANCE messages were issued, add a summary */
483 - if (ofl->ofl_guideflags & FLG_OFG_ISSUED)
485 + if (ofl->ofl_guideflags & FLG_OFG_ISSUED) {
484 486 ld_eprintf(ofl, ERR_GUIDANCE, MSG_INTL(MSG_GUIDE_SUMMARY));
487 + ofl->ofl_guideflags &= ~FLG_OFG_ISSUED;
488 + }
485 489
486 490 /*
491 + * One final check for any new warnings we found that should fail the
492 + * link edit.
493 + */
494 + if ((ofl->ofl_flags & (FLG_OF_WARN | FLG_OF_FATWARN)) ==
495 + (FLG_OF_WARN | FLG_OF_FATWARN))
496 + return (ld_exit(ofl));
497 +
498 + /*
487 499 * For performance reasons we don't actually free up the memory we've
488 500 * allocated, it will be freed when we exit.
489 501 *
490 502 * But the below line can be uncommented if/when we want to measure how
491 503 * our memory consumption and freeing are doing. We should be able to
492 504 * free all the memory that has been allocated as part of the link-edit
493 505 * process.
494 506 */
495 507 /* ld_ofl_cleanup(ofl); */
496 508 return (0);
497 509 }
498 510
499 511 /*
500 512 * Cleanup an Ifl_desc.
501 513 */
502 514 static void
503 515 ifl_list_cleanup(APlist *apl)
504 516 {
505 517 Aliste idx;
506 518 Ifl_desc *ifl;
507 519
508 520 for (APLIST_TRAVERSE(apl, idx, ifl)) {
509 521 if (ifl->ifl_elf)
510 522 (void) elf_end(ifl->ifl_elf);
511 523 }
512 524 }
513 525
514 526 /*
515 527 * Cleanup all memory that has been dynamically allocated during libld
516 528 * processing and elf_end() all Elf descriptors that are still open.
517 529 */
518 530 void
519 531 ld_ofl_cleanup(Ofl_desc *ofl)
520 532 {
521 533 Ld_heap *chp, *php;
522 534 Ar_desc *adp;
523 535 Aliste idx;
524 536
525 537 ifl_list_cleanup(ofl->ofl_objs);
526 538 ofl->ofl_objs = NULL;
527 539 ifl_list_cleanup(ofl->ofl_sos);
528 540 ofl->ofl_sos = NULL;
529 541
530 542 for (APLIST_TRAVERSE(ofl->ofl_ars, idx, adp)) {
531 543 Ar_aux *aup;
532 544 Elf_Arsym *arsym;
533 545
534 546 for (arsym = adp->ad_start, aup = adp->ad_aux;
535 547 arsym->as_name; ++arsym, ++aup) {
536 548 if ((aup->au_mem) && (aup->au_mem != FLG_ARMEM_PROC)) {
537 549 (void) elf_end(aup->au_mem->am_elf);
538 550
539 551 /*
540 552 * Null out all entries to this member so
541 553 * that we don't attempt to elf_end() it again.
542 554 */
543 555 ld_ar_member(adp, arsym, aup, 0);
544 556 }
545 557 }
546 558 (void) elf_end(adp->ad_elf);
547 559 }
548 560 ofl->ofl_ars = NULL;
549 561
550 562 (void) elf_end(ofl->ofl_elf);
551 563 (void) elf_end(ofl->ofl_welf);
552 564
553 565 for (chp = ld_heap, php = NULL; chp; php = chp, chp = chp->lh_next) {
554 566 if (php)
555 567 (void) munmap((void *)php,
556 568 (size_t)php->lh_end - (size_t)php);
557 569 }
558 570 if (php)
559 571 (void) munmap((void *)php, (size_t)php->lh_end - (size_t)php);
560 572
561 573 ld_heap = NULL;
562 574 }
↓ open down ↓ |
66 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX