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.macho;
25
26import java.nio.ByteBuffer;
27import java.util.ArrayList;
28
29import jdk.tools.jaotc.binformat.macho.MachO.symtab_command;
30import jdk.tools.jaotc.binformat.macho.MachO.nlist_64;
31import jdk.tools.jaotc.binformat.macho.MachOSymbol;
32import jdk.tools.jaotc.binformat.macho.MachOByteBuffer;
33
34final class MachOSymtab {
35
36    /**
37     * ByteBuffer holding the LC_SYMTAB command contents
38     */
39    private final ByteBuffer symtabCmd;
40
41    private int symtabDataSize;
42
43    private final ArrayList<MachOSymbol> localSymbols = new ArrayList<>();
44    private final ArrayList<MachOSymbol> globalSymbols = new ArrayList<>();
45    private final ArrayList<MachOSymbol> undefSymbols = new ArrayList<>();
46
47    /**
48     * number of symbols added
49     */
50    private int symbolCount;
51
52    /**
53     * String holding symbol table strings
54     */
55    private final StringBuilder strTabContent = new StringBuilder();
56
57    /**
58     * Keeps track of bytes in string table since strTabContent.length() is number of chars, not bytes.
59     */
60    private int strTabNrOfBytes = 0;
61
62    MachOSymtab() {
63        symtabCmd = MachOByteBuffer.allocate(symtab_command.totalsize);
64
65        symtabCmd.putInt(symtab_command.cmd.off, symtab_command.LC_SYMTAB);
66        symtabCmd.putInt(symtab_command.cmdsize.off, symtab_command.totalsize);
67
68        symbolCount = 0;
69
70    }
71
72    static int getAlign() {
73        return (4);
74    }
75
76    MachOSymbol addSymbolEntry(String name, byte type, byte secHdrIndex, long offset) {
77        // Get the current symbol index and append symbol name to string table.
78        int index;
79        MachOSymbol sym;
80
81        if (name.isEmpty()) {
82            index = 0;
83            strTabContent.append('\0');
84            strTabNrOfBytes += 1;
85            sym = new MachOSymbol(symbolCount, index, type, secHdrIndex, offset);
86            localSymbols.add(sym);
87        } else {
88            // We can't trust strTabContent.length() since that is
89            // chars (UTF16), keep track of bytes on our own.
90            index = strTabNrOfBytes;
91            strTabContent.append("_").append(name).append('\0');
92            // + 1 for null, + 1 for "_"
93            strTabNrOfBytes += (name.getBytes().length + 1 + 1);
94
95            sym = new MachOSymbol(symbolCount, index, type, secHdrIndex, offset);
96            switch (type) {
97                case nlist_64.N_EXT:
98                    undefSymbols.add(sym);
99                    break;
100                case nlist_64.N_SECT:
101                case nlist_64.N_UNDF:  // null symbol
102                    localSymbols.add(sym);
103                    break;
104                case nlist_64.N_SECT | nlist_64.N_EXT:
105                    globalSymbols.add(sym);
106                    break;
107                default:
108                    System.out.println("Unsupported Symbol type " + type);
109                    break;
110            }
111        }
112        symbolCount++;
113        return (sym);
114    }
115
116    void setOffset(int symoff) {
117        symtabCmd.putInt(symtab_command.symoff.off, symoff);
118    }
119
120    // Update the symbol indexes once all symbols have been added.
121    // This is required since we'll be reordering the symbols in the
122    // file to be in the order of Local, global and Undefined.
123    void updateIndexes() {
124        int index = 0;
125
126        // Update the local symbol indexes
127        for (int i = 0; i < localSymbols.size(); i++) {
128            MachOSymbol sym = localSymbols.get(i);
129            sym.setIndex(index++);
130        }
131
132        // Update the global symbol indexes
133        for (int i = 0; i < globalSymbols.size(); i++) {
134            MachOSymbol sym = globalSymbols.get(i);
135            sym.setIndex(index++);
136        }
137
138        // Update the undefined symbol indexes
139        for (int i = index; i < undefSymbols.size(); i++) {
140            MachOSymbol sym = undefSymbols.get(i);
141            sym.setIndex(index++);
142        }
143    }
144
145    // Update LC_SYMTAB command fields based on the number of symbols added
146    // return the file size taken up by symbol table entries and strings
147    int calcSizes() {
148        int stroff;
149
150        stroff = symtabCmd.getInt(symtab_command.symoff.off) + (nlist_64.totalsize * symbolCount);
151        symtabCmd.putInt(symtab_command.nsyms.off, symbolCount);
152        symtabCmd.putInt(symtab_command.stroff.off, stroff);
153        symtabCmd.putInt(symtab_command.strsize.off, strTabNrOfBytes);
154        symtabDataSize = (nlist_64.totalsize * symbolCount) + strTabNrOfBytes;
155
156        return (symtabDataSize);
157    }
158
159    int getNumLocalSyms() {
160        return localSymbols.size();
161    }
162
163    int getNumGlobalSyms() {
164        return globalSymbols.size();
165    }
166
167    int getNumUndefSyms() {
168        return undefSymbols.size();
169    }
170
171    byte[] getCmdArray() {
172        return symtabCmd.array();
173    }
174
175    // Create a single byte array that contains the symbol table entries
176    // and string table
177    byte[] getDataArray() {
178        ByteBuffer symtabData = MachOByteBuffer.allocate(symtabDataSize);
179        byte[] retarray;
180
181        // Add the local symbols
182        for (int i = 0; i < localSymbols.size(); i++) {
183            MachOSymbol sym = localSymbols.get(i);
184            byte[] arr = sym.getArray();
185            symtabData.put(arr);
186        }
187        // Add the global symbols
188        for (int i = 0; i < globalSymbols.size(); i++) {
189            MachOSymbol sym = globalSymbols.get(i);
190            byte[] arr = sym.getArray();
191            symtabData.put(arr);
192        }
193        // Add the undefined symbols
194        for (int i = 0; i < undefSymbols.size(); i++) {
195            MachOSymbol sym = undefSymbols.get(i);
196            byte[] arr = sym.getArray();
197            symtabData.put(arr);
198        }
199
200        // Add the stringtable
201        byte[] strs = strTabContent.toString().getBytes();
202        symtabData.put(strs);
203
204        retarray = symtabData.array();
205
206        return (retarray);
207    }
208}
209