1 /* 2 * Copyright (c) 2016, 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.jnilibelf; 25 26 import static jdk.tools.jaotc.jnilibelf.UnsafeAccess.UNSAFE; 27 28 import java.io.File; 29 import java.nio.ByteBuffer; 30 import java.nio.charset.StandardCharsets; 31 import java.util.ArrayList; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 36 import jdk.tools.jaotc.jnilibelf.JNILibELFAPI.ELF; 37 import jdk.tools.jaotc.jnilibelf.JNILibELFAPI.LibELF; 38 import jdk.tools.jaotc.jnilibelf.JNILibELFAPI.LibELF.Elf_Type; 39 40 /** 41 * A class abstraction of an ELF file. 42 * 43 */ 44 public class JNIELFContainer { 45 46 private String outputFileName; 47 private File outFile; 48 private int outFileDesc; 49 50 /** 51 * Pointer to Elf file. This is the same as struct Elf found in libelf.h 52 */ 53 private Pointer elfPtr; 54 55 /** 56 * Class of the ELF container - one of ELFCLASS32 or ELFCLASS64. 57 */ 58 private final int elfClass; 59 60 /** 61 * Pointer to ELF Header. 62 */ 63 private Pointer ehdrPtr; 64 65 /** 66 * Pointer to Program Header. 67 */ 68 private Pointer phdrPtr; 69 70 /** 71 * String holding .shstrtab contents. 72 */ 73 private String shStrTabContent = ""; 74 75 /** 76 * Map of local symbol indexes to ELF symbol entries. 77 */ 78 private List<ELFSymbol> localSymbolIndex = new ArrayList<>(); 79 80 /** 81 * Map of global symbol indexes to ELF symbol entries. 82 */ 83 private List<ELFSymbol> globalSymbolIndex = new ArrayList<>(); 84 85 /** 86 * String holding .strtab contents. 87 */ 88 private StringBuilder strTabContent = new StringBuilder(); 89 90 /** 91 * Keeps track of nr of bytes in .strtab since strTabContent.length() is number of chars, not 92 * bytes. 93 */ 94 private int strTabNrOfBytes = 0; 95 96 /** 97 * A hashtable that holds (section-name, relocation-table) pairs. For example, [(".rela.text", 98 * rela-text-reloc-entries), (".rela.plt", rela-plt-reloc-entries), ...]. 99 */ 100 private Map<ELFContainer, ArrayList<Pointer>> relocTables = new HashMap<>(); 101 102 /** 103 * Create reloca; 0 => false and non-zero => true. 104 */ 105 private final int createReloca; 106 107 /** 108 * Construct an ELFContainer in preparation for a disk image with file {@code prefix}. 109 * 110 * @param fileName name of ELF file to be created 111 */ 112 public JNIELFContainer(String fileName, String aotVersion) { 113 // Check for version compatibility 114 if (!JNILibELFAPI.elfshim_version().equals(aotVersion)) { 115 throw new InternalError("libelfshim version mismatch: " + JNILibELFAPI.elfshim_version() + " vs " + aotVersion); 116 } 117 118 elfClass = JNIELFTargetInfo.getELFClass(); 119 createReloca = JNIELFTargetInfo.createReloca(); 120 outputFileName = fileName; 121 } 122 123 /** 124 * Get the local ELF symbol table. 125 * 126 * @return local symbol table 127 */ 128 public List<ELFSymbol> getLocalSymbols() { 129 return localSymbolIndex; 130 } 131 132 /** 133 * Get the global ELF symbol table. 134 * 135 * @return list of global ELF symbol table entries 136 */ 137 public List<ELFSymbol> getGlobalSymbols() { 138 return globalSymbolIndex; 139 } 140 141 /** 142 * Get string table content (.strtab). 143 * 144 * @return string table content 145 */ 146 public String getStrTabContent() { 147 return strTabContent.toString(); 148 } 149 150 /** 151 * Get section header string table content (.shstrtab). 152 * 153 * @return section header string table content 154 */ 155 public String getShStrTabContent() { 156 return shStrTabContent; 157 } 158 159 /** 160 * Get relocation tables. 161 * 162 * @return relocation tables 163 */ 164 public Map<ELFContainer, ArrayList<Pointer>> getRelocTables() { 165 return relocTables; 166 } 167 168 /** 169 * Get the index of first non-local symbol in symbol table. 170 * 171 * @return symbol table index 172 */ 173 public int getFirstNonLocalSymbolIndex() { 174 return localSymbolIndex.size(); 175 } 176 177 /** 178 * Create ELF header of type {@code ececType}. 179 * 180 * @param type type of ELF executable 181 */ 182 public void createELFHeader(int type) { 183 // Check for version compatibility 184 if (JNILibELFAPI.elf_version(ELF.EV_CURRENT) == ELF.EV_NONE) { 185 throw new InternalError("ELF version mismatch"); 186 } 187 188 outFile = constructRelocFile(outputFileName); 189 // Open a temporary file for the shared library to be created 190 // TODO: Revisit file permissions; need to add execute permission 191 outFileDesc = JNILibELFAPI.open_rw(outFile.getPath()); 192 193 if (outFileDesc == -1) { 194 System.out.println("Failed to open file " + outFile.getPath() + " to write relocatable object."); 195 } 196 197 elfPtr = JNILibELFAPI.elf_begin(outFileDesc, LibELF.Elf_Cmd.ELF_C_WRITE.intValue(), new Pointer(0L)); 198 if (elfPtr == null) { 199 throw new InternalError("elf_begin failed"); 200 } 201 202 // Allocate new Ehdr of current architecture class 203 204 ehdrPtr = JNILibELFAPI.gelf_newehdr(elfPtr, elfClass); 205 206 JNILibELFAPI.ehdr_set_data_encoding(ehdrPtr, JNIELFTargetInfo.getELFEndian()); 207 JNILibELFAPI.set_Ehdr_e_machine(elfClass, ehdrPtr, JNIELFTargetInfo.getELFArch()); 208 JNILibELFAPI.set_Ehdr_e_type(elfClass, ehdrPtr, type); 209 JNILibELFAPI.set_Ehdr_e_version(elfClass, ehdrPtr, ELF.EV_CURRENT); 210 } 211 212 /** 213 * If the file name has a .so extension, replace it with .o extension. Else just add .o 214 * extension 215 * 216 * @param fileName 217 * @return File object 218 */ 219 private static File constructRelocFile(String fileName) { 220 File relocFile = new File(fileName); 221 if (relocFile.exists()) { 222 if (!relocFile.delete()) { 223 throw new InternalError("Failed to delete existing " + fileName + " file"); 224 } 225 } 226 return relocFile; 227 } 228 229 /** 230 * Create {@code count} number of Program headers. 231 * 232 * @param count number of program headers to create 233 * @return true upon success; false upon failure 234 */ 235 public boolean createProgramHeader(int count) { 236 phdrPtr = JNILibELFAPI.gelf_newphdr(elfPtr, count); 237 if (phdrPtr == null) { 238 System.out.println("gelf_newphdr error"); 239 return false; 240 } 241 return true; 242 } 243 244 /** 245 * Set program header to be of type self. 246 * 247 * @return true 248 */ 249 public boolean setProgHdrTypeToSelf() { 250 // Set program header to be of type self 251 JNILibELFAPI.phdr_set_type_self(elfClass, ehdrPtr, phdrPtr); 252 // And thus mark it as dirty so that elfUpdate can recompute the structures 253 JNILibELFAPI.elf_flagphdr(elfPtr, LibELF.Elf_Cmd.ELF_C_SET.intValue(), LibELF.ELF_F_DIRTY); 254 // TODO: Error checking; look at the return value of elf_update 255 // and call elf_errmsg appropriately. 256 return true; 257 } 258 259 /** 260 * Create a section. The corresponding section header and section data are created by calling 261 * the necessary libelf APIs. The section that is created is inserted into the ELF container. 262 * 263 * @param secName name of the section 264 * @param scnData section data 265 * @param dataType data type 266 * @param align section alignment 267 * @param scnType section type 268 * @param scnFlags section flags 269 * @param scnLink sh_link field of Elf{32,64}_Shdr 270 * @param scnInfo sh_info field of Elf{32,64}_Shdr 271 * @return section index 272 */ 273 public int createSection(String secName, byte[] scnData, Elf_Type dataType, int align, int scnType, int scnFlags, int scnLink, int scnInfo) { 274 // Create a new section 275 Pointer scnPtr = JNILibELFAPI.elf_newscn(elfPtr); 276 if (scnPtr == null) { 277 throw new InternalError("elf_newscn error"); 278 } 279 280 // Allocate section data for the section 281 Pointer scnDataPtr = JNILibELFAPI.elf_newdata(scnPtr); 282 if (scnDataPtr == null) { 283 String errMsg = JNILibELFAPI.elf_errmsg(-1); 284 throw new InternalError("elf_newdata error: " + errMsg); 285 } 286 287 // Get the pointer to section header associated with the new section 288 Pointer scnHdrPtr = JNILibELFAPI.elf64_getshdr(scnPtr); 289 290 // Add name of the section to section name string 291 // If secName is null, point the name to the 0th index 292 // that holds `\0' 293 byte[] modScnData; 294 if (secName.isEmpty()) { 295 JNILibELFAPI.set_Shdr_sh_name(elfClass, scnHdrPtr, 0); 296 modScnData = scnData; 297 } else { 298 if (secName.equals(".shstrtab")) { 299 // Modify .shstrtab data by inserting '\0' at index 0 300 String shstrtabSecName = ".shstrtab" + '\0'; 301 // Additional byte for the '\0' at position 0 302 ByteBuffer nbuf = ByteBuffer.allocate(scnData.length + 1 + shstrtabSecName.length()); 303 nbuf.put(0, (byte) 0); 304 nbuf.position(1); 305 nbuf.put(scnData); 306 nbuf.position(scnData.length + 1); 307 // Add the section name ".shstrtab" to its own data 308 nbuf.put(shstrtabSecName.getBytes(StandardCharsets.UTF_8)); 309 modScnData = nbuf.array(); 310 JNILibELFAPI.set_Shdr_sh_name(elfClass, scnHdrPtr, scnData.length + 1); 311 // Set strtab section index 312 JNILibELFAPI.set_Ehdr_e_shstrndx(elfClass, ehdrPtr, JNILibELFAPI.elf_ndxscn(scnPtr)); 313 } else if (secName.equals(".strtab")) { 314 // Modify strtab section data to insert '\0' at position 0. 315 // Additional byte for the '\0' at position 0 316 ByteBuffer nbuf = ByteBuffer.allocate(scnData.length + 1); 317 nbuf.put(0, (byte) 0); 318 nbuf.position(1); 319 nbuf.put(scnData); 320 modScnData = nbuf.array(); 321 // Set the sh_name 322 JNILibELFAPI.set_Shdr_sh_name(elfClass, scnHdrPtr, shStrTabContent.length() + 1); 323 // Add scnName to stringList 324 shStrTabContent += secName + '\0'; 325 } else { 326 // Set the sh_name 327 JNILibELFAPI.set_Shdr_sh_name(elfClass, scnHdrPtr, shStrTabContent.length() + 1); 328 // Add scnName to stringList 329 shStrTabContent += secName + '\0'; 330 modScnData = scnData; 331 } 332 } 333 334 final int scnDataBufSize = modScnData.length; 335 336 Pointer scnDataBufPtr = null; 337 if (scnType != ELF.SHT_NOBITS) { 338 // Allocate native memory for section data 339 final long address = UNSAFE.allocateMemory(scnDataBufSize + 1); 340 scnDataBufPtr = new Pointer(address); 341 scnDataBufPtr.put(modScnData); 342 } else { 343 scnDataBufPtr = new Pointer(0L); 344 } 345 346 // Set data descriptor fields 347 JNILibELFAPI.set_Data_d_align(scnDataPtr, align); 348 JNILibELFAPI.set_Data_d_buf(scnDataPtr, scnDataBufPtr); 349 JNILibELFAPI.set_Data_d_size(scnDataPtr, scnDataBufSize); 350 JNILibELFAPI.set_Data_d_off(scnDataPtr, 0); 351 JNILibELFAPI.set_Data_d_type(scnDataPtr, dataType.intValue()); 352 JNILibELFAPI.set_Data_d_version(scnDataPtr, ELF.EV_CURRENT); 353 354 JNILibELFAPI.set_Shdr_sh_type(elfClass, scnHdrPtr, scnType); 355 JNILibELFAPI.set_Shdr_sh_flags(elfClass, scnHdrPtr, scnFlags); 356 JNILibELFAPI.set_Shdr_sh_entsize(elfClass, scnHdrPtr, 0); // TODO: Is this right?? 357 JNILibELFAPI.set_Shdr_sh_link(elfClass, scnHdrPtr, scnLink); 358 JNILibELFAPI.set_Shdr_sh_info(elfClass, scnHdrPtr, scnInfo); 359 360 // Add hash section to section pointer list 361 int index = JNILibELFAPI.elf_ndxscn(scnPtr); 362 return index; 363 } 364 365 /** 366 * Create an ELF symbol entry for a symbol with the given properties. 367 * 368 * @param name name of the section in which symName is referenced 369 * @param type type of symName 370 * @param bind binding of symName 371 * @param secHdrIndex section header index of the section in which symName is referenced 372 * (st_shndx of ELF symbol entry) 373 * @param size symName size (st_size of ELF symbol entry) 374 * @param value symName value (st_value of ELF symbol entry) 375 * @param isLocal true if symbol is local. 376 */ 377 public ELFSymbol createELFSymbolEntry(String name, int type, int bind, int secHdrIndex, int size, int value, boolean isLocal) { 378 // Get the current symbol index and append symbol name to string table. 379 int index; 380 if (name.isEmpty()) { 381 index = 0; 382 } else { 383 // NOTE: The +1 comes from the null symbol! 384 // We can't trust strTabContent.length() since that is chars (UTF16), keep track of 385 // bytes on our own. 386 index = strTabNrOfBytes + 1; 387 strTabContent.append(name).append('\0'); 388 strTabNrOfBytes += name.getBytes(StandardCharsets.UTF_8).length + 1; 389 } 390 391 // Create ELF symbol entry 392 long address = JNILibELFAPI.create_sym_entry(elfClass, index, type, bind, secHdrIndex, size, value); 393 if (address == 0) { 394 throw new InternalError("create_sym_entry failed"); 395 } 396 Pointer ptr = new Pointer(address); 397 398 if (isLocal) { 399 final int localIndex = localSymbolIndex.size(); 400 ELFSymbol symbol = new ELFSymbol(name, localIndex, ptr, isLocal); 401 localSymbolIndex.add(symbol); 402 return symbol; 403 } else { 404 final int globalIndex = globalSymbolIndex.size(); 405 ELFSymbol symbol = new ELFSymbol(name, globalIndex, ptr, isLocal); 406 globalSymbolIndex.add(symbol); 407 return symbol; 408 } 409 } 410 411 /** 412 * Create an ELF relocation entry for given symbol {@code name} to section {@code secname}. 413 * 414 * @param container the section 415 * @param offset offset into the section contents at which the relocation needs to be applied 416 * @param type ELF type of the relocation entry 417 * @param addend Addend for for relocation of type reloca 418 */ 419 public void createELFRelocationEntry(ELFContainer container, int offset, int type, int addend, ELFSymbol elfSymbol) { 420 // Get the index of the symbol. 421 int index; 422 if (elfSymbol.isLocal()) { 423 index = elfSymbol.getIndex(); 424 } else { 425 /* 426 * For global symbol entries the index will be offset by the number of local symbols 427 * which will be listed first in the symbol table. 428 */ 429 index = elfSymbol.getIndex() + localSymbolIndex.size(); 430 } 431 432 long address = JNILibELFAPI.create_reloc_entry(elfClass, offset, index, type, addend, createReloca); 433 if (address == 0) { 434 throw new InternalError("create_reloc_entry failed"); 435 } 436 Pointer ptr = new Pointer(address); 437 /* 438 * If section name associated with this symbol is set to undefined i.e., secname is null, 439 * symIndex is undef i.e., 0. 440 */ 441 if (relocTables.get(container) == null) { 442 // Allocate a new table and add it to the hash table of reloc tables 443 relocTables.put(container, new ArrayList<>()); 444 } 445 446 // Add the entry 447 relocTables.get(container).add(ptr); 448 } 449 450 /** 451 * Invokes native libelf function loff_t elf_update (Elf *elfPtr, Elf_Cmd cmd). 452 * 453 * @param cmd command 454 * @return return value of the native function called 455 */ 456 public boolean elfUpdate(LibELF.Elf_Cmd cmd) { 457 JNILibELFAPI.elf_update(elfPtr, cmd.intValue()); 458 // TODO: Error checking; look at the return value of elf_update 459 // and call elf_errmsg appropriately. 460 return true; 461 } 462 463 /** 464 * Wrapper function that invokes int elf_end (Elf *elfPtr). and closes ELF output file 465 * descriptor 466 * 467 * @return true 468 */ 469 public boolean elfEnd() { 470 // Finish ELF processing 471 JNILibELFAPI.elf_end(elfPtr); 472 // Close file descriptor 473 JNILibELFAPI.close(outFileDesc); 474 return true; 475 } 476 }