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 package jdk.tools.jaotc.binformat.pecoff; 25 26 import java.io.IOException; 27 import java.nio.charset.StandardCharsets; 28 import java.util.ArrayList; 29 import java.util.Collection; 30 import java.util.List; 31 import java.util.Map; 32 33 import jdk.tools.jaotc.binformat.Container; 34 import jdk.tools.jaotc.binformat.BinaryContainer; 35 import jdk.tools.jaotc.binformat.ByteContainer; 36 import jdk.tools.jaotc.binformat.CodeContainer; 37 import jdk.tools.jaotc.binformat.ReadOnlyDataContainer; 38 import jdk.tools.jaotc.binformat.Relocation; 39 import jdk.tools.jaotc.binformat.Relocation.RelocType; 40 import jdk.tools.jaotc.binformat.Symbol; 41 import jdk.tools.jaotc.binformat.NativeSymbol; 42 import jdk.tools.jaotc.binformat.Symbol.Binding; 43 import jdk.tools.jaotc.binformat.Symbol.Kind; 44 45 import jdk.tools.jaotc.binformat.pecoff.PECoff; 46 import jdk.tools.jaotc.binformat.pecoff.PECoffSymbol; 47 import jdk.tools.jaotc.binformat.pecoff.PECoffTargetInfo; 48 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_FILE_HEADER; 49 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SECTION_HEADER; 50 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SYMBOL; 51 import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_RELOCATION; 52 53 public class JPECoffRelocObject { 54 55 private final BinaryContainer binContainer; 56 57 private final PECoffContainer pecoffContainer; 58 59 private final int segmentSize; 60 61 public JPECoffRelocObject(BinaryContainer binContainer, String outputFileName, String aotVersion) { 62 this.binContainer = binContainer; 63 this.pecoffContainer = new PECoffContainer(outputFileName, aotVersion); 64 this.segmentSize = binContainer.getCodeSegmentSize(); 65 if (segmentSize != 64) { 66 System.out.println("binContainer alignment size not 64 bytes, update JPECoffRelocObject"); 67 } 68 } 69 70 private PECoffSection createByteSection(ArrayList<PECoffSection>sections, 71 String sectName, 72 byte [] scnData, 73 boolean hasRelocs, 74 int scnFlags) { 75 76 PECoffSection sect = new PECoffSection(sectName, 77 scnData, 78 scnFlags, 79 hasRelocs, 80 sections.size()); 81 // Add this section to our list 82 sections.add(sect); 83 84 return (sect); 85 } 86 87 private void createByteSection(ArrayList<PECoffSection>sections, 88 ByteContainer c, int scnFlags) { 89 PECoffSection sect; 90 boolean hasRelocs = c.hasRelocations(); 91 byte[] scnData = c.getByteArray(); 92 93 sect = createByteSection(sections, c.getContainerName(), 94 scnData, hasRelocs, 95 scnFlags); 96 97 c.setSectionId(sect.getSectionId()); 98 } 99 100 private void createCodeSection(ArrayList<PECoffSection>sections, CodeContainer c) { 101 createByteSection(sections, c, IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | 102 IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_EXECUTE | 103 IMAGE_SECTION_HEADER.IMAGE_SCN_ALIGN_64BYTES | 104 IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_CODE); 105 } 106 107 private void createReadOnlySection(ArrayList<PECoffSection>sections, ReadOnlyDataContainer c) { 108 createByteSection(sections, c, IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | 109 IMAGE_SECTION_HEADER.IMAGE_SCN_ALIGN_64BYTES | 110 IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA); 111 } 112 113 private void createReadWriteSection(ArrayList<PECoffSection>sections, ByteContainer c) { 114 int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | 115 IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_WRITE | 116 IMAGE_SECTION_HEADER.IMAGE_SCN_ALIGN_64BYTES; 117 118 if (c.getByteArray().length > 0) 119 scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA; 120 else 121 scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_UNINITIALIZED_DATA; 122 123 createByteSection(sections, c, scnFlags); 124 } 125 126 /** 127 * Create an PECoff 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 createPECoffRelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException { 134 ArrayList<PECoffSection> sections = new ArrayList<PECoffSection>(); 135 136 // Create text section 137 createCodeSection(sections, binContainer.getCodeContainer()); 138 createReadOnlySection(sections, binContainer.getMetaspaceNamesContainer()); 139 createReadOnlySection(sections, binContainer.getKlassesOffsetsContainer()); 140 createReadOnlySection(sections, binContainer.getMethodsOffsetsContainer()); 141 createReadOnlySection(sections, binContainer.getKlassesDependenciesContainer()); 142 createReadWriteSection(sections, binContainer.getMetaspaceGotContainer()); 143 createReadWriteSection(sections, binContainer.getMetadataGotContainer()); 144 createReadWriteSection(sections, binContainer.getMethodStateContainer()); 145 createReadWriteSection(sections, binContainer.getOopGotContainer()); 146 createReadWriteSection(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 153 // createExternalLinkage(); 154 155 createCodeSection(sections, binContainer.getExtLinkageContainer()); 156 createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer()); 157 158 // Allocate PECoff Header 159 PECoffHeader header = new PECoffHeader(); 160 161 // Get PECoff symbol data from BinaryContainer object's symbol tables 162 PECoffSymtab symtab = createPECoffSymbolTables(sections, symbols); 163 164 // Add Linker Directives Section 165 createByteSection(sections, ".drectve", 166 symtab.getDirectiveArray(), false, 167 IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_INFO | 168 IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_REMOVE | 169 IMAGE_SECTION_HEADER.IMAGE_SCN_ALIGN_1BYTES); 170 171 // Create the Relocation Tables 172 PECoffRelocTable pecoffRelocs = createPECoffRelocTable(sections, relocationTable); 173 174 // File Output Order 175 // 176 // HEADER (Need address of Symbol Table + symbol count) 177 // SECTIONS (Need pointer to Section Data, Relocation Table) 178 // DIRECTIVES 179 // SYMBOL TABLE 180 // SYMBOLS 181 // SECTION DATA 182 // RELOCATION TABLE 183 184 // Calculate Offset for Symbol table 185 int file_offset = IMAGE_FILE_HEADER.totalsize + 186 (IMAGE_SECTION_HEADER.totalsize*sections.size()); 187 188 // Update Header fields 189 header.setSectionCount(sections.size()); 190 header.setSymbolCount(symtab.getSymtabCount()); 191 header.setSymbolOff(file_offset); 192 193 // Calculate file offset for first section 194 file_offset += ((symtab.getSymtabCount() * IMAGE_SYMBOL.totalsize) + 195 symtab.getStrtabSize()); 196 // And round it up 197 file_offset = (file_offset + (sections.get(0).getDataAlign()-1)) & 198 ~((sections.get(0).getDataAlign()-1)); 199 200 // Calc file offsets for section data 201 for (int i = 0; i < sections.size(); i++) { 202 PECoffSection sect = sections.get(i); 203 file_offset = (file_offset + (sect.getDataAlign()-1)) & 204 ~((sect.getDataAlign()-1)); 205 sect.setOffset(file_offset); 206 file_offset += sect.getSize(); 207 } 208 209 // Update relocation sizing information in each section 210 for (int i = 0; i < sections.size(); i++) { 211 PECoffSection sect = sections.get(i); 212 if (sect.hasRelocations()) { 213 int nreloc = pecoffRelocs.getNumRelocs(i); 214 sect.setReloff(file_offset); 215 sect.setRelcount(nreloc); 216 // extended relocations add an addition entry 217 if (nreloc > 0xFFFF) nreloc++; 218 file_offset += (nreloc * IMAGE_RELOCATION.totalsize); 219 } 220 } 221 222 // Write out the Header 223 pecoffContainer.writeBytes(header.getArray()); 224 225 // Write out the section table 226 for (int i = 0; i < sections.size(); i++) { 227 PECoffSection sect = sections.get(i); 228 pecoffContainer.writeBytes(sect.getArray(), PECoffSection.getShdrAlign()); 229 } 230 231 // Write out the symbol table and string table 232 pecoffContainer.writeBytes(symtab.getSymtabArray(), 4); 233 pecoffContainer.writeBytes(symtab.getStrtabArray(), 1); 234 235 // Write out each section contents 236 for (int i = 0; i < sections.size(); i++) { 237 PECoffSection sect = sections.get(i); 238 pecoffContainer.writeBytes(sect.getDataArray(), sect.getDataAlign()); 239 } 240 241 // Write out Relocation Tables 242 for (int i = 0; i < sections.size(); i++) { 243 if (pecoffRelocs.getNumRelocs(i) > 0) { 244 pecoffContainer.writeBytes(pecoffRelocs.getRelocData(i)); 245 } 246 } 247 pecoffContainer.close(); 248 } 249 250 /** 251 * Construct PECoff symbol data from BinaryContainer object's symbol tables. Both dynamic PECoff 252 * symbol table and PECoff symbol table are created from BinaryContainer's symbol info. 253 * 254 * @param symbols 255 */ 256 private PECoffSymtab createPECoffSymbolTables(ArrayList<PECoffSection> sections, Collection<Symbol> symbols) { 257 PECoffSymtab symtab = new PECoffSymtab(); 258 259 // First, create the initial null symbol. This is a local symbol. 260 // symtab.addSymbolEntry("", (byte)0, (byte)0, (byte)0, 0, 0); 261 262 // Now create PECoff symbol entries for all symbols. 263 for (Symbol symbol : symbols) { 264 // Get the index of section this symbol is defined in. 265 int secHdrIndex = symbol.getSection().getSectionId(); 266 PECoffSymbol pecoffSymbol = symtab.addSymbolEntry(symbol.getName(), getPECoffTypeOf(symbol), getPECoffClassOf(symbol), (byte)secHdrIndex, symbol.getOffset(), symbol.getSize()); 267 symbol.setNativeSymbol((NativeSymbol)pecoffSymbol); 268 } 269 return (symtab); 270 } 271 272 private static byte getPECoffTypeOf(Symbol sym) { 273 Kind kind = sym.getKind(); 274 if (kind == Symbol.Kind.NATIVE_FUNCTION || kind == Symbol.Kind.JAVA_FUNCTION) { 275 return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_FUNCTION; 276 } 277 return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_NONE; 278 } 279 280 private static byte getPECoffClassOf(Symbol sym) { 281 Binding binding = sym.getBinding(); 282 if (binding == Symbol.Binding.GLOBAL) { 283 return IMAGE_SYMBOL.IMAGE_SYM_CLASS_EXTERNAL; 284 } 285 return IMAGE_SYMBOL.IMAGE_SYM_CLASS_STATIC; 286 } 287 288 /** 289 * Construct a PECoff relocation table from BinaryContainer object's relocation tables. 290 * 291 * @param sections 292 * @param relocationTable 293 */ 294 private PECoffRelocTable createPECoffRelocTable(ArrayList<PECoffSection> sections, 295 Map<Symbol, List<Relocation>> relocationTable) { 296 297 PECoffRelocTable pecoffRelocTable = new PECoffRelocTable(sections.size()); 298 /* 299 * For each of the symbols with associated relocation records, create a PECoff relocation 300 * entry. 301 */ 302 for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) { 303 List<Relocation> relocs = entry.getValue(); 304 Symbol symbol = entry.getKey(); 305 306 for (Relocation reloc : relocs) { 307 createRelocation(symbol, reloc, pecoffRelocTable); 308 } 309 } 310 311 for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) { 312 createRelocation(entry.getKey(), entry.getValue(), pecoffRelocTable); 313 } 314 315 return (pecoffRelocTable); 316 } 317 318 private void createRelocation(Symbol symbol, Relocation reloc, PECoffRelocTable pecoffRelocTable) { 319 RelocType relocType = reloc.getType(); 320 321 int pecoffRelocType = getPECoffRelocationType(relocType); 322 PECoffSymbol sym = (PECoffSymbol)symbol.getNativeSymbol(); 323 int symno = sym.getIndex(); 324 int sectindex = reloc.getSection().getSectionId(); 325 int offset = reloc.getOffset(); 326 int addend = 0; 327 328 switch (relocType) { 329 case FOREIGN_CALL_DIRECT: 330 case JAVA_CALL_DIRECT: 331 case STUB_CALL_DIRECT: 332 case FOREIGN_CALL_INDIRECT_GOT: { 333 // Create relocation entry 334 addend = -4; // Size in bytes of the patch location 335 // Relocation should be applied at the location after call operand 336 offset = offset + reloc.getSize() + addend; 337 break; 338 } 339 case FOREIGN_CALL_DIRECT_FAR: { 340 // Create relocation entry 341 addend = -8; // Size in bytes of the patch location 342 // Relocation should be applied at the location after call operand 343 // 10 = 2 (jmp [r]) + 8 (imm64) 344 offset = offset + reloc.getSize() + addend - 2; 345 break; 346 } 347 case FOREIGN_CALL_INDIRECT: 348 case JAVA_CALL_INDIRECT: 349 case STUB_CALL_INDIRECT: { 350 // Do nothing. 351 return; 352 } 353 case EXTERNAL_DATA_REFERENCE_FAR: { 354 // Create relocation entry 355 addend = -4; // Size of 32-bit address of the GOT 356 /* 357 * Relocation should be applied before the test instruction to the move instruction. 358 * offset points to the test instruction after the instruction that loads 359 * the address of polling page. So set the offset appropriately. 360 */ 361 offset = offset + addend; 362 break; 363 } 364 case METASPACE_GOT_REFERENCE: 365 case EXTERNAL_PLT_TO_GOT: 366 case STATIC_STUB_TO_STATIC_METHOD: 367 case STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT: { 368 addend = -4; // Size of 32-bit address of the GOT 369 /* 370 * Relocation should be applied before the test instruction to 371 * the move instruction. reloc.getOffset() points to the 372 * test instruction after the instruction that loads the 373 * address of polling page. So set the offset appropriately. 374 */ 375 offset = offset + addend; 376 break; 377 } 378 case EXTERNAL_GOT_TO_PLT: 379 case LOADTIME_ADDRESS: { 380 // this is load time relocations 381 break; 382 } 383 default: 384 throw new InternalError("Unhandled relocation type: " + relocType); 385 } 386 pecoffRelocTable.createRelocationEntry(sectindex, offset, symno, pecoffRelocType); 387 } 388 389 // Return IMAGE_RELOCATION Type based on relocType 390 private static int getPECoffRelocationType(RelocType relocType) { 391 int pecoffRelocType = 0; // R_<ARCH>_NONE if #define'd to 0 for all values of ARCH 392 switch (PECoffTargetInfo.getPECoffArch()) { 393 case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64: 394 if (relocType == RelocType.FOREIGN_CALL_DIRECT || 395 relocType == RelocType.JAVA_CALL_DIRECT || 396 relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) { 397 pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32; 398 } else if (relocType == RelocType.STUB_CALL_DIRECT) { 399 pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32; 400 } else if (relocType == RelocType.FOREIGN_CALL_DIRECT_FAR) { 401 pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ADDR64; 402 } else if (relocType == RelocType.FOREIGN_CALL_INDIRECT || 403 relocType == RelocType.JAVA_CALL_INDIRECT || 404 relocType == RelocType.STUB_CALL_INDIRECT) { 405 pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ABSOLUTE; 406 } else if ((relocType == RelocType.EXTERNAL_DATA_REFERENCE_FAR)) { 407 pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32; 408 } else if (relocType == RelocType.METASPACE_GOT_REFERENCE || 409 relocType == RelocType.EXTERNAL_PLT_TO_GOT || 410 relocType == RelocType.STATIC_STUB_TO_STATIC_METHOD || 411 relocType == RelocType.STATIC_STUB_TO_HOTSPOT_LINKAGE_GOT) { 412 pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32; 413 } else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT || 414 relocType == RelocType.LOADTIME_ADDRESS) { 415 pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ADDR64; 416 } else { 417 assert false : "Unhandled relocation type: " + relocType; 418 } 419 break; 420 default: 421 System.out.println("Relocation Type mapping: Unhandled architecture"); 422 } 423 return pecoffRelocType; 424 } 425 }