Print this page
ld: implement -ztype and rework option parsing

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/sgs/libld/common/args.c
          +++ new/usr/src/cmd/sgs/libld/common/args.c
↓ open down ↓ 29 lines elided ↑ open up ↑
  30   30   * Copyright 2017 RackTop Systems.
  31   31   */
  32   32  
  33   33  /*
  34   34   * Publicly available flags are defined in ld(1).   The following flags are
  35   35   * private, and may be removed at any time.
  36   36   *
  37   37   *    OPTION                    MEANING
  38   38   *
  39   39   *    -z dtrace=symbol          assigns symbol to PT_SUNWDTRACE segment,
  40      - *                              providing scratch area for dtrace processing.
       40 + *                              providing scratch area for dtrace processing.
  41   41   *
  42   42   *    -z noreloc                suppress relocation processing.  This provides
  43   43   *                              a mechanism for validating kernel module symbol
  44   44   *                              resolution that would normally incur fatal
  45   45   *                              relocation errors.
  46   46   *
  47   47   *    -z rtldinfo=symbol        assigns symbol to SUNW_RTLDINF dynamic tag,
  48   48   *                              providing pre-initialization specific routines
  49   49   *                              for TLS initialization.
  50   50   *
↓ open down ↓ 45 lines elided ↑ open up ↑
  96   96  } Setstate;
  97   97  
  98   98  static Setstate dflag   = SET_UNKNOWN;
  99   99  static Setstate zdflag  = SET_UNKNOWN;
 100  100  static Setstate Qflag   = SET_UNKNOWN;
 101  101  static Setstate Bdflag  = SET_UNKNOWN;
 102  102  static Setstate zfwflag = SET_UNKNOWN;
 103  103  
 104  104  static Boolean  aflag   = FALSE;
 105  105  static Boolean  bflag   = FALSE;
 106      -static Boolean  rflag   = FALSE;
 107  106  static Boolean  sflag   = FALSE;
 108  107  static Boolean  zinflag = FALSE;
 109  108  static Boolean  zlflag  = FALSE;
 110  109  static Boolean  Bgflag  = FALSE;
 111  110  static Boolean  Blflag  = FALSE;
 112  111  static Boolean  Beflag  = FALSE;
 113  112  static Boolean  Bsflag  = FALSE;
 114  113  static Boolean  Dflag   = FALSE;
 115      -static Boolean  Gflag   = FALSE;
 116  114  static Boolean  Vflag   = FALSE;
 117  115  
      116 +enum output_type {
      117 +        OT_RELOC,               /* relocatable object */
      118 +        OT_SHARED,              /* shared object */
      119 +        OT_EXEC,                /* dynamic executable */
      120 +        OT_KMOD,                /* kernel module */
      121 +};
      122 +
      123 +static enum output_type otype = OT_EXEC;
      124 +
 118  125  /*
 119  126   * ztflag's state is set by pointing it to the matching string:
 120  127   *      text | textoff | textwarn
 121  128   */
 122  129  static const char       *ztflag = NULL;
 123  130  
 124  131  /*
 125  132   * Remember the guidance flags that result from the initial -z guidance
 126  133   * option, so that they can be compared to any that follow. We only want
 127  134   * to issue a warning when they differ.
↓ open down ↓ 93 lines elided ↑ open up ↑
 221  228          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRL));
 222  229          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRREL));
 223  230          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRS));
 224  231          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRSN));
 225  232          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRSGRP));
 226  233          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZSCAP));
 227  234          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTARG));
 228  235          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZT));
 229  236          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTO));
 230  237          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTW));
      238 +        (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTY));
 231  239          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZWRAP));
 232  240          (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZVER));
 233  241  }
 234  242  
 235  243  /*
 236  244   * Rescan the archives seen on the command line in order
 237  245   * to handle circularly dependent archives, stopping when
 238  246   * no further member extraction occurs.
 239  247   *
 240  248   * entry:
↓ open down ↓ 65 lines elided ↑ open up ↑
 306  314           * in exactly the same manner we would have if no option were present.
 307  315           */
 308  316          if ((ofl->ofl_guideflags & (FLG_OFG_ENABLE | FLG_OFG_NO_ALL)) ==
 309  317              (FLG_OFG_ENABLE | FLG_OFG_NO_ALL))
 310  318                  ofl->ofl_guideflags &= ~FLG_OFG_ENABLE;
 311  319  
 312  320          if (Plibpath && (Llibdir || Ulibdir))
 313  321                  ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_YP),
 314  322                      Llibdir ? 'L' : 'U');
 315  323  
 316      -        if (rflag) {
 317      -                if (dflag == SET_UNKNOWN)
 318      -                        dflag = SET_FALSE;
      324 +        if ((otype == OT_RELOC) || (otype == OT_KMOD)) {
      325 +                if (otype == OT_RELOC) {
      326 +                        if (dflag == SET_UNKNOWN)
      327 +                                dflag = SET_FALSE;
      328 +                        if ((dflag == SET_TRUE) &&
      329 +                            OFL_GUIDANCE(ofl, FLG_OFG_NO_KMOD)) {
      330 +                                ld_eprintf(ofl, ERR_GUIDANCE,
      331 +                                    MSG_INTL(MSG_GUIDE_KMOD));
      332 +                        }
      333 +                } else if (otype == OT_KMOD) {
      334 +                        if (dflag != SET_UNKNOWN) {
      335 +                                ld_eprintf(ofl, ERR_FATAL,
      336 +                                    MSG_INTL(MSG_MARG_INCOMP),
      337 +                                    MSG_INTL(MSG_MARG_TYPE_KMOD),
      338 +                                    MSG_ORIG(MSG_ARG_D));
      339 +                        }
      340 +
      341 +                        dflag = SET_TRUE;
      342 +                }
      343 +
 319  344                  /*
 320  345                   * Combining relocations when building a relocatable
 321  346                   * object isn't allowed.  Warn the user, but proceed.
 322  347                   */
 323      -                if (ofl->ofl_flags & FLG_OF_COMREL)
      348 +                if (ofl->ofl_flags & FLG_OF_COMREL) {
      349 +                        const char *msg;
      350 +
      351 +                        if (otype == OT_RELOC) {
      352 +                                msg = MSG_INTL(MSG_MARG_REL);
      353 +                        } else {
      354 +                                msg = MSG_INTL(MSG_MARG_TYPE_KMOD);
      355 +                        }
 324  356                          ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_MARG_INCOMP),
 325      -                            MSG_INTL(MSG_MARG_REL),
      357 +                            msg,
 326  358                              MSG_ORIG(MSG_ARG_ZCOMBRELOC));
      359 +                }
 327  360                  ofl->ofl_flags |= FLG_OF_RELOBJ;
      361 +
      362 +                if (otype == OT_KMOD)
      363 +                        ofl->ofl_flags |= FLG_OF_KMOD;
 328  364          } else {
 329  365                  /*
 330  366                   * Translating object capabilities to symbol capabilities is
 331  367                   * only meaningful when creating a relocatable object.
 332  368                   */
 333  369                  if (ofl->ofl_flags & FLG_OF_OTOSCAP)
 334  370                          ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_ONLY),
 335  371                              MSG_ORIG(MSG_ARG_ZSYMBOLCAP),
 336  372                              MSG_INTL(MSG_MARG_REL));
 337  373  
