1 /* 2 * Copyright (c) 2016, 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; 25 26 import static org.graalvm.compiler.hotspot.meta.HotSpotAOTProfilingPlugin.Options.TieredAOT; 27 28 import java.io.ByteArrayOutputStream; 29 import java.io.DataOutputStream; 30 import java.io.IOException; 31 import java.util.ArrayList; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 36 import jdk.tools.jaotc.binformat.Symbol.Binding; 37 import jdk.tools.jaotc.binformat.Symbol.Kind; 38 import jdk.tools.jaotc.binformat.elf.JELFRelocObject; 39 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 40 41 /** 42 * A format-agnostic container class that holds various components of a binary. 43 * 44 * <p> 45 * This class holds information necessary to create platform-specific binary containers such as 46 * ELFContainer for Linux and Solaris operating systems or yet-to be created MachOContainer for Mac 47 * OS or PEContainer for MS Windows operating systems. 48 * 49 * <p> 50 * Method APIs provided by this class are used to construct and populate platform-independent 51 * contents of a binary as the first step to create a binary representation of code generated by a 52 * compiler backend such as Graal. 53 * 54 * <p> 55 * Methods to record and access code section contents, symbols and relocations are provided. 56 */ 57 public class BinaryContainer implements SymbolTable { 58 59 private final int codeSegmentSize; 60 61 private final int codeEntryAlignment; 62 63 /** 64 * Container holding code bits and any other related information. 65 */ 66 private final CodeContainer codeContainer; 246 //@formatter:on 247 }; 248 249 static { 250 for (String[] entry : map) { 251 functionNamesToAOTSymbols.put(entry[0], entry[1]); 252 } 253 } 254 255 /** 256 * Allocates a {@code BinaryContainer} object whose content will be generated in a file with the 257 * prefix {@code prefix}. It also initializes internal code container, symbol table and 258 * relocation tables. 259 */ 260 public BinaryContainer(GraalHotSpotVMConfig config, String jvmVersion) { 261 this.codeSegmentSize = config.codeSegmentSize; 262 this.codeEntryAlignment = config.codeEntryAlignment; 263 264 // read only, code 265 codeContainer = new CodeContainer(".text", this); 266 extLinkageContainer = new CodeContainer(".hotspot.linkage.plt", this); 267 268 // read only, info 269 configContainer = new ReadOnlyDataContainer(".config", this); 270 metaspaceNamesContainer = new ReadOnlyDataContainer(".metaspace.names", this); 271 methodsOffsetsContainer = new ReadOnlyDataContainer(".methods.offsets", this); 272 klassesOffsetsContainer = new ReadOnlyDataContainer(".klasses.offsets", this); 273 klassesDependenciesContainer = new ReadOnlyDataContainer(".klasses.dependencies", this); 274 275 headerContainer = new HeaderContainer(jvmVersion, new ReadOnlyDataContainer(".header", this)); 276 stubsOffsetsContainer = new ReadOnlyDataContainer(".stubs.offsets", this); 277 codeSegmentsContainer = new ReadOnlyDataContainer(".code.segments", this); 278 constantDataContainer = new ReadOnlyDataContainer(".method.constdata", this); 279 280 // needs relocation patching at load time by the loader 281 methodMetadataContainer = new ReadOnlyDataContainer(".method.metadata", this); 282 283 // writable sections 284 metaspaceGotContainer = new ByteContainer(".metaspace.got", this); 285 metadataGotContainer = new ByteContainer(".metadata.got", this); 286 methodStateContainer = new ByteContainer(".method.state", this); 287 oopGotContainer = new ByteContainer(".oop.got", this); 288 extLinkageGOTContainer = new ByteContainer(".hotspot.linkage.got", this); 289 290 addGlobalSymbols(); 291 292 recordConfiguration(config); 293 } 294 295 private void recordConfiguration(GraalHotSpotVMConfig config) { 296 // @formatter:off 297 boolean[] booleanFlags = { config.cAssertions, // Debug VM 298 config.useCompressedOops, 299 config.useCompressedClassPointers, 300 config.compactFields, 301 config.useG1GC, 302 config.useCMSGC, 303 config.useTLAB, 304 config.useBiasedLocking, 305 TieredAOT.getValue(), 306 config.enableContended, 307 config.restrictContended, 308 }; 473 Symbol gotSymbol = extLinkageGOTContainer.createGotSymbol(s); 474 extLinkageGOTContainer.createSymbol(gotSymbol.getOffset(), Kind.OBJECT, Binding.GLOBAL, 8, name); 475 } 476 477 /** 478 * Create a platform-specific binary file representing the content of the 479 * {@code BinaryContainer} object. 480 * 481 * This method is called after creating and performing any necessary changes to the contents of 482 * code stream, symbol tables and relocation tables is completely finalized 483 * 484 * @param outputFileName name of output file 485 * 486 * @throws IOException in case of file creation failure 487 */ 488 public void createBinary(String outputFileName, String aotVersion) throws IOException { 489 String osName = System.getProperty("os.name"); 490 switch (osName) { 491 case "Linux": 492 case "SunOS": 493 JELFRelocObject elfso = new JELFRelocObject(this, outputFileName, aotVersion); 494 elfso.createELFRelocObject(relocationTable, symbolTable.values()); 495 break; 496 default: 497 throw new InternalError("Unsupported platform: " + osName); 498 } 499 } 500 501 /** 502 * Add symbol to the symbol table. If the existing symbol is undefined and the specified symbol 503 * is not undefined, replace the existing symbol information with that specified. 504 * 505 * @param symInfo symbol information to be added 506 */ 507 public void addSymbol(Symbol symInfo) { 508 if (symInfo.getName().startsWith("got.") && !(symInfo instanceof GotSymbol)) { 509 throw new InternalError("adding got. without being GotSymbol"); 510 } 511 if (symbolTable.containsKey(symInfo.getName())) { 512 throw new InternalError("Symbol: " + symInfo.getName() + " already exists in SymbolTable"); 513 } else { 514 // System.out.println("# Symbol [" + name + "] [" + symInfo.getValue() + "] [" + 515 // symInfo.getSection().getContainerName() + "] [" + symInfo.getSize() + "]"); 516 symbolTable.put(symInfo.getName(), symInfo); 718 String gotInitName = "got.init." + metaspaceName; 719 GotSymbol slot1Symbol = metaspaceGotContainer.createGotSymbol(gotInitName); 720 GotSymbol slot2Symbol = metaspaceGotContainer.createGotSymbol(gotName); 721 722 slot1Symbol.getIndex(); // check alignment and ignore result 723 // Get the index (offset/8) to the got in the .metaspace.got section 724 return slot2Symbol.getIndex(); 725 } 726 727 public int addMethodsCount(int count, ReadOnlyDataContainer container) { 728 return appendInt(count, container); 729 } 730 731 private static int appendInt(int count, ReadOnlyDataContainer container) { 732 int offset = container.getByteStreamSize(); 733 container.appendInt(count); 734 return offset; 735 } 736 737 /** 738 * Add constant data as follows. - Adding the data to the method.constdata section 739 * 740 * @param data 741 * @param alignment 742 * @return the offset in the method.constdata of the data 743 */ 744 public int addConstantData(byte[] data, int alignment) { 745 // Get the current length of the metaspaceNameContainer 746 int constantDataOffset = alignUp(constantDataContainer, alignment); 747 constantDataContainer.appendBytes(data, 0, data.length); 748 alignUp(constantDataContainer, alignment); // Post alignment 749 return constantDataOffset; 750 } 751 752 public int alignUp(ByteContainer container, int alignment) { 753 if (Integer.bitCount(alignment) != 1) { 754 throw new IllegalArgumentException("Must be a power of 2"); 755 } 756 int offset = container.getByteStreamSize(); 757 int aligned = (offset + (alignment - 1)) & -alignment; 758 if (aligned < offset || (aligned & (alignment - 1)) != 0) { 759 throw new RuntimeException("Error aligning: " + offset + " -> " + aligned); 760 } 761 if (aligned != offset) { 762 int nullArraySz = aligned - offset; | 1 /* 2 * Copyright (c) 2016, 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; 25 26 import static org.graalvm.compiler.hotspot.meta.HotSpotAOTProfilingPlugin.Options.TieredAOT; 27 28 import java.io.ByteArrayOutputStream; 29 import java.io.DataOutputStream; 30 import java.io.IOException; 31 import java.util.ArrayList; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 36 import jdk.tools.jaotc.binformat.Symbol.Binding; 37 import jdk.tools.jaotc.binformat.Symbol.Kind; 38 import jdk.tools.jaotc.binformat.elf.JELFRelocObject; 39 import jdk.tools.jaotc.binformat.macho.JMachORelocObject; 40 import jdk.tools.jaotc.binformat.pecoff.JPECoffRelocObject; 41 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 42 43 /** 44 * A format-agnostic container class that holds various components of a binary. 45 * 46 * <p> 47 * This class holds information necessary to create platform-specific binary containers such as 48 * ELFContainer for Linux and Solaris operating systems or MachOContainer for Mac 49 * OS or PEContainer for MS Windows operating systems. 50 * 51 * <p> 52 * Method APIs provided by this class are used to construct and populate platform-independent 53 * contents of a binary as the first step to create a binary representation of code generated by a 54 * compiler backend such as Graal. 55 * 56 * <p> 57 * Methods to record and access code section contents, symbols and relocations are provided. 58 */ 59 public class BinaryContainer implements SymbolTable { 60 61 private final int codeSegmentSize; 62 63 private final int codeEntryAlignment; 64 65 /** 66 * Container holding code bits and any other related information. 67 */ 68 private final CodeContainer codeContainer; 248 //@formatter:on 249 }; 250 251 static { 252 for (String[] entry : map) { 253 functionNamesToAOTSymbols.put(entry[0], entry[1]); 254 } 255 } 256 257 /** 258 * Allocates a {@code BinaryContainer} object whose content will be generated in a file with the 259 * prefix {@code prefix}. It also initializes internal code container, symbol table and 260 * relocation tables. 261 */ 262 public BinaryContainer(GraalHotSpotVMConfig config, String jvmVersion) { 263 this.codeSegmentSize = config.codeSegmentSize; 264 this.codeEntryAlignment = config.codeEntryAlignment; 265 266 // read only, code 267 codeContainer = new CodeContainer(".text", this); 268 extLinkageContainer = new CodeContainer(".hs.plt.linkage", this); 269 270 // read only, info 271 configContainer = new ReadOnlyDataContainer(".config", this); 272 metaspaceNamesContainer = new ReadOnlyDataContainer(".meta.names", this); 273 methodsOffsetsContainer = new ReadOnlyDataContainer(".methods.offsets", this); 274 klassesOffsetsContainer = new ReadOnlyDataContainer(".kls.offsets", this); 275 klassesDependenciesContainer = new ReadOnlyDataContainer(".kls.dependencies", this); 276 277 headerContainer = new HeaderContainer(jvmVersion, new ReadOnlyDataContainer(".header", this)); 278 stubsOffsetsContainer = new ReadOnlyDataContainer(".stubs.offsets", this); 279 codeSegmentsContainer = new ReadOnlyDataContainer(".code.segments", this); 280 constantDataContainer = new ReadOnlyDataContainer(".meth.constdata", this); 281 282 // needs relocation patching at load time by the loader 283 methodMetadataContainer = new ReadOnlyDataContainer(".meth.metadata", this); 284 285 // writable sections 286 metaspaceGotContainer = new ByteContainer(".meta.got", this); 287 metadataGotContainer = new ByteContainer(".metadata.got", this); 288 methodStateContainer = new ByteContainer(".meth.state", this); 289 oopGotContainer = new ByteContainer(".oop.got", this); 290 extLinkageGOTContainer = new ByteContainer(".hs.got.linkage", this); 291 292 addGlobalSymbols(); 293 294 recordConfiguration(config); 295 } 296 297 private void recordConfiguration(GraalHotSpotVMConfig config) { 298 // @formatter:off 299 boolean[] booleanFlags = { config.cAssertions, // Debug VM 300 config.useCompressedOops, 301 config.useCompressedClassPointers, 302 config.compactFields, 303 config.useG1GC, 304 config.useCMSGC, 305 config.useTLAB, 306 config.useBiasedLocking, 307 TieredAOT.getValue(), 308 config.enableContended, 309 config.restrictContended, 310 }; 475 Symbol gotSymbol = extLinkageGOTContainer.createGotSymbol(s); 476 extLinkageGOTContainer.createSymbol(gotSymbol.getOffset(), Kind.OBJECT, Binding.GLOBAL, 8, name); 477 } 478 479 /** 480 * Create a platform-specific binary file representing the content of the 481 * {@code BinaryContainer} object. 482 * 483 * This method is called after creating and performing any necessary changes to the contents of 484 * code stream, symbol tables and relocation tables is completely finalized 485 * 486 * @param outputFileName name of output file 487 * 488 * @throws IOException in case of file creation failure 489 */ 490 public void createBinary(String outputFileName, String aotVersion) throws IOException { 491 String osName = System.getProperty("os.name"); 492 switch (osName) { 493 case "Linux": 494 case "SunOS": 495 JELFRelocObject elfobj = new JELFRelocObject(this, outputFileName, aotVersion); 496 elfobj.createELFRelocObject(relocationTable, symbolTable.values()); 497 break; 498 case "Mac OS X": 499 JMachORelocObject machobj = new JMachORelocObject(this, outputFileName); 500 machobj.createMachORelocObject(relocationTable, symbolTable.values()); 501 break; 502 default: 503 if (osName.startsWith("Windows")) { 504 JPECoffRelocObject pecoffobj = new JPECoffRelocObject(this, outputFileName, aotVersion); 505 pecoffobj.createPECoffRelocObject(relocationTable, symbolTable.values()); 506 break; 507 } 508 else 509 throw new InternalError("Unsupported platform: " + osName); 510 } 511 } 512 513 /** 514 * Add symbol to the symbol table. If the existing symbol is undefined and the specified symbol 515 * is not undefined, replace the existing symbol information with that specified. 516 * 517 * @param symInfo symbol information to be added 518 */ 519 public void addSymbol(Symbol symInfo) { 520 if (symInfo.getName().startsWith("got.") && !(symInfo instanceof GotSymbol)) { 521 throw new InternalError("adding got. without being GotSymbol"); 522 } 523 if (symbolTable.containsKey(symInfo.getName())) { 524 throw new InternalError("Symbol: " + symInfo.getName() + " already exists in SymbolTable"); 525 } else { 526 // System.out.println("# Symbol [" + name + "] [" + symInfo.getValue() + "] [" + 527 // symInfo.getSection().getContainerName() + "] [" + symInfo.getSize() + "]"); 528 symbolTable.put(symInfo.getName(), symInfo); 730 String gotInitName = "got.init." + metaspaceName; 731 GotSymbol slot1Symbol = metaspaceGotContainer.createGotSymbol(gotInitName); 732 GotSymbol slot2Symbol = metaspaceGotContainer.createGotSymbol(gotName); 733 734 slot1Symbol.getIndex(); // check alignment and ignore result 735 // Get the index (offset/8) to the got in the .metaspace.got section 736 return slot2Symbol.getIndex(); 737 } 738 739 public int addMethodsCount(int count, ReadOnlyDataContainer container) { 740 return appendInt(count, container); 741 } 742 743 private static int appendInt(int count, ReadOnlyDataContainer container) { 744 int offset = container.getByteStreamSize(); 745 container.appendInt(count); 746 return offset; 747 } 748 749 /** 750 * Add constant data as follows. - Adding the data to the meth.constdata section 751 * 752 * @param data 753 * @param alignment 754 * @return the offset in the meth.constdata of the data 755 */ 756 public int addConstantData(byte[] data, int alignment) { 757 // Get the current length of the metaspaceNameContainer 758 int constantDataOffset = alignUp(constantDataContainer, alignment); 759 constantDataContainer.appendBytes(data, 0, data.length); 760 alignUp(constantDataContainer, alignment); // Post alignment 761 return constantDataOffset; 762 } 763 764 public int alignUp(ByteContainer container, int alignment) { 765 if (Integer.bitCount(alignment) != 1) { 766 throw new IllegalArgumentException("Must be a power of 2"); 767 } 768 int offset = container.getByteStreamSize(); 769 int aligned = (offset + (alignment - 1)) & -alignment; 770 if (aligned < offset || (aligned & (alignment - 1)) != 0) { 771 throw new RuntimeException("Error aligning: " + offset + " -> " + aligned); 772 } 773 if (aligned != offset) { 774 int nullArraySz = aligned - offset; |