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