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