/* * Copyright 2016-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.macho; import com.facebook.buck.charset.NulTerminatedCharsetDecoder; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.primitives.UnsignedInteger; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; public class LoadCommandUtils { private LoadCommandUtils() {} /** * This is a kind of umbrella method that returns you LoadCommand object depending on the contents * of the given bytes array. * * @param buffer Buffer which contains at least values for the LoadCommand fields, positioned at * the first byte of the command (cmd field) * @return LoadCommandCommonFields that is suitable to handle the given bytes array. */ public static LoadCommand createLoadCommandFromBuffer( ByteBuffer buffer, NulTerminatedCharsetDecoder nulTerminatedCharsetDecoder) { int position = buffer.position(); UnsignedInteger cmd = UnsignedInteger.fromIntBits(buffer.getInt()); buffer.position(position); if (SegmentCommand.VALID_CMD_VALUES.contains(cmd)) { return SegmentCommandUtils.createFromBuffer(buffer, nulTerminatedCharsetDecoder); } else if (cmd.equals(SymTabCommand.LC_SYMTAB)) { return SymTabCommandUtils.createFromBuffer(buffer); } else if (cmd.equals(UUIDCommand.LC_UUID)) { return UUIDCommandUtils.createFromBuffer(buffer); } else if (LinkEditDataCommand.VALID_CMD_VALUES.contains(cmd)) { return LinkEditDataCommandUtils.createFromBuffer(buffer); } else { return UnknownCommandUtils.createFromBuffer(buffer); } } /** * Enumerates the load commands in the given mach binary which is represented by the buffer by * calling the given callback, starting at buffer's position. * * @param buffer The buffer which holds all data. * @param callback The Function object which should be called on each LoadCommand enumeration * event. The argument of the function is the LoadCommand object. If Function returns * Boolean.TRUE then enumeration will continue; otherwise enumeration will stop and callback * will not be called anymore. * @throws IOException */ public static void enumerateLoadCommandsInFile( ByteBuffer buffer, NulTerminatedCharsetDecoder nulTerminatedCharsetDecoder, Function<LoadCommand, Boolean> callback) { MachoHeader header = MachoHeaderUtils.createFromBuffer(buffer); int firstCommandOffset = MachoHeaderUtils.getHeaderSize(header); int relativeCommandOffset = 0; for (int i = 0; i < header.getNcmds().intValue(); i++) { buffer.position(firstCommandOffset + relativeCommandOffset); LoadCommand command = LoadCommandUtils.createLoadCommandFromBuffer(buffer, nulTerminatedCharsetDecoder); if (!callback.apply(command)) { break; } relativeCommandOffset += command.getLoadCommandCommonFields().getCmdsize().intValue(); } } /** * Finds all load commands with the given type in the buffer starting at the buffer's position. * Example usage is: * * <p>ImmutableList<MyLoadCommand> results = findLoadCommandsWithClass(buffer, * MyLoadCommand.class); * * @param buffer The buffer which holds all data. * @param type Load command's class, like SomeLoadCommand.class. * @param <T> Return type of the load command, like SomeLoadCommand. * @return List with all load commands of the given type. * @throws IOException */ @SuppressWarnings("unchecked") public static <T extends LoadCommand> ImmutableList<T> findLoadCommandsWithClass( ByteBuffer buffer, NulTerminatedCharsetDecoder nulTerminatedCharsetDecoder, final Class<T> type) { final List<T> results = new ArrayList<>(); enumerateLoadCommandsInFile( buffer, nulTerminatedCharsetDecoder, input -> { if (type.isInstance(input)) { results.add((T) input); } return true; }); return ImmutableList.copyOf(results); } }