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