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 76 public JMachORelocObject(BinaryContainer binContainer, String outputFileName) { 77 this.binContainer = binContainer; 78 this.machoContainer = new MachOContainer(outputFileName); 79 this.segmentSize = binContainer.getCodeSegmentSize(); 80 } 81 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 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 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 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 */ 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 */ 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 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 */ 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 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 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 }