1 /* 2 * Copyright (c) 2016, 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.binformat.elf; 25 26 import java.io.IOException; 27 import java.util.ArrayList; 28 import java.util.Collection; 29 import java.util.List; 30 import java.util.Map; 31 32 import jdk.tools.jaotc.binformat.BinaryContainer; 33 import jdk.tools.jaotc.binformat.ByteContainer; 34 import jdk.tools.jaotc.binformat.CodeContainer; 35 import jdk.tools.jaotc.binformat.ReadOnlyDataContainer; 36 import jdk.tools.jaotc.binformat.Relocation; 37 import jdk.tools.jaotc.binformat.Relocation.RelocType; 38 import jdk.tools.jaotc.binformat.Symbol; 39 import jdk.tools.jaotc.binformat.Symbol.Binding; 40 import jdk.tools.jaotc.binformat.Symbol.Kind; 41 42 import jdk.tools.jaotc.binformat.elf.ElfSymbol; 43 import jdk.tools.jaotc.binformat.elf.ElfTargetInfo; 44 import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Ehdr; 45 import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Shdr; 46 import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Sym; 47 import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Rela; 48 49 public class JELFRelocObject { 50 51 private final BinaryContainer binContainer; 52 53 private final ElfContainer elfContainer; 54 55 private final int segmentSize; 56 57 public JELFRelocObject(BinaryContainer binContainer, String outputFileName) { 58 this.binContainer = binContainer; 59 this.elfContainer = new ElfContainer(outputFileName); 60 this.segmentSize = binContainer.getCodeSegmentSize(); 61 } 62 63 private static ElfSection createByteSection(ArrayList<ElfSection> sections, 64 String sectName, 65 byte[] scnData, 66 boolean hasRelocs, 67 int align, 68 int scnFlags, 69 int scnType) { 70 71 ElfSection sect = new ElfSection(sectName, scnData, scnFlags, scnType, 72 hasRelocs, align, sections.size()); 73 // Add this section to our list 74 sections.add(sect); 75 76 return (sect); 77 } 78 79 private void createByteSection(ArrayList<ElfSection> sections, 80 ByteContainer c, int scnFlags) { 81 ElfSection sect; 82 boolean hasRelocs = c.hasRelocations(); 83 byte[] scnData = c.getByteArray(); 84 85 int scnType = Elf64_Shdr.SHT_PROGBITS; 86 boolean zeros = !hasRelocs; 87 if (zeros) { 88 for (byte b : scnData) { 89 if (b != 0) { 90 zeros = false; 91 break; 92 } 93 } 94 if (zeros) { 95 scnType = Elf64_Shdr.SHT_NOBITS; 96 } 97 } 98 99 sect = createByteSection(sections, c.getContainerName(), 100 scnData, hasRelocs, segmentSize, 101 scnFlags, scnType); 102 c.setSectionId(sect.getSectionId()); 103 } 104 105 private void createCodeSection(ArrayList<ElfSection> sections, CodeContainer c) { 106 createByteSection(sections, c, Elf64_Shdr.SHF_ALLOC | Elf64_Shdr.SHF_EXECINSTR); 107 } 108 109 private void createReadOnlySection(ArrayList<ElfSection> sections, ReadOnlyDataContainer c) { 110 createByteSection(sections, c, Elf64_Shdr.SHF_ALLOC); 111 } 112 113 private void createReadWriteSection(ArrayList<ElfSection> sections, ByteContainer c) { 114 createByteSection(sections, c, Elf64_Shdr.SHF_ALLOC | Elf64_Shdr.SHF_WRITE); 115 } 116 117 /** 118 * Create an ELF relocatable object 119 * 120 * @param relocationTable 121 * @param symbols 122 * @throws IOException throws {@code IOException} as a result of file system access failures. 123 */ 124 public void createELFRelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException { 125 // Allocate ELF Header 126 ElfHeader eh = new ElfHeader(); 127 128 ArrayList<ElfSection> sections = new ArrayList<>(); 129 130 // Create the null section 131 createByteSection(sections, null, null, false, 1, 0, 0); 132 133 // Create text section 134 createCodeSection(sections, binContainer.getCodeContainer()); 135 createReadOnlySection(sections, binContainer.getMetaspaceNamesContainer()); 136 createReadOnlySection(sections, binContainer.getKlassesOffsetsContainer()); 137 createReadOnlySection(sections, binContainer.getMethodsOffsetsContainer()); 138 createReadOnlySection(sections, binContainer.getKlassesDependenciesContainer()); 139 createReadOnlySection(sections, binContainer.getMethodMetadataContainer()); 140 createReadOnlySection(sections, binContainer.getStubsOffsetsContainer()); 141 createReadOnlySection(sections, binContainer.getHeaderContainer().getContainer()); 142 createReadOnlySection(sections, binContainer.getCodeSegmentsContainer()); 143 createReadOnlySection(sections, binContainer.getConstantDataContainer()); 144 createReadOnlySection(sections, binContainer.getConfigContainer()); 145 createReadWriteSection(sections, binContainer.getKlassesGotContainer()); 146 createReadWriteSection(sections, binContainer.getCountersGotContainer()); 147 createReadWriteSection(sections, binContainer.getMetadataGotContainer()); 148 createReadWriteSection(sections, binContainer.getOopGotContainer()); 149 createReadWriteSection(sections, binContainer.getMethodStateContainer()); 150 createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer()); 151 152 // Get ELF symbol data from BinaryContainer object's symbol tables 153 ElfSymtab symtab = createELFSymbolTables(symbols); 154 155 // Create string table section and symbol table sections in 156 // that order since symtab section needs to set the index of 157 // strtab in sh_link field 158 ElfSection strTabSection = createByteSection(sections, ".strtab", 159 symtab.getStrtabArray(), 160 false, 1, 0, 161 Elf64_Shdr.SHT_STRTAB); 162 163 // Now create .symtab section with the symtab data constructed. 164 // On Linux, sh_link of symtab contains the index of string table 165 // its symbols reference and sh_info contains the index of first 166 // non-local symbol 167 ElfSection symTabSection = createByteSection(sections, ".symtab", 168 symtab.getSymtabArray(), 169 false, 8, 0, 170 Elf64_Shdr.SHT_SYMTAB); 171 symTabSection.setLink(strTabSection.getSectionId()); 172 symTabSection.setInfo(symtab.getNumLocalSyms()); 173 174 ElfRelocTable elfRelocTable = createElfRelocTable(sections, relocationTable); 175 176 createElfRelocSections(sections, elfRelocTable, symTabSection.getSectionId()); 177 178 // Now, finally, after creating all sections, create shstrtab section 179 ElfSection shStrTabSection = createByteSection(sections, ".shstrtab", 180 null, false, 1, 0, 181 Elf64_Shdr.SHT_STRTAB); 182 eh.setSectionStrNdx(shStrTabSection.getSectionId()); 183 184 // Update all section offsets and the Elf header section offset 185 // Write the Header followed by the contents of each section 186 // and then the section structures (section table). 187 int file_offset = Elf64_Ehdr.totalsize; 188 189 // and round it up 190 file_offset = (file_offset + (sections.get(1).getDataAlign() - 1)) & 191 ~((sections.get(1).getDataAlign() - 1)); 192 193 // Calc file offsets for section data skipping null section 194 for (int i = 1; i < sections.size(); i++) { 195 ElfSection sect = sections.get(i); 196 file_offset = (file_offset + (sect.getDataAlign() - 1)) & 197 ~((sect.getDataAlign() - 1)); 198 sect.setOffset(file_offset); 199 file_offset += sect.getSize(); 200 } 201 202 // Align the section table 203 file_offset = (file_offset + (ElfSection.getShdrAlign() - 1)) & 204 ~((ElfSection.getShdrAlign() - 1)); 205 206 // Update the Elf Header with the offset of the first Elf64_Shdr 207 // and the number of sections. 208 eh.setSectionOff(file_offset); 209 eh.setSectionNum(sections.size()); 210 211 // Write out the Header 212 elfContainer.writeBytes(eh.getArray()); 213 214 // Write out each section contents skipping null section 215 for (int i = 1; i < sections.size(); i++) { 216 ElfSection sect = sections.get(i); 217 elfContainer.writeBytes(sect.getDataArray(), sect.getDataAlign()); 218 } 219 220 // Write out the section table 221 for (int i = 0; i < sections.size(); i++) { 222 ElfSection sect = sections.get(i); 223 elfContainer.writeBytes(sect.getArray(), ElfSection.getShdrAlign()); 224 } 225 226 elfContainer.close(); 227 } 228 229 /** 230 * Construct ELF symbol data from BinaryContainer object's symbol tables. Both dynamic ELF symbol 231 * table and ELF symbol table are created from BinaryContainer's symbol info. 232 * 233 * @param symbols 234 */ 235 private static ElfSymtab createELFSymbolTables(Collection<Symbol> symbols) { 236 ElfSymtab symtab = new ElfSymtab(); 237 238 // First, create the initial null symbol. This is a local symbol. 239 symtab.addSymbolEntry("", (byte) 0, (byte) 0, Elf64_Shdr.SHN_UNDEF, 0, 0); 240 241 // Now create ELF symbol entries for all symbols. 242 for (Symbol symbol : symbols) { 243 // Get the index of section this symbol is defined in. 244 int secHdrIndex = symbol.getSection().getSectionId(); 245 ElfSymbol elfSymbol = symtab.addSymbolEntry(symbol.getName(), getELFTypeOf(symbol), getELFBindOf(symbol), (byte) secHdrIndex, symbol.getOffset(), symbol.getSize()); 246 symbol.setNativeSymbol(elfSymbol); 247 } 248 return (symtab); 249 } 250 251 private static byte getELFTypeOf(Symbol sym) { 252 Kind kind = sym.getKind(); 253 if (kind == Symbol.Kind.NATIVE_FUNCTION || kind == Symbol.Kind.JAVA_FUNCTION) { 254 return Elf64_Sym.STT_FUNC; 255 } else if (kind == Symbol.Kind.OBJECT) { 256 return Elf64_Sym.STT_OBJECT; 257 } 258 return Elf64_Sym.STT_NOTYPE; 259 } 260 261 private static byte getELFBindOf(Symbol sym) { 262 Binding binding = sym.getBinding(); 263 if (binding == Symbol.Binding.GLOBAL) { 264 return Elf64_Sym.STB_GLOBAL; 265 } 266 return Elf64_Sym.STB_LOCAL; 267 } 268 269 /** 270 * Construct a Elf relocation table from BinaryContainer object's relocation tables. 271 * 272 * @param sections 273 * @param relocationTable 274 */ 275 private ElfRelocTable createElfRelocTable(ArrayList<ElfSection> sections, 276 Map<Symbol, List<Relocation>> relocationTable) { 277 278 ElfRelocTable elfRelocTable = new ElfRelocTable(sections.size()); 279 /* 280 * For each of the symbols with associated relocation records, create a Elf relocation entry. 281 */ 282 for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) { 283 List<Relocation> relocs = entry.getValue(); 284 Symbol symbol = entry.getKey(); 285 286 for (Relocation reloc : relocs) { 287 createRelocation(symbol, reloc, elfRelocTable); 288 } 289 } 290 291 for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) { 292 createRelocation(entry.getKey(), entry.getValue(), elfRelocTable); 293 } 294 295 return (elfRelocTable); 296 } 297 298 private static void createRelocation(Symbol symbol, Relocation reloc, ElfRelocTable elfRelocTable) { 299 RelocType relocType = reloc.getType(); 300 301 int elfRelocType = getELFRelocationType(relocType); 302 ElfSymbol sym = (ElfSymbol) symbol.getNativeSymbol(); 303 int symno = sym.getIndex(); 304 int sectindex = reloc.getSection().getSectionId(); 305 int offset = reloc.getOffset(); 306 int addend = 0; 307 308 switch (relocType) { 309 case JAVA_CALL_DIRECT: 310 case STUB_CALL_DIRECT: 311 case FOREIGN_CALL_INDIRECT_GOT: { 312 // Create relocation entry 313 addend = -4; // Size in bytes of the patch location 314 // Relocation should be applied at the location after call operand 315 offset = offset + reloc.getSize() + addend; 316 break; 317 } 318 case JAVA_CALL_INDIRECT: 319 case METASPACE_GOT_REFERENCE: 320 case EXTERNAL_PLT_TO_GOT: { 321 addend = -4; // Size of 32-bit address of the GOT 322 /* 323 * Relocation should be applied before the test instruction to the move instruction. 324 * reloc.getOffset() points to the test instruction after the instruction that loads the address of 325 * polling page. So set the offset appropriately. 326 */ 327 offset = offset + addend; 328 break; 329 } 330 case EXTERNAL_GOT_TO_PLT: { 331 // this is load time relocations 332 break; 333 } 334 default: 335 throw new InternalError("Unhandled relocation type: " + relocType); 336 } 337 elfRelocTable.createRelocationEntry(sectindex, offset, symno, elfRelocType, addend); 338 } 339 340 private static int getELFRelocationType(RelocType relocType) { 341 int elfRelocType = 0; // R_<ARCH>_NONE if #define'd to 0 for all values of ARCH 342 switch (ElfTargetInfo.getElfArch()) { 343 case Elf64_Ehdr.EM_X86_64: 344 // Return R_X86_64_* entries based on relocType 345 if (relocType == RelocType.JAVA_CALL_DIRECT || 346 relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) { 347 elfRelocType = Elf64_Rela.R_X86_64_PLT32; 348 } else if (relocType == RelocType.STUB_CALL_DIRECT) { 349 elfRelocType = Elf64_Rela.R_X86_64_PC32; 350 } else if (relocType == RelocType.JAVA_CALL_INDIRECT) { 351 elfRelocType = Elf64_Rela.R_X86_64_NONE; 352 } else if (relocType == RelocType.METASPACE_GOT_REFERENCE || 353 relocType == RelocType.EXTERNAL_PLT_TO_GOT) { 354 elfRelocType = Elf64_Rela.R_X86_64_PC32; 355 } else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT) { 356 elfRelocType = Elf64_Rela.R_X86_64_64; 357 } else { 358 assert false : "Unhandled relocation type: " + relocType; 359 } 360 break; 361 default: 362 System.out.println("Relocation Type mapping: Unhandled architecture"); 363 } 364 return elfRelocType; 365 } 366 367 private static void createElfRelocSections(ArrayList<ElfSection> sections, 368 ElfRelocTable elfRelocTable, 369 int symtabsectidx) { 370 371 // Grab count before we create new sections 372 int count = sections.size(); 373 374 for (int i = 0; i < count; i++) { 375 if (elfRelocTable.getNumRelocs(i) > 0) { 376 ElfSection sect = sections.get(i); 377 String relname = ".rela" + sect.getName(); 378 ElfSection relocSection = createByteSection(sections, relname, 379 elfRelocTable.getRelocData(i), 380 false, 8, 0, Elf64_Shdr.SHT_RELA); 381 relocSection.setLink(symtabsectidx); 382 relocSection.setInfo(sect.getSectionId()); 383 } 384 } 385 } 386 }