| /* |
| * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * |
| * File Layout generated by JMachORelocObject |
| * |
| * MachO Header |
| * Load Commands |
| * LC_SEGMENT_64 |
| * - Sections |
| * LC_VERSION_MIN_MAX |
| * LC_SYMTAB |
| * LC_DYSYMTAB |
| * Section Data |
| * Relocation entries |
| * Symbol table |
| * |
| */ |
| |
| package jdk.tools.jaotc.binformat.macho; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| |
| import jdk.tools.jaotc.binformat.BinaryContainer; |
| import jdk.tools.jaotc.binformat.ByteContainer; |
| import jdk.tools.jaotc.binformat.CodeContainer; |
| import jdk.tools.jaotc.binformat.ReadOnlyDataContainer; |
| import jdk.tools.jaotc.binformat.Relocation; |
| import jdk.tools.jaotc.binformat.Relocation.RelocType; |
| import jdk.tools.jaotc.binformat.Symbol; |
| import jdk.tools.jaotc.binformat.Symbol.Kind; |
| |
| import jdk.tools.jaotc.binformat.macho.MachO.section_64; |
| import jdk.tools.jaotc.binformat.macho.MachO.mach_header_64; |
| import jdk.tools.jaotc.binformat.macho.MachO.segment_command_64; |
| import jdk.tools.jaotc.binformat.macho.MachO.version_min_command; |
| import jdk.tools.jaotc.binformat.macho.MachO.symtab_command; |
| import jdk.tools.jaotc.binformat.macho.MachO.dysymtab_command; |
| import jdk.tools.jaotc.binformat.macho.MachO.nlist_64; |
| import jdk.tools.jaotc.binformat.macho.MachO.reloc_info; |
| import jdk.tools.jaotc.binformat.macho.MachOContainer; |
| import jdk.tools.jaotc.binformat.macho.MachOTargetInfo; |
| import jdk.tools.jaotc.binformat.macho.MachOSymtab; |
| import jdk.tools.jaotc.binformat.macho.MachORelocTable; |
| |
| public class JMachORelocObject { |
| |
| private final BinaryContainer binContainer; |
| |
| private final MachOContainer machoContainer; |
| |
| private final int segmentSize; |
| |
| public JMachORelocObject(BinaryContainer binContainer, String outputFileName) { |
| this.binContainer = binContainer; |
| this.machoContainer = new MachOContainer(outputFileName); |
| this.segmentSize = binContainer.getCodeSegmentSize(); |
| } |
| |
| private void createByteSection(ArrayList<MachOSection> sections, |
| ByteContainer c, String sectName, String segName, int scnFlags) { |
| |
| if (c.getByteArray().length == 0) { |
| // System.out.println("Skipping creation of " + sectName + " section, no data\n"); |
| } |
| |
| MachOSection sect = new MachOSection(sectName, |
| segName, |
| c.getByteArray(), |
| scnFlags, |
| c.hasRelocations(), |
| segmentSize); |
| // Add this section to our list |
| sections.add(sect); |
| |
| // Record the section Id (0 relative) |
| c.setSectionId(sections.size() - 1); |
| |
| // TODO: Clear out code section data to allow for GC |
| // c.clear(); |
| } |
| |
| private void createCodeSection(ArrayList<MachOSection> sections, CodeContainer c) { |
| createByteSection(sections, c, /* c.getContainerName() */ "__text", "__TEXT", |
| section_64.S_ATTR_PURE_INSTRUCTIONS | |
| section_64.S_ATTR_SOME_INSTRUCTIONS); |
| } |
| |
| private void createReadOnlySection(ArrayList<MachOSection> sections, ReadOnlyDataContainer c) { |
| createByteSection(sections, c, c.getContainerName(), "__TEXT", |
| section_64.S_ATTR_SOME_INSTRUCTIONS); |
| } |
| |
| private void createReadWriteSection(ArrayList<MachOSection> sections, ByteContainer c) { |
| createByteSection(sections, c, c.getContainerName(), "__DATA", section_64.S_REGULAR); |
| } |
| |
| /** |
| * Create an MachO relocatable object |
| * |
| * @param relocationTable |
| * @param symbols |
| * @throws IOException throws {@code IOException} as a result of file system access failures. |
| */ |
| public void createMachORelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException { |
| // Allocate MachO Header |
| // with 4 load commands |
| // LC_SEGMENT_64 |
| // LC_VERSION_MIN_MACOSX |
| // LC_SYMTAB |
| // LC_DYSYMTAB |
| |
| MachOHeader mh = new MachOHeader(); |
| |
| ArrayList<MachOSection> sections = new ArrayList<>(); |
| |
| // Create Sections contained in the main Segment LC_SEGMENT_64 |
| |
| createCodeSection(sections, binContainer.getCodeContainer()); |
| createReadOnlySection(sections, binContainer.getMetaspaceNamesContainer()); |
| createReadOnlySection(sections, binContainer.getKlassesOffsetsContainer()); |
| createReadOnlySection(sections, binContainer.getMethodsOffsetsContainer()); |
| createReadOnlySection(sections, binContainer.getKlassesDependenciesContainer()); |
| createReadOnlySection(sections, binContainer.getMethodMetadataContainer()); |
| createReadOnlySection(sections, binContainer.getStubsOffsetsContainer()); |
| createReadOnlySection(sections, binContainer.getHeaderContainer().getContainer()); |
| createReadOnlySection(sections, binContainer.getCodeSegmentsContainer()); |
| createReadOnlySection(sections, binContainer.getConstantDataContainer()); |
| createReadOnlySection(sections, binContainer.getConfigContainer()); |
| createReadWriteSection(sections, binContainer.getKlassesGotContainer()); |
| createReadWriteSection(sections, binContainer.getCountersGotContainer()); |
| createReadWriteSection(sections, binContainer.getMetadataGotContainer()); |
| createReadWriteSection(sections, binContainer.getMethodStateContainer()); |
| createReadWriteSection(sections, binContainer.getOopGotContainer()); |
| createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer()); |
| |
| // Update the Header sizeofcmds size. |
| // This doesn't include the Header struct size |
| mh.setCmdSizes(4, segment_command_64.totalsize + |
| (section_64.totalsize * sections.size()) + |
| version_min_command.totalsize + |
| symtab_command.totalsize + |
| dysymtab_command.totalsize); |
| |
| // Initialize file offset for data past commands |
| int file_offset = mach_header_64.totalsize + mh.getCmdSize(); |
| // and round it up |
| file_offset = (file_offset + (sections.get(0).getAlign() - 1)) & ~((sections.get(0).getAlign() - 1)); |
| long address = 0; |
| int segment_offset = file_offset; |
| |
| for (int i = 0; i < sections.size(); i++) { |
| MachOSection sect = sections.get(i); |
| file_offset = (file_offset + (sect.getAlign() - 1)) & ~((sect.getAlign() - 1)); |
| address = (address + (sect.getAlign() - 1)) & ~((sect.getAlign() - 1)); |
| sect.setOffset(file_offset); |
| sect.setAddr(address); |
| file_offset += sect.getSize(); |
| address += sect.getSize(); |
| } |
| |
| // File size for Segment data |
| int segment_size = file_offset - segment_offset; |
| |
| // Create the LC_SEGMENT_64 Segment which contains the MachOSections |
| MachOSegment seg = new MachOSegment(segment_command_64.totalsize + |
| (section_64.totalsize * sections.size()), |
| segment_offset, |
| segment_size, |
| sections.size()); |
| |
| MachOVersion vers = new MachOVersion(); |
| |
| // Get symbol data from BinaryContainer object's symbol tables |
| MachOSymtab symtab = createMachOSymbolTables(sections, symbols); |
| |
| // Create LC_DYSYMTAB command |
| MachODySymtab dysymtab = new MachODySymtab(symtab.getNumLocalSyms(), |
| symtab.getNumGlobalSyms(), |
| symtab.getNumUndefSyms()); |
| |
| // Create the Relocation Tables |
| MachORelocTable machORelocs = createMachORelocTable(sections, relocationTable, symtab); |
| // Calculate file offset for relocation data |
| file_offset = (file_offset + (MachORelocTable.getAlign() - 1)) & ~((MachORelocTable.getAlign() - 1)); |
| |
| // Update relocation sizing information in each section |
| for (int i = 0; i < sections.size(); i++) { |
| MachOSection sect = sections.get(i); |
| if (sect.hasRelocations()) { |
| int nreloc = machORelocs.getNumRelocs(i); |
| sect.setReloff(file_offset); |
| sect.setRelcount(nreloc); |
| file_offset += (nreloc * reloc_info.totalsize); |
| } |
| } |
| |
| // Calculate and set file offset for symbol table data |
| file_offset = (file_offset + (MachOSymtab.getAlign() - 1)) & ~((MachOSymtab.getAlign() - 1)); |
| symtab.setOffset(file_offset); |
| |
| // Write Out Header |
| machoContainer.writeBytes(mh.getArray()); |
| // Write out first Segment |
| machoContainer.writeBytes(seg.getArray()); |
| // Write out sections within first Segment |
| for (int i = 0; i < sections.size(); i++) { |
| MachOSection sect = sections.get(i); |
| machoContainer.writeBytes(sect.getArray()); |
| } |
| |
| // Write out LC_VERSION_MIN_MACOSX command |
| machoContainer.writeBytes(vers.getArray()); |
| |
| // Write out LC_SYMTAB command |
| symtab.calcSizes(); |
| machoContainer.writeBytes(symtab.getCmdArray()); |
| |
| // Write out LC_DYSYMTAB command |
| machoContainer.writeBytes(dysymtab.getArray()); |
| |
| // Write out data associated with each Section |
| for (int i = 0; i < sections.size(); i++) { |
| MachOSection sect = sections.get(i); |
| machoContainer.writeBytes(sect.getDataArray(), sect.getAlign()); |
| } |
| |
| // Write out the relocation tables for all sections |
| for (int i = 0; i < sections.size(); i++) { |
| if (machORelocs.getNumRelocs(i) > 0) { |
| machoContainer.writeBytes(machORelocs.getRelocData(i), MachORelocTable.getAlign()); |
| } |
| } |
| |
| // Write out data associated with LC_SYMTAB |
| machoContainer.writeBytes(symtab.getDataArray(), MachOSymtab.getAlign()); |
| |
| machoContainer.close(); |
| } |
| |
| /** |
| * Construct MachO symbol data from BinaryContainer object's symbol tables. Both dynamic MachO |
| * symbol table and MachO symbol table are created from BinaryContainer's symbol info. |
| * |
| * @param sections |
| * @param symbols |
| */ |
| private static MachOSymtab createMachOSymbolTables(ArrayList<MachOSection> sections, |
| Collection<Symbol> symbols) { |
| MachOSymtab symtab = new MachOSymtab(); |
| // First, create the initial null symbol. This is a local symbol. |
| symtab.addSymbolEntry("", (byte) nlist_64.N_UNDF, (byte) 0, 0); |
| |
| // Now create MachO symbol entries for all symbols. |
| for (Symbol symbol : symbols) { |
| int sectionId = symbol.getSection().getSectionId(); |
| |
| // Symbol offsets are relative to the section memory addr |
| long sectionAddr = sections.get(sectionId).getAddr(); |
| |
| MachOSymbol machoSymbol = symtab.addSymbolEntry(symbol.getName(), |
| getMachOTypeOf(symbol), |
| (byte) sectionId, |
| symbol.getOffset() + sectionAddr); |
| symbol.setNativeSymbol(machoSymbol); |
| } |
| |
| // Now that all symbols are enterred, update the |
| // symbol indexes. This is necessary since they will |
| // be reordered based on local, global and undefined. |
| symtab.updateIndexes(); |
| |
| return (symtab); |
| } |
| |
| private static byte getMachOTypeOf(Symbol sym) { |
| Kind kind = sym.getKind(); |
| byte type = nlist_64.N_UNDF; |
| |
| // Global or Local |
| if (sym.getBinding() == Symbol.Binding.GLOBAL) { |
| type = nlist_64.N_EXT; |
| } |
| // If Function or Data, add section type |
| if (kind == Symbol.Kind.NATIVE_FUNCTION || |
| kind == Symbol.Kind.JAVA_FUNCTION || |
| kind == Symbol.Kind.OBJECT) { |
| type |= (nlist_64.N_SECT); |
| } |
| |
| return (type); |
| } |
| |
| /** |
| * Construct a MachO relocation table from BinaryContainer object's relocation tables. |
| * |
| * @param sections |
| * @param relocationTable |
| * @param symtab |
| */ |
| private MachORelocTable createMachORelocTable(ArrayList<MachOSection> sections, |
| Map<Symbol, List<Relocation>> relocationTable, |
| MachOSymtab symtab) { |
| |
| MachORelocTable machORelocTable = new MachORelocTable(sections.size()); |
| /* |
| * For each of the symbols with associated relocation records, create a MachO relocation entry. |
| */ |
| for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) { |
| List<Relocation> relocs = entry.getValue(); |
| Symbol symbol = entry.getKey(); |
| |
| for (Relocation reloc : relocs) { |
| createRelocation(symbol, reloc, machORelocTable); |
| } |
| } |
| |
| for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) { |
| createRelocation(entry.getKey(), entry.getValue(), machORelocTable); |
| } |
| |
| return (machORelocTable); |
| } |
| |
| private static void createRelocation(Symbol symbol, Relocation reloc, MachORelocTable machORelocTable) { |
| RelocType relocType = reloc.getType(); |
| |
| int machORelocType = getMachORelocationType(relocType); |
| MachOSymbol sym = (MachOSymbol) symbol.getNativeSymbol(); |
| int symno = sym.getIndex(); |
| int sectindex = reloc.getSection().getSectionId(); |
| int offset = reloc.getOffset(); |
| int pcrel = 0; |
| int length = 0; |
| int isextern = 1; |
| |
| switch (relocType) { |
| case JAVA_CALL_DIRECT: |
| case STUB_CALL_DIRECT: |
| case FOREIGN_CALL_INDIRECT_GOT: { |
| // Create relocation entry |
| int addend = -4; // Size in bytes of the patch location |
| // Relocation should be applied at the location after call operand |
| offset = offset + reloc.getSize() + addend; |
| pcrel = 1; |
| length = 2; |
| break; |
| } |
| case JAVA_CALL_INDIRECT: { |
| // Do nothing. |
| return; |
| } |
| case METASPACE_GOT_REFERENCE: |
| case EXTERNAL_PLT_TO_GOT: { |
| int addend = -4; // Size of 32-bit address of the GOT |
| /* |
| * Relocation should be applied before the test instruction to the move instruction. |
| * reloc.getOffset() points to the test instruction after the instruction that loads the address of |
| * polling page. So set the offset appropriately. |
| */ |
| offset = offset + addend; |
| pcrel = 1; |
| length = 2; |
| break; |
| } |
| case EXTERNAL_GOT_TO_PLT: { |
| // this is load time relocations |
| pcrel = 0; |
| length = 3; |
| break; |
| } |
| default: |
| throw new InternalError("Unhandled relocation type: " + relocType); |
| } |
| machORelocTable.createRelocationEntry(sectindex, offset, symno, |
| pcrel, length, isextern, |
| machORelocType); |
| } |
| |
| private static int getMachORelocationType(RelocType relocType) { |
| int machORelocType = 0; |
| switch (MachOTargetInfo.getMachOArch()) { |
| case mach_header_64.CPU_TYPE_X86_64: |
| // Return X86_64_RELOC_* entries based on relocType |
| if (relocType == RelocType.JAVA_CALL_DIRECT || |
| relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) { |
| machORelocType = reloc_info.X86_64_RELOC_BRANCH; |
| } else if (relocType == RelocType.STUB_CALL_DIRECT) { |
| machORelocType = reloc_info.X86_64_RELOC_BRANCH; |
| } else if (relocType == RelocType.JAVA_CALL_INDIRECT) { |
| machORelocType = reloc_info.X86_64_RELOC_NONE; |
| } else if (relocType == RelocType.METASPACE_GOT_REFERENCE || |
| relocType == RelocType.EXTERNAL_PLT_TO_GOT) { |
| machORelocType = reloc_info.X86_64_RELOC_BRANCH; |
| } else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT) { |
| machORelocType = reloc_info.X86_64_RELOC_UNSIGNED; |
| } else { |
| assert false : "Unhandled relocation type: " + relocType; |
| } |
| break; |
| default: |
| System.out.println("Relocation Type mapping: Unhandled architecture"); |
| } |
| return machORelocType; |
| } |
| } |