/* * 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_WRITE; import com.facebook.buck.cxx.elf.Elf; import com.facebook.buck.cxx.elf.ElfSection; import com.facebook.buck.cxx.elf.ElfSymbolTable; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.Pair; import com.facebook.buck.step.ExecutionContext; import com.facebook.buck.step.Step; import com.facebook.buck.step.StepExecutionResult; import com.facebook.buck.util.MoreIterables; import com.facebook.buck.util.RichStream; import com.facebook.buck.util.immutables.BuckStyleTuple; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Optional; import org.immutables.value.Value; /** A step which scrubs an ELF symbol table of information relevant to dynamic linking. */ @Value.Immutable @BuckStyleTuple abstract class AbstractElfSymbolTableScrubberStep implements Step { @VisibleForTesting static final int STABLE_SECTION = 1; abstract ProjectFilesystem getFilesystem(); abstract Path getPath(); abstract String getSection(); abstract boolean isAllowMissing(); private ElfSymbolTable fixUpSymbolTable(ElfSymbolTable table) { ImmutableList.Builder<ElfSymbolTable.Entry> entries = ImmutableList.builder(); // The first symbol serves as the undefined symbol index, so always include it and start // processing symbols after it. entries.add(table.entries.get(0)); // Fixup and add the remaining entries. RichStream.from(MoreIterables.enumerate(table.entries)) .skip(1) // Generate a new sanitized symbol table entry. .map( pair -> new ElfSymbolTable.Entry( pair.getSecond().st_name, pair.getSecond().st_info, pair.getSecond().st_other, // A section index of 0 is special and means the symbol is undefined, so we // must maintain that. Otherwise, if it's non-zero, fix it up to an arbitrary // stable section value so the number and ordering of sections can never affect // the content of the symbol table. pair.getSecond().st_shndx > 0 ? STABLE_SECTION : pair.getSecond().st_shndx, // Substitute non-zero addresses, dependent on size/layout of sections with a // stable address determined by the index of this symbol table entry in the // symbol table. pair.getSecond().st_value == 0 ? 0 : pair.getFirst(), // For functions, set the size to zero. pair.getSecond().st_info.st_type == ElfSymbolTable.Entry.Info.Type.STT_FUNC ? 0 : pair.getSecond().st_size)) .forEach(entries::add); return new ElfSymbolTable(entries.build()); } @Override public StepExecutionResult execute(ExecutionContext context) throws IOException { try (FileChannel channel = FileChannel.open( getFilesystem().resolve(getPath()), StandardOpenOption.READ, StandardOpenOption.WRITE)) { MappedByteBuffer buffer = channel.map(READ_WRITE, 0, channel.size()); Elf elf = new Elf(buffer); // Locate the symbol table section. Optional<ElfSection> section = elf.getSectionByName(getSection()).map(Pair::getSecond); if (!section.isPresent()) { if (isAllowMissing()) { return StepExecutionResult.SUCCESS; } else { throw new IOException( String.format( "Error parsing ELF file %s: no such section \"%s\"", getPath(), getSection())); } } // Read in and fixup the symbol table then write it back out. ElfSymbolTable table = ElfSymbolTable.parse(elf.header.ei_class, section.get().body); ElfSymbolTable fixedUpTable = fixUpSymbolTable(table); Preconditions.checkState(table.entries.size() == fixedUpTable.entries.size()); section.get().body.rewind(); fixedUpTable.write(elf.header.ei_class, section.get().body); } return StepExecutionResult.SUCCESS; } @Override public final String getShortName() { return "scrub_symbol_table"; } @Override public String getDescription(ExecutionContext context) { return String.format("Scrub ELF symbol table %s in %s", getSection(), getPath()); } }