/*
* 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.equalToObject;
import static org.junit.Assert.assertThat;
import com.facebook.buck.charset.NulTerminatedCharsetDecoder;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.primitives.UnsignedLong;
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 SegmentCommandUtilsTest {
@Test
public void testAligning() throws Exception {
assertThat(SegmentCommandUtils.alignValue(10), equalTo(32 * 1024));
assertThat(SegmentCommandUtils.alignValue(100 * 1024), equalTo(128 * 1024));
assertThat(SegmentCommandUtils.alignValue(987 * 1024), equalTo(992 * 1024));
}
@Test
public void testGettingHeaderSize64Bit() throws Exception {
byte[] bytes = SegmentCommandTestData.getBigEndian64Bits();
final int commandSize = bytes.length;
SegmentCommand command =
SegmentCommandUtils.createFromBuffer(
ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN),
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()));
assertThat(SegmentCommandUtils.getSegmentCommandHeaderSize(command), equalTo(commandSize));
}
@Test
public void testGettingHeaderSize32Bit() throws Exception {
byte[] bytes = SegmentCommandTestData.getBigEndian32Bits();
final int commandSize = bytes.length;
SegmentCommand command =
SegmentCommandUtils.createFromBuffer(
ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN),
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()));
assertThat(SegmentCommandUtils.getSegmentCommandHeaderSize(command), equalTo(commandSize));
}
@Test
public void testUpdatingSegmentCommandInByteBuffer64Bit() throws Exception {
byte[] bytes = SegmentCommandTestData.getBigEndian64Bits();
final int commandSize = bytes.length;
SegmentCommand command =
SegmentCommandUtils.createFromBuffer(
ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN),
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()));
SegmentCommand updated =
command
.withFilesize(UnsignedLong.fromLongBits(1234))
.withVmsize(UnsignedLong.fromLongBits(SegmentCommandUtils.alignValue(4321)));
ByteBuffer buffer = ByteBuffer.allocate(commandSize);
SegmentCommandUtils.updateSegmentCommand(buffer, command, updated);
buffer.position(0);
SegmentCommand commandInBuffer =
SegmentCommandUtils.createFromBuffer(
buffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()));
assertThat(commandInBuffer.getFilesize(), equalToObject(updated.getFilesize()));
assertThat(commandInBuffer.getVmsize(), equalToObject(updated.getVmsize()));
}
@Test
public void testUpdatingSegmentCommandInByteBuffer32Bit() throws Exception {
byte[] bytes = SegmentCommandTestData.getBigEndian32Bits();
final int commandSize = bytes.length;
SegmentCommand command =
SegmentCommandUtils.createFromBuffer(
ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN),
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()));
SegmentCommand updated =
command
.withFilesize(UnsignedLong.fromLongBits(1234))
.withVmsize(UnsignedLong.fromLongBits(SegmentCommandUtils.alignValue(4321)));
ByteBuffer buffer = ByteBuffer.allocate(commandSize);
SegmentCommandUtils.updateSegmentCommand(buffer, command, updated);
buffer.position(0);
SegmentCommand commandInBuffer =
SegmentCommandUtils.createFromBuffer(
buffer, new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()));
assertThat(commandInBuffer.getFilesize(), equalToObject(updated.getFilesize()));
assertThat(commandInBuffer.getVmsize(), equalToObject(updated.getVmsize()));
}
@Test
public void testEnumeratingSections64Bit() throws Exception {
final int sectionSize = SectionTestData.getBigEndian64Bit().length;
byte[] sectionData1 = SectionTestData.getBigEndian64Bit();
sectionData1[51] = (byte) 0x01; // offset = 1
byte[] sectionData2 = SectionTestData.getBigEndian64Bit();
sectionData2[0] = (byte) 0x44; // sectname = "DECTNAME"
sectionData2[16] = (byte) 0x44; // segname = "DEGNAME"
sectionData2[51] = (byte) 0x02; // offset = 2
byte[] sectionData3 = SectionTestData.getBigEndian64Bit();
sectionData3[0] = (byte) 0x4C; // sectname = "LECTNAME"
sectionData3[16] = (byte) 0x4C; // segname = "LEGNAME"
sectionData3[51] = (byte) 0x03; // offset = 3
byte[] segmentBytes = SegmentCommandTestData.getBigEndian64Bits();
segmentBytes[67] = (byte) 0x03; // nsects = 3
SegmentCommand command =
SegmentCommandUtils.createFromBuffer(
ByteBuffer.wrap(segmentBytes).order(ByteOrder.BIG_ENDIAN),
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()));
ByteBuffer buffer =
ByteBuffer.allocate(
command.getLoadCommandCommonFields().getCmdsize().intValue() + 3 * sectionSize);
buffer.order(ByteOrder.BIG_ENDIAN);
SegmentCommandUtils.writeCommandToBuffer(command, buffer, true);
buffer.put(sectionData1);
buffer.put(sectionData2);
buffer.put(sectionData3);
final List<Section> enumeratedSections = new ArrayList<>();
SegmentCommandUtils.enumerateSectionsInSegmentLoadCommand(
buffer,
new MachoMagicInfo(UnsignedInteger.fromIntBits(0xFEEDFACF)),
command,
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()),
input -> {
enumeratedSections.add(input);
return Boolean.TRUE;
});
assertThat(enumeratedSections.size(), equalTo(3));
assertThat(enumeratedSections.get(0).getSectname(), equalToObject("SECTNAME"));
assertThat(enumeratedSections.get(0).getSegname(), equalToObject("SEGNAME"));
assertThat(
enumeratedSections.get(0).getOffset(), equalToObject(UnsignedInteger.fromIntBits(0x01)));
assertThat(enumeratedSections.get(1).getSectname(), equalToObject("DECTNAME"));
assertThat(enumeratedSections.get(1).getSegname(), equalToObject("DEGNAME"));
assertThat(
enumeratedSections.get(1).getOffset(), equalToObject(UnsignedInteger.fromIntBits(0x02)));
assertThat(enumeratedSections.get(2).getSectname(), equalToObject("LECTNAME"));
assertThat(enumeratedSections.get(2).getSegname(), equalToObject("LEGNAME"));
assertThat(
enumeratedSections.get(2).getOffset(), equalToObject(UnsignedInteger.fromIntBits(0x03)));
}
@Test
public void testEnumeratingSections32Bit() throws Exception {
final int sectionSize = SectionTestData.getBigEndian32Bit().length;
byte[] sectionData1 = SectionTestData.getBigEndian32Bit();
sectionData1[43] = (byte) 0x01; // offset = 1
byte[] sectionData2 = SectionTestData.getBigEndian32Bit();
sectionData2[0] = (byte) 0x44; // sectname = "DECTNAME"
sectionData2[16] = (byte) 0x44; // segname = "DEGNAME"
sectionData2[43] = (byte) 0x02; // offset = 2
byte[] sectionData3 = SectionTestData.getBigEndian32Bit();
sectionData3[0] = (byte) 0x4C; // sectname = "LECTNAME"
sectionData3[16] = (byte) 0x4C; // segname = "LEGNAME"
sectionData3[43] = (byte) 0x03; // offset = 3
byte[] segmentBytes = SegmentCommandTestData.getBigEndian32Bits();
segmentBytes[51] = (byte) 0x03; // nsects = 3
SegmentCommand command =
SegmentCommandUtils.createFromBuffer(
ByteBuffer.wrap(segmentBytes).order(ByteOrder.BIG_ENDIAN),
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()));
ByteBuffer buffer =
ByteBuffer.allocate(
command.getLoadCommandCommonFields().getCmdsize().intValue() + 3 * sectionSize);
buffer.order(ByteOrder.BIG_ENDIAN);
SegmentCommandUtils.writeCommandToBuffer(command, buffer, false);
buffer.put(sectionData1);
buffer.put(sectionData2);
buffer.put(sectionData3);
final List<Section> enumeratedSections = new ArrayList<>();
SegmentCommandUtils.enumerateSectionsInSegmentLoadCommand(
buffer,
new MachoMagicInfo(UnsignedInteger.fromIntBits(0xFEEDFACE)),
command,
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()),
input -> {
enumeratedSections.add(input);
return Boolean.TRUE;
});
assertThat(enumeratedSections.size(), equalTo(3));
assertThat(enumeratedSections.get(0).getSectname(), equalToObject("SECTNAME"));
assertThat(enumeratedSections.get(0).getSegname(), equalToObject("SEGNAME"));
assertThat(
enumeratedSections.get(0).getOffset(), equalToObject(UnsignedInteger.fromIntBits(0x01)));
assertThat(enumeratedSections.get(1).getSectname(), equalToObject("DECTNAME"));
assertThat(enumeratedSections.get(1).getSegname(), equalToObject("DEGNAME"));
assertThat(
enumeratedSections.get(1).getOffset(), equalToObject(UnsignedInteger.fromIntBits(0x02)));
assertThat(enumeratedSections.get(2).getSectname(), equalToObject("LECTNAME"));
assertThat(enumeratedSections.get(2).getSegname(), equalToObject("LEGNAME"));
assertThat(
enumeratedSections.get(2).getOffset(), equalToObject(UnsignedInteger.fromIntBits(0x03)));
}
@Test
public void testEnumeratingSectionsWorksRegardingOfCmdsize() throws Exception {
// There was a bug when section's offset was computed incorrectly: offset = cmd.offset + cmdsize
// It was fixed roughly by the next formula: offset = cmd.offset + cmd.sizeOfHeader
// So this test checks this
final int sectionSize = SectionTestData.getBigEndian64Bit().length;
byte[] sectionData1 = SectionTestData.getBigEndian64Bit();
sectionData1[51] = (byte) 0x01; // offset = 1
byte[] sectionData2 = SectionTestData.getBigEndian64Bit();
sectionData2[0] = (byte) 0x44; // sectname = "DECTNAME"
sectionData2[16] = (byte) 0x44; // segname = "DEGNAME"
sectionData2[51] = (byte) 0x02; // offset = 2
byte[] sectionData3 = SectionTestData.getBigEndian64Bit();
sectionData3[0] = (byte) 0x4C; // sectname = "LECTNAME"
sectionData3[16] = (byte) 0x4C; // segname = "LEGNAME"
sectionData3[51] = (byte) 0x03; // offset = 3
byte[] segmentBytes = SegmentCommandTestData.getBigEndian64Bits();
segmentBytes[6] = (byte) 0xAA;
segmentBytes[7] = (byte) 0xFF; // cmdsize = 0xAAFF == 43755 bytes!!!
segmentBytes[67] = (byte) 0x03; // nsects = 3
SegmentCommand command =
SegmentCommandUtils.createFromBuffer(
ByteBuffer.wrap(segmentBytes).order(ByteOrder.BIG_ENDIAN),
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()));
ByteBuffer buffer =
ByteBuffer.allocate(
command.getLoadCommandCommonFields().getCmdsize().intValue() + 3 * sectionSize);
buffer.order(ByteOrder.BIG_ENDIAN);
SegmentCommandUtils.writeCommandToBuffer(command, buffer, true);
buffer.put(sectionData1);
buffer.put(sectionData2);
buffer.put(sectionData3);
final List<Section> enumeratedSections = new ArrayList<>();
SegmentCommandUtils.enumerateSectionsInSegmentLoadCommand(
buffer,
new MachoMagicInfo(UnsignedInteger.fromIntBits(0xFEEDFACF)),
command,
new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()),
input -> {
enumeratedSections.add(input);
return Boolean.TRUE;
});
assertThat(enumeratedSections.size(), equalTo(3));
assertThat(enumeratedSections.get(0).getSectname(), equalToObject("SECTNAME"));
assertThat(enumeratedSections.get(0).getSegname(), equalToObject("SEGNAME"));
assertThat(
enumeratedSections.get(0).getOffset(), equalToObject(UnsignedInteger.fromIntBits(0x01)));
assertThat(enumeratedSections.get(1).getSectname(), equalToObject("DECTNAME"));
assertThat(enumeratedSections.get(1).getSegname(), equalToObject("DEGNAME"));
assertThat(
enumeratedSections.get(1).getOffset(), equalToObject(UnsignedInteger.fromIntBits(0x02)));
assertThat(enumeratedSections.get(2).getSectname(), equalToObject("LECTNAME"));
assertThat(enumeratedSections.get(2).getSegname(), equalToObject("LEGNAME"));
assertThat(
enumeratedSections.get(2).getOffset(), equalToObject(UnsignedInteger.fromIntBits(0x03)));
}
}