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 
24 package jdk.tools.jaotc.binformat.macho;
25 
26 import java.nio.ByteBuffer;
27 import java.util.ArrayList;
28 
29 import jdk.tools.jaotc.binformat.macho.MachO.symtab_command;
30 import jdk.tools.jaotc.binformat.macho.MachO.nlist_64;
31 import jdk.tools.jaotc.binformat.macho.MachOSymbol;
32 import jdk.tools.jaotc.binformat.macho.MachOByteBuffer;
33 
34 final 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 
MachOSymtab()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 
getAlign()72     static int getAlign() {
73         return (4);
74     }
75 
addSymbolEntry(String name, byte type, byte secHdrIndex, long offset)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 
setOffset(int symoff)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.
updateIndexes()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
calcSizes()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 
getNumLocalSyms()159     int getNumLocalSyms() {
160         return localSymbols.size();
161     }
162 
getNumGlobalSyms()163     int getNumGlobalSyms() {
164         return globalSymbols.size();
165     }
166 
getNumUndefSyms()167     int getNumUndefSyms() {
168         return undefSymbols.size();
169     }
170 
getCmdArray()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
getDataArray()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