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