1 /*
   2  * Copyright (c) 2016, 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.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.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.elf.Elf;
  46 import jdk.tools.jaotc.binformat.elf.ElfSymbol;
  47 import jdk.tools.jaotc.binformat.elf.ElfTargetInfo;
  48 import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Ehdr;
  49 import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Shdr;
  50 import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Sym;
  51 import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Rel;
  52 import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Rela;
  53 
  54 public class JELFRelocObject {
  55 
  56     private final BinaryContainer binContainer;
  57 
  58     private final ElfContainer elfContainer;
  59 
  60     private final int segmentSize;
  61 
  62     public JELFRelocObject(BinaryContainer binContainer, String outputFileName, String aotVersion) {
  63         this.binContainer = binContainer;
  64         this.elfContainer = new ElfContainer(outputFileName, aotVersion);
  65         this.segmentSize = binContainer.getCodeSegmentSize();
  66     }
  67 
  68     private ElfSection createByteSection(ArrayList<ElfSection>sections,
  69                                          String sectName,
  70                                          byte [] scnData,
  71                                          boolean hasRelocs,
  72                                          int scnFlags,
  73                                          int scnType) {
  74 
  75         ElfSection sect = new ElfSection(sectName,
  76                                          scnData,
  77                                          scnFlags,
  78                                          scnType,
  79                                          hasRelocs,
  80                                          sections.size());
  81         // Add this section to our list
  82         sections.add(sect);
  83 
  84         return (sect);
  85     }
  86 
  87     private void createByteSection(ArrayList<ElfSection>sections,
  88                                    ByteContainer c, int scnFlags) {
  89         ElfSection sect;
  90         boolean hasRelocs = c.hasRelocations();
  91         byte[] scnData = c.getByteArray();
  92 
  93         int scnType = Elf64_Shdr.SHT_PROGBITS;
  94         boolean zeros = hasRelocs;
  95         if (zeros) {
  96             for (byte b : scnData) {
  97                 if (b != 0) {
  98                     zeros = false;
  99                     break;
 100                 }
 101             }
 102             if (zeros) {
 103                 scnType = Elf64_Shdr.SHT_NOBITS;
 104             }
 105         }
 106 
 107         sect = createByteSection(sections, c.getContainerName(),
 108                                  scnData, hasRelocs,
 109                                  scnFlags, scnType);
 110         c.setSectionId(sect.getSectionId());
 111     }
 112 
 113     private void createCodeSection(ArrayList<ElfSection>sections, CodeContainer c) {
 114         createByteSection(sections, c, Elf64_Shdr.SHF_ALLOC | Elf64_Shdr.SHF_EXECINSTR);
 115     }
 116 
 117     private void createReadOnlySection(ArrayList<ElfSection>sections, ReadOnlyDataContainer c) {
 118         createByteSection(sections, c, Elf64_Shdr.SHF_ALLOC);
 119     }
 120 
 121     private void createReadWriteSection(ArrayList<ElfSection>sections, ByteContainer c) {
 122         createByteSection(sections, c, Elf64_Shdr.SHF_ALLOC | Elf64_Shdr.SHF_WRITE);
 123     }
 124 
 125     /**
 126      * Create an ELF relocatable object
 127      *
 128      * @param relocationTable
 129      * @param symbols
 130      * @throws IOException throws {@code IOException} as a result of file system access failures.
 131      */
 132     public void createELFRelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException {
 133         // Allocate ELF Header
 134         ElfHeader eh = new ElfHeader();
 135 
 136         ArrayList<ElfSection> sections = new ArrayList<ElfSection>();
 137 
 138         // Create the null section
 139         createByteSection(sections, null, null, false, 0, 0);
 140 
 141         // Create text section
 142         createCodeSection(sections, binContainer.getCodeContainer());
 143         createReadOnlySection(sections, binContainer.getMetaspaceNamesContainer());
 144         createReadOnlySection(sections, binContainer.getKlassesOffsetsContainer());
 145         createReadOnlySection(sections, binContainer.getMethodsOffsetsContainer());
 146         createReadOnlySection(sections, binContainer.getKlassesDependenciesContainer());
 147         createReadWriteSection(sections, binContainer.getMetaspaceGotContainer());
 148         createReadWriteSection(sections, binContainer.getMetadataGotContainer());
 149         createReadWriteSection(sections, binContainer.getMethodStateContainer());
 150         createReadWriteSection(sections, binContainer.getOopGotContainer());
 151         createReadWriteSection(sections, binContainer.getMethodMetadataContainer());
 152         createReadOnlySection(sections, binContainer.getStubsOffsetsContainer());
 153         createReadOnlySection(sections, binContainer.getHeaderContainer().getContainer());
 154         createReadOnlySection(sections, binContainer.getCodeSegmentsContainer());
 155         createReadOnlySection(sections, binContainer.getConstantDataContainer());
 156         createReadOnlySection(sections, binContainer.getConfigContainer());
 157 
 158         // createExternalLinkage();
 159 
 160         createCodeSection(sections, binContainer.getExtLinkageContainer());
 161         createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer());
 162 
 163         // Get ELF symbol data from BinaryContainer object's symbol tables
 164         ElfSymtab symtab = createELFSymbolTables(sections, symbols);
 165 
 166         // Create string table section and symbol table sections in
 167         // that order since symtab section needs to set the index of
 168         // strtab in sh_link field
 169         ElfSection strTabSection = createByteSection(sections,
 170                                                      ".strtab",
 171                                                      symtab.getStrtabArray(),
 172                                                      false,
 173                                                      0,
 174                                                      Elf64_Shdr.SHT_STRTAB);
 175 
 176         // Now create .symtab section with the symtab data constructed.
 177         // On Linux, sh_link of symtab contains the index of string table
 178         // its symbols reference and sh_info contains the index of first
 179         // non-local symbol
 180         ElfSection symTabSection = createByteSection(sections,
 181                                                     ".symtab",
 182                                                     symtab.getSymtabArray(),
 183                                                     false,
 184                                                     0,
 185                                                     Elf64_Shdr.SHT_SYMTAB);
 186         symTabSection.setLink(strTabSection.getSectionId());
 187         symTabSection.setInfo(symtab.getNumLocalSyms());
 188 
 189         ElfRelocTable elfRelocTable = createElfRelocTable(sections,
 190                                                           relocationTable);
 191 
 192         createElfRelocSections(sections, elfRelocTable, symTabSection.getSectionId());
 193 
 194         // Now, finally, after creating all sections, create shstrtab section
 195         ElfSection shStrTabSection = createByteSection(sections,
 196                                                     ".shstrtab",
 197                                                     null,
 198                                                     false,
 199                                                     0,
 200                                                     Elf64_Shdr.SHT_STRTAB);
 201         eh.setSectionStrNdx(shStrTabSection.getSectionId());
 202 
 203         // Update all section offsets and the Elf header section offset
 204         // Write the Header followed by the contents of each section
 205         // and then the section structures (section table).
 206         int file_offset = Elf64_Ehdr.totalsize;
 207 
 208         // and round it up
 209         file_offset = (file_offset + (sections.get(1).getDataAlign()-1)) &
 210                       ~((sections.get(1).getDataAlign()-1));
 211 
 212         // Calc file offsets for section data skipping null section
 213         for (int i = 1; i < sections.size(); i++) {
 214             ElfSection sect = sections.get(i);
 215             file_offset = (file_offset + (sect.getDataAlign()-1)) &
 216                            ~((sect.getDataAlign()-1));
 217             sect.setOffset(file_offset);
 218             file_offset += sect.getSize();
 219         }
 220 
 221         // Align the section table
 222         file_offset = (file_offset + (ElfSection.getShdrAlign()-1)) &
 223                       ~((ElfSection.getShdrAlign()-1));
 224 
 225         // Update the Elf Header with the offset of the first Elf64_Shdr
 226         // and the number of sections.
 227         eh.setSectionOff(file_offset);
 228         eh.setSectionNum(sections.size());
 229 
 230         // Write out the Header
 231         elfContainer.writeBytes(eh.getArray());
 232 
 233         // Write out each section contents skipping null section
 234         for (int i = 1; i < sections.size(); i++) {
 235             ElfSection sect = sections.get(i);
 236             elfContainer.writeBytes(sect.getDataArray(), sect.getDataAlign());
 237         }
 238 
 239         // Write out the section table
 240         for (int i = 0; i < sections.size(); i++) {
 241             ElfSection sect = sections.get(i);
 242             elfContainer.writeBytes(sect.getArray(), ElfSection.getShdrAlign());
 243         }
 244 
 245         elfContainer.close();
 246     }
 247     /**
 248      * Construct ELF symbol data from BinaryContainer object's symbol tables. Both dynamic ELF
 249      * symbol table and ELF symbol table are created from BinaryContainer's symbol info.
 250      *
 251      * @param symbols
 252      */
 253     private ElfSymtab createELFSymbolTables(ArrayList<ElfSection> sections, Collection<Symbol> symbols) {
 254         ElfSymtab symtab = new ElfSymtab();
 255 
 256         // First, create the initial null symbol. This is a local symbol.
 257         symtab.addSymbolEntry("", (byte)0, (byte)0, Elf64_Shdr.SHN_UNDEF, 0, 0);
 258 
 259         // Now create ELF 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             ElfSymbol elfSymbol = symtab.addSymbolEntry(symbol.getName(), getELFTypeOf(symbol), getELFBindOf(symbol), (byte)secHdrIndex, symbol.getOffset(), symbol.getSize());
 264             symbol.setNativeSymbol((NativeSymbol)elfSymbol);
 265         }
 266         return (symtab);
 267     }
 268 
 269     private static byte getELFTypeOf(Symbol sym) {
 270         Kind kind = sym.getKind();
 271         if (kind == Symbol.Kind.NATIVE_FUNCTION || kind == Symbol.Kind.JAVA_FUNCTION) {
 272             return Elf64_Sym.STT_FUNC;
 273         } else if (kind == Symbol.Kind.OBJECT) {
 274             return Elf64_Sym.STT_OBJECT;
 275         }
 276         return Elf64_Sym.STT_NOTYPE;
 277     }
 278 
 279     private static byte getELFBindOf(Symbol sym) {
 280         Binding binding = sym.getBinding();
 281         if (binding == Symbol.Binding.GLOBAL) {
 282             return Elf64_Sym.STB_GLOBAL;
 283         }
 284         return Elf64_Sym.STB_LOCAL;
 285     }
 286 
 287     /**
 288      * Construct a Elf relocation table from BinaryContainer object's relocation tables.
 289      *
 290      * @param sections
 291      * @param relocationTable
 292      */
 293     private ElfRelocTable createElfRelocTable(ArrayList<ElfSection> sections,
 294                                               Map<Symbol, List<Relocation>> relocationTable) {
 295 
 296         ElfRelocTable elfRelocTable = new ElfRelocTable(sections.size());
 297         /*
 298          * For each of the symbols with associated relocation records, create a Elf relocation
 299          * entry.
 300          */
 301         for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) {
 302             List<Relocation> relocs = entry.getValue();
 303             Symbol symbol = entry.getKey();
 304 
 305             for (Relocation reloc : relocs) {
 306                 createRelocation(symbol, reloc, elfRelocTable);
 307             }
 308         }
 309 
 310         for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) {
 311             createRelocation(entry.getKey(), entry.getValue(), elfRelocTable);
 312         }
 313 
 314         return (elfRelocTable);
 315     }
 316 
 317     private void createRelocation(Symbol symbol, Relocation reloc, ElfRelocTable elfRelocTable) {
 318         RelocType relocType = reloc.getType();
 319 
 320         int elfRelocType = getELFRelocationType(relocType);
 321         ElfSymbol sym = (ElfSymbol)symbol.getNativeSymbol();
 322         int symno = sym.getIndex();
 323         int sectindex = reloc.getSection().getSectionId();
 324         int offset = reloc.getOffset();
 325         int addend = 0;
 326 
 327         switch (relocType) {
 328             case FOREIGN_CALL_DIRECT:
 329             case JAVA_CALL_DIRECT:
 330             case STUB_CALL_DIRECT:
 331             case FOREIGN_CALL_INDIRECT_GOT: {
 332                 // Create relocation entry
 333                 // System.out.println("getELFRelocationType: PLT relocation type using X86_64_RELOC_BRANCH");
 334                 addend = -4; // Size in bytes of the patch location
 335                 // Relocation should be applied at the location after call operand
 336                 offset = offset + reloc.getSize() + addend;
 337                 break;
 338             }
 339             case FOREIGN_CALL_DIRECT_FAR: {
 340                 // Create relocation entry
 341                 addend = -8; // Size in bytes of the patch location
 342                 // Relocation should be applied at the location after call operand
 343                 // 10 = 2 (jmp [r]) + 8 (imm64)
 344                 offset = offset + reloc.getSize() + addend - 2;
 345                 break;
 346             }
 347             case FOREIGN_CALL_INDIRECT:
 348             case JAVA_CALL_INDIRECT:
 349             case STUB_CALL_INDIRECT: {
 350                 // Do nothing.
 351                 return;
 352             }
 353             case EXTERNAL_DATA_REFERENCE_FAR: {
 354                 // Create relocation entry
 355                 addend = -4; // Size of 32-bit address of the GOT
 356                 /*
 357                  * Relocation should be applied before the test instruction to the move instruction.
 358                  * offset points to the test instruction after the instruction that loads
 359                  * the address of polling page. So set the offset appropriately.
 360                  */
 361                 offset = offset + addend;
 362                 break;
 363             }
 364             case METASPACE_GOT_REFERENCE:
 365             case EXTERNAL_PLT_TO_GOT:
 366             case STATIC_STUB_TO_STATIC_METHOD:
 367             case STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT: {
 368                 addend = -4; // Size of 32-bit address of the GOT
 369                 /*
 370                  * Relocation should be applied before the test instruction to
 371                  * the move instruction. reloc.getOffset() points to the
 372                  * test instruction after the instruction that loads the
 373                  * address of polling page. So set the offset appropriately.
 374                  */
 375                 offset = offset + addend;
 376                 break;
 377             }
 378             case EXTERNAL_GOT_TO_PLT:
 379             case LOADTIME_ADDRESS: {
 380                 // this is load time relocations
 381                 break;
 382             }
 383             default:
 384                 throw new InternalError("Unhandled relocation type: " + relocType);
 385         }
 386         elfRelocTable.createRelocationEntry(sectindex, offset, symno, elfRelocType, addend);
 387     }
 388 
 389     private static int getELFRelocationType(RelocType relocType) {
 390         int elfRelocType = 0; // R_<ARCH>_NONE if #define'd to 0 for all values of ARCH
 391         switch (ElfTargetInfo.getElfArch()) {
 392             case Elf64_Ehdr.EM_X86_64:
 393                 // Return R_X86_64_* entries based on relocType
 394                 if (relocType == RelocType.FOREIGN_CALL_DIRECT ||
 395                     relocType == RelocType.JAVA_CALL_DIRECT ||
 396                     relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) {
 397                     elfRelocType = Elf64_Rela.R_X86_64_PLT32;
 398                 } else if (relocType == RelocType.STUB_CALL_DIRECT) {
 399                     elfRelocType = Elf64_Rela.R_X86_64_PC32;
 400                 } else if (relocType == RelocType.FOREIGN_CALL_DIRECT_FAR) {
 401                     elfRelocType = Elf64_Rela.R_X86_64_64;
 402                 } else if (relocType == RelocType.FOREIGN_CALL_INDIRECT ||
 403                            relocType == RelocType.JAVA_CALL_INDIRECT ||
 404                            relocType == RelocType.STUB_CALL_INDIRECT) {
 405                     elfRelocType = Elf64_Rela.R_X86_64_NONE;
 406                 } else if ((relocType == RelocType.EXTERNAL_DATA_REFERENCE_FAR)) {
 407                     elfRelocType = Elf64_Rela.R_X86_64_GOTPCREL;
 408                 } else if (relocType == RelocType.METASPACE_GOT_REFERENCE ||
 409                            relocType == RelocType.EXTERNAL_PLT_TO_GOT ||
 410                            relocType == RelocType.STATIC_STUB_TO_STATIC_METHOD ||
 411                            relocType == RelocType.STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT) {
 412                     elfRelocType = Elf64_Rela.R_X86_64_PC32;
 413                 } else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT ||
 414                            relocType == RelocType.LOADTIME_ADDRESS) {
 415                     elfRelocType = Elf64_Rela.R_X86_64_64;
 416                 } else {
 417                     assert false : "Unhandled relocation type: " + relocType;
 418                 }
 419                 break;
 420             default:
 421                 System.out.println("Relocation Type mapping: Unhandled architecture");
 422         }
 423         return elfRelocType;
 424     }
 425 
 426     private void createElfRelocSections(ArrayList<ElfSection> sections,
 427                                         ElfRelocTable elfRelocTable,
 428                                         int symtabsectidx) {
 429 
 430         // Grab count before we create new sections
 431         int count = sections.size();
 432 
 433         for (int i = 0; i < count; i++) {
 434             if (elfRelocTable.getNumRelocs(i) > 0) {
 435                 ElfSection sect = sections.get(i);
 436                 String relname = ".rela" + sect.getName();
 437                 ElfSection relocSection = createByteSection(sections,
 438                                                             relname,
 439                                                             elfRelocTable.getRelocData(i),
 440                                                             false,
 441                                                             0,
 442                                                             Elf64_Shdr.SHT_RELA);
 443                 relocSection.setLink(symtabsectidx);
 444                 relocSection.setInfo(sect.getSectionId());
 445             }
 446         }
 447     }
 448 }