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