-
Notifications
You must be signed in to change notification settings - Fork 7
/
GolangFunctionRecovery.java
677 lines (611 loc) · 24.4 KB
/
GolangFunctionRecovery.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
//Finds and creates functions with their original names, in Golang based PE and ELF files. Functions which have already been found by Ghidra will be renamed if a suitable name is found.
//@author Max 'Libra' Kersten of Trellix' Advanced Research Center, based on the work by padorka@cujoai (https://github.com/getCUJO/ThreatIntel/blob/master/Scripts/Ghidra/go_func.py)
//@category Golang
//@keybinding
//@menupath
//@toolbar
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
public class GolangFunctionRecovery extends GhidraScript {
/**
* A boolean which defines if logging should be enabled. When prioritising
* speed, one might not be interested in getting all messages, but rather only
* the concluding message, along with potential error messages. As such, this
* boolean specifies if more logging should be enabled or disabled.</br>
* </br>
* The default value of this field is <code>true</code>.
*/
private static final boolean ENABLE_LOGGING = true;
/**
* The mask to perform the logical AND with on the magic value
*/
private static final int MAGIC_MASK = 0xffffffff;
/**
* The magic value for Golang 1.20 and above
*
* @see <a href=
* "https://github.com/golang/go/blob/master/src/debug/gosym/pclntab.go">Golang
* pclntab source code</a>
*/
private static final int GO_120 = 0xfffffff1;
/**
* The magic value for Golang 1.18
*
* @see <a href=
* "https://github.com/golang/go/blob/master/src/debug/gosym/pclntab.go">Golang
* pclntab source code</a>
*/
private static final int GO_118 = 0xfffffff0;
/**
* The magic value for Golang 1.16 through version 1.17
*
* @see <a href=
* "https://github.com/golang/go/blob/master/src/debug/gosym/pclntab.go">Golang
* pclntab source code</a>
*/
private static final int GO_116 = 0xfffffffa;
/**
* The magic value for Golang 1.2 through version 1.15
*
* @see <a href=
* "https://github.com/golang/go/blob/master/src/debug/gosym/pclntab.go">Golang
* pclntab source code</a>
*/
private static final int GO_12 = 0xfffffffb;
/**
* The default PC Quantum size (minimal instruction size), used in x86, x86_64,
* and WASM
*
* @see <a href=
* "https://github.com/golang/gofrontend/blob/master/libgo/goarch.sh">Golang
* architecture documentation</a>
*/
private static final int INSTRUCTION_SIZE_ONE = 1;
/**
* The PC Quantum size (minimal instruction size), used in RISCV, RISCV x64,
* S390, S390X, SH, and SHbe
*
* @see <a href=
* "https://github.com/golang/gofrontend/blob/master/libgo/goarch.sh">Golang
* architecture documentation</a>
*/
private static final int INSTRUCTION_SIZE_TWO = 2;
/**
* The PC Quantum size (minimal instruction size), used in ALPHA, ARM, ARMbe,
* M68K, MIPS, MIPSle, MIPS64p32, MIPS64p32le, MIPS64, MIPS64le, NIOS2, PPC,
* PPC64, PPC64le, SPARC, and SPARC64
*
* @see <a href=
* "https://github.com/golang/gofrontend/blob/master/libgo/goarch.sh">Golang
* architecture documentation</a>
*/
private static final int INSTRUCTION_SIZE_FOUR = 4;
/**
* The size of a pointer on X86
*/
private static final int POINTER_SIZE_X86 = 4;
/**
* The size of a pointer on X64
*/
private static final int POINTER_SIZE_X64 = 8;
/**
* The amount of functions which were recovered
*/
private static int functionCount = 0;
@Override
protected void run() throws Exception {
// Get the executable format of the sample
String executableFormat = currentProgram.getExecutableFormat();
// Declare the pclntab variable
Address pclntab;
// Check if the executable format is a PE file
if (executableFormat.equalsIgnoreCase("Portable Executable (PE)")) {
// Optionally print a message to state the file type which has been detected
log("PE file found");
// The declaration and initialisation of potential pclntab magic values
String[] pclntabMagicValues = { "\\xfb\\xff\\xff\\xff\\x00\\x00", "\\xfa\\xff\\xff\\xff\\x00\\x00",
"\\xf0\\xff\\xff\\xff\\x00\\x00", "\\xf1\\xff\\xff\\xff\\x00\\x00" };
// Get the gopclntab address by magic value
pclntab = getGopclntabByMagicValue(pclntabMagicValues);
} else if (executableFormat.equalsIgnoreCase("Executable and Linking Format (ELF)")) { // Check if the
// executable format is
// an ELF file
// Optionally print a message to state the file type which has been detected
log("ELF file found");
// Get the gopclntab address by section name
pclntab = getGopclntabBySectionName(".gopclntab");
} else if (executableFormat.equalsIgnoreCase("Mac OS X Mach-O")) {
// Optionally print a message to state the file type which has been detected
log("Mach-O file found");
// Get the gopclntab address by section name
pclntab = getGopclntabBySectionName("__gopclntab");
} else {
/*
* Print an error message informing the user of the failure to find a suitable
* executable format
*/
printerr("Unspported file format: " + executableFormat);
// Return, thus ending the script's execution
return;
}
// If the pclntab could not be found, the script ends
if (pclntab == null) {
// Print an error message with the reason of the failure
printerr("Cannot find the pclntab!");
// End the script's execution
return;
}
/*
* If execution continues, the pclntab was found. Optionally print a message to
* inform the user of the progress
*/
log(String.format("pclntab found at 0x%x!", pclntab.getOffset()));
// Declare and initialise the pclntab magic value
int magic = getInt(pclntab) & MAGIC_MASK;
// Recover function names for functions in Golang version 1.20 and above
if (magic == GO_120) {
println("Golang 1.20 found, note that this script is experimental for this Golang version!");
recoverFunctionNamesGo118Plus(pclntab);
} else if (magic == GO_118) {
// Recover function names for functions in Golang version 1.18 and above
recoverFunctionNamesGo118Plus(pclntab);
} else if (magic == GO_116) { // Determine if the magic value matches Golang version 1.16 and 1.17
// Recover function names for functions in Golang versions 1.16 and 1.17
renameFunc116(pclntab);
} else if (magic == GO_12) {// Determine if the magic value matches Golang 1.15 through version 1.2
/*
* Recover function names for functions in Golang version 1.15 through version
* 1.2
*/
recoverFunctionNamesGo12(pclntab);
} else {
// No matching magic value was found, of which the user is informed
println("Unable to determine the .gopclntab magic value, so the assumption is made that it is Go 1.2 compatible");
/*
* Recover function names for functions in Golang version 1.15 through version
* 1.2
*/
recoverFunctionNamesGo12(pclntab);
}
/*
* Inform the analyst of the total number of functions which has been renamed
* and/or created
*/
println("Total number of functions renamed and/or created: " + functionCount);
}
/**
* A wrapper function for the
* {@link ghidra.app.script.GhidraScript#println(String)} which is only called
* if the {@link #ENABLE_LOGGING} is <code>true</code>. The logging that is
* (potentially) passing through this function, is meant as optional logging.
* The final conclusion, as well as the logging of any error messages, should be
* printed via direct calls. The easy-to-omit nature of optional messages speeds
* up automated analysis by limiting the number of print calls.
*
* @param message
*/
private void log(String message) {
if (ENABLE_LOGGING) {
println(message);
}
}
/**
* Gets the gopclntab starting address based on a found magic value, if any
*
* @param pclntabMagicValues possible magic values as byte strings written as
* strings (i.e. "\xab\xbc")
* @return the starting address of the gopclntab if it is found, null if it is
* not found
* @throws MemoryAccessException
* @throws AddressOutOfBoundsException
*/
private Address getGopclntabByMagicValue(String[] pclntabMagicValues)
throws MemoryAccessException, AddressOutOfBoundsException {
// Iterate over all magic values
for (String magic : pclntabMagicValues) {
/*
* Look for the magic bytes within the current program, starting at the minimum
* address, with a maximum of 100 results
*/
Address[] pclntabs = findBytes(null, magic, 100);
// Iterate over all results
for (Address pclntab : pclntabs) {
log("Potential pclntab found at : " + Long.toHexString(pclntab.getOffset()));
/*
* Bytes have been found based on the given magic value
*/
// Get the instruction's size quantum
byte instructionSizeQuantum = getByte(pclntab.add(6));
// Get the pointer size
byte pointerSize = getByte(pclntab.add(7));
/*
* Verify if both the instruction's quantum size and pointer size match the
* conditions, meaning the pclntab has been found
*/
if ((instructionSizeQuantum != INSTRUCTION_SIZE_ONE && instructionSizeQuantum != INSTRUCTION_SIZE_TWO
&& instructionSizeQuantum != INSTRUCTION_SIZE_FOUR) == false
|| (pointerSize != POINTER_SIZE_X86 && pointerSize != POINTER_SIZE_X64) == false) {
return pclntab;
}
}
}
// If no results match the criteria, null is returned
return null;
}
/**
* Gets the gopclntab by its section name (being ".gopclntab")
*
* @return the starting address of the ".gopclntab" section
*/
private Address getGopclntabBySectionName(String sectionName) {
// Iterate over all memory blocks within the program
for (MemoryBlock memoryBlock : getMemoryBlocks()) {
// Check if the block's name equals (ignoring the casing) the gopclntab section
if (memoryBlock.getName().equalsIgnoreCase(sectionName)) {
// Return the starting address of this section if it is found
return memoryBlock.getStart();
}
}
// Return null if the section is not found
return null;
}
/**
* Gets the length of the ascii string, from the given address until the first
* null byte (0x00).
*
* @param start the address to start looking for the null byte (0x00)
* @return the number of characters, excluding the null byte (0x00)
* @throws MemoryAccessException
*/
private int getAsciiStringLength(Address start) throws MemoryAccessException {
/*
* Declare and initialise the length variable, which is to be returned from this
* function
*/
int length = 0;
/*
* Iterate until no address is available anymore, or until the while-loop is
* broken
*/
while (start != null) {
// Get the byte at the given address
byte b = getByte(start);
// Check if the byte's value is null
if (b == 0) {
// Break the loop if it is
break;
}
// If the byte isn't null, increment the length
length++;
// Additionally, increment the address by fetching the next
start = start.add(1);
// Jump to the top of the while-loop
}
// Return the length of the string, once the while-loop is broken
return length;
}
/**
* Creates a new function, or renames the function if it already exists, based
* on the newly found name, which is obtained via the name address variable
*
* @param functionAddress the address of the function
* @param nameAddress the address of the function's new name
* @throws DuplicateNameException
* @throws InvalidInputException
*/
private void createOrRenameFunction(Address functionAddress, Address nameAddress)
throws DuplicateNameException, InvalidInputException {
// Check if the variable is instantiated
if (nameAddress == null) {
// Return from the function if this is the case
return;
}
// Get the data at the given address
Data functionNameData = getDataAt(nameAddress);
// If no data resides at this address
if (functionNameData == null) {
try {
int length = getAsciiStringLength(nameAddress);
clearListing(nameAddress, nameAddress.add(length));
// Create an ASCII string within Ghidra
functionNameData = createAsciiString(nameAddress, length);
} catch (Exception e) {
// Print an error if the ASCII string creation fails
printerr(String.format("Unable to create an ASCII string at 0x%x!", nameAddress.getOffset()));
// Return from the function if this fails
return;
}
}
/*
* Get the function name by getting the data's value, which in this case is a
* String but needs to be cast as the getValue function returns an Object
*/
String functionName = (String) functionNameData.getValue();
// If the function name is null, blank, or empty
if (functionName == null || functionName.isBlank()) {
// Print an error
printerr(String.format("No function name found at 0x%s!", Long.toHexString(nameAddress.getOffset())));
// Return from the function
return;
}
// Gets the function at the given address
Function func = getFunctionAt(functionAddress);
// If there is a function at the given address
if (func != null) {
// Get the old name
String functionNameOld = func.getName();
// Get the new name, removing spaces
String functionNameNew = functionName.replaceAll(" ", "").replaceAll("Â", "").replaceAll("·", "");
/*
* Replacing the function with a name which already exists, throws a
* ghidra.util.exception.DuplicateNameException. This check avoids this
* exception by only replacing a function name if the old and new name are not
* the same.
*/
if (functionNameOld.equalsIgnoreCase(functionNameNew) == false) {
// Change the function name
try {
func.setName(functionNameNew, SourceType.USER_DEFINED);
} catch (DuplicateNameException ex) {
func.setName(functionNameNew + "_", SourceType.USER_DEFINED);
}
// Optionally print the renamed function
log("Function renamed from \"" + functionNameOld + "\" to \"" + functionNameNew + "\", located at 0x"
+ Long.toHexString(functionAddress.getOffset()));
// Increment the function count
functionCount++;
}
} else {
// If no function exists at the given address, create one
func = createFunction(functionAddress, functionName);
// Optionally print the function name and address
log("Function \"" + functionName + "\" created at 0x" + Long.toHexString(functionAddress.getOffset()));
// Increment the function count
functionCount++;
}
}
/**
* Recovers function names for functions in Golang version 1.15 through version
* 1.2
*
* @param pclntab the start address of the pclntab
* @throws MemoryAccessException
* @throws AddressOutOfBoundsException
* @throws DuplicateNameException
* @throws InvalidInputException
*/
private void recoverFunctionNamesGo12(Address pclntab)
throws MemoryAccessException, AddressOutOfBoundsException, DuplicateNameException, InvalidInputException {
// Get the pointer size
byte pointerSize = getByte(pclntab.add(7));
// Declare the number of functions tab variable
long nFunctionTab;
// If the pointer size fits a x64 system
if (pointerSize == POINTER_SIZE_X64) {
// Get a long value from the given address
nFunctionTab = getLong(pclntab.add(8));
} else { // Assume x86, meaning 4 bytes in size
// Get an integer value from the given address
nFunctionTab = getInt(pclntab.add(8));
}
// Get the function tab address
Address functionTab = pclntab.add(8 + pointerSize);
// Declare a copy of the function tab, named p
Address p = functionTab;
// Declare the function address variable
Address functionAddress;
// Declare the name offset variable
long nameOffset;
// Iterate over the number of functions
for (int i = 0; i < nFunctionTab; i++) {
// Check if the script's execution is cancelled
if (monitor.isCancelled()) {
// Break this loop, thus exiting the script's execution early
break;
}
// If the pointer size fits a x64 system
if (pointerSize == POINTER_SIZE_X64) {
// Get the function address
functionAddress = currentProgram.getAddressFactory().getAddress(Long.toHexString(getLong(p)).trim());
// Increment p with the pointer size to move it to the next usable address
p = p.add(pointerSize);
// Get the name offset as a long, since the architecture is x64
nameOffset = getLong(p);
} else { // Assume x86, meaning 4 bytes in size
// Get the function address
functionAddress = currentProgram.getAddressFactory().getAddress(Long.toHexString(getInt(p)));
// Increment p with the pointer size to move it to the next usable address
p = p.add(pointerSize);
// Get the name offset as an integer, since the architecture is x86
nameOffset = getInt(p);
}
// Increment p with the pointer size to move it to the next usable address
p = p.add(pointerSize);
/*
* Gets the name pointer, which is located directly after the function name,
* hence the addition of the name offset and the pointer size to move to the
* next usable address
*/
Address namePointer = pclntab.add(nameOffset + pointerSize);
// Get the address of the name, based on the pointer, which is always 32 bits in
// size
Address nameAddress = pclntab.add(getInt(namePointer));
// Address nameAddress = pclntab.add(namePointer.getOffset());
/*
* Create or rename the function at the address, with the name at the given
* address
*/
createOrRenameFunction(functionAddress, nameAddress);
}
}
/**
* Recovers function names for functions in Golang version 1.16 and version 1.17
*
* @param pclntab the start address of the pclntab
* @throws MemoryAccessException
* @throws AddressOutOfBoundsException
* @throws DuplicateNameException
* @throws InvalidInputException
*/
private void renameFunc116(Address pclntab)
throws MemoryAccessException, AddressOutOfBoundsException, DuplicateNameException, InvalidInputException {
// Get the size of the pointer
byte pointerSize = getByte(pclntab.add(7));
// Declare variables, whose value will depend on the architecture
long nFunctionTab;
long offset;
Address functionNameTab;
// If the pointer's size is equal to the size of a pointer on a x64 system
if (pointerSize == POINTER_SIZE_X64) {
// Get the corresponding long value
nFunctionTab = getLong(pclntab.add(8));
// Calculate the next offset
offset = getLong(pclntab.add(8 + 2 * pointerSize));
// Get the function name tab's address
functionNameTab = pclntab.add(offset);
// Calculate the next offset
offset = getLong(pclntab.add(8 + 6 * pointerSize));
} else { // Assume x86, meaning 4 bytes in size
// Get the corresponding integer value
nFunctionTab = getInt(pclntab.add(8));
// Calculate the next offset
offset = getInt(pclntab.add(8 + 2 * pointerSize));
// Get the function name tab's address
functionNameTab = pclntab.add(offset);
// Calculate the next offset
offset = getInt(pclntab.add(8 + 6 * pointerSize));
}
// Get the address of the function tab
Address functionTab = pclntab.add(offset);
// Declare and initiate a copy of the function tab
Address p = functionTab;
// Declare several variables for later use
Address functionAddress;
long functionDataOffset;
Address namePointer;
Address nameAddress;
// Iterate over the number of functions in the tab
for (int i = 0; i < nFunctionTab; i++) {
// Check if the script's execution is cancelled
if (monitor.isCancelled()) {
// Break this loop, thus exiting the script's execution early
break;
}
// If the pointer size is one of a x64 system
if (pointerSize == POINTER_SIZE_X64) {
// Get the function's address
functionAddress = currentProgram.getAddressFactory().getAddress(Long.toHexString(getLong(p)).trim());
// Adjust the offset
p = p.add(pointerSize);
// Get the function data's offset
functionDataOffset = getLong(p);
} else { // Assume x86, meaning 4 bytes in size
// Get the function's address
functionAddress = currentProgram.getAddressFactory().getAddress(Long.toHexString(getInt(p)).trim());
// Adjust the offset
p = p.add(pointerSize);
// Get the function data's offset
functionDataOffset = getInt(p);
}
// Move p to the next address
p = p.add(pointerSize);
// Get the function name pointer
namePointer = functionTab.add(functionDataOffset + pointerSize);
// Get the address of the function name, which is always 32 bits in size
nameAddress = functionNameTab.add(getInt(namePointer));
/*
* Create or rename the function at the address, with the name at the given
* address
*/
createOrRenameFunction(functionAddress, nameAddress);
}
}
/**
* Recovers function names for functions in Golang version 1.18 and above
*
* @param pclntab the start address of the pclntab
* @throws MemoryAccessException
* @throws AddressOutOfBoundsException
* @throws DuplicateNameException
* @throws InvalidInputException
*/
private void recoverFunctionNamesGo118Plus(Address pclntab)
throws MemoryAccessException, AddressOutOfBoundsException, DuplicateNameException, InvalidInputException {
// Get the pointer size
byte pointerSize = getByte(pclntab.add(7));
// Declare several variables
long nFunctionTab;
long textStart;
long offset;
Address functionNameTab;
// Check if the pointer size matches a x64 system's pointer size
if (pointerSize == POINTER_SIZE_X64) {
// Get the number of functions tab address
nFunctionTab = getLong(pclntab.add(8));
// Get the start of the text
textStart = getLong(pclntab.add(8 + 2 * pointerSize));
// Calculate the next offset
offset = getLong(pclntab.add(8 + 3 * pointerSize));
// Get the address of the function name tab
functionNameTab = pclntab.add(offset);
// Calculate the next offset
offset = getLong(pclntab.add(8 + 7 * pointerSize));
} else { // Assume x86, meaning 4 bytes in size
// Get the number of functions tab address
nFunctionTab = getInt(pclntab.add(8));
// Get the start of the text
textStart = getInt(pclntab.add(8 + 2 * pointerSize));
// Calculate the next offset
offset = getInt(pclntab.add(8 + 3 * pointerSize));
// Get the address of the function name tab
functionNameTab = pclntab.add(offset);
// Calculate the next offset
offset = getInt(pclntab.add(8 + 7 * pointerSize));
}
// Get the address of the function tab
Address functionTab = pclntab.add(offset);
// Instantiate a copy of the function tab for later use
Address p = functionTab;
// Define the field size within the function tab, which is always 4
int functabFieldSize = 4;
// Declare several variables for later use
Address functionAddress;
int functionDataOffset;
Address namePointer;
Address nameAddress;
// Iterate over all functions
for (int i = 0; i < nFunctionTab; i++) {
// Check if the script's execution is cancelled
if (monitor.isCancelled()) {
// Break this loop, thus exiting the script's execution early
break;
}
// Get the address for the current function, which is always 32 bits in size
functionAddress = currentProgram.getAddressFactory()
.getAddress(Long.toHexString(getInt(p) + textStart).trim());
// Adjust p
p = p.add(functabFieldSize);
// Get the function data offset
functionDataOffset = getInt(p);
// Adjust p
p = p.add(functabFieldSize);
// Get the pointer to the name
namePointer = functionTab.add(functionDataOffset + functabFieldSize);
// Get the pointer to the address, which is always 32 bits
nameAddress = functionNameTab.add(getInt(namePointer));
/*
* Create or rename the function at the address, with the name at the given
* address
*/
createOrRenameFunction(functionAddress, nameAddress);
}
}
}