1 /*
   2  * Copyright (c) 2016, 2018, 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;
  27 
  28 import java.util.ArrayList;
  29 import java.util.HashMap;
  30 import java.util.List;
  31 import java.util.Map.Entry;
  32 
  33 import org.graalvm.compiler.code.CompilationResult;
  34 import org.graalvm.compiler.debug.DebugContext;
  35 import org.graalvm.compiler.hotspot.HotSpotHostBackend;
  36 import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProvider;
  37 import org.graalvm.compiler.hotspot.stubs.Stub;
  38 
  39 import jdk.tools.jaotc.binformat.BinaryContainer;
  40 import jdk.tools.jaotc.binformat.ByteContainer;
  41 import jdk.tools.jaotc.binformat.HeaderContainer;
  42 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
  43 import jdk.vm.ci.hotspot.HotSpotVMConfigStore;
  44 import jdk.vm.ci.hotspot.VMField;
  45 
  46 final class DataBuilder {
  47 
  48     private final Main main;
  49 
  50     private final HotSpotHostBackend backend;
  51 
  52     private final List<AOTCompiledClass> classes;
  53 
  54     /**
  55      * Target-independent container in which text symbols and code bytes are created.
  56      */
  57     private final BinaryContainer binaryContainer;
  58 
  59     private static final HashMap<Long, String> vmAddresses = new HashMap<>();
  60 
  61     DataBuilder(Main main, HotSpotHostBackend backend, List<AOTCompiledClass> classes, BinaryContainer binaryContainer) {
  62         this.main = main;
  63         this.backend = backend;
  64         this.classes = classes;
  65         this.binaryContainer = binaryContainer;
  66         fillVMAddresses(HotSpotJVMCIRuntime.runtime().getConfigStore());
  67     }
  68 
  69     /**
  70      * Returns a value-name map of all {@link VMField} fields.
  71      */
  72     private static void fillVMAddresses(HotSpotVMConfigStore config) {
  73         for (VMField vmField : config.getFields().values()) {
  74             if (vmField.value != null && vmField.value instanceof Long) {
  75                 final long address = (Long) vmField.value;
  76                 String value = vmField.name;
  77                 /*
  78                  * Some fields don't contain addresses but integer values. At least don't add zero
  79                  * entries to avoid matching null addresses.
  80                  */
  81                 if (address != 0) {
  82                     vmAddresses.put(address, value);
  83                 }
  84             }
  85         }
  86         for (Entry<String, Long> vmAddress : config.getAddresses().entrySet()) {
  87             final long address = vmAddress.getValue();
  88             String value = vmAddress.getKey();
  89             String old = vmAddresses.put(address, value);
  90             if (old != null) {
  91                 throw new InternalError("already in map: address: " + address + ", current: " + value + ", old: " + old);
  92             }
  93         }
  94     }
  95 
  96     /**
  97      * Get the C/C++ function name associated with the foreign call target {@code address}.
  98      *
  99      * @param address native address
 100      * @return C/C++ functio name associated with the native address
 101      */
 102     static String getVMFunctionNameForAddress(long address) {
 103         return vmAddresses.get(address);
 104     }
 105 
 106     /**
 107      * Returns the host backend used for this compilation.
 108      *
 109      * @return host backend
 110      */
 111     HotSpotHostBackend getBackend() {
 112         return backend;
 113     }
 114 
 115     /**
 116      * Returns the binary container for this compilation.
 117      *
 118      * @return binary container
 119      */
 120     BinaryContainer getBinaryContainer() {
 121         return binaryContainer;
 122     }
 123 
 124     /**
 125      * Prepare data with all compiled classes and stubs.
 126      *
 127      * @param debug
 128      *
 129      * @throws Exception
 130      */
 131     @SuppressWarnings("try")
 132     void prepareData(DebugContext debug) throws Exception {
 133         try (Timer t = new Timer(main, "Parsing compiled code")) {
 134             /*
 135              * Copy compiled code into code section container and calls stubs (PLT trampoline).
 136              */
 137             CodeSectionProcessor codeSectionProcessor = new CodeSectionProcessor(this);
 138             for (AOTCompiledClass c : classes) {
 139                 // For each class we need 2 GOT slots:
 140                 // first - for initialized klass
 141                 // second - only for loaded klass
 142                 c.addAOTKlassData(binaryContainer);
 143                 codeSectionProcessor.process(c);
 144             }
 145         }
 146 
 147         AOTCompiledClass stubCompiledCode = retrieveStubCode(debug);
 148 
 149         // Free memory!
 150         try (Timer t = main.options.verbose ? new Timer(main, "Freeing memory") : null) {
 151             main.printer.printMemoryUsage();
 152             System.gc();
 153         }
 154 
 155         MetadataBuilder metadataBuilder = null;
 156         try (Timer t = new Timer(main, "Processing metadata")) {
 157             /*
 158              * Generate metadata for compiled code and copy it into metadata section. Create
 159              * relocation information for all references (call, constants, etc) in compiled code.
 160              */
 161             metadataBuilder = new MetadataBuilder(this);
 162             metadataBuilder.processMetadata(classes, stubCompiledCode);
 163         }
 164 
 165         // Free memory!
 166         try (Timer t = main.options.verbose ? new Timer(main, "Freeing memory") : null) {
 167             main.printer.printMemoryUsage();
 168             System.gc();
 169         }
 170 
 171         try (Timer t = new Timer(main, "Preparing stubs binary")) {
 172             prepareStubsBinary(stubCompiledCode);
 173         }
 174         try (Timer t = new Timer(main, "Preparing compiled binary")) {
 175             // Should be called after Stubs because they can set dependent klasses.
 176             prepareCompiledBinary();
 177         }
 178     }
 179 
 180     /**
 181      * Get all stubs from Graal and add them to the code section.
 182      *
 183      * @param debug
 184      */
 185     @SuppressWarnings("try")
 186     private AOTCompiledClass retrieveStubCode(DebugContext debug) {
 187         ArrayList<CompiledMethodInfo> stubs = new ArrayList<>();
 188         HotSpotForeignCallsProvider foreignCallsProvider = backend.getProviders().getForeignCalls();
 189         for (Stub stub : foreignCallsProvider.getStubs()) {
 190             try (DebugContext.Scope scope = debug.scope("CompileStubs")) {
 191                 CompilationResult result = stub.getCompilationResult(debug, backend);
 192                 CompiledMethodInfo cm = new CompiledMethodInfo(result, new AOTStub(stub, backend, debug.getOptions()));
 193                 stubs.add(cm);
 194             } catch (Throwable e) {
 195                 throw debug.handle(e);
 196             }
 197         }
 198         AOTCompiledClass stubCompiledCode = new AOTCompiledClass(stubs);
 199         CodeSectionProcessor codeSectionProcessor = new CodeSectionProcessor(this);
 200         codeSectionProcessor.process(stubCompiledCode);
 201         return stubCompiledCode;
 202     }
 203 
 204     /**
 205      * Prepare metaspace.offsets section.
 206      */
 207     private void prepareCompiledBinary() {
 208         for (AOTCompiledClass c : classes) {
 209             // Create records for compiled AOT methods.
 210             c.putMethodsData(binaryContainer);
 211         }
 212         // Create records for compiled AOT classes.
 213         AOTCompiledClass.putAOTKlassData(binaryContainer);
 214 
 215         // Fill in AOTHeader
 216         HeaderContainer header = binaryContainer.getHeaderContainer();
 217         header.setClassesCount(AOTCompiledClass.getClassesCount());
 218         header.setMethodsCount(CompiledMethodInfo.getMethodsCount());
 219         // Record size of got sections
 220         ByteContainer bc = binaryContainer.getKlassesGotContainer();
 221         header.setKlassesGotSize((bc.getByteStreamSize() / 8));
 222         bc = binaryContainer.getMetadataGotContainer();
 223         header.setMetadataGotSize((bc.getByteStreamSize() / 8));
 224         bc = binaryContainer.getOopGotContainer();
 225         header.setOopGotSize((bc.getByteStreamSize() / 8));
 226     }
 227 
 228     /**
 229      * Prepare stubs.offsets section.
 230      */
 231     private void prepareStubsBinary(AOTCompiledClass compiledClass) {
 232         // For each of the compiled stubs, create records holding information about
 233         // them.
 234         ArrayList<CompiledMethodInfo> compiledStubs = compiledClass.getCompiledMethods();
 235         int cntStubs = compiledStubs.size();
 236         BinaryContainer.addMethodsCount(cntStubs, binaryContainer.getStubsOffsetsContainer());
 237         for (CompiledMethodInfo methodInfo : compiledStubs) {
 238             // Note, stubs have different offsets container.
 239             methodInfo.addMethodOffsets(binaryContainer, binaryContainer.getStubsOffsetsContainer());
 240         }
 241     }
 242 
 243 }