↓ open down ↓ 30 lines elided ↑ open up ↑
 368  404          if (ofl->ofl_interp && (ofl->ofl_flags1 & FLG_OF1_NOINTRP))
 369  405                  ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP),
 370  406                      MSG_ORIG(MSG_ARG_CI), MSG_ORIG(MSG_ARG_ZNOINTERP));
 371  407  
 372  408          if ((ofl->ofl_flags1 & (FLG_OF1_NRLXREL | FLG_OF1_RLXREL)) ==
 373  409              (FLG_OF1_NRLXREL | FLG_OF1_RLXREL))
 374  410                  ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP),
 375  411                      MSG_ORIG(MSG_ARG_ZRELAXRELOC),
 376  412                      MSG_ORIG(MSG_ARG_ZNORELAXRELOC));
 377  413  
 378      -        if (ofl->ofl_filtees && !Gflag)
      414 +        if (ofl->ofl_filtees && (otype != OT_SHARED))
 379  415                  ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_ST_ONLYAVL),
 380  416                      ((ofl->ofl_flags & FLG_OF_AUX) ?
 381  417                      MSG_INTL(MSG_MARG_FILTER_AUX) : MSG_INTL(MSG_MARG_FILTER)));
 382  418  
 383  419          if (dflag != SET_FALSE) {
 384  420                  /*
 385  421                   * Set -Bdynamic on by default, setting is rechecked as input
 386  422                   * files are processed.
 387  423                   */
 388  424                  ofl->ofl_flags |=
↓ open down ↓ 33 lines elided ↑ open up ↑
 422  458                   */
 423  459                  if ((ztflag == MSG_ORIG(MSG_ARG_ZTEXTOFF)) ||
 424  460                      ((ztflag == NULL) && bflag)) {
 425  461                          ofl->ofl_flags1 |= FLG_OF1_TEXTOFF;
 426  462                          ofl->ofl_guideflags |= FLG_OFG_NO_TEXT;
 427  463                  } else if (ztflag == MSG_ORIG(MSG_ARG_ZTEXT)) {
 428  464                          ofl->ofl_flags |= FLG_OF_PURETXT;
 429  465                          ofl->ofl_guideflags |= FLG_OFG_NO_TEXT;
 430  466                  }
 431  467  
 432      -                if (Gflag || !rflag) {
      468 +                if ((otype == OT_SHARED) || (otype == OT_EXEC)) {
 433  469                          /*
 434  470                           * Create a dynamic object.  -Bdirect indicates that all
 435  471                           * references should be bound directly.  This also
 436  472                           * enables lazyloading.  Individual symbols can be
 437  473                           * bound directly (or not) using mapfiles and the
 438  474                           * DIRECT (NODIRECT) qualifier.  With this capability,
 439  475                           * each syminfo entry is tagged SYMINFO_FLG_DIRECTBIND.
 440  476                           * Prior to this per-symbol direct binding, runtime
 441  477                           * direct binding was controlled via the DF_1_DIRECT
 442  478                           * flag.  This flag affected all references from the
↓ open down ↓ 19 lines elided ↑ open up ↑
 462  498                           * references to external objects can still be affected
 463  499                           * by -zdirect or mapfile DIRECT directives.
 464  500                           */
 465  501                          if (Bdflag == SET_FALSE) {
 466  502                                  ofl->ofl_flags1 |= (FLG_OF1_NDIRECT |
 467  503                                      FLG_OF1_NGLBDIR | FLG_OF1_ALNODIR);
 468  504                                  ofl->ofl_flags |= FLG_OF_SYMINFO;
 469  505                          }
 470  506                  }
 471  507  
 472      -                if (!Gflag && !rflag) {
      508 +                if (otype == OT_EXEC) {
 473  509                          /*
 474  510                           * Dynamically linked executable.
 475  511                           */
 476  512                          ofl->ofl_flags |= FLG_OF_EXEC;
 477  513  
 478  514                          if (zdflag != SET_FALSE)
 479  515                                  ofl->ofl_flags |= FLG_OF_NOUNDEF;
 480  516  
 481  517                          /*
 482  518                           * -z textwarn is the default for executables, and
↓ open down ↓ 3 lines elided ↑ open up ↑
 486  522                          ofl->ofl_guideflags |= FLG_OFG_NO_TEXT;
 487  523  
 488  524                          if (Bsflag)
 489  525                                  ld_eprintf(ofl, ERR_FATAL,
 490  526                                      MSG_INTL(MSG_ARG_DY_INCOMP),
 491  527                                      MSG_ORIG(MSG_ARG_BSYMBOLIC));
 492  528                          if (ofl->ofl_soname)
 493  529                                  ld_eprintf(ofl, ERR_FATAL,
 494  530                                      MSG_INTL(MSG_MARG_DY_INCOMP),
 495  531                                      MSG_INTL(MSG_MARG_SONAME));
 496      -                } else if (!rflag) {
      532 +                } else if (otype == OT_SHARED) {
 497  533                          /*
 498  534                           * Shared library.
 499  535                           */
 500  536                          ofl->ofl_flags |= FLG_OF_SHAROBJ;
 501  537  
 502  538                          /*
 503  539                           * By default, print text relocation warnings for
 504  540                           * executables but *not* for shared objects. However,
 505  541                           * if -z guidance is on, issue warnings for shared
 506  542                           * objects as well.
↓ open down ↓ 29 lines elided ↑ open up ↑
 536  572                          if (ztflag == NULL)
 537  573                                  ofl->ofl_flags1 |= FLG_OF1_TEXTOFF;
 538  574                          ofl->ofl_guideflags |= FLG_OFG_NO_TEXT;
 539  575  
 540  576                          if (ofl->ofl_interp)
 541  577                                  ld_eprintf(ofl, ERR_FATAL,
 542  578                                      MSG_INTL(MSG_MARG_INCOMP),
 543  579                                      MSG_INTL(MSG_MARG_REL),
 544  580                                      MSG_ORIG(MSG_ARG_CI));
 545  581                  }
      582 +
      583 +                assert((ofl->ofl_flags & (FLG_OF_SHAROBJ|FLG_OF_EXEC)) !=
      584 +                    (FLG_OF_SHAROBJ|FLG_OF_EXEC));
 546  585          } else {
 547  586                  ofl->ofl_flags |= FLG_OF_STATIC;
 548  587  
 549  588                  if (bflag)
 550  589                          ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP),
 551  590                              MSG_ORIG(MSG_ARG_B));
 552  591                  if (ofl->ofl_soname)
 553  592                          ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_ST_INCOMP),
 554  593                              MSG_INTL(MSG_MARG_SONAME));
 555  594                  if (ofl->ofl_depaudit)
↓ open down ↓ 1 lines elided ↑ open up ↑
 557  596                              MSG_ORIG(MSG_ARG_CP));
 558  597                  if (ofl->ofl_audit)
 559  598                          ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP),
 560  599                              MSG_ORIG(MSG_ARG_P));
 561  600                  if (ofl->ofl_config)
 562  601                          ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP),
 563  602                              MSG_ORIG(MSG_ARG_C));
 564  603                  if (ztflag)
 565  604                          ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP),
 566  605                              MSG_ORIG(MSG_ARG_ZTEXTALL));
 567      -                if (Gflag)
      606 +                if (otype == OT_SHARED)
 568  607                          ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_ST_INCOMP),
 569  608                              MSG_INTL(MSG_MARG_SO));
 570      -                if (aflag && rflag)
      609 +                if (aflag && (otype == OT_RELOC))
 571  610                          ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_INCOMP),
 572  611                              MSG_ORIG(MSG_ARG_A), MSG_INTL(MSG_MARG_REL));
 573  612  
 574      -                if (rflag) {
      613 +                if (otype == OT_RELOC) {
 575  614                          /*
 576  615                           * We can only strip the symbol table and string table
 577  616                           * if no output relocations will refer to them.
 578  617                           */
 579  618                          if (sflag)
 580  619                                  ld_eprintf(ofl, ERR_WARNING,
 581  620                                      MSG_INTL(MSG_ARG_STRIP),
 582  621                                      MSG_INTL(MSG_MARG_REL),
 583  622                                      MSG_INTL(MSG_MARG_STRIP));
 584  623  
↓ open down ↓ 125 lines elided ↑ open up ↑
 710  749   */
 711  750  static uintptr_t
 712  751  createargv(Ofl_desc *ofl, int *usage)
 713  752  {
 714  753          int             argc = 0, idx = 0, ooptind;
 715  754          uintptr_t       ret;
 716  755          char            **argv, *p0;
 717  756  
 718  757          /*
 719  758           * The argument being examined is either:
 720      -         *      ld32=   or
      759 +         *      ld32=   or
 721  760           *      ld64=
 722  761           */
 723  762  #if     defined(_LP64)
 724  763          if (optarg[2] == '3')
 725  764                  return (0);
 726  765  #else
 727  766          if (optarg[2] == '6')
 728  767                  return (0);
 729  768  #endif
 730  769  
↓ open down ↓ 179 lines elided ↑ open up ↑
 910  949           * the flags kept in the output descriptor.
 911  950           */
 912  951          initial_guidance_flags = ofl_guideflags;
 913  952          ofl->ofl_guideflags |= ofl_guideflags;
 914  953          return (TRUE);
 915  954  }
 916  955  
 917  956  /*
 918  957   * Parse the -z assert-deflib option. This option can appear in two different
 919  958   * forms:
 920      - *      -z assert-deflib
 921      - *      -z assert-deflib=libfred.so
      959 + *      -z assert-deflib
      960 + *      -z assert-deflib=libfred.so
 922  961   *
 923  962   * Either form enables this option, the latter form marks libfred.so as an
 924  963   * exempt library from the check. It is valid to have multiple invocations of
 925  964   * the second form. We silently ignore mulitple occurrences of the first form
 926  965   * and multiple invocations of the first form when the second form also occurs.
 927  966   *
 928  967   * We only return false when we have an internal error, such as the failure of
 929  968   * aplist_append. Every other time we return true, but we have the appropriate
 930  969   * fatal flags set beacuse of the ld_eprintf.
 931  970   */
↓ open down ↓ 262 lines elided ↑ open up ↑
1194 1233                          if (*optarg != '\0') {
1195 1234                                  if ((ofl->ofl_depaudit =
1196 1235                                      add_string(ofl->ofl_depaudit,
1197 1236                                      optarg)) == (const char *)S_ERROR)
1198 1237                                          return (S_ERROR);
1199 1238                          }
1200 1239                          break;
1201 1240  
1202 1241                  case 'r':
1203 1242                          DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL));
1204      -                        rflag = TRUE;
     1243 +                        otype = OT_RELOC;
1205 1244                          break;
1206 1245  
1207 1246                  case 'R':
1208 1247                          DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg));
1209 1248  
1210 1249                          /*
1211 1250                           * Multiple instances of this option may occur.  Each
1212 1251                           * additional instance is effectively concatenated to
1213 1252                           * the previous separated by a colon.
1214 1253                           */
↓ open down ↓ 287 lines elided ↑ open up ↑
1502 1541  
1503 1542                          /*
1504 1543                           * Process everything related to -z assert-deflib. This
1505 1544                           * must be done in pass 1 because it gets used in pass
1506 1545                           * 2.
1507 1546                           */
1508 1547                          } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_ASSDEFLIB),
1509 1548                              MSG_ARG_ASSDEFLIB_SIZE) == 0) {
1510 1549                                  if (assdeflib_parse(ofl, optarg) != TRUE)
1511 1550                                          return (S_ERROR);
     1551 +                        } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_TYPE),
     1552 +                            MSG_ARG_TYPE_SIZE) == 0) {
     1553 +                                char *p = optarg + MSG_ARG_TYPE_SIZE;
     1554 +                                if (*p != '=') {
     1555 +                                        ld_eprintf(ofl, ERR_FATAL,
     1556 +                                            MSG_INTL(MSG_ARG_ILLEGAL),
     1557 +                                            MSG_ORIG(MSG_ARG_Z), optarg);
     1558 +                                        return (S_ERROR);
     1559 +                                }
     1560 +
     1561 +                                p++;
     1562 +                                if (strcmp(p,
     1563 +                                    MSG_ORIG(MSG_ARG_TYPE_RELOC)) == 0) {
     1564 +                                        otype = OT_RELOC;
     1565 +                                } else if (strcmp(p,
     1566 +                                    MSG_ORIG(MSG_ARG_TYPE_EXEC)) == 0) {
     1567 +                                        otype = OT_EXEC;
     1568 +                                } else if (strcmp(p,
     1569 +                                    MSG_ORIG(MSG_ARG_TYPE_SHARED)) == 0) {
     1570 +                                        otype = OT_SHARED;
     1571 +                                } else if (strcmp(p,
     1572 +                                    MSG_ORIG(MSG_ARG_TYPE_KMOD)) == 0) {
     1573 +                                        otype = OT_KMOD;
     1574 +                                } else {
     1575 +                                        ld_eprintf(ofl, ERR_FATAL,
     1576 +                                            MSG_INTL(MSG_ARG_ILLEGAL),
     1577 +                                            MSG_ORIG(MSG_ARG_Z), optarg);
     1578 +                                        return (S_ERROR);
     1579 +                                }
1512 1580                          /*
1513 1581                           * The following options just need validation as they
1514 1582                           * are interpreted on the second pass through the
1515 1583                           * command line arguments.
1516 1584                           */
1517 1585                          } else if (
1518 1586                              strncmp(optarg, MSG_ORIG(MSG_ARG_INITARRAY),
1519 1587                              MSG_ARG_INITARRAY_SIZE) &&
1520 1588                              strncmp(optarg, MSG_ORIG(MSG_ARG_FINIARRAY),
1521 1589                              MSG_ARG_FINIARRAY_SIZE) &&
↓ open down ↓ 98 lines elided ↑ open up ↑
1620 1688                              MSG_ORIG(MSG_STR_LD_DYNAMIC)) &&
1621 1689                              strcmp(optarg, MSG_ORIG(MSG_ARG_STATIC))) {
1622 1690                                  ld_eprintf(ofl, ERR_FATAL,
1623 1691                                      MSG_INTL(MSG_ARG_ILLEGAL),
1624 1692                                      MSG_ORIG(MSG_ARG_CB), optarg);
1625 1693                          }
1626 1694                          break;
1627 1695  
1628 1696                  case 'G':
1629 1697                          DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL));
1630      -                        Gflag = TRUE;
     1698 +                        otype = OT_SHARED;
1631 1699                          break;
1632 1700  
1633 1701                  case 'L':
1634 1702                          DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg));
1635 1703                          break;
1636 1704  
1637 1705                  case 'M':
1638 1706                          DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg));
1639 1707                          if (aplist_append(&(ofl->ofl_maps), optarg,
1640 1708                              AL_CNT_OFL_MAPFILES) == NULL)
↓ open down ↓ 543 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX