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 package jdk.tools.jaotc.binformat.pecoff;
  25 
  26 import java.io.IOException;
  27 import java.nio.charset.StandardCharsets;
  28 import java.util.ArrayList;
  29 import java.util.Collection;
  30 import java.util.List;
  31 import java.util.Map;
  32 
  33 import jdk.tools.jaotc.binformat.Container;
  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.NativeSymbol;
  42 import jdk.tools.jaotc.binformat.Symbol.Binding;
  43 import jdk.tools.jaotc.binformat.Symbol.Kind;
  44 
  45 import jdk.tools.jaotc.binformat.pecoff.PECoff;
  46 import jdk.tools.jaotc.binformat.pecoff.PECoffSymbol;
  47 import jdk.tools.jaotc.binformat.pecoff.PECoffTargetInfo;
  48 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_FILE_HEADER;
  49 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SECTION_HEADER;
  50 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SYMBOL;
  51 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_RELOCATION;
  52 
  53 public class JPECoffRelocObject {
  54 
  55     private final BinaryContainer binContainer;
  56 
  57     private final PECoffContainer pecoffContainer;
  58 
  59     private final int segmentSize;
  60 
  61     public JPECoffRelocObject(BinaryContainer binContainer, String outputFileName, String aotVersion) {
  62         this.binContainer = binContainer;
  63         this.pecoffContainer = new PECoffContainer(outputFileName, aotVersion);
  64         this.segmentSize = binContainer.getCodeSegmentSize();
  65     }
  66 
  67     private PECoffSection createByteSection(ArrayList<PECoffSection>sections, 
  68                                          String sectName,
  69                                          byte [] scnData,
  70                                          boolean hasRelocs,
  71                                          int scnFlags) {
  72 
  73         PECoffSection sect = new PECoffSection(sectName,
  74                                          scnData,
  75                                          scnFlags,
  76                                          hasRelocs,
  77                                          sections.size());
  78         // Add this section to our list
  79         sections.add(sect);
  80 
  81         return (sect);                                         
  82     }
  83 
  84     private void createByteSection(ArrayList<PECoffSection>sections, 
  85                                    ByteContainer c, int scnFlags) {
  86         PECoffSection sect;
  87         boolean hasRelocs = c.hasRelocations();
  88         byte[] scnData = c.getByteArray();
  89 
  90         sect = createByteSection(sections, c.getContainerName(), 
  91                                  scnData, hasRelocs, 
  92                                  scnFlags);
  93 
  94         c.setSectionId(sect.getSectionId());                                         
  95     }
  96 
  97     private void createCodeSection(ArrayList<PECoffSection>sections, CodeContainer c) {
  98         createByteSection(sections, c, IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | 
  99                                        IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_EXECUTE | 
 100                                        IMAGE_SECTION_HEADER.IMAGE_SCN_ALIGN_16BYTES | 
 101                                        IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_CODE);
 102     }
 103 
 104     private void createReadOnlySection(ArrayList<PECoffSection>sections, ReadOnlyDataContainer c) {
 105         createByteSection(sections, c, IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ |
 106                                        IMAGE_SECTION_HEADER.IMAGE_SCN_ALIGN_16BYTES |
 107                                        IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA);
 108     }
 109 
 110     private void createReadWriteSection(ArrayList<PECoffSection>sections, ByteContainer c) {
 111         int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | 
 112                        IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_WRITE | 
 113                        IMAGE_SECTION_HEADER.IMAGE_SCN_ALIGN_8BYTES;
 114  
 115         if (c.getByteArray().length > 0)
 116             scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA;
 117         else
 118             scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_UNINITIALIZED_DATA;
 119 
 120         createByteSection(sections, c, scnFlags);
 121     }
 122 
 123     /**
 124      * Create an PECoff relocatable object 
 125      *
 126      * @param relocationTable
 127      * @param symbols
 128      * @throws IOException throws {@code IOException} as a result of file system access failures.
 129      */
 130     public void createPECoffRelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException {
 131         ArrayList<PECoffSection> sections = new ArrayList<PECoffSection>();
 132 
 133         // Create text section
 134         createCodeSection(sections, binContainer.getCodeContainer());
 135         createReadOnlySection(sections, binContainer.getMetaspaceNamesContainer());
 136         createReadOnlySection(sections, binContainer.getKlassesOffsetsContainer());
 137         createReadOnlySection(sections, binContainer.getMethodsOffsetsContainer());
 138         createReadOnlySection(sections, binContainer.getKlassesDependenciesContainer());
 139         createReadWriteSection(sections, binContainer.getMetaspaceGotContainer());
 140         createReadWriteSection(sections, binContainer.getMetadataGotContainer());
 141         createReadWriteSection(sections, binContainer.getMethodStateContainer());
 142         createReadWriteSection(sections, binContainer.getOopGotContainer());
 143         createReadWriteSection(sections, binContainer.getMethodMetadataContainer());
 144         createReadOnlySection(sections, binContainer.getStubsOffsetsContainer());
 145         createReadOnlySection(sections, binContainer.getHeaderContainer().getContainer());
 146         createReadOnlySection(sections, binContainer.getCodeSegmentsContainer());
 147         createReadOnlySection(sections, binContainer.getConstantDataContainer());
 148         createReadOnlySection(sections, binContainer.getConfigContainer());
 149 
 150         // createExternalLinkage();
 151 
 152         createCodeSection(sections, binContainer.getExtLinkageContainer());
 153         createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer());
 154 
 155         // Allocate PECoff Header
 156         PECoffHeader header = new PECoffHeader();
 157 
 158         // Get PECoff symbol data from BinaryContainer object's symbol tables
 159         PECoffSymtab symtab = createPECoffSymbolTables(sections, symbols);
 160 
 161         // Add Linker Directives Section
 162         createByteSection(sections, ".drectve", 
 163                           symtab.getDirectiveArray(), false,
 164                           IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_INFO |
 165                           IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_REMOVE |
 166                           IMAGE_SECTION_HEADER.IMAGE_SCN_ALIGN_1BYTES);
 167 
 168         // Create the Relocation Tables
 169         PECoffRelocTable pecoffRelocs = createPECoffRelocTable(sections, relocationTable);
 170 
 171         // File Output Order
 172         //
 173         //   HEADER           (Need address of Symbol Table + symbol count)
 174         //   SECTIONS         (Need pointer to Section Data, Relocation Table)
 175         //   DIRECTIVES
 176         //   SYMBOL TABLE
 177         //   SYMBOLS
 178         //   SECTION DATA
 179         //   RELOCATION TABLE
 180         
 181         // Calculate Offset for Symbol table
 182         int file_offset = IMAGE_FILE_HEADER.totalsize +
 183                           (IMAGE_SECTION_HEADER.totalsize*sections.size());
 184 
 185         // Update Header fields
 186         header.setSectionCount(sections.size());
 187         header.setSymbolCount(symtab.getSymtabCount());
 188         header.setSymbolOff(file_offset);
 189 
 190         // Calculate file offset for first section
 191         file_offset += ((symtab.getSymtabCount() * IMAGE_SYMBOL.totalsize) + 
 192                         symtab.getStrtabSize());
 193         // And round it up
 194         file_offset = (file_offset + (sections.get(0).getDataAlign()-1)) &
 195                       ~((sections.get(0).getDataAlign()-1));
 196 
 197         // Calc file offsets for section data 
 198         for (int i = 0; i < sections.size(); i++) {
 199             PECoffSection sect = sections.get(i);
 200             file_offset = (file_offset + (sect.getDataAlign()-1)) &
 201                            ~((sect.getDataAlign()-1));
 202             sect.setOffset(file_offset);
 203             file_offset += sect.getSize();
 204         }
 205 
 206         // Update relocation sizing information in each section
 207         for (int i = 0; i < sections.size(); i++) {
 208             PECoffSection sect = sections.get(i);
 209             if (sect.hasRelocations()) {
 210                 int nreloc = pecoffRelocs.getNumRelocs(i);
 211                 sect.setReloff(file_offset);
 212                 sect.setRelcount(nreloc);
 213                 // extended relocations add an addition entry 
 214                 if (nreloc > 0xFFFF) nreloc++;
 215                 file_offset += (nreloc * IMAGE_RELOCATION.totalsize);
 216             }
 217         }
 218 
 219         // Write out the Header
 220         pecoffContainer.writeBytes(header.getArray());
 221         
 222         // Write out the section table
 223         for (int i = 0; i < sections.size(); i++) {
 224             PECoffSection sect = sections.get(i);
 225             pecoffContainer.writeBytes(sect.getArray(), PECoffSection.getShdrAlign());
 226         }
 227 
 228         // Write out the symbol table and string table
 229         pecoffContainer.writeBytes(symtab.getSymtabArray(), 4);
 230         pecoffContainer.writeBytes(symtab.getStrtabArray(), 1);
 231 
 232         // Write out each section contents 
 233         for (int i = 0; i < sections.size(); i++) {
 234             PECoffSection sect = sections.get(i);
 235             pecoffContainer.writeBytes(sect.getDataArray(), sect.getDataAlign());
 236         }
 237 
 238         // Write out Relocation Tables
 239         for (int i = 0; i < sections.size(); i++) {
 240             if (pecoffRelocs.getNumRelocs(i) > 0) {
 241                 pecoffContainer.writeBytes(pecoffRelocs.getRelocData(i));
 242             }
 243         }
 244         pecoffContainer.close();
 245     }
 246 
 247     /**
 248      * Construct PECoff symbol data from BinaryContainer object's symbol tables. Both dynamic PECoff
 249      * symbol table and PECoff symbol table are created from BinaryContainer's symbol info.
 250      *
 251      * @param symbols
 252      */
 253     private PECoffSymtab createPECoffSymbolTables(ArrayList<PECoffSection> sections, Collection<Symbol> symbols) {
 254         PECoffSymtab symtab = new PECoffSymtab();
 255 
 256         // First, create the initial null symbol. This is a local symbol.
 257         // symtab.addSymbolEntry("", (byte)0, (byte)0, (byte)0, 0, 0);
 258 
 259         // Now create PECoff symbol entries for all symbols.
 260         for (Symbol symbol : symbols) {
 261             // Get the index of section this symbol is defined in.
 262             int secHdrIndex = symbol.getSection().getSectionId();
 263             PECoffSymbol pecoffSymbol = symtab.addSymbolEntry(symbol.getName(), getPECoffTypeOf(symbol), getPECoffClassOf(symbol), (byte)secHdrIndex, symbol.getOffset(), symbol.getSize());
 264             symbol.setNativeSymbol((NativeSymbol)pecoffSymbol);
 265         }
 266         return (symtab);
 267     }
 268 
 269     private static byte getPECoffTypeOf(Symbol sym) {
 270         Kind kind = sym.getKind();
 271         if (kind == Symbol.Kind.NATIVE_FUNCTION || kind == Symbol.Kind.JAVA_FUNCTION) {
 272             return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_FUNCTION;
 273         }
 274         return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_NONE;
 275     }
 276 
 277     private static byte getPECoffClassOf(Symbol sym) {
 278         Binding binding = sym.getBinding();
 279         if (binding == Symbol.Binding.GLOBAL) {
 280             return IMAGE_SYMBOL.IMAGE_SYM_CLASS_EXTERNAL;
 281         }
 282         return IMAGE_SYMBOL.IMAGE_SYM_CLASS_STATIC;
 283     }
 284 
 285     /**
 286      * Construct a PECoff relocation table from BinaryContainer object's relocation tables.
 287      *
 288      * @param sections
 289      * @param relocationTable
 290      */
 291     private PECoffRelocTable createPECoffRelocTable(ArrayList<PECoffSection> sections,
 292                                               Map<Symbol, List<Relocation>> relocationTable) {
 293 
 294         PECoffRelocTable pecoffRelocTable = new PECoffRelocTable(sections.size());
 295         /*
 296          * For each of the symbols with associated relocation records, create a PECoff relocation
 297          * entry.
 298          */
 299         for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) {
 300             List<Relocation> relocs = entry.getValue();
 301             Symbol symbol = entry.getKey();
 302 
 303             for (Relocation reloc : relocs) {
 304                 createRelocation(symbol, reloc, pecoffRelocTable);
 305             }
 306         }
 307 
 308         for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) {
 309             createRelocation(entry.getKey(), entry.getValue(), pecoffRelocTable);
 310         }
 311 
 312         return (pecoffRelocTable);
 313     }
 314 
 315     private void createRelocation(Symbol symbol, Relocation reloc, PECoffRelocTable pecoffRelocTable) {
 316         RelocType relocType = reloc.getType();
 317 
 318         int pecoffRelocType = getPECoffRelocationType(relocType);
 319         PECoffSymbol sym = (PECoffSymbol)symbol.getNativeSymbol();
 320         int symno = sym.getIndex();
 321         int sectindex = reloc.getSection().getSectionId();
 322         int offset = reloc.getOffset();
 323         int addend = 0;
 324 
 325         switch (relocType) {
 326             case FOREIGN_CALL_DIRECT:
 327             case JAVA_CALL_DIRECT:
 328             case STUB_CALL_DIRECT:
 329             case FOREIGN_CALL_INDIRECT_GOT: {
 330                 // Create relocation entry
 331                 addend = -4; // Size in bytes of the patch location
 332                 // Relocation should be applied at the location after call operand
 333                 offset = offset + reloc.getSize() + addend;
 334                 break;
 335             }
 336             case FOREIGN_CALL_DIRECT_FAR: {
 337                 // Create relocation entry
 338                 addend = -8; // Size in bytes of the patch location
 339                 // Relocation should be applied at the location after call operand
 340                 // 10 = 2 (jmp [r]) + 8 (imm64)
 341                 offset = offset + reloc.getSize() + addend - 2;
 342                 break;
 343             }
 344             case FOREIGN_CALL_INDIRECT:
 345             case JAVA_CALL_INDIRECT:
 346             case STUB_CALL_INDIRECT: {
 347                 // Do nothing.
 348                 return;
 349             }
 350             case EXTERNAL_DATA_REFERENCE_FAR: {
 351                 // Create relocation entry
 352                 addend = -4; // Size of 32-bit address of the GOT
 353                 /*
 354                  * Relocation should be applied before the test instruction to the move instruction.
 355                  * offset points to the test instruction after the instruction that loads
 356                  * the address of polling page. So set the offset appropriately.
 357                  */
 358                 offset = offset + addend;
 359                 break;
 360             }
 361             case METASPACE_GOT_REFERENCE:
 362             case EXTERNAL_PLT_TO_GOT:
 363             case STATIC_STUB_TO_STATIC_METHOD:
 364             case STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT: {
 365                 addend = -4; // Size of 32-bit address of the GOT
 366                 /*
 367                  * Relocation should be applied before the test instruction to
 368                  * the move instruction. reloc.getOffset() points to the
 369                  * test instruction after the instruction that loads the
 370                  * address of polling page. So set the offset appropriately.
 371                  */
 372                 offset = offset + addend;
 373                 break;
 374             }
 375             case EXTERNAL_GOT_TO_PLT:
 376             case LOADTIME_ADDRESS: {
 377                 // this is load time relocations
 378                 break;
 379             }
 380             default:
 381                 throw new InternalError("Unhandled relocation type: " + relocType);
 382         }
 383         pecoffRelocTable.createRelocationEntry(sectindex, offset, symno, pecoffRelocType);
 384     }
 385 
 386     // Return IMAGE_RELOCATION Type based on relocType
 387     private static int getPECoffRelocationType(RelocType relocType) {
 388         int pecoffRelocType = 0; // R_<ARCH>_NONE if #define'd to 0 for all values of ARCH
 389         switch (PECoffTargetInfo.getPECoffArch()) {
 390             case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
 391                 if (relocType == RelocType.FOREIGN_CALL_DIRECT ||
 392                     relocType == RelocType.JAVA_CALL_DIRECT ||
 393                     relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) {
 394                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
 395                 } else if (relocType == RelocType.STUB_CALL_DIRECT) {
 396                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
 397                 } else if (relocType == RelocType.FOREIGN_CALL_DIRECT_FAR) {
 398                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ADDR64;
 399                 } else if (relocType == RelocType.FOREIGN_CALL_INDIRECT ||
 400                            relocType == RelocType.JAVA_CALL_INDIRECT ||
 401                            relocType == RelocType.STUB_CALL_INDIRECT) {
 402                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ABSOLUTE;
 403                 } else if ((relocType == RelocType.EXTERNAL_DATA_REFERENCE_FAR)) {
 404                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
 405                 } else if (relocType == RelocType.METASPACE_GOT_REFERENCE ||
 406                            relocType == RelocType.EXTERNAL_PLT_TO_GOT ||
 407                            relocType == RelocType.STATIC_STUB_TO_STATIC_METHOD ||
 408                            relocType == RelocType.STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT) {
 409                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
 410                 } else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT ||
 411                            relocType == RelocType.LOADTIME_ADDRESS) {
 412                     pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ADDR64;
 413                 } else {
 414                     assert false : "Unhandled relocation type: " + relocType;
 415                 }
 416                 break;
 417             default:
 418                 System.out.println("Relocation Type mapping: Unhandled architecture");
 419         }
 420         return pecoffRelocType;
 421     }
 422 }