/*
 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.tools.jaotc.jnilibelf;

import static jdk.tools.jaotc.jnilibelf.UnsafeAccess.UNSAFE;

import java.io.File;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jdk.tools.jaotc.jnilibelf.JNILibELFAPI.ELF;
import jdk.tools.jaotc.jnilibelf.JNILibELFAPI.LibELF;
import jdk.tools.jaotc.jnilibelf.JNILibELFAPI.LibELF.Elf_Type;

A class abstraction of an ELF file.
/** * A class abstraction of an ELF file. * */
public class JNIELFContainer { private String outputFileName; private File outFile; private int outFileDesc;
Pointer to Elf file. This is the same as struct Elf found in libelf.h
/** * Pointer to Elf file. This is the same as struct Elf found in libelf.h */
private Pointer elfPtr;
Class of the ELF container - one of ELFCLASS32 or ELFCLASS64.
/** * Class of the ELF container - one of ELFCLASS32 or ELFCLASS64. */
private final int elfClass;
Pointer to ELF Header.
/** * Pointer to ELF Header. */
private Pointer ehdrPtr;
Pointer to Program Header.
/** * Pointer to Program Header. */
private Pointer phdrPtr;
String holding .shstrtab contents.
/** * String holding .shstrtab contents. */
private String shStrTabContent = "";
Map of local symbol indexes to ELF symbol entries.
/** * Map of local symbol indexes to ELF symbol entries. */
private List<ELFSymbol> localSymbolIndex = new ArrayList<>();
Map of global symbol indexes to ELF symbol entries.
/** * Map of global symbol indexes to ELF symbol entries. */
private List<ELFSymbol> globalSymbolIndex = new ArrayList<>();
String holding .strtab contents.
/** * String holding .strtab contents. */
private StringBuilder strTabContent = new StringBuilder();
Keeps track of nr of bytes in .strtab since strTabContent.length() is number of chars, not bytes.
/** * Keeps track of nr of bytes in .strtab since strTabContent.length() is number of chars, not * bytes. */
private int strTabNrOfBytes = 0;
A hashtable that holds (section-name, relocation-table) pairs. For example, [(".rela.text", rela-text-reloc-entries), (".rela.plt", rela-plt-reloc-entries), ...].
/** * A hashtable that holds (section-name, relocation-table) pairs. For example, [(".rela.text", * rela-text-reloc-entries), (".rela.plt", rela-plt-reloc-entries), ...]. */
private Map<ELFContainer, ArrayList<Pointer>> relocTables = new HashMap<>();
Create reloca; 0 => false and non-zero => true.
/** * Create reloca; 0 => false and non-zero => true. */
private final int createReloca;
Construct an ELFContainer in preparation for a disk image with file prefix.
Params:
  • fileName – name of ELF file to be created
/** * Construct an ELFContainer in preparation for a disk image with file {@code prefix}. * * @param fileName name of ELF file to be created */
public JNIELFContainer(String fileName, String aotVersion) { // Check for version compatibility if (!JNILibELFAPI.elfshim_version().equals(aotVersion)) { throw new InternalError("libelfshim version mismatch: " + JNILibELFAPI.elfshim_version() + " vs " + aotVersion); } elfClass = JNIELFTargetInfo.getELFClass(); createReloca = JNIELFTargetInfo.createReloca(); outputFileName = fileName; }
Get the local ELF symbol table.
Returns:local symbol table
/** * Get the local ELF symbol table. * * @return local symbol table */
public List<ELFSymbol> getLocalSymbols() { return localSymbolIndex; }
Get the global ELF symbol table.
Returns:list of global ELF symbol table entries
/** * Get the global ELF symbol table. * * @return list of global ELF symbol table entries */
public List<ELFSymbol> getGlobalSymbols() { return globalSymbolIndex; }
Get string table content (.strtab).
Returns:string table content
/** * Get string table content (.strtab). * * @return string table content */
public String getStrTabContent() { return strTabContent.toString(); }
Get section header string table content (.shstrtab).
Returns:section header string table content
/** * Get section header string table content (.shstrtab). * * @return section header string table content */
public String getShStrTabContent() { return shStrTabContent; }
Get relocation tables.
Returns:relocation tables
/** * Get relocation tables. * * @return relocation tables */
public Map<ELFContainer, ArrayList<Pointer>> getRelocTables() { return relocTables; }
Get the index of first non-local symbol in symbol table.
Returns:symbol table index
/** * Get the index of first non-local symbol in symbol table. * * @return symbol table index */
public int getFirstNonLocalSymbolIndex() { return localSymbolIndex.size(); }
Create ELF header of type ececType.
Params:
  • type – type of ELF executable
/** * Create ELF header of type {@code ececType}. * * @param type type of ELF executable */
public void createELFHeader(int type) { // Check for version compatibility if (JNILibELFAPI.elf_version(ELF.EV_CURRENT) == ELF.EV_NONE) { throw new InternalError("ELF version mismatch"); } outFile = constructRelocFile(outputFileName); // Open a temporary file for the shared library to be created // TODO: Revisit file permissions; need to add execute permission outFileDesc = JNILibELFAPI.open_rw(outFile.getPath()); if (outFileDesc == -1) { System.out.println("Failed to open file " + outFile.getPath() + " to write relocatable object."); } elfPtr = JNILibELFAPI.elf_begin(outFileDesc, LibELF.Elf_Cmd.ELF_C_WRITE.intValue(), new Pointer(0L)); if (elfPtr == null) { throw new InternalError("elf_begin failed"); } // Allocate new Ehdr of current architecture class ehdrPtr = JNILibELFAPI.gelf_newehdr(elfPtr, elfClass); JNILibELFAPI.ehdr_set_data_encoding(ehdrPtr, JNIELFTargetInfo.getELFEndian()); JNILibELFAPI.set_Ehdr_e_machine(elfClass, ehdrPtr, JNIELFTargetInfo.getELFArch()); JNILibELFAPI.set_Ehdr_e_type(elfClass, ehdrPtr, type); JNILibELFAPI.set_Ehdr_e_version(elfClass, ehdrPtr, ELF.EV_CURRENT); }
If the file name has a .so extension, replace it with .o extension. Else just add .o extension
Params:
  • fileName –
Returns:File object
/** * If the file name has a .so extension, replace it with .o extension. Else just add .o * extension * * @param fileName * @return File object */
private static File constructRelocFile(String fileName) { File relocFile = new File(fileName); if (relocFile.exists()) { if (!relocFile.delete()) { throw new InternalError("Failed to delete existing " + fileName + " file"); } } return relocFile; }
Create count number of Program headers.
Params:
  • count – number of program headers to create
Returns:true upon success; false upon failure
/** * Create {@code count} number of Program headers. * * @param count number of program headers to create * @return true upon success; false upon failure */
public boolean createProgramHeader(int count) { phdrPtr = JNILibELFAPI.gelf_newphdr(elfPtr, count); if (phdrPtr == null) { System.out.println("gelf_newphdr error"); return false; } return true; }
Set program header to be of type self.
Returns:true
/** * Set program header to be of type self. * * @return true */
public boolean setProgHdrTypeToSelf() { // Set program header to be of type self JNILibELFAPI.phdr_set_type_self(elfClass, ehdrPtr, phdrPtr); // And thus mark it as dirty so that elfUpdate can recompute the structures JNILibELFAPI.elf_flagphdr(elfPtr, LibELF.Elf_Cmd.ELF_C_SET.intValue(), LibELF.ELF_F_DIRTY); // TODO: Error checking; look at the return value of elf_update // and call elf_errmsg appropriately. return true; }
Create a section. The corresponding section header and section data are created by calling the necessary libelf APIs. The section that is created is inserted into the ELF container.
Params:
  • secName – name of the section
  • scnData – section data
  • dataType – data type
  • align – section alignment
  • scnType – section type
  • scnFlags – section flags
  • scnLink – sh_link field of Elf{32,64}_Shdr
  • scnInfo – sh_info field of Elf{32,64}_Shdr
Returns:section index
/** * Create a section. The corresponding section header and section data are created by calling * the necessary libelf APIs. The section that is created is inserted into the ELF container. * * @param secName name of the section * @param scnData section data * @param dataType data type * @param align section alignment * @param scnType section type * @param scnFlags section flags * @param scnLink sh_link field of Elf{32,64}_Shdr * @param scnInfo sh_info field of Elf{32,64}_Shdr * @return section index */
public int createSection(String secName, byte[] scnData, Elf_Type dataType, int align, int scnType, int scnFlags, int scnLink, int scnInfo) { // Create a new section Pointer scnPtr = JNILibELFAPI.elf_newscn(elfPtr); if (scnPtr == null) { throw new InternalError("elf_newscn error"); } // Allocate section data for the section Pointer scnDataPtr = JNILibELFAPI.elf_newdata(scnPtr); if (scnDataPtr == null) { String errMsg = JNILibELFAPI.elf_errmsg(-1); throw new InternalError("elf_newdata error: " + errMsg); } // Get the pointer to section header associated with the new section Pointer scnHdrPtr = JNILibELFAPI.elf64_getshdr(scnPtr); // Add name of the section to section name string // If secName is null, point the name to the 0th index // that holds `\0' byte[] modScnData; if (secName.isEmpty()) { JNILibELFAPI.set_Shdr_sh_name(elfClass, scnHdrPtr, 0); modScnData = scnData; } else { if (secName.equals(".shstrtab")) { // Modify .shstrtab data by inserting '\0' at index 0 String shstrtabSecName = ".shstrtab" + '\0'; // Additional byte for the '\0' at position 0 ByteBuffer nbuf = ByteBuffer.allocate(scnData.length + 1 + shstrtabSecName.length()); nbuf.put(0, (byte) 0); nbuf.position(1); nbuf.put(scnData); nbuf.position(scnData.length + 1); // Add the section name ".shstrtab" to its own data nbuf.put(shstrtabSecName.getBytes(StandardCharsets.UTF_8)); modScnData = nbuf.array(); JNILibELFAPI.set_Shdr_sh_name(elfClass, scnHdrPtr, scnData.length + 1); // Set strtab section index JNILibELFAPI.set_Ehdr_e_shstrndx(elfClass, ehdrPtr, JNILibELFAPI.elf_ndxscn(scnPtr)); } else if (secName.equals(".strtab")) { // Modify strtab section data to insert '\0' at position 0. // Additional byte for the '\0' at position 0 ByteBuffer nbuf = ByteBuffer.allocate(scnData.length + 1); nbuf.put(0, (byte) 0); nbuf.position(1); nbuf.put(scnData); modScnData = nbuf.array(); // Set the sh_name JNILibELFAPI.set_Shdr_sh_name(elfClass, scnHdrPtr, shStrTabContent.length() + 1); // Add scnName to stringList shStrTabContent += secName + '\0'; } else { // Set the sh_name JNILibELFAPI.set_Shdr_sh_name(elfClass, scnHdrPtr, shStrTabContent.length() + 1); // Add scnName to stringList shStrTabContent += secName + '\0'; modScnData = scnData; } } final int scnDataBufSize = modScnData.length; Pointer scnDataBufPtr = null; if (scnType != ELF.SHT_NOBITS) { // Allocate native memory for section data final long address = UNSAFE.allocateMemory(scnDataBufSize + 1); scnDataBufPtr = new Pointer(address); scnDataBufPtr.put(modScnData); } else { scnDataBufPtr = new Pointer(0L); } // Set data descriptor fields JNILibELFAPI.set_Data_d_align(scnDataPtr, align); JNILibELFAPI.set_Data_d_buf(scnDataPtr, scnDataBufPtr); JNILibELFAPI.set_Data_d_size(scnDataPtr, scnDataBufSize); JNILibELFAPI.set_Data_d_off(scnDataPtr, 0); JNILibELFAPI.set_Data_d_type(scnDataPtr, dataType.intValue()); JNILibELFAPI.set_Data_d_version(scnDataPtr, ELF.EV_CURRENT); JNILibELFAPI.set_Shdr_sh_type(elfClass, scnHdrPtr, scnType); JNILibELFAPI.set_Shdr_sh_flags(elfClass, scnHdrPtr, scnFlags); JNILibELFAPI.set_Shdr_sh_entsize(elfClass, scnHdrPtr, 0); // TODO: Is this right?? JNILibELFAPI.set_Shdr_sh_link(elfClass, scnHdrPtr, scnLink); JNILibELFAPI.set_Shdr_sh_info(elfClass, scnHdrPtr, scnInfo); // Add hash section to section pointer list int index = JNILibELFAPI.elf_ndxscn(scnPtr); return index; }
Create an ELF symbol entry for a symbol with the given properties.
Params:
  • name – name of the section in which symName is referenced
  • type – type of symName
  • bind – binding of symName
  • secHdrIndex – section header index of the section in which symName is referenced (st_shndx of ELF symbol entry)
  • size – symName size (st_size of ELF symbol entry)
  • value – symName value (st_value of ELF symbol entry)
  • isLocal – true if symbol is local.
/** * Create an ELF symbol entry for a symbol with the given properties. * * @param name name of the section in which symName is referenced * @param type type of symName * @param bind binding of symName * @param secHdrIndex section header index of the section in which symName is referenced * (st_shndx of ELF symbol entry) * @param size symName size (st_size of ELF symbol entry) * @param value symName value (st_value of ELF symbol entry) * @param isLocal true if symbol is local. */
public ELFSymbol createELFSymbolEntry(String name, int type, int bind, int secHdrIndex, int size, int value, boolean isLocal) { // Get the current symbol index and append symbol name to string table. int index; if (name.isEmpty()) { index = 0; } else { // NOTE: The +1 comes from the null symbol! // We can't trust strTabContent.length() since that is chars (UTF16), keep track of // bytes on our own. index = strTabNrOfBytes + 1; strTabContent.append(name).append('\0'); strTabNrOfBytes += name.getBytes(StandardCharsets.UTF_8).length + 1; } // Create ELF symbol entry long address = JNILibELFAPI.create_sym_entry(elfClass, index, type, bind, secHdrIndex, size, value); if (address == 0) { throw new InternalError("create_sym_entry failed"); } Pointer ptr = new Pointer(address); if (isLocal) { final int localIndex = localSymbolIndex.size(); ELFSymbol symbol = new ELFSymbol(name, localIndex, ptr, isLocal); localSymbolIndex.add(symbol); return symbol; } else { final int globalIndex = globalSymbolIndex.size(); ELFSymbol symbol = new ELFSymbol(name, globalIndex, ptr, isLocal); globalSymbolIndex.add(symbol); return symbol; } }
Create an ELF relocation entry for given symbol name to section secname.
Params:
  • container – the section
  • offset – offset into the section contents at which the relocation needs to be applied
  • type – ELF type of the relocation entry
  • addend – Addend for for relocation of type reloca
/** * Create an ELF relocation entry for given symbol {@code name} to section {@code secname}. * * @param container the section * @param offset offset into the section contents at which the relocation needs to be applied * @param type ELF type of the relocation entry * @param addend Addend for for relocation of type reloca */
public void createELFRelocationEntry(ELFContainer container, int offset, int type, int addend, ELFSymbol elfSymbol) { // Get the index of the symbol. int index; if (elfSymbol.isLocal()) { index = elfSymbol.getIndex(); } else { /* * For global symbol entries the index will be offset by the number of local symbols * which will be listed first in the symbol table. */ index = elfSymbol.getIndex() + localSymbolIndex.size(); } long address = JNILibELFAPI.create_reloc_entry(elfClass, offset, index, type, addend, createReloca); if (address == 0) { throw new InternalError("create_reloc_entry failed"); } Pointer ptr = new Pointer(address); /* * If section name associated with this symbol is set to undefined i.e., secname is null, * symIndex is undef i.e., 0. */ if (relocTables.get(container) == null) { // Allocate a new table and add it to the hash table of reloc tables relocTables.put(container, new ArrayList<>()); } // Add the entry relocTables.get(container).add(ptr); }
Invokes native libelf function loff_t elf_update (Elf *elfPtr, Elf_Cmd cmd).
Params:
  • cmd – command
Returns:return value of the native function called
/** * Invokes native libelf function loff_t elf_update (Elf *elfPtr, Elf_Cmd cmd). * * @param cmd command * @return return value of the native function called */
public boolean elfUpdate(LibELF.Elf_Cmd cmd) { JNILibELFAPI.elf_update(elfPtr, cmd.intValue()); // TODO: Error checking; look at the return value of elf_update // and call elf_errmsg appropriately. return true; }
Wrapper function that invokes int elf_end (Elf *elfPtr). and closes ELF output file descriptor
Returns:true
/** * Wrapper function that invokes int elf_end (Elf *elfPtr). and closes ELF output file * descriptor * * @return true */
public boolean elfEnd() { // Finish ELF processing JNILibELFAPI.elf_end(elfPtr); // Close file descriptor JNILibELFAPI.close(outFileDesc); return true; } }