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