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  * File Layout generated by JMachORelocObject
  27  *
  28  * MachO Header
  29  * Load Commands
  30  *   LC_SEGMENT_64
  31  *    - Sections
  32  *   LC_VERSION_MIN_MAX
  33  *   LC_SYMTAB
  34  *   LC_DYSYMTAB
  35  * Section Data
  36  * Relocation entries
  37  * Symbol table
  38  *
  39  */
  40 
  41 package jdk.tools.jaotc.binformat.macho;
  42 
  43 import java.io.IOException;
  44 import java.util.ArrayList;
  45 import java.util.Collection;
  46 import java.util.List;
  47 import java.util.Map;
  48 
  49 import jdk.tools.jaotc.binformat.BinaryContainer;
  50 import jdk.tools.jaotc.binformat.ByteContainer;
  51 import jdk.tools.jaotc.binformat.CodeContainer;
  52 import jdk.tools.jaotc.binformat.ReadOnlyDataContainer;
  53 import jdk.tools.jaotc.binformat.Relocation;
  54 import jdk.tools.jaotc.binformat.Relocation.RelocType;
  55 import jdk.tools.jaotc.binformat.Symbol;
  56 import jdk.tools.jaotc.binformat.NativeSymbol;
  57 import jdk.tools.jaotc.binformat.Symbol.Binding;
  58 import jdk.tools.jaotc.binformat.Symbol.Kind;
  59 
  60 import jdk.tools.jaotc.binformat.macho.MachO;
  61 import jdk.tools.jaotc.binformat.macho.MachO.section_64;
  62 import jdk.tools.jaotc.binformat.macho.MachO.mach_header_64;
  63 import jdk.tools.jaotc.binformat.macho.MachO.segment_command_64;
  64 import jdk.tools.jaotc.binformat.macho.MachO.version_min_command;
  65 import jdk.tools.jaotc.binformat.macho.MachO.symtab_command;
  66 import jdk.tools.jaotc.binformat.macho.MachO.dysymtab_command;
  67 import jdk.tools.jaotc.binformat.macho.MachO.nlist_64;
  68 import jdk.tools.jaotc.binformat.macho.MachO.reloc_info;
  69 import jdk.tools.jaotc.binformat.macho.MachOContainer;
  70 import jdk.tools.jaotc.binformat.macho.MachOTargetInfo;
  71 import jdk.tools.jaotc.binformat.macho.MachOSymtab;
  72 import jdk.tools.jaotc.binformat.macho.MachORelocTable;
  73 
  74 public class JMachORelocObject {
  75 
  76     private final BinaryContainer binContainer;
  77 
  78     private final MachOContainer machoContainer;
  79 
  80     private final int segmentSize;
  81 
  82     public JMachORelocObject(BinaryContainer binContainer, String outputFileName) {
  83         this.binContainer = binContainer;
  84         this.machoContainer = new MachOContainer(outputFileName);
  85         this.segmentSize = binContainer.getCodeSegmentSize();
  86     }
  87 
  88     private void createByteSection(ArrayList<MachOSection>sections,
  89                                    ByteContainer c, String sectName, String segName, int scnFlags) {
  90 
  91         if (c.getByteArray().length == 0) {
  92             // System.out.println("Skipping creation of " + sectName + " section, no data\n");
  93         }
  94 
  95         MachOSection sect = new MachOSection(sectName,
  96                                              segName,
  97                                              c.getByteArray(),
  98                                              scnFlags,
  99                                              c.hasRelocations(),
 100                                              segmentSize);
 101         // Add this section to our list
 102         sections.add(sect);
 103 
 104         // Record the section Id (0 relative)
 105         c.setSectionId(sections.size()-1);
 106 
 107         // TODO: Clear out code section data to allow for GC
 108         // c.clear();
 109     }
 110 
 111     private void createCodeSection(ArrayList<MachOSection>sections, CodeContainer c) {
 112         createByteSection(sections, c, /*c.getContainerName()*/ "__text", "__TEXT",
 113                           section_64.S_ATTR_PURE_INSTRUCTIONS|
 114                           section_64.S_ATTR_SOME_INSTRUCTIONS);
 115     }
 116 
 117     private void createReadOnlySection(ArrayList<MachOSection>sections, ReadOnlyDataContainer c) {
 118         createByteSection(sections, c, c.getContainerName(), "__TEXT",
 119                           section_64.S_ATTR_SOME_INSTRUCTIONS);
 120     }
 121 
 122     private void createReadWriteSection(ArrayList<MachOSection>sections, ByteContainer c) {
 123         createByteSection(sections, c, c.getContainerName(), "__DATA", section_64.S_REGULAR);
 124     }
 125 
 126     /**
 127      * Create an MachO relocatable object
 128      *
 129      * @param relocationTable
 130      * @param symbols
 131      * @throws IOException throws {@code IOException} as a result of file system access failures.
 132      */
 133     public void createMachORelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException {
 134         // Allocate MachO Header
 135         // with 4 load commands
 136         //   LC_SEGMENT_64
 137         //   LC_VERSION_MIN_MACOSX
 138         //   LC_SYMTAB
 139         //   LC_DYSYMTAB
 140 
 141         MachOHeader mh = new MachOHeader();
 142 
 143         ArrayList<MachOSection> sections = new ArrayList<MachOSection>();
 144 
 145         // Create Sections contained in the main Segment LC_SEGMENT_64
 146 
 147         createCodeSection(sections, binContainer.getCodeContainer());
 148         createReadOnlySection(sections, binContainer.getMetaspaceNamesContainer());
 149         createReadOnlySection(sections, binContainer.getKlassesOffsetsContainer());
 150         createReadOnlySection(sections, binContainer.getMethodsOffsetsContainer());
 151         createReadOnlySection(sections, binContainer.getKlassesDependenciesContainer());
 152         createReadWriteSection(sections, binContainer.getMetaspaceGotContainer());
 153         createReadWriteSection(sections, binContainer.getMetadataGotContainer());
 154         createReadWriteSection(sections, binContainer.getMethodStateContainer());
 155         createReadWriteSection(sections, binContainer.getOopGotContainer());
 156         createReadWriteSection(sections, binContainer.getMethodMetadataContainer());
 157         createReadOnlySection(sections, binContainer.getStubsOffsetsContainer());
 158         createReadOnlySection(sections, binContainer.getHeaderContainer().getContainer());
 159         createReadOnlySection(sections, binContainer.getCodeSegmentsContainer());
 160         createReadOnlySection(sections, binContainer.getConstantDataContainer());
 161         createReadOnlySection(sections, binContainer.getConfigContainer());
 162 
 163         // createExternalLinkage();
 164 
 165         createCodeSection(sections, binContainer.getExtLinkageContainer());
 166         createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer());
 167         // Update the Header sizeofcmds size.
 168         // This doesn't include the Header struct size
 169         mh.setCmdSizes(4, segment_command_64.totalsize +
 170                           (section_64.totalsize * sections.size()) +
 171                           version_min_command.totalsize +
 172                           symtab_command.totalsize +
 173                           dysymtab_command.totalsize);
 174 
 175         // Initialize file offset for data past commands
 176         int file_offset = mach_header_64.totalsize + mh.getCmdSize();
 177         // and round it up
 178         file_offset = (file_offset + (sections.get(0).getAlign()-1)) & ~((sections.get(0).getAlign()-1));
 179         long address = 0;
 180         int segment_offset = file_offset;
 181 
 182         for (int i = 0; i < sections.size(); i++) {
 183             MachOSection sect = sections.get(i);
 184             file_offset = (file_offset + (sect.getAlign()-1)) & ~((sect.getAlign()-1));
 185             address = (address + (sect.getAlign()-1)) & ~((sect.getAlign()-1));
 186             sect.setOffset(file_offset);
 187             sect.setAddr(address);
 188             file_offset += sect.getSize();
 189             address += sect.getSize();
 190         }
 191 
 192         // File size for Segment data
 193         int segment_size = file_offset - segment_offset;
 194 
 195         // Create the LC_SEGMENT_64 Segment which contains the MachOSections
 196         MachOSegment seg = new MachOSegment(segment_command_64.totalsize +
 197                                             (section_64.totalsize * sections.size()),
 198                                             segment_offset,
 199                                             segment_size,
 200                                             sections.size());
 201 
 202 
 203         MachOVersion vers = new MachOVersion();
 204 
 205         // Get symbol data from BinaryContainer object's symbol tables
 206         MachOSymtab symtab = createMachOSymbolTables(sections, symbols);
 207 
 208         // Create LC_DYSYMTAB command
 209         MachODySymtab dysymtab = new MachODySymtab(symtab.getNumLocalSyms(),
 210                                                    symtab.getNumGlobalSyms(),
 211                                                    symtab.getNumUndefSyms());
 212 
 213         // Create the Relocation Tables
 214         MachORelocTable machORelocs = createMachORelocTable(sections, relocationTable, symtab);
 215         // Calculate file offset for relocation data
 216         file_offset = (file_offset + (machORelocs.getAlign()-1)) & ~((machORelocs.getAlign()-1));
 217 
 218         // Update relocation sizing information in each section
 219         for (int i = 0; i < sections.size(); i++) {
 220             MachOSection sect = sections.get(i);
 221             if (sect.hasRelocations()) {
 222                 int nreloc = machORelocs.getNumRelocs(i);
 223                 sect.setReloff(file_offset);
 224                 sect.setRelcount(nreloc);
 225                 file_offset += (nreloc * reloc_info.totalsize);
 226             }
 227         }
 228 
 229         // Calculate and set file offset for symbol table data
 230         file_offset = (file_offset + (symtab.getAlign()-1)) & ~((symtab.getAlign()-1));
 231         symtab.setOffset(file_offset);
 232 
 233 
 234         // Write Out Header
 235         machoContainer.writeBytes(mh.getArray());
 236         // Write out first Segment
 237         machoContainer.writeBytes(seg.getArray());
 238         // Write out sections within first Segment
 239         for (int i = 0; i < sections.size(); i++) {
 240             MachOSection sect = sections.get(i);
 241             machoContainer.writeBytes(sect.getArray());
 242         }
 243 
 244         // Write out LC_VERSION_MIN_MACOSX command
 245         machoContainer.writeBytes(vers.getArray());
 246 
 247         // Write out LC_SYMTAB command
 248         symtab.calcSizes();
 249         machoContainer.writeBytes(symtab.getCmdArray());
 250 
 251         // Write out LC_DYSYMTAB command
 252         machoContainer.writeBytes(dysymtab.getArray());
 253 
 254         // Write out data associated with each Section
 255         for (int i = 0; i < sections.size(); i++) {
 256             MachOSection sect = sections.get(i);
 257             machoContainer.writeBytes(sect.getDataArray(), sect.getAlign());
 258         }
 259 
 260         // Write out the relocation tables for all sections
 261         for (int i = 0; i < sections.size(); i++) {
 262             if (machORelocs.getNumRelocs(i) > 0)
 263                 machoContainer.writeBytes(machORelocs.getRelocData(i), machORelocs.getAlign());
 264         }
 265 
 266         // Write out data associated with LC_SYMTAB
 267         machoContainer.writeBytes(symtab.getDataArray(), symtab.getAlign());
 268 
 269         machoContainer.close();
 270     }
 271 
 272     /**
 273      * Construct MachO symbol data from BinaryContainer object's symbol tables. Both dynamic MachO
 274      * symbol table and MachO symbol table are created from BinaryContainer's symbol info.
 275      *
 276      * @param symbols
 277      * @param symtab
 278      */
 279     private MachOSymtab createMachOSymbolTables(ArrayList<MachOSection>sections,
 280                                          Collection<Symbol> symbols) {
 281         MachOSymtab symtab = new MachOSymtab();
 282         // First, create the initial null symbol. This is a local symbol.
 283         symtab.addSymbolEntry("", (byte)nlist_64.N_UNDF, (byte)0, (long)0);
 284 
 285         // Now create MachO symbol entries for all symbols.
 286         for (Symbol symbol : symbols) {
 287             int sectionId = symbol.getSection().getSectionId();
 288 
 289             // Symbol offsets are relative to the section memory addr
 290             long sectionAddr = sections.get(sectionId).getAddr();
 291 
 292             MachOSymbol machoSymbol = symtab.addSymbolEntry(symbol.getName(),
 293                                          getMachOTypeOf(symbol),
 294                                          (byte)sectionId,
 295                                          symbol.getOffset() + sectionAddr);
 296             symbol.setNativeSymbol((NativeSymbol)machoSymbol);
 297         }
 298 
 299         // Now that all symbols are enterred, update the
 300         // symbol indexes.  This is necessary since they will
 301         // be reordered based on local, global and undefined.
 302         symtab.updateIndexes();
 303 
 304         return (symtab);
 305     }
 306 
 307     private static byte getMachOTypeOf(Symbol sym) {
 308         Kind kind = sym.getKind();
 309         byte type = nlist_64.N_UNDF;
 310 
 311         // Global or Local
 312         if (sym.getBinding() == Symbol.Binding.GLOBAL)
 313             type = nlist_64.N_EXT;
 314 
 315         // If Function or Data, add section type
 316         if (kind == Symbol.Kind.NATIVE_FUNCTION ||
 317             kind == Symbol.Kind.JAVA_FUNCTION   ||
 318             kind == Symbol.Kind.OBJECT) {
 319             type |= (nlist_64.N_SECT);
 320         }
 321 
 322         return (type);
 323     }
 324 
 325     /**
 326      * Construct a MachO relocation table from BinaryContainer object's relocation tables.
 327      *
 328      * @param sections
 329      * @param relocationTable
 330      * @param symtab
 331      */
 332     private MachORelocTable createMachORelocTable(ArrayList<MachOSection> sections,
 333                                                   Map<Symbol, List<Relocation>> relocationTable,
 334                                                   MachOSymtab symtab) {
 335 
 336         MachORelocTable machORelocTable = new MachORelocTable(sections.size());
 337         /*
 338          * For each of the symbols with associated relocation records, create a MachO relocation
 339          * entry.
 340          */
 341         for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) {
 342             List<Relocation> relocs = entry.getValue();
 343             Symbol symbol = entry.getKey();
 344 
 345             for (Relocation reloc : relocs) {
 346                 createRelocation(symbol, reloc, machORelocTable);
 347             }
 348         }
 349 
 350         for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) {
 351             createRelocation(entry.getKey(), entry.getValue(), machORelocTable);
 352         }
 353 
 354         return (machORelocTable);
 355     }
 356 
 357     private void createRelocation(Symbol symbol, Relocation reloc, MachORelocTable machORelocTable) {
 358         RelocType relocType = reloc.getType();
 359 
 360         int machORelocType = getMachORelocationType(relocType);
 361         MachOSymbol sym = (MachOSymbol)symbol.getNativeSymbol();
 362         int symno = sym.getIndex();
 363         int sectindex = reloc.getSection().getSectionId();
 364         int offset = reloc.getOffset();
 365         int pcrel = 0;
 366         int length = 0;
 367         int isextern = 1;
 368 
 369 /*
 370         System.out.println("reloctype: " + relocType + " size is " +
 371                             reloc.getSize() + " offset is " + offset +
 372                             " Section Index is " + (sectindex) +
 373                             " Symbol Index is " + symno +
 374                             " Symbol Name is " + symbol.getName() + "\n");
 375 */
 376 
 377         switch (relocType) {
 378             case FOREIGN_CALL_DIRECT:
 379             case JAVA_CALL_DIRECT:
 380             case STUB_CALL_DIRECT:
 381             case FOREIGN_CALL_INDIRECT_GOT: {
 382                 // Create relocation entry
 383                 // System.out.println("getMachORelocationType: PLT relocation type using X86_64_RELOC_BRANCH");
 384                 int addend = -4; // Size in bytes of the patch location
 385                 // Relocation should be applied at the location after call operand
 386                 offset = offset + reloc.getSize() + addend;
 387                 pcrel = 1; length = 2;
 388                 break;
 389             }
 390             case FOREIGN_CALL_DIRECT_FAR: {
 391                 // Create relocation entry
 392                 int addend = -8; // Size in bytes of the patch location
 393                 // Relocation should be applied at the location after call operand
 394                 // 10 = 2 (jmp [r]) + 8 (imm64)
 395                 offset = offset + reloc.getSize() + addend - 2;
 396                 pcrel = 0; length = 3;
 397                 break;
 398             }
 399             case FOREIGN_CALL_INDIRECT:
 400             case JAVA_CALL_INDIRECT:
 401             case STUB_CALL_INDIRECT: {
 402                 // Do nothing.
 403                 return;
 404             }
 405             case EXTERNAL_DATA_REFERENCE_FAR: {
 406                 // Create relocation entry
 407                 int addend = -4; // Size of 32-bit address of the GOT
 408                 /*
 409                  * Relocation should be applied before the test instruction to the move instruction.
 410                  * offset points to the test instruction after the instruction that loads
 411                  * the address of polling page. So set the offset appropriately.
 412                  */
 413                 offset = offset + addend;
 414                 pcrel = 0; length = 2;
 415                 break;
 416             }
 417             case METASPACE_GOT_REFERENCE:
 418             case EXTERNAL_PLT_TO_GOT:
 419             case STATIC_STUB_TO_STATIC_METHOD:
 420             case STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT: {
 421                 int addend = -4; // Size of 32-bit address of the GOT
 422                 /*
 423                  * Relocation should be applied before the test instruction to
 424                  * the move instruction. reloc.getOffset() points to the
 425                  * test instruction after the instruction that loads the
 426                  * address of polling page. So set the offset appropriately.
 427                  */
 428                 offset = offset + addend;
 429                 pcrel = 1; length = 2;
 430                 break;
 431             }
 432             case EXTERNAL_GOT_TO_PLT:
 433             case LOADTIME_ADDRESS: {
 434                 // this is load time relocations
 435                 pcrel = 0; length = 3;
 436                 break;
 437             }
 438             default:
 439                 throw new InternalError("Unhandled relocation type: " + relocType);
 440         }
 441         machORelocTable.createRelocationEntry(sectindex, offset, symno,
 442                                               pcrel, length, isextern,
 443                                               machORelocType);
 444     }
 445 
 446     private static int getMachORelocationType(RelocType relocType) {
 447         int machORelocType = 0;
 448         switch (MachOTargetInfo.getMachOArch()) {
 449             case mach_header_64.CPU_TYPE_X86_64:
 450                 // Return X86_64_RELOC_* entries based on relocType
 451                 if (relocType == RelocType.FOREIGN_CALL_DIRECT || relocType == RelocType.JAVA_CALL_DIRECT || relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) {
 452                     machORelocType = reloc_info.X86_64_RELOC_BRANCH;
 453                 } else if (relocType == RelocType.STUB_CALL_DIRECT) {
 454                     machORelocType = reloc_info.X86_64_RELOC_BRANCH;
 455                 } else if (relocType == RelocType.FOREIGN_CALL_DIRECT_FAR) {
 456                     machORelocType = reloc_info.X86_64_RELOC_UNSIGNED;
 457                 } else if (relocType == RelocType.FOREIGN_CALL_INDIRECT || relocType == RelocType.JAVA_CALL_INDIRECT || relocType == RelocType.STUB_CALL_INDIRECT) {
 458                     machORelocType = reloc_info.X86_64_RELOC_NONE;
 459                 } else if ((relocType == RelocType.EXTERNAL_DATA_REFERENCE_FAR)) {
 460                     machORelocType = reloc_info.X86_64_RELOC_GOT;
 461                 } else if (relocType == RelocType.METASPACE_GOT_REFERENCE || relocType == RelocType.EXTERNAL_PLT_TO_GOT || relocType == RelocType.STATIC_STUB_TO_STATIC_METHOD ||
 462                                 relocType == RelocType.STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT) {
 463                     machORelocType = reloc_info.X86_64_RELOC_BRANCH;
 464                 } else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT || relocType == RelocType.LOADTIME_ADDRESS) {
 465                     machORelocType = reloc_info.X86_64_RELOC_UNSIGNED;
 466                 } else {
 467                     assert false : "Unhandled relocation type: " + relocType;
 468                 }
 469                 break;
 470             default:
 471                 System.out.println("Relocation Type mapping: Unhandled architecture");
 472         }
 473         return machORelocType;
 474     }
 475 }