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