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
24package jdk.tools.jaotc.binformat.pecoff;
25
26import java.io.IOException;
27import java.util.ArrayList;
28import java.util.Collection;
29import java.util.List;
30import java.util.Map;
31
32import jdk.tools.jaotc.binformat.BinaryContainer;
33import jdk.tools.jaotc.binformat.ByteContainer;
34import jdk.tools.jaotc.binformat.CodeContainer;
35import jdk.tools.jaotc.binformat.ReadOnlyDataContainer;
36import jdk.tools.jaotc.binformat.Relocation;
37import jdk.tools.jaotc.binformat.Relocation.RelocType;
38import jdk.tools.jaotc.binformat.Symbol;
39import jdk.tools.jaotc.binformat.Symbol.Binding;
40import jdk.tools.jaotc.binformat.Symbol.Kind;
41
42import jdk.tools.jaotc.binformat.pecoff.PECoffSymbol;
43import jdk.tools.jaotc.binformat.pecoff.PECoffTargetInfo;
44import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_FILE_HEADER;
45import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SECTION_HEADER;
46import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SYMBOL;
47import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_RELOCATION;
48
49public class JPECoffRelocObject {
50
51    private final BinaryContainer binContainer;
52
53    private final PECoffContainer pecoffContainer;
54
55    private final int sectionAlignment;
56
57    public JPECoffRelocObject(BinaryContainer binContainer, String outputFileName) {
58        this.binContainer = binContainer;
59        this.pecoffContainer = new PECoffContainer(outputFileName);
60        this.sectionAlignment = binContainer.getCodeSegmentSize();
61    }
62
63    private static PECoffSection createByteSection(ArrayList<PECoffSection> sections, String sectName, byte[] scnData,
64                    boolean hasRelocs, int scnFlags, int sectAlign) {
65
66        PECoffSection sect = new PECoffSection(sectName, scnData, scnFlags, sectAlign, hasRelocs, sections.size());
67        // Add this section to our list
68        sections.add(sect);
69
70        return (sect);
71    }
72
73    private static void createByteSection(ArrayList<PECoffSection> sections, ByteContainer c, int scnFlags, int sectAlign) {
74        PECoffSection sect;
75        boolean hasRelocs = c.hasRelocations();
76        byte[] scnData = c.getByteArray();
77
78        sect = createByteSection(sections, c.getContainerName(), scnData, hasRelocs, scnFlags, sectAlign);
79
80        c.setSectionId(sect.getSectionId());
81    }
82
83    private void createCodeSection(ArrayList<PECoffSection> sections, CodeContainer c) {
84        int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_EXECUTE | IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_CODE;
85        createByteSection(sections, c, scnFlags, sectionAlignment);
86    }
87
88    private void createReadOnlySection(ArrayList<PECoffSection> sections, ReadOnlyDataContainer c) {
89        int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA;
90        createByteSection(sections, c, scnFlags, sectionAlignment);
91    }
92
93    private void createReadWriteSection(ArrayList<PECoffSection> sections, ByteContainer c) {
94        int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_WRITE;
95
96        if (c.getByteArray().length > 0) {
97            scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA;
98        } else {
99            scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_UNINITIALIZED_DATA;
100        }
101        createByteSection(sections, c, scnFlags, sectionAlignment);
102    }
103
104    /**
105     * Create an PECoff relocatable object
106     *
107     * @param relocationTable
108     * @param symbols
109     * @throws IOException throws {@code IOException} as a result of file system access failures.
110     */
111    public void createPECoffRelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException {
112        ArrayList<PECoffSection> sections = new ArrayList<>();
113
114        // Create text section
115        createCodeSection(sections, binContainer.getCodeContainer());
116        createReadOnlySection(sections, binContainer.getMetaspaceNamesContainer());
117        createReadOnlySection(sections, binContainer.getKlassesOffsetsContainer());
118        createReadOnlySection(sections, binContainer.getMethodsOffsetsContainer());
119        createReadOnlySection(sections, binContainer.getKlassesDependenciesContainer());
120        createReadOnlySection(sections, binContainer.getMethodMetadataContainer());
121        createReadOnlySection(sections, binContainer.getStubsOffsetsContainer());
122        createReadOnlySection(sections, binContainer.getHeaderContainer().getContainer());
123        createReadOnlySection(sections, binContainer.getCodeSegmentsContainer());
124        createReadOnlySection(sections, binContainer.getConstantDataContainer());
125        createReadOnlySection(sections, binContainer.getConfigContainer());
126        createReadWriteSection(sections, binContainer.getKlassesGotContainer());
127        createReadWriteSection(sections, binContainer.getCountersGotContainer());
128        createReadWriteSection(sections, binContainer.getMetadataGotContainer());
129        createReadWriteSection(sections, binContainer.getMethodStateContainer());
130        createReadWriteSection(sections, binContainer.getOopGotContainer());
131        createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer());
132
133        // Allocate PECoff Header
134        PECoffHeader header = new PECoffHeader();
135
136        // Get PECoff symbol data from BinaryContainer object's symbol tables
137        PECoffSymtab symtab = createPECoffSymbolTables(symbols);
138
139        // Add Linker Directives Section
140        int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_INFO | IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_REMOVE;
141        createByteSection(sections, ".drectve", symtab.getDirectiveArray(), false, scnFlags, 1 /* 1 byte alignment */);
142
143        // Create the Relocation Tables
144        PECoffRelocTable pecoffRelocs = createPECoffRelocTable(sections, relocationTable);
145
146        // File Output Order
147        //
148        // HEADER (Need address of Symbol Table + symbol count)
149        // SECTIONS (Need pointer to Section Data, Relocation Table)
150        // DIRECTIVES
151        // SYMBOL TABLE
152        // SYMBOLS
153        // SECTION DATA
154        // RELOCATION TABLE
155
156        // Calculate Offset for Symbol table
157        int file_offset = IMAGE_FILE_HEADER.totalsize +
158                        (IMAGE_SECTION_HEADER.totalsize * sections.size());
159
160        // Update Header fields
161        header.setSectionCount(sections.size());
162        header.setSymbolCount(symtab.getSymtabCount());
163        header.setSymbolOff(file_offset);
164
165        // Calculate file offset for first section
166        file_offset += ((symtab.getSymtabCount() * IMAGE_SYMBOL.totalsize) +
167                        symtab.getStrtabSize());
168        // And round it up
169        file_offset = (file_offset + (sections.get(0).getDataAlign() - 1)) &
170                        ~((sections.get(0).getDataAlign() - 1));
171
172        // Calc file offsets for section data
173        for (int i = 0; i < sections.size(); i++) {
174            PECoffSection sect = sections.get(i);
175            file_offset = (file_offset + (sect.getDataAlign() - 1)) &
176                            ~((sect.getDataAlign() - 1));
177            sect.setOffset(file_offset);
178            file_offset += sect.getSize();
179        }
180
181        // Update relocation sizing information in each section
182        for (int i = 0; i < sections.size(); i++) {
183            PECoffSection sect = sections.get(i);
184            if (sect.hasRelocations()) {
185                int nreloc = pecoffRelocs.getNumRelocs(i);
186                sect.setReloff(file_offset);
187                sect.setRelcount(nreloc);
188                // extended relocations add an addition entry
189                if (nreloc > 0xFFFF) {
190                    nreloc++;
191                }
192                file_offset += (nreloc * IMAGE_RELOCATION.totalsize);
193            }
194        }
195
196        // Write out the Header
197        pecoffContainer.writeBytes(header.getArray());
198
199        // Write out the section table
200        for (int i = 0; i < sections.size(); i++) {
201            PECoffSection sect = sections.get(i);
202            pecoffContainer.writeBytes(sect.getArray(), PECoffSection.getShdrAlign());
203        }
204
205        // Write out the symbol table and string table
206        pecoffContainer.writeBytes(symtab.getSymtabArray(), 4);
207        pecoffContainer.writeBytes(symtab.getStrtabArray(), 1);
208
209        // Write out each section contents
210        for (int i = 0; i < sections.size(); i++) {
211            PECoffSection sect = sections.get(i);
212            pecoffContainer.writeBytes(sect.getDataArray(), sect.getDataAlign());
213        }
214
215        // Write out Relocation Tables
216        for (int i = 0; i < sections.size(); i++) {
217            if (pecoffRelocs.getNumRelocs(i) > 0) {
218                pecoffContainer.writeBytes(pecoffRelocs.getRelocData(i));
219            }
220        }
221        pecoffContainer.close();
222    }
223
224    /**
225     * Construct PECoff symbol data from BinaryContainer object's symbol tables. Both dynamic PECoff
226     * symbol table and PECoff symbol table are created from BinaryContainer's symbol info.
227     *
228     * @param symbols
229     */
230    private static PECoffSymtab createPECoffSymbolTables(Collection<Symbol> symbols) {
231        PECoffSymtab symtab = new PECoffSymtab();
232
233        // First, create the initial null symbol. This is a local symbol.
234        // symtab.addSymbolEntry("", (byte)0, (byte)0, (byte)0, 0, 0);
235
236        // Now create PECoff symbol entries for all symbols.
237        for (Symbol symbol : symbols) {
238            // Get the index of section this symbol is defined in.
239            int secHdrIndex = symbol.getSection().getSectionId();
240            PECoffSymbol pecoffSymbol = symtab.addSymbolEntry(symbol.getName(), getPECoffTypeOf(symbol), getPECoffClassOf(symbol), (byte) secHdrIndex, symbol.getOffset());
241            symbol.setNativeSymbol(pecoffSymbol);
242        }
243        return (symtab);
244    }
245
246    private static byte getPECoffTypeOf(Symbol sym) {
247        Kind kind = sym.getKind();
248        if (kind == Symbol.Kind.NATIVE_FUNCTION || kind == Symbol.Kind.JAVA_FUNCTION) {
249            return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_FUNCTION;
250        }
251        return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_NONE;
252    }
253
254    private static byte getPECoffClassOf(Symbol sym) {
255        Binding binding = sym.getBinding();
256        if (binding == Symbol.Binding.GLOBAL) {
257            return IMAGE_SYMBOL.IMAGE_SYM_CLASS_EXTERNAL;
258        }
259        return IMAGE_SYMBOL.IMAGE_SYM_CLASS_STATIC;
260    }
261
262    /**
263     * Construct a PECoff relocation table from BinaryContainer object's relocation tables.
264     *
265     * @param sections
266     * @param relocationTable
267     */
268    private PECoffRelocTable createPECoffRelocTable(ArrayList<PECoffSection> sections, Map<Symbol, List<Relocation>> relocationTable) {
269
270        PECoffRelocTable pecoffRelocTable = new PECoffRelocTable(sections.size());
271        /*
272         * For each of the symbols with associated relocation records, create a PECoff relocation entry.
273         */
274        for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) {
275            List<Relocation> relocs = entry.getValue();
276            Symbol symbol = entry.getKey();
277
278            for (Relocation reloc : relocs) {
279                createRelocation(symbol, reloc, pecoffRelocTable);
280            }
281        }
282
283        for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) {
284            createRelocation(entry.getKey(), entry.getValue(), pecoffRelocTable);
285        }
286
287        return (pecoffRelocTable);
288    }
289
290    private static void createRelocation(Symbol symbol, Relocation reloc, PECoffRelocTable pecoffRelocTable) {
291        RelocType relocType = reloc.getType();
292
293        int pecoffRelocType = getPECoffRelocationType(relocType);
294        PECoffSymbol sym = (PECoffSymbol) symbol.getNativeSymbol();
295        int symno = sym.getIndex();
296        int sectindex = reloc.getSection().getSectionId();
297        int offset = reloc.getOffset();
298        int addend = 0;
299
300        switch (relocType) {
301            case JAVA_CALL_DIRECT:
302            case STUB_CALL_DIRECT:
303            case FOREIGN_CALL_INDIRECT_GOT: {
304                // Create relocation entry
305                addend = -4; // Size in bytes of the patch location
306                // Relocation should be applied at the location after call operand
307                offset = offset + reloc.getSize() + addend;
308                break;
309            }
310            case JAVA_CALL_INDIRECT: {
311                // Do nothing.
312                return;
313            }
314            case METASPACE_GOT_REFERENCE:
315            case EXTERNAL_PLT_TO_GOT: {
316                addend = -4; // Size of 32-bit address of the GOT
317                /*
318                 * Relocation should be applied before the test instruction to the move instruction.
319                 * reloc.getOffset() points to the test instruction after the instruction that loads the address of
320                 * polling page. So set the offset appropriately.
321                 */
322                offset = offset + addend;
323                break;
324            }
325            case EXTERNAL_GOT_TO_PLT: {
326                // this is load time relocations
327                break;
328            }
329            default:
330                throw new InternalError("Unhandled relocation type: " + relocType);
331        }
332        pecoffRelocTable.createRelocationEntry(sectindex, offset, symno, pecoffRelocType);
333    }
334
335    // Return IMAGE_RELOCATION Type based on relocType
336    private static int getPECoffRelocationType(RelocType relocType) {
337        int pecoffRelocType = 0; // R_<ARCH>_NONE if #define'd to 0 for all values of ARCH
338        switch (PECoffTargetInfo.getPECoffArch()) {
339            case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
340                if (relocType == RelocType.JAVA_CALL_DIRECT ||
341                    relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) {
342                    pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
343                } else if (relocType == RelocType.STUB_CALL_DIRECT) {
344                    pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
345                } else if (relocType == RelocType.JAVA_CALL_INDIRECT) {
346                    pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ABSOLUTE;
347                } else if (relocType == RelocType.METASPACE_GOT_REFERENCE ||
348                           relocType == RelocType.EXTERNAL_PLT_TO_GOT) {
349                    pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
350                } else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT) {
351                    pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ADDR64;
352                } else {
353                    assert false : "Unhandled relocation type: " + relocType;
354                }
355                break;
356            default:
357                System.out.println("Relocation Type mapping: Unhandled architecture");
358        }
359        return pecoffRelocType;
360    }
361}
362