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