static void relativizeOsoSymbols()

in src/com/facebook/buck/cxx/toolchain/objectfile/Machos.java [96:291]


  static void relativizeOsoSymbols(FileChannel file, ImmutableMap<Path, Path> cellRoots)
      throws IOException, MachoException {
    cellRoots.forEach(
        (from, to) -> {
          Preconditions.checkArgument(from.isAbsolute());
          Preconditions.checkArgument(!to.isAbsolute());
        });

    long size = file.size();
    MappedByteBuffer map = file.map(FileChannel.MapMode.READ_WRITE, 0, size);

    MachoHeader header = getHeader(map);

    int symbolTableOffset = 0;
    int symbolTableCount = 0;
    int stringTableOffset = 0;
    int stringTableSizePosition = 0;
    int stringTableSize = 0;
    boolean symbolTableSegmentFound = false;
    int segmentSizePosition = 0;
    long segmentSize = 0;
    boolean linkEditSegmentFound = false;
    int segmentFileSizePosition = 0;
    int segment64FileSizePosition = 0;

    int commandsCount = header.getCommandsCount();
    for (int i = 0; i < commandsCount; i++) {
      int commandStart = map.position(); // NOPMD
      int command = ObjectFileScrubbers.getLittleEndianInt(map);
      int commandSize = ObjectFileScrubbers.getLittleEndianInt(map); // NOPMD
      switch (command) {
        case LC_SYMTAB:
          symbolTableOffset = ObjectFileScrubbers.getLittleEndianInt(map);
          symbolTableCount = ObjectFileScrubbers.getLittleEndianInt(map);
          stringTableOffset = ObjectFileScrubbers.getLittleEndianInt(map);
          stringTableSizePosition = map.position();
          stringTableSize = ObjectFileScrubbers.getLittleEndianInt(map);
          symbolTableSegmentFound = true;
          break;
        case LC_SEGMENT:
          byte[] segmentNameBytes = ObjectFileScrubbers.getBytes(map, 16);
          String segmentName = new String(segmentNameBytes, Charsets.US_ASCII);
          if (segmentName.startsWith(LINKEDIT)) {
            linkEditSegmentFound = true;
            /* vm address */ ObjectFileScrubbers.getLittleEndianInt(map);
            /* vm size */ ObjectFileScrubbers.getLittleEndianInt(map);
            /* segment file offset */ ObjectFileScrubbers.getLittleEndianInt(map);
            segmentFileSizePosition = map.position();
            segmentSize = ObjectFileScrubbers.getLittleEndianInt(map);
            /* maximum vm protection */ ObjectFileScrubbers.getLittleEndianInt(map);
            /* initial vm protection */ ObjectFileScrubbers.getLittleEndianInt(map);
            /* number of sections */ ObjectFileScrubbers.getLittleEndianInt(map);
            /* flags */ ObjectFileScrubbers.getLittleEndianInt(map);

            if (segmentSizePosition != 0) {
              throw new MachoException("multiple map segment commands map string table");
            }
            segmentSizePosition = segmentFileSizePosition;
          }
          break;
        case LC_SEGMENT_64:
          byte[] segment64NameBytes = ObjectFileScrubbers.getBytes(map, 16);
          String segment64Name = new String(segment64NameBytes, Charsets.US_ASCII);
          if (segment64Name.startsWith(LINKEDIT)) {
            linkEditSegmentFound = true;
            /* vm address */ ObjectFileScrubbers.getLittleEndianLong(map);
            /* vm size */ ObjectFileScrubbers.getLittleEndianLong(map);
            /* segment file offset */ ObjectFileScrubbers.getLittleEndianLong(map);
            segment64FileSizePosition = map.position();
            segmentSize = ObjectFileScrubbers.getLittleEndianLong(map);
            /* maximum vm protection */ ObjectFileScrubbers.getLittleEndianInt(map);
            /* initial vm protection */ ObjectFileScrubbers.getLittleEndianInt(map);
            /* number of sections */ ObjectFileScrubbers.getLittleEndianInt(map);
            /* flags */ ObjectFileScrubbers.getLittleEndianInt(map);

            if (segmentSizePosition != 0) {
              throw new MachoException("multiple map segment commands map string table");
            }
            segmentSizePosition = segment64FileSizePosition;
          }
          break;
      }
      map.position(commandStart + commandSize);
    }

    if (!linkEditSegmentFound) {
      /*The OSO entries are identified in segments named __LINKEDIT. If no segment is found with
      that name, there is nothing to scrub.*/
      return;
    }
    if (stringTableSize == 0) {
      return;
    }

    if (!isValidFilesize(header, segmentSize)) {
      throw new MachoException("32bit map segment file size too big");
    }

    if (!symbolTableSegmentFound) {
      throw new MachoException("LC_SYMTAB command not found");
    }
    if (stringTableOffset + stringTableSize != size) {
      throw new MachoException("String table does not end at end of file");
    }
    if (segmentSizePosition == 0 || segmentSize == 0) {
      throw new MachoException("LC_SEGMENT or LC_SEGMENT_64 command for string table not found");
    }

    map.position(stringTableOffset);
    if (map.get() != 0x20) {
      throw new MachoException("First character in the string table is not a space");
    }
    if (map.get() != 0x00) {
      throw new MachoException("Second character in the string table is not a NUL");
    }
    int currentStringTableOffset = map.position();

    byte[] stringTableBytes = new byte[stringTableSize];
    map.position(stringTableOffset);
    map.get(stringTableBytes);

    map.position(symbolTableOffset);

    // NB: We need to rewrite the string table as it's not deterministic and it would break
    //     caching behavior. On the other hand, the symbol table order is deterministic.

    boolean is64bit = header.getIs64Bit();
    Map<byte[], byte[]> replacementPathMap = generateReplacementMap(cellRoots);
    IntIntMap strings = new IntIntMap4a(symbolTableCount, 0.75f, NO_VALUE_MARKER);
    for (int i = 0; i < symbolTableCount; i++) {
      // Each LC_SYMTAB entry consists of the following fields:
      // - String Index: 4 bytes (offset into the string table)
      // - Type: 1 byte
      // - Section: 1 byte
      // - Description: 2 bytes
      // - Value: 8 bytes on 64bit, 4 bytes on 32bit
      int stringTableIndexPosition = map.position();
      int stringTableIndex = ObjectFileScrubbers.getLittleEndianInt(map);
      byte type = map.get();

      if (stringTableIndex >= 2) {
        int newStringTableIndex = strings.get(stringTableIndex);
        if (newStringTableIndex == NO_VALUE_MARKER) {
          ByteBuffer charByteBuffer =
              ObjectFileScrubbers.getCharByteBuffer(stringTableBytes, stringTableIndex);

          if (type == N_OSO) {
            Optional<ByteBuffer> maybeRewrittenCharByteBuffer =
                tryRewritingMatchingPath(stringTableBytes, stringTableIndex, replacementPathMap);
            if (maybeRewrittenCharByteBuffer.isPresent()) {
              charByteBuffer = maybeRewrittenCharByteBuffer.get();
            }

            int valuePosition = stringTableIndexPosition + 8;
            map.position(valuePosition);
            int lastModifiedValue = ObjectFileCommonModificationDate.COMMON_MODIFICATION_TIME_STAMP;
            if (is64bit) {
              ObjectFileScrubbers.putLittleEndianLong(map, lastModifiedValue);
            } else {
              ObjectFileScrubbers.putLittleEndianInt(map, lastModifiedValue);
            }
          }
          ObjectFileScrubbers.putCharByteBuffer(map, currentStringTableOffset, charByteBuffer);

          newStringTableIndex = currentStringTableOffset - stringTableOffset;
          strings.put(stringTableIndex, newStringTableIndex);

          currentStringTableOffset = map.position();
        }
        map.position(stringTableIndexPosition);
        ObjectFileScrubbers.putLittleEndianInt(map, newStringTableIndex);
      }

      int symtabEntrySize = 4 + 1 + 1 + 2 + (is64bit ? 8 : 4);
      int nextSymtabEntryOffset = stringTableIndexPosition + symtabEntrySize;
      map.position(nextSymtabEntryOffset);
    }

    map.position(stringTableSizePosition);
    int newStringTableSize = currentStringTableOffset - stringTableOffset;
    ObjectFileScrubbers.putLittleEndianInt(map, newStringTableSize);

    map.position(segmentSizePosition);
    long newSize = segmentSize + (newStringTableSize - stringTableSize);
    if (isValidFilesize(header, newSize)) {
      if (header.getIs64Bit()) {
        ObjectFileScrubbers.putLittleEndianLong(map, newSize);
      } else {
        ObjectFileScrubbers.putLittleEndianInt(map, (int) newSize);
      }
    } else {
      throw new MachoException("32bit scrubbed map segment file size too big");
    }

    file.truncate(currentStringTableOffset);
  }