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 }