/* * 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 static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertThat; import com.facebook.buck.charset.NulTerminatedCharsetDecoder; import com.google.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; import com.google.common.primitives.UnsignedInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.junit.Test; public class LoadCommandUtilsTest { @Test public void testEnumeratingLoadCommands() throws Exception { byte[] header = MachoHeaderTestData.getBigEndian64Bit(); header[19] = 2; // ncmds header[23] = 16; // sizeofcmds byte[] commandBytes = BaseEncoding.base16() .decode( "000000AA00000008" + // command 1 is AA, size 8 "000000BB00000008"); // command 2 is BB, size 8 ByteBuffer buffer = ByteBuffer.allocate(MachoHeaderTestData.getBigEndian64Bit().length + commandBytes.length) .put(header) .put(commandBytes) .order(ByteOrder.BIG_ENDIAN); final List<LoadCommand> result = new ArrayList<>(); buffer.position(0); LoadCommandUtils.enumerateLoadCommandsInFile( buffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()), input -> { result.add(input); return Boolean.TRUE; }); assertThat(result.size(), equalTo(2)); assertThat( result.get(0).getLoadCommandCommonFields().getCmd(), equalTo(UnsignedInteger.fromIntBits(0xAA))); assertThat( result.get(0).getLoadCommandCommonFields().getCmdsize(), equalTo(UnsignedInteger.fromIntBits(0x08))); assertThat( result.get(1).getLoadCommandCommonFields().getCmd(), equalTo(UnsignedInteger.fromIntBits(0xBB))); assertThat( result.get(1).getLoadCommandCommonFields().getCmdsize(), equalTo(UnsignedInteger.fromIntBits(0x08))); } @Test public void testFindingLoadCommands() throws Exception { byte[] header = MachoHeaderTestData.getBigEndian64Bit(); header[19] = 3; // ncmds header[23] = 16; // sizeofcmds byte[] symtabBytes = SymTabCommandTestData.getBigEndian(); byte[] uuid1Bytes = UUIDCommandTestData.getBigEndian(); uuid1Bytes[8] = (byte) 0x11; byte[] uuid2Bytes = UUIDCommandTestData.getBigEndian(); uuid2Bytes[8] = (byte) 0x22; ByteBuffer buffer = ByteBuffer.allocate( MachoHeaderTestData.getBigEndian64Bit().length + symtabBytes.length + uuid1Bytes.length + uuid2Bytes.length) .order(ByteOrder.BIG_ENDIAN) .put(header) .put(uuid1Bytes) .put(symtabBytes) .put(uuid2Bytes); buffer.position(0); ImmutableList<UUIDCommand> uuidCommands = LoadCommandUtils.findLoadCommandsWithClass( buffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()), UUIDCommand.class); assertThat(uuidCommands.size(), equalTo(2)); assertThat(uuidCommands.get(0).getUuid().toString(), startsWith("11")); assertThat( uuidCommands.get(0).getLoadCommandCommonFields().getOffsetInBinary(), equalTo(header.length + 0)); assertThat(uuidCommands.get(1).getUuid().toString(), startsWith("22")); assertThat( uuidCommands.get(1).getLoadCommandCommonFields().getOffsetInBinary(), equalTo(header.length + 48)); buffer.position(0); ImmutableList<SymTabCommand> symTabCommands = LoadCommandUtils.findLoadCommandsWithClass( buffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()), SymTabCommand.class); assertThat(symTabCommands.size(), equalTo(1)); assertThat( symTabCommands.get(0).getLoadCommandCommonFields().getOffsetInBinary(), equalTo(header.length + 24)); } @Test public void testCreatingLoadCommandsFromBuffer() throws Exception { byte[] segmentBytes = SegmentCommandTestData.getBigEndian64Bits(); byte[] symtabBytes = SymTabCommandTestData.getBigEndian(); byte[] uuidBytes = UUIDCommandTestData.getBigEndian(); byte[] linkEditBytes = LinkEditCommandTestData.getCodeSignBigEndian(); byte[] unknownBytes = { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, }; ByteBuffer byteBuffer = ByteBuffer.allocate( segmentBytes.length + symtabBytes.length + uuidBytes.length + linkEditBytes.length + unknownBytes.length) .order(ByteOrder.BIG_ENDIAN); byteBuffer .put(segmentBytes) .put(symtabBytes) .put(uuidBytes) .put(linkEditBytes) .put(unknownBytes); byteBuffer.position(0); assertThat( LoadCommandUtils.createLoadCommandFromBuffer( byteBuffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder())), instanceOf(SegmentCommand.class)); assertThat( LoadCommandUtils.createLoadCommandFromBuffer( byteBuffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder())), instanceOf(SymTabCommand.class)); assertThat( LoadCommandUtils.createLoadCommandFromBuffer( byteBuffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder())), instanceOf(UUIDCommand.class)); assertThat( LoadCommandUtils.createLoadCommandFromBuffer( byteBuffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder())), instanceOf(LinkEditDataCommand.class)); assertThat( LoadCommandUtils.createLoadCommandFromBuffer( byteBuffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder())), instanceOf(UnknownCommand.class)); } }