1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 
  26 package jdk.tools.jaotc.binformat.pecoff;
  27 
  28 import java.io.IOException;
  29 import java.util.ArrayList;
  30 import java.util.Collection;
  31 import java.util.List;
  32 import java.util.Map;
  33 
  34 import jdk.tools.jaotc.binformat.BinaryContainer;
  35 import jdk.tools.jaotc.binformat.ByteContainer;
  36 import jdk.tools.jaotc.binformat.CodeContainer;
  37 import jdk.tools.jaotc.binformat.ReadOnlyDataContainer;
  38 import jdk.tools.jaotc.binformat.Relocation;
  39 import jdk.tools.jaotc.binformat.Relocation.RelocType;
  40 import jdk.tools.jaotc.binformat.Symbol;
  41 import jdk.tools.jaotc.binformat.Symbol.Binding;
  42 import jdk.tools.jaotc.binformat.Symbol.Kind;
  43 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_FILE_HEADER;
  44 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_RELOCATION;
  45 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SECTION_HEADER;
  46 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SYMBOL;
  47 
  48 public class JPECoffRelocObject {
  49 
  50     private final BinaryContainer binContainer;
  51 
  52     private final PECoffContainer pecoffContainer;
  53 
  54     private final int sectionAlignment;
  55 
  56     public JPECoffRelocObject(BinaryContainer binContainer, String outputFileName) {
  57         this.binContainer = binContainer;
  58         this.pecoffContainer = new PECoffContainer(outputFileName);
  59         this.sectionAlignment = binContainer.getCodeSegmentSize();
  60     }
  61 
  62     private static PECoffSection createByteSection(ArrayList<PECoffSection> sections, String sectName, byte[] scnData,
  63                     boolean hasRelocs, int scnFlags, int sectAlign) {
  64 
  65         PECoffSection sect = new PECoffSection(sectName, scnData, scnFlags, sectAlign, hasRelocs, sections.size());
  66         // Add this section to our list
  67         sections.add(sect);
  68 
  69         return (sect);
  70     }
  71 
  72     private static void createByteSection(ArrayList<PECoffSection> sections, ByteContainer c, int scnFlags, int sectAlign) {
  73         PECoffSection sect;
  74         boolean hasRelocs = c.hasRelocations();
  75         byte[] scnData = c.getByteArray();
  76 
  77         sect = createByteSection(sections, c.getContainerName(), scnData, hasRelocs, scnFlags, sectAlign);
  78 
  79         c.setSectionId(sect.getSectionId());
  80     }
  81 
  82     private void createCodeSection(ArrayList<PECoffSection> sections, CodeContainer c) {
  83         int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_EXECUTE | IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_CODE;
  84         createByteSection(sections, c, scnFlags, sectionAlignment);
  85     }
  86 
  87     private void createReadOnlySection(ArrayList<PECoffSection> sections, ReadOnlyDataContainer c) {
  88         int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA;
  89         createByteSection(sections, c, scnFlags, sectionAlignment);
  90     }
  91 
  92     private void createReadWriteSection(ArrayList<PECoffSection> sections, ByteContainer c) {
  93         int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_WRITE;
  94 
  95         if (c.getByteArray().length > 0) {
  96             scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA;
  97         } else {
  98             scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_UNINITIALIZED_DATA;
  99         }
 100         createByteSection(sections, c, scnFlags, sectionAlignment);
 101     }
 102 
 103     /**
 104      * Creates a PECoff relocatable object.
 105      *
 106      * @param relocationTable
 107      * @param symbols
 108      * @throws IOException throws {@code IOException} as a result of file system access failures.
 109      */
 110     public void createPECoffRelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException {
 111         ArrayList<PECoffSection> sections = new ArrayList<>();
 112 
 113         // Create text section
 114         createCodeSection(sections, binContainer.getCodeContainer());
 115         createReadOnlySection(sections, binContainer.getMetaspaceNamesContainer());
 116         createReadOnlySection(sections, binContainer.getKlassesOffsetsContainer());
 117         createReadOnlySection(sections, binContainer.getMethodsOffsetsContainer());
 118         createReadOnlySection(sections, binContainer.getKlassesDependenciesContainer());
 119         createReadOnlySection(sections, binContainer.getMethodMetadataContainer());
 120         createReadOnlySection(sections, binContainer.getStubsOffsetsContainer());
 121         createReadOnlySection(sections, binContainer.getHeaderContainer().getContainer());
 122         createReadOnlySection(sections, binContainer.getCodeSegmentsContainer());
 123         createReadOnlySection(sections, binContainer.getConstantDataContainer());
 124         createReadOnlySection(sections, binContainer.getConfigContainer());
 125         createReadWriteSection(sections, binContainer.getKlassesGotContainer());
 126         createReadWriteSection(sections, binContainer.getCountersGotContainer());
 127         createReadWriteSection(sections, binContainer.getMetadataGotContainer());
 128         createReadWriteSection(sections, binContainer.getMethodStateContainer());
 129         createReadWriteSection(sections, binContainer.getOopGotContainer());
 130         createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer());
 131 
 132         // Allocate PECoff Header
 133         PECoffHeader header = new PECoffHeader();
 134 
 135         // Get PECoff symbol data from BinaryContainer object's symbol tables
 136         PECoffSymtab symtab = createPECoffSymbolTables(symbols);
 137 
 138         // Add Linker Directives Section
 139         int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_INFO | IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_REMOVE;
 140         createByteSection(sections, ".drectve", symtab.getDirectiveArray(), false, scnFlags, 1 /*
 141                                                                                                 * 1
 142                                                                                                 * byte
 143                                                                                                 * alignment
 144                                                                                                 */);
 145 
 146         // Create the Relocation Tables
 147         PECoffRelocTable pecoffRelocs = createPECoffRelocTable(sections, relocationTable);
 148 
 149         // File Output Order
 150         //
 151         // HEADER (Need address of Symbol Table + symbol count)
 152         // SECTIONS (Need pointer to Section Data, Relocation Table)
 153         // DIRECTIVES
 154         // SYMBOL TABLE
 155         // SYMBOLS
 156         // SECTION DATA
 157         // RELOCATION TABLE
 158 
 159         // Calculate Offset for Symbol table
 160         int fileOffset = IMAGE_FILE_HEADER.totalsize +
 161                         (IMAGE_SECTION_HEADER.totalsize * sections.size());
 162 
 163         // Update Header fields
 164         header.setSectionCount(sections.size());
 165         header.setSymbolCount(symtab.getSymtabCount());
 166         header.setSymbolOff(fileOffset);
 167 
 168         // Calculate file offset for first section
 169         fileOffset += ((symtab.getSymtabCount() * IMAGE_SYMBOL.totalsize) +
 170                         symtab.getStrtabSize());
 171         // And round it up
 172         fileOffset = (fileOffset + (sections.get(0).getDataAlign() - 1)) &
 173                         ~((sections.get(0).getDataAlign() - 1));
 174 
 175         // Calc file offsets for section data
 176         for (int i = 0; i < sections.size(); i++) {
 177             PECoffSection sect = sections.get(i);
 178             fileOffset = (fileOffset + (sect.getDataAlign() - 1)) &
 179                             ~((sect.getDataAlign() - 1));
 180             sect.setOffset(fileOffset);
 181             fileOffset += sect.getSize();
 182         }
 183 
 184         // Update relocation sizing information in each section
 185         for (int i = 0; i < sections.size(); i++) {
 186             PECoffSection sect = sections.get(i);
 187             if (sect.hasRelocations()) {
 188                 int nreloc = pecoffRelocs.getNumRelocs(i);
 189                 sect.setReloff(fileOffset);
 190                 sect.setRelcount(nreloc);
 191                 // extended relocations add an addition entry
 192                 if (nreloc > 0xFFFF) {
 193                     nreloc++;
 194                 }
 195                 fileOffset += (nreloc * IMAGE_RELOCATION.totalsize);
 196             }
 197         }
 198 
 199         // Write out the Header
 200         pecoffContainer.writeBytes(header.getArray());
 201 
 202         // Write out the section table
 203         for (int i = 0; i < sections.size(); i++) {
 204             PECoffSection sect = sections.get(i);
 205             pecoffContainer.writeBytes(sect.getArray(), PECoffSection.getShdrAlign());
 206         }
 207 
 208         // Write out the symbol table and string table
 209         pecoffContainer.writeBytes(symtab.getSymtabArray(), 4);
 210         pecoffContainer.writeBytes(symtab.getStrtabArray(), 1);
 211 
 212         // Write out each section contents
 213         for (int i = 0; i < sections.size(); i++) {
 214             PECoffSection sect = sections.get(i);
 215             pecoffContainer.writeBytes(sect.getDataArray(), sect.getDataAlign());
 216         }
 217 
 218         // Write out Relocation Tables
 219         for (int i = 0; i < sections.size(); i++) {
 220             if (pecoffRelocs.getNumRelocs(i) > 0) {
 221                 pecoffContainer.writeBytes(pecoffRelocs.getRelocData(i));
 222             }
 223         }
 224         pecoffContainer.close();
 225     }
 226 
 227     /**
 228      * Construct PECoff symbol data from BinaryContainer object's symbol tables. Both dynamic PECoff
 229      * symbol table and PECoff symbol table are created from BinaryContainer's symbol info.
 230      *
 231      * @param symbols
 232      */
 233     private static PECoffSymtab createPECoffSymbolTables(Collection<Symbol> symbols) {
 234         PECoffSymtab symtab = new PECoffSymtab();
 235 
 236         // First, create the initial null symbol. This is a local symbol.
 237         // symtab.addSymbolEntry("", (byte)0, (byte)0, (byte)0, 0, 0);
 238 
 239         // Now create PECoff symbol entries for all symbols.
 240         for (Symbol symbol : symbols) {
 241             // Get the index of section this symbol is defined in.
 242             int secHdrIndex = symbol.getSection().getSectionId();
 243             PECoffSymbol pecoffSymbol = symtab.addSymbolEntry(symbol.getName(), getPECoffTypeOf(symbol), getPECoffClassOf(symbol), (byte) secHdrIndex, symbol.getOffset());
 244             symbol.setNativeSymbol(pecoffSymbol);
 245         }
 246         return (symtab);
 247     }
 248 
 249     private static byte getPECoffTypeOf(Symbol sym) {
 250         Kind kind = sym.getKind();
 251         if (kind == Symbol.Kind.NATIVE_FUNCTION || kind == Symbol.Kind.JAVA_FUNCTION) {
 252             return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_FUNCTION;
 253         }
 254         return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_NONE;
 255     }
 256 
 257     private static byte getPECoffClassOf(Symbol sym) {
 258         Binding binding = sym.getBinding();
 259         if (binding == Symbol.Binding.GLOBAL) {
 260             return IMAGE_SYMBOL.IMAGE_SYM_CLASS_EXTERNAL;
 261         }
 262         return IMAGE_SYMBOL.IMAGE_SYM_CLASS_STATIC;
 263     }
 264 
 265     /**
 266      * Construct a PECoff relocation table from BinaryContainer object's relocation tables.
 267      *
 268      * @param sections
 269      * @param relocationTable
 270      */
 271     private PECoffRelocTable createPECoffRelocTable(ArrayList<PECoffSection> sections, Map<Symbol, List<Relocation>> relocationTable) {
 272 
 273         PECoffRelocTable pecoffRelocTable = new PECoffRelocTable(sections.size());
 274         /*
 275          * For each of the symbols with associated relocation records, create a PECoff relocation
 276          * entry.
 277          */
 278         for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) {
 279             List<Relocation> relocs = entry.getValue();
 280             Symbol symbol = entry.getKey();
 281 
 282             for (Relocation reloc : relocs) {
 283                 createRelocation(symbol, reloc, pecoffRelocTable);
 284             }
 285         }
 286 
 287         for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) {
 288             createRelocation(entry.getKey(), entry.getValue(), pecoffRelocTable);
 289         }
 290 
 291         return (pecoffRelocTable);
 292     }
 293 
 294     private static void createRelocation(Symbol symbol, Relocation reloc, PECoffRelocTable pecoffRelocTable) {
 295         RelocType relocType = reloc.getType();
 296 
 297         int pecoffRelocType = getPECoffRelocationType(relocType);
 298         PECoffSymbol sym = (PECoffSymbol) symbol.getNativeSymbol();
 299         int symno = sym.getIndex();
 300         int sectindex = reloc.getSection().getSectionId();
 301         int offset = reloc.getOffset();
 302         int addend = 0;
 303 
 304         switch (relocType) {
 305             case JAVA_CALL_DIRECT:
 306             case STUB_CALL_DIRECT:
 307             case FOREIGN_CALL_INDIRECT_GOT: {
 308                 // Create relocation entry
 309                 addend = -4; // Size in bytes of the patch location
 310                 // Relocation should be applied at the location after call operand
 311                 offset = offset + reloc.getSize() + addend;
 312                 break;
 313             }
 314             case JAVA_CALL_INDIRECT: {
 315                 // Do nothing.
 316                 return;
 317             }
 318             case METASPACE_GOT_REFERENCE:
 319             case EXTERNAL_PLT_TO_GOT: {
 320                 addend = -4; // Size of 32-bit address of the GOT
 321                 /*
 322                  * Relocation should be applied before the test instruction to the move instruction.
 323                  * reloc.getOffset() points to the test instruction after the instruction that loads
 324                  * the address of polling page. So set the offset appropriately.
 325                  */
 326                 offset = offset + addend;
 327                 break;
 328             }
 329             case EXTERNAL_GOT_TO_PLT: {
 330                 // this is load time relocations
 331                 break;
 332             }
 333             default:
 334                 throw new InternalError("Unhandled relocation type: " + relocType);
 335         }
 336         pecoffRelocTable.createRelocationEntry(sectindex, offset, symno, pecoffRelocType);
 337     }
 338 
 339     // Return IMAGE_RELOCATION Type based on relocType
 340     private static int getPECoffRelocationType(RelocType relocType) {
 341         int pecoffRelocType = 0; // R_<ARCH>_NONE if #define'd to 0 for all values of ARCH
 342         switch (PECoffTargetInfo.getPECoffArch()) {
 343             case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
 344                 if (relocType == RelocType.JAVA_CALL_DIRECT ||
 345                                 relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) {
 346                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
 347                 } else if (relocType == RelocType.STUB_CALL_DIRECT) {
 348                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
 349                 } else if (relocType == RelocType.JAVA_CALL_INDIRECT) {
 350                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ABSOLUTE;
 351                 } else if (relocType == RelocType.METASPACE_GOT_REFERENCE ||
 352                                 relocType == RelocType.EXTERNAL_PLT_TO_GOT) {
 353                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
 354                 } else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT) {
 355                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ADDR64;
 356                 } else {
 357                     assert false : "Unhandled relocation type: " + relocType;
 358                 }
 359                 break;
 360             default:
 361                 System.out.println("Relocation Type mapping: Unhandled architecture");
 362         }
 363         return pecoffRelocType;
 364     }
 365 }