/*
* 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.cxx;
import static java.nio.channels.FileChannel.MapMode.READ_ONLY;
import com.facebook.buck.cxx.elf.Elf;
import com.facebook.buck.cxx.elf.ElfSection;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.shell.DefaultShellStep;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.StepExecutionResult;
import com.facebook.buck.util.immutables.BuckStyleTuple;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.immutables.value.Value;
/**
* A step which extracts specific sections from an ELF file and compacts them into a new ELF file.
*/
@Value.Immutable
@BuckStyleTuple
abstract class AbstractElfExtractSectionsStep implements Step {
abstract ProjectFilesystem getFilesystem();
abstract ImmutableList<String> getObjcopyPrefix();
abstract Path getInput();
abstract Path getOutput();
abstract ImmutableSet<String> getSections();
// We want to compact the sections into the new ELF file, so find out the new addresses of each
// section.
private ImmutableMap<String, Long> getNewSectionAddresses() throws IOException {
ImmutableMap.Builder<String, Long> addresses = ImmutableMap.builder();
try (FileChannel channel =
FileChannel.open(getFilesystem().resolve(getInput()), StandardOpenOption.READ)) {
MappedByteBuffer buffer = channel.map(READ_ONLY, 0, channel.size());
Elf elf = new Elf(buffer);
// We start placing sections right after the program headers.
long end = elf.header.e_phoff + elf.header.e_phnum * elf.header.e_phentsize;
for (int index = 0; index < elf.getNumberOfSections(); index++) {
ElfSection section = elf.getSectionByIndex(index);
String name = elf.getSectionName(section.header);
// If this is a target section, assign it the current address, then increment the next
// address by this sections size.
if (getSections().contains(name)) {
addresses.put(name, end);
end += section.header.sh_size;
}
}
}
return addresses.build();
}
private ImmutableList<String> getObjcopyCommand(ImmutableMap<String, Long> addresses) {
ImmutableList.Builder<String> args = ImmutableList.builder();
args.addAll(getObjcopyPrefix());
for (String section : getSections()) {
Long address = addresses.get(section);
if (address != null) {
args.add(
"--only-section",
section,
"--change-section-address",
String.format("%s=0x%x", section, address));
}
}
args.add(getInput().toString());
args.add(getOutput().toString());
return args.build();
}
@Override
public StepExecutionResult execute(ExecutionContext context)
throws IOException, InterruptedException {
ImmutableMap<String, Long> addresses = getNewSectionAddresses();
Step objcopy =
new DefaultShellStep(
getFilesystem().getRootPath(),
/* args */ getObjcopyCommand(addresses),
/* env */ ImmutableMap.of());
return objcopy.execute(context);
}
@Override
public final String getShortName() {
return "scrub_symbol_table";
}
@Override
public String getDescription(ExecutionContext context) {
return String.format(
"Extract sections %s from %s", Joiner.on(", ").join(getSections()), getInput());
}
}