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