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