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