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