1 /*
   2  * Copyright (c) 2016, 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.elf;
  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.BinaryContainer;
  34 import jdk.tools.jaotc.binformat.ByteContainer;
  35 import jdk.tools.jaotc.binformat.CodeContainer;
  36 import jdk.tools.jaotc.binformat.ReadOnlyDataContainer;
  37 import jdk.tools.jaotc.binformat.Relocation;
  38 import jdk.tools.jaotc.binformat.Relocation.RelocType;
  39 import jdk.tools.jaotc.binformat.Symbol;
  40 import jdk.tools.jaotc.binformat.Symbol.Binding;
  41 import jdk.tools.jaotc.binformat.Symbol.Kind;
  42 import jdk.tools.jaotc.jnilibelf.ELFContainer;
  43 import jdk.tools.jaotc.jnilibelf.ELFSymbol;
  44 import jdk.tools.jaotc.jnilibelf.JNIELFContainer;
  45 import jdk.tools.jaotc.jnilibelf.JNIELFRelocation;
  46 import jdk.tools.jaotc.jnilibelf.JNIELFTargetInfo;
  47 import jdk.tools.jaotc.jnilibelf.JNILibELFAPI.ELF;
  48 import jdk.tools.jaotc.jnilibelf.JNILibELFAPI.LibELF.Elf_Cmd;
  49 import jdk.tools.jaotc.jnilibelf.JNILibELFAPI.LibELF.Elf_Type;
  50 import jdk.tools.jaotc.jnilibelf.Pointer;
  51 
  52 public class JELFRelocObject {
  53 
  54     private final BinaryContainer binContainer;
  55 
  56     private final JNIELFContainer elfContainer;
  57 
  58     private final int segmentSize;
  59 
  60     public JELFRelocObject(BinaryContainer binContainer, String outputFileName, String aotVersion) {
  61         this.binContainer = binContainer;
  62         this.elfContainer = new JNIELFContainer(outputFileName, aotVersion);
  63         this.segmentSize = binContainer.getCodeSegmentSize();
  64     }
  65 
  66     private void createByteSection(ByteContainer c, int scnFlags) {
  67         byte[] scnData = c.getByteArray();
  68         int scnType = ELF.SHT_PROGBITS;
  69         boolean zeros = !c.hasRelocations();
  70         if (zeros) {
  71             for (byte b : scnData) {
  72                 if (b != 0) {
  73                     zeros = false;
  74                     break;
  75                 }
  76             }
  77             if (zeros) {
  78                 scnType = ELF.SHT_NOBITS;
  79             }
  80         }
  81 
  82         int sectionId = elfContainer.createSection(c.getContainerName(), scnData, Elf_Type.ELF_T_BYTE, segmentSize, scnType, scnFlags, ELF.SHN_UNDEF, 0);
  83         c.setSectionId(sectionId);
  84         // Clear out code section data to allow for GC
  85         c.clear();
  86     }
  87 
  88     private void createCodeSection(CodeContainer c) {
  89         createByteSection(c, ELF.SHF_ALLOC | ELF.SHF_EXECINSTR);
  90     }
  91 
  92     private void createReadOnlySection(ReadOnlyDataContainer c) {
  93         createByteSection(c, ELF.SHF_ALLOC);
  94     }
  95 
  96     private void createReadWriteSection(ByteContainer c) {
  97         createByteSection(c, ELF.SHF_ALLOC | ELF.SHF_WRITE);
  98     }
  99 
 100     /**
 101      * Create an ELF relocatable object using jdk.tools.jaotc.jnilibelf API.
 102      *
 103      * @param relocationTable
 104      * @param symbols
 105      * @throws IOException throws {@code IOException} as a result of file system access failures.
 106      */
 107     public void createELFRelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException {
 108         // Allocate ELF Header
 109         elfContainer.createELFHeader(ELF.ET_REL);
 110 
 111         // Create text section
 112         createCodeSection(binContainer.getCodeContainer());
 113         createReadOnlySection(binContainer.getMetaspaceNamesContainer());
 114         createReadOnlySection(binContainer.getKlassesOffsetsContainer());
 115         createReadOnlySection(binContainer.getMethodsOffsetsContainer());
 116         createReadOnlySection(binContainer.getKlassesDependenciesContainer());
 117         createReadWriteSection(binContainer.getMetaspaceGotContainer());
 118         createReadWriteSection(binContainer.getMetadataGotContainer());
 119         createReadWriteSection(binContainer.getMethodStateContainer());
 120         createReadWriteSection(binContainer.getOopGotContainer());
 121         createReadWriteSection(binContainer.getMethodMetadataContainer());
 122         createReadOnlySection(binContainer.getStubsOffsetsContainer());
 123         createReadOnlySection(binContainer.getHeaderContainer().getContainer());
 124         createReadOnlySection(binContainer.getCodeSegmentsContainer());
 125         createReadOnlySection(binContainer.getConstantDataContainer());
 126         createReadOnlySection(binContainer.getConfigContainer());
 127 
 128         // createExternalLinkage();
 129 
 130         createCodeSection(binContainer.getExtLinkageContainer());
 131         createReadWriteSection(binContainer.getExtLinkageGOTContainer());
 132 
 133         // Get ELF symbol data from BinaryContainer object's symbol tables
 134         createELFSymbolTables(symbols);
 135 
 136         // Create string table section and symbol table sections in
 137         // that order since symtab section needs to set the index of strtab in sh_link field
 138         int strTabSectionIndex = elfContainer.createSection(".strtab", elfContainer.getStrTabContent().getBytes(StandardCharsets.UTF_8), Elf_Type.ELF_T_BYTE, 1, ELF.SHT_STRTAB, 0, ELF.SHN_UNDEF, 0);
 139 
 140         // Now create .symtab section with the symtab data constructed. On Linux, sh_link of symtab
 141         // contains the index of string table its symbols reference and
 142         // sh_info contains the index of first non-local symbol
 143         int scnInfo = elfContainer.getFirstNonLocalSymbolIndex();
 144         int symTabSectionIndex = elfContainer.createSection(".symtab", getELFSymbolTableData(), Elf_Type.ELF_T_SYM, 8, ELF.SHT_SYMTAB, ELF.SHF_ALLOC, strTabSectionIndex, scnInfo);
 145 
 146         buildRelocations(relocationTable, symTabSectionIndex);
 147 
 148         // Now, finally, after creating all sections, create shstrtab section
 149         elfContainer.createSection(".shstrtab", elfContainer.getShStrTabContent().getBytes(StandardCharsets.UTF_8), Elf_Type.ELF_T_BYTE, 1, ELF.SHT_STRTAB, 0, ELF.SHN_UNDEF, 0);
 150 
 151         // Run elf_update
 152         elfContainer.elfUpdate(Elf_Cmd.ELF_C_NULL);
 153 
 154         // Run elfUpdate again to write it out.
 155         elfContainer.elfUpdate(Elf_Cmd.ELF_C_WRITE);
 156         // Finish ELF processing
 157         elfContainer.elfEnd();
 158     }
 159 
 160     private void buildRelocations(Map<Symbol, List<Relocation>> relocationTable, final int symTabSectionIndex) {
 161         /*
 162          * Create relocation sections. This needs to be done after symbol table sections were
 163          * created since relocation entries will need indices of sections to which they apply.
 164          */
 165         createELFRelocationTables(relocationTable);
 166         createAllRelocationSections(new SymTabELFContainer(symTabSectionIndex));
 167     }
 168 
 169     /**
 170      * Construct ELF symbol data from BinaryContainer object's symbol tables. Both dynamic ELF
 171      * symbol table and ELF symbol table are created from BinaryContainer's symbol info.
 172      *
 173      * @param symbols
 174      */
 175     private void createELFSymbolTables(Collection<Symbol> symbols) {
 176         // First, create the initial null symbol. This is a local symbol.
 177         elfContainer.createELFSymbolEntry("", 0, 0, ELF.SHN_UNDEF, 0, 0, true);
 178 
 179         // Now create ELF symbol entries for all symbols.
 180         for (Symbol symbol : symbols) {
 181             // Get the index of section this symbol is defined in.
 182             int secHdrIndex = symbol.getSection().getSectionId();
 183             boolean isLocal = (symbol.getBinding() == Binding.LOCAL);
 184             ELFSymbol elfSymbol = elfContainer.createELFSymbolEntry(symbol.getName(), getELFTypeOf(symbol), getELFBindOf(symbol), secHdrIndex, symbol.getSize(), symbol.getOffset(), isLocal);
 185             symbol.setElfSymbol(elfSymbol);
 186         }
 187     }
 188 
 189     /**
 190      * Construct ELF symbol data from BinaryContainer object's symbol tables.
 191      *
 192      * @return a byte array containing the symbol table
 193      */
 194     private byte[] getELFSymbolTableData() {
 195         final int entrySize = JNIELFTargetInfo.sizeOfSymtabEntry();
 196 
 197         // First, add all local symbols.
 198         List<ELFSymbol> localSymbols = elfContainer.getLocalSymbols();
 199         List<ELFSymbol> globalSymbols = elfContainer.getGlobalSymbols();
 200 
 201         int localSymCount = localSymbols.size();
 202         int globalSymCount = globalSymbols.size();
 203         byte[] sectionDataArray = new byte[(localSymCount + globalSymCount) * entrySize];
 204 
 205         for (int i = 0; i < localSymCount; i++) {
 206             ELFSymbol symbol = localSymbols.get(i);
 207             Pointer address = symbol.getAddress();
 208             address.copyBytesTo(sectionDataArray, entrySize, i * entrySize);
 209         }
 210 
 211         // Next, add all global symbols.
 212 
 213         for (int i = 0; i < globalSymCount; i++) {
 214             ELFSymbol symbol = globalSymbols.get(i);
 215             Pointer address = symbol.getAddress();
 216             address.copyBytesTo(sectionDataArray, entrySize, (localSymCount + i) * entrySize);
 217         }
 218 
 219         return sectionDataArray;
 220     }
 221 
 222     private static int getELFTypeOf(Symbol sym) {
 223         Kind kind = sym.getKind();
 224         if (kind == Symbol.Kind.NATIVE_FUNCTION || kind == Symbol.Kind.JAVA_FUNCTION) {
 225             return ELF.STT_FUNC;
 226         } else if (kind == Symbol.Kind.OBJECT) {
 227             return ELF.STT_OBJECT;
 228         }
 229         return ELF.STT_NOTYPE;
 230     }
 231 
 232     private static int getELFBindOf(Symbol sym) {
 233         Binding binding = sym.getBinding();
 234         if (binding == Symbol.Binding.GLOBAL) {
 235             return ELF.STB_GLOBAL;
 236         }
 237         return ELF.STB_LOCAL;
 238     }
 239 
 240     /**
 241      * Construct ELF relocation section data from BinaryContainer object's relocation tables.
 242      *
 243      * @param relocationTable
 244      */
 245     private void createELFRelocationTables(Map<Symbol, List<Relocation>> relocationTable) {
 246         /*
 247          * For each of the symbols with associated relocation records, create an ELF relocation
 248          * entry.
 249          */
 250         for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) {
 251             List<Relocation> relocs = entry.getValue();
 252             Symbol symbol = entry.getKey();
 253 
 254             for (Relocation reloc : relocs) {
 255                 createRelocation(symbol, reloc);
 256             }
 257         }
 258 
 259         for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) {
 260             createRelocation(entry.getKey(), entry.getValue());
 261         }
 262     }
 263 
 264     private void createRelocation(Symbol symbol, Relocation reloc) {
 265         RelocType relocType = reloc.getType();
 266         int elfRelocType = getELFRelocationType(relocType);
 267 
 268         switch (relocType) {
 269             case FOREIGN_CALL_DIRECT:
 270             case JAVA_CALL_DIRECT:
 271             case STUB_CALL_DIRECT:
 272             case FOREIGN_CALL_INDIRECT_GOT: {
 273                 // Create relocation entry
 274                 int addend = -4; // Size in bytes of the patch location
 275                 // Relocation should be applied at the location after call operand
 276                 int offset = reloc.getOffset() + reloc.getSize() + addend;
 277                 elfContainer.createELFRelocationEntry(reloc.getSection(), offset, elfRelocType, addend, symbol.getElfSymbol());
 278                 break;
 279             }
 280             case FOREIGN_CALL_DIRECT_FAR: {
 281                 // Create relocation entry
 282                 int addend = -8; // Size in bytes of the patch location
 283                 // Relocation should be applied at the location after call operand
 284                 // 10 = 2 (jmp [r]) + 8 (imm64)
 285                 int offset = reloc.getOffset() + reloc.getSize() + addend - 2;
 286                 elfContainer.createELFRelocationEntry(reloc.getSection(), offset, elfRelocType, addend, symbol.getElfSymbol());
 287                 break;
 288             }
 289             case FOREIGN_CALL_INDIRECT:
 290             case JAVA_CALL_INDIRECT:
 291             case STUB_CALL_INDIRECT: {
 292                 // Do nothing.
 293                 break;
 294             }
 295             case EXTERNAL_DATA_REFERENCE_FAR: {
 296                 // Create relocation entry
 297                 int addend = -4; // Size of 32-bit address of the GOT
 298                 /*
 299                  * Relocation should be applied before the test instruction to the move instruction.
 300                  * reloc.getOffset() points to the test instruction after the instruction that loads
 301                  * the address of polling page. So set the offset appropriately.
 302                  */
 303                 int offset = reloc.getOffset() + addend;
 304                 elfContainer.createELFRelocationEntry(reloc.getSection(), offset, elfRelocType, addend, symbol.getElfSymbol());
 305                 break;
 306             }
 307             case METASPACE_GOT_REFERENCE:
 308             case EXTERNAL_PLT_TO_GOT:
 309             case STATIC_STUB_TO_STATIC_METHOD:
 310             case STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT: {
 311                 int addend = -4; // Size of 32-bit address of the GOT
 312                 /*
 313                  * Relocation should be applied before the test instruction to the move instruction.
 314                  * reloc.getOffset() points to the test instruction after the instruction that loads
 315                  * the address of polling page. So set the offset appropriately.
 316                  */
 317                 int offset = reloc.getOffset() + addend;
 318                 elfContainer.createELFRelocationEntry(reloc.getSection(), offset, elfRelocType, addend, symbol.getElfSymbol());
 319                 break;
 320             }
 321             case EXTERNAL_GOT_TO_PLT:
 322             case LOADTIME_ADDRESS: {
 323                 // this is load time relocations
 324                 elfContainer.createELFRelocationEntry(reloc.getSection(), reloc.getOffset(), elfRelocType, 0, symbol.getElfSymbol());
 325                 break;
 326             }
 327             default:
 328                 throw new InternalError("Unhandled relocation type: " + relocType);
 329         }
 330     }
 331 
 332     // TODO: Populate the mapping of RelocType to ELF relocation types
 333     private static int getELFRelocationType(RelocType relocType) {
 334         int elfRelocType = 0; // R_<ARCH>_NONE if #define'd to 0 for all values of ARCH
 335         switch (JNIELFTargetInfo.getELFArch()) {
 336             case ELF.EM_X64_64:
 337                 // Return R_X86_64_* entries based on relocType
 338                 if (relocType == RelocType.FOREIGN_CALL_DIRECT || relocType == RelocType.JAVA_CALL_DIRECT || relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) {
 339                     elfRelocType = JNIELFRelocation.X86_64.R_X86_64_PLT32;
 340                 } else if (relocType == RelocType.STUB_CALL_DIRECT) {
 341                     elfRelocType = JNIELFRelocation.X86_64.R_X86_64_PC32;
 342                 } else if (relocType == RelocType.FOREIGN_CALL_DIRECT_FAR) {
 343                     elfRelocType = JNIELFRelocation.X86_64.R_X86_64_64;
 344                 } else if (relocType == RelocType.FOREIGN_CALL_INDIRECT || relocType == RelocType.JAVA_CALL_INDIRECT || relocType == RelocType.STUB_CALL_INDIRECT) {
 345                     elfRelocType = JNIELFRelocation.X86_64.R_X86_64_NONE;
 346                 } else if ((relocType == RelocType.EXTERNAL_DATA_REFERENCE_FAR)) {
 347                     elfRelocType = JNIELFRelocation.X86_64.R_X86_64_GOTPCREL;
 348                 } else if (relocType == RelocType.METASPACE_GOT_REFERENCE || relocType == RelocType.EXTERNAL_PLT_TO_GOT || relocType == RelocType.STATIC_STUB_TO_STATIC_METHOD ||
 349                                 relocType == RelocType.STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT) {
 350                     elfRelocType = JNIELFRelocation.X86_64.R_X86_64_PC32;
 351                 } else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT || relocType == RelocType.LOADTIME_ADDRESS) {
 352                     elfRelocType = JNIELFRelocation.X86_64.R_X86_64_64;
 353                 } else {
 354                     assert false : "Unhandled relocation type: " + relocType;
 355                 }
 356                 break;
 357             default:
 358                 System.out.println("Relocation Type mapping: Unhandled architecture");
 359         }
 360         return elfRelocType;
 361     }
 362 
 363     private void createAllRelocationSections(ELFContainer symtab) {
 364         for (Map.Entry<ELFContainer, ArrayList<Pointer>> entry : elfContainer.getRelocTables().entrySet()) {
 365             createRelocationSection(entry.getKey(), entry.getValue(), symtab);
 366         }
 367     }
 368 
 369     private void createRelocationSection(ELFContainer container, ArrayList<Pointer> relocations, ELFContainer symtab) {
 370         String secName = container.getContainerName();
 371         int entrySize = JNIELFTargetInfo.sizeOfRelocEntry();
 372         int numEntries = relocations.size();
 373         byte[] sectionDataBytes = new byte[numEntries * entrySize];
 374 
 375         for (int index = 0; index < relocations.size(); index++) {
 376             Pointer entry = relocations.get(index);
 377             entry.copyBytesTo(sectionDataBytes, entrySize, index * entrySize);
 378         }
 379         String fullSecName;
 380         // If relocDat is non-null create section
 381         if (sectionDataBytes.length > 0) {
 382             int scnType;
 383             Elf_Type dataType;
 384             if (JNIELFTargetInfo.createReloca() == 0) {
 385                 scnType = ELF.SHT_REL;
 386                 dataType = Elf_Type.ELF_T_REL;
 387                 fullSecName = ".rel" + secName;
 388             } else {
 389                 scnType = ELF.SHT_RELA;
 390                 dataType = Elf_Type.ELF_T_RELA;
 391                 fullSecName = ".rela" + secName;
 392             }
 393             // assert compareBytes(relocData.toByteArray(), sectionDataBytes) : "******* Bad array
 394             // copy";
 395             // sh_link holds the index of section header of symbol table associated with this
 396             // relocation table.
 397             // sh_info holds the index of section header to which this relocation table applies
 398             // to.
 399             elfContainer.createSection(fullSecName, sectionDataBytes, dataType, 8, scnType, ELF.SHF_ALLOC, symtab.getSectionId(), container.getSectionId());
 400         }
 401     }
 402 
 403     private static class SymTabELFContainer implements ELFContainer {
 404         private final int symTabSectionIndex;
 405 
 406         public SymTabELFContainer(int symTabSectionIndex) {
 407             this.symTabSectionIndex = symTabSectionIndex;
 408         }
 409 
 410         @Override
 411         public String getContainerName() {
 412             return ".symtab";
 413         }
 414 
 415         @Override
 416         public int getSectionId() {
 417             return symTabSectionIndex;
 418         }
 419     }
 420 }