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 }