1 /* 2 * Copyright (c) 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 package jdk.tools.jaotc; 25 26 import java.io.BufferedReader; 27 import java.io.File; 28 import java.io.InputStream; 29 import java.io.InputStreamReader; 30 import java.util.stream.Stream; 31 32 final class Linker { 33 34 private final Options options; 35 private String objectFileName; 36 private String libraryFileName; 37 private String linkerCmd; 38 39 String objFile() { 40 return objectFileName; 41 } 42 43 String libFile() { 44 return libraryFileName; 45 } 46 47 private static String getString(InputStream stream) { 48 BufferedReader br = new BufferedReader(new InputStreamReader(stream)); 49 Stream<String> lines = br.lines(); 50 StringBuilder sb = new StringBuilder(); 51 lines.iterator().forEachRemaining(e -> sb.append(e)); 52 return sb.toString(); 53 } 54 55 Linker(Main main) throws Exception { 56 this.options = main.options; 57 String name = options.outputName; 58 objectFileName = name; 59 libraryFileName = name; 60 61 if (options.linkerpath != null && !(new File(options.linkerpath).exists())) { 62 throw new InternalError("Invalid linker path: " + options.linkerpath); 63 } 64 String linkerPath; 65 String linkerCheck; 66 67 switch (options.osName) { 68 case "Linux": 69 if (name.endsWith(".so")) { 70 objectFileName = name.substring(0, name.length() - ".so".length()); 71 } 72 objectFileName = objectFileName + ".o"; 73 linkerPath = (options.linkerpath != null) ? options.linkerpath : "ld"; 74 linkerCmd = linkerPath + " -shared -z noexecstack -o " + libraryFileName + " " + objectFileName; 75 linkerCheck = linkerPath + " -v"; 76 break; 77 case "SunOS": 78 if (name.endsWith(".so")) { 79 objectFileName = name.substring(0, name.length() - ".so".length()); 80 } 81 objectFileName = objectFileName + ".o"; 82 linkerPath = (options.linkerpath != null) ? options.linkerpath : "ld"; 83 linkerCmd = linkerPath + " -shared -o " + libraryFileName + " " + objectFileName; 84 linkerCheck = linkerPath + " -V"; 85 break; 86 case "Mac OS X": 87 if (name.endsWith(".dylib")) { 88 objectFileName = name.substring(0, name.length() - ".dylib".length()); 89 } 90 objectFileName = objectFileName + ".o"; 91 linkerPath = (options.linkerpath != null) ? options.linkerpath : "ld"; 92 linkerCmd = linkerPath + " -dylib -o " + libraryFileName + " " + objectFileName; 93 linkerCheck = linkerPath + " -v"; 94 break; 95 default: 96 if (options.osName.startsWith("Windows")) { 97 if (name.endsWith(".dll")) { 98 objectFileName = name.substring(0, name.length() - ".dll".length()); 99 } 100 objectFileName = objectFileName + ".obj"; 101 linkerPath = (options.linkerpath != null) ? options.linkerpath : getWindowsLinkPath(); 102 if (linkerPath == null) { 103 throw new InternalError("Can't locate Microsoft Visual Studio amd64 link.exe"); 104 } 105 linkerCmd = linkerPath + " /DLL /OPT:NOREF /NOLOGO /NOENTRY" + " /OUT:" + libraryFileName + " " + objectFileName; 106 linkerCheck = null; // link.exe presence is verified already 107 break; 108 } else { 109 throw new InternalError("Unsupported platform: " + options.osName); 110 } 111 } 112 113 // Check linker presence on platforms by printing its version 114 if (linkerCheck != null) { 115 Process p = Runtime.getRuntime().exec(linkerCheck); 116 final int exitCode = p.waitFor(); 117 if (exitCode != 0) { 118 throw new InternalError(getString(p.getErrorStream())); 119 } 120 } 121 } 122 123 void link() throws Exception { 124 Process p = Runtime.getRuntime().exec(linkerCmd); 125 final int exitCode = p.waitFor(); 126 if (exitCode != 0) { 127 String errorMessage = getString(p.getErrorStream()); 128 if (errorMessage.isEmpty()) { 129 errorMessage = getString(p.getInputStream()); 130 } 131 throw new InternalError(errorMessage); 132 } 133 File objFile = new File(objectFileName); 134 boolean keepObjFile = Boolean.parseBoolean(System.getProperty("aot.keep.objFile", "false")); 135 if (objFile.exists() && !keepObjFile) { 136 if (!objFile.delete()) { 137 throw new InternalError("Failed to delete " + objectFileName + " file"); 138 } 139 } 140 // Make non-executable for all. 141 File libFile = new File(libraryFileName); 142 if (libFile.exists() && !options.osName.startsWith("Windows")) { 143 if (!libFile.setExecutable(false, false)) { 144 throw new InternalError("Failed to change attribute for " + libraryFileName + " file"); 145 } 146 } 147 148 } 149 150 /** 151 * Visual Studio supported versions Search Order is: VS2013, VS2015, VS2012 152 */ 153 public enum VSVERSIONS { 154 VS2013("VS120COMNTOOLS", "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\amd64\\link.exe"), 155 VS2015("VS140COMNTOOLS", "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\amd64\\link.exe"), 156 VS2012("VS110COMNTOOLS", "C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\bin\\amd64\\link.exe"); 157 158 private final String envvariable; 159 private final String wkp; 160 161 VSVERSIONS(String envvariable, String wellknownpath) { 162 this.envvariable = envvariable; 163 this.wkp = wellknownpath; 164 } 165 166 String EnvVariable() { 167 return envvariable; 168 } 169 170 String WellKnownPath() { 171 return wkp; 172 } 173 } 174 175 /** 176 * Search for Visual Studio link.exe Search Order is: VS2013, VS2015, VS2012 177 */ 178 private static String getWindowsLinkPath() { 179 String link = "\\VC\\bin\\amd64\\link.exe"; 180 181 /** 182 * First try searching the paths pointed to by the VS environment variables. 183 */ 184 for (VSVERSIONS vs : VSVERSIONS.values()) { 185 String vspath = System.getenv(vs.EnvVariable()); 186 if (vspath != null) { 187 File commonTools = new File(vspath); 188 File vsRoot = commonTools.getParentFile().getParentFile(); 189 File linkPath = new File(vsRoot, link); 190 if (linkPath.exists()) { 191 return linkPath.getPath(); 192 } 193 } 194 } 195 196 /** 197 * If we didn't find via the VS environment variables, try the well known paths 198 */ 199 for (VSVERSIONS vs : VSVERSIONS.values()) { 200 String wkp = vs.WellKnownPath(); 201 if (new File(wkp).exists()) { 202 return wkp; 203 } 204 } 205 206 return null; 207 } 208 209 }