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