Cleanup of segments
git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk/SWIG@1008 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
parent
1d65600856
commit
7598a72387
8 changed files with 211 additions and 112 deletions
|
|
@ -33,19 +33,26 @@ extern "C" {
|
||||||
#define MAX_PATH 1024
|
#define MAX_PATH 1024
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Low level memory management */
|
||||||
|
|
||||||
|
extern int wad_memory_init();
|
||||||
|
extern void *wad_malloc(int nbytes);
|
||||||
|
extern void wad_release_memory();
|
||||||
|
extern char *wad_strdup(const char *c);
|
||||||
|
|
||||||
/* Memory segments */
|
/* Memory segments */
|
||||||
typedef struct WadSegment {
|
typedef struct WadSegment {
|
||||||
char *base; /* Base address for symbol lookup */
|
char *base; /* Base address for symbol lookup */
|
||||||
char *vaddr; /* Virtual address start */
|
char *vaddr; /* Virtual address start */
|
||||||
unsigned long size; /* Size of the segment (bytes) */
|
unsigned long size; /* Size of the segment (bytes) */
|
||||||
unsigned long offset; /* Offset into mapped object */
|
unsigned long offset; /* Offset into mapped object */
|
||||||
char mapname[MAX_PATH]; /* Filename mapped to this region */
|
char *mapname; /* Filename mapped to this region */
|
||||||
char mappath[MAX_PATH]; /* Full path to mapname */
|
char *mappath; /* Full path to mapname */
|
||||||
|
struct WadSegment *next; /* Next segment */
|
||||||
} WadSegment;
|
} WadSegment;
|
||||||
|
|
||||||
extern WadSegment *wad_segment_read();
|
extern int wad_segment_read();
|
||||||
extern WadSegment *wad_segment_find(WadSegment *s, void *vaddr);
|
extern WadSegment *wad_segment_find(void *vaddr);
|
||||||
extern void wad_segment_release(WadSegment *s);
|
|
||||||
|
|
||||||
/* Object file handling */
|
/* Object file handling */
|
||||||
typedef struct WadObjectFile {
|
typedef struct WadObjectFile {
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
# These are the files that make up the WAD core
|
# These are the files that make up the WAD core
|
||||||
WADSRCS = return.c default.c stack.c stab.c elf.c object.c init.c segment.c signal.c
|
WADSRCS = memory.c return.c default.c stack.c stab.c elf.c object.c init.c segment.c signal.c
|
||||||
WADOBJS = return.o default.o stack.o stab.o elf.o object.o signal.o segment.o init.o
|
WADOBJS = memory.o return.o default.o stack.o stab.o elf.o object.o signal.o segment.o init.o
|
||||||
INCLUDE = -I../Include -I. $(SINCLUDE)
|
INCLUDE = -I../Include -I. $(SINCLUDE)
|
||||||
WADOPT = -DWAD_SOLARIS
|
WADOPT = -DWAD_LINUX
|
||||||
|
|
||||||
# Location of your Python installation
|
# Location of your Python installation
|
||||||
PYINCLUDE = -I/usr/local/include/python2.0
|
PYINCLUDE = -I/usr/local/include/python2.0
|
||||||
|
|
@ -23,21 +23,21 @@ TCLSRCS = wadtcl.cxx
|
||||||
TCLOBJS = wadtcl.o
|
TCLOBJS = wadtcl.o
|
||||||
|
|
||||||
# Location of your Perl installation
|
# Location of your Perl installation
|
||||||
PERLINCLUDE = -I/usr/perl5/5.00503/sun4-solaris/CORE
|
PERLINCLUDE = -I/usr/lib/perl5/5.00503/i386-linux/CORE
|
||||||
PERLSRCS = wadpl.cxx
|
PERLSRCS = wadpl.cxx
|
||||||
PERLOBJS = wadpl.o
|
PERLOBJS = wadpl.o
|
||||||
|
|
||||||
# C Compiler
|
# C Compiler
|
||||||
CC = cc
|
CC = gcc
|
||||||
CFLAGS = #
|
CFLAGS = #-fpic
|
||||||
|
|
||||||
# C++ Compiler
|
# C++ Compiler
|
||||||
CXX = CC
|
CXX = c++
|
||||||
CXXFLAGS = #-Kpic
|
CXXFLAGS = #-fpic
|
||||||
|
|
||||||
# Linking options
|
# Linking options
|
||||||
CLINK =
|
CLINK =
|
||||||
CXXLINK = CC -G
|
CXXLINK = g++ -shared
|
||||||
|
|
||||||
# Rules for creation of a .o file from .cxx
|
# Rules for creation of a .o file from .cxx
|
||||||
.SUFFIXES: .cxx
|
.SUFFIXES: .cxx
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ int wad_debug_mode = 0;
|
||||||
void wad_init() {
|
void wad_init() {
|
||||||
static int init = 0;
|
static int init = 0;
|
||||||
|
|
||||||
|
wad_memory_init();
|
||||||
if (getenv("WAD_DEBUG_SEGMENT")) {
|
if (getenv("WAD_DEBUG_SEGMENT")) {
|
||||||
wad_debug_mode |= DEBUG_SEGMENT;
|
wad_debug_mode |= DEBUG_SEGMENT;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
140
Tools/WAD/Wad/memory.c
Normal file
140
Tools/WAD/Wad/memory.c
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* memory.c
|
||||||
|
*
|
||||||
|
* This file provides simple mmap() based memory management for WAD. Since
|
||||||
|
* the process heap-allocator might be corrupted when WAD is invoked, we
|
||||||
|
* have to do all of our own memory management. However, since WAD mostly
|
||||||
|
* just collects data, we only provide the function wad_malloc(). To
|
||||||
|
* release all allocated memory, the wad_release_memory() function should
|
||||||
|
* be used.
|
||||||
|
*
|
||||||
|
* Author(s) : David Beazley (beazley@cs.uchicago.edu)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001. The University of Chicago
|
||||||
|
* See the file LICENSE for information on usage and redistribution.
|
||||||
|
* ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#include "wad.h"
|
||||||
|
|
||||||
|
typedef struct _WadMemory {
|
||||||
|
int npages; /* Number of pages */
|
||||||
|
int last; /* Last offset in page */
|
||||||
|
struct _WadMemory *next; /* Pointer to next allocation */
|
||||||
|
} WadMemory;
|
||||||
|
|
||||||
|
static WadMemory *current = 0; /* Current memory block */
|
||||||
|
static int pagesize = 0; /* System page size */
|
||||||
|
static int devzero = 0;
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* wad_memory_init()
|
||||||
|
*
|
||||||
|
* Initialize the WAD allocator.
|
||||||
|
* ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
int wad_memory_init() {
|
||||||
|
pagesize = getpagesize();
|
||||||
|
devzero = open("/dev/zero", O_RDWR);
|
||||||
|
if (devzero < 0) {
|
||||||
|
printf("WAD: couldn't open /dev/zero.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* wad_page_alloc()
|
||||||
|
*
|
||||||
|
* Allocate pages using mmap
|
||||||
|
* ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void *wad_page_alloc(int npages) {
|
||||||
|
void *m;
|
||||||
|
m = mmap(NULL, npages*pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, devzero, 0);
|
||||||
|
if (((long) m) == -1) return 0;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* wad_malloc()
|
||||||
|
*
|
||||||
|
* Allocate memory using mmap(). If the allocation is smaller than half a page,
|
||||||
|
* We'll look at current to see if there is enough space. If so, we'll just
|
||||||
|
* use that memory. Otherwise, we'll allocate a new page. If the allocation
|
||||||
|
* request is larger than a page, we'll round up to the nearest page size and
|
||||||
|
* do a special allocation.
|
||||||
|
* ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void *wad_malloc(int nbytes) {
|
||||||
|
void *ptr;
|
||||||
|
WadMemory *wm;
|
||||||
|
char *c;
|
||||||
|
int npages;
|
||||||
|
printf("wad_malloc: %d\n", nbytes);
|
||||||
|
if (nbytes >= (pagesize >> 2)) {
|
||||||
|
/* Large allocation. */
|
||||||
|
npages = ((nbytes + sizeof(WadMemory))/pagesize) + 1;
|
||||||
|
ptr = wad_page_alloc(npages);
|
||||||
|
if (!ptr) return 0;
|
||||||
|
wm = (WadMemory *)ptr;
|
||||||
|
wm->npages = npages;
|
||||||
|
wm->last = sizeof(WadMemory);
|
||||||
|
wm->next = current;
|
||||||
|
current = wm;
|
||||||
|
c = (char *) current + (current->last);
|
||||||
|
current->last += ((nbytes & ~0x7) + 8);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
/* Small allocation. See if there are any regions big enough */
|
||||||
|
wm = current;
|
||||||
|
while (wm) {
|
||||||
|
if (((wm->npages*pagesize) - wm->last) > nbytes) {
|
||||||
|
/* Yep. Found a region */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!wm) {
|
||||||
|
wm = (WadMemory *) wad_page_alloc(1);
|
||||||
|
if (!wm) return 0;
|
||||||
|
wm->npages = 1;
|
||||||
|
wm->last = sizeof(WadMemory);
|
||||||
|
wm->next = current;
|
||||||
|
current = wm;
|
||||||
|
}
|
||||||
|
c = ((char *) wm) + (wm->last);
|
||||||
|
wm->last += ((nbytes & ~0x7) + 8);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* wad_release_memory()
|
||||||
|
*
|
||||||
|
* Releases all memory previously allocated by wad_malloc()
|
||||||
|
* ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void wad_release_memory() {
|
||||||
|
WadMemory *wm, *next;
|
||||||
|
|
||||||
|
wm = current;
|
||||||
|
while (wm) {
|
||||||
|
next = wm->next;
|
||||||
|
munmap(wm, wm->npages*pagesize);
|
||||||
|
wm = next;
|
||||||
|
}
|
||||||
|
current = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* wad_strdup()
|
||||||
|
*
|
||||||
|
* Duplicate a string
|
||||||
|
* ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
char *wad_strdup(const char *c) {
|
||||||
|
char *t;
|
||||||
|
if (!c) c = "";
|
||||||
|
t = (char *) wad_malloc(strlen(c)+1);
|
||||||
|
strcpy(t,c);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -48,12 +48,13 @@ segment_read(int fd, WadSegment *s)
|
||||||
pbuffer[8] = 0;
|
pbuffer[8] = 0;
|
||||||
pbuffer[17] = 0;
|
pbuffer[17] = 0;
|
||||||
pbuffer[31] = 0;
|
pbuffer[31] = 0;
|
||||||
s->mapname[0] = 0;
|
|
||||||
s->mappath[0] = 0;
|
|
||||||
if (len >= 49) {
|
if (len >= 49) {
|
||||||
strcpy(s->mapname, pbuffer+49);
|
s->mapname = wad_strdup(pbuffer+49);
|
||||||
strcpy(s->mappath, pbuffer+49);
|
s->mappath = s->mapname;
|
||||||
}
|
} else {
|
||||||
|
s->mapname = "";
|
||||||
|
s->mappath = s->mapname;
|
||||||
|
}
|
||||||
if (linux_first) {
|
if (linux_first) {
|
||||||
strcpy(linux_firstsegment, s->mappath);
|
strcpy(linux_firstsegment, s->mappath);
|
||||||
linux_first = 0;
|
linux_first = 0;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ segment_read(int fs, WadSegment *s) {
|
||||||
|
|
||||||
n = read(fs, &pmap, sizeof(prmap_t));
|
n = read(fs, &pmap, sizeof(prmap_t));
|
||||||
if (n <= 0) return 0;
|
if (n <= 0) return 0;
|
||||||
strncpy(s->mapname, pmap.pr_mapname, MAX_PATH);
|
s->mapname = wad_strdup(pmap.pr_mapname);
|
||||||
|
s->mappath = (char *) wad_malloc(20+strlen(pmap.pr_mapname));
|
||||||
strcpy(s->mappath,"/proc/self/object/");
|
strcpy(s->mappath,"/proc/self/object/");
|
||||||
strcat(s->mappath,pmap.pr_mapname);
|
strcat(s->mappath,pmap.pr_mapname);
|
||||||
s->vaddr = (char *) pmap.pr_vaddr;
|
s->vaddr = (char *) pmap.pr_vaddr;
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,6 @@
|
||||||
|
|
||||||
#include "wad.h"
|
#include "wad.h"
|
||||||
|
|
||||||
/* General comments:
|
|
||||||
|
|
||||||
The segment map gets returned as an array of WadSegment structures.
|
|
||||||
Due to memory constraints, this is usually located in a memory mapped
|
|
||||||
region or similar section of memory.
|
|
||||||
|
|
||||||
The first element of the memory map describes the segment map (address and size)
|
|
||||||
so that it can be cleaned up later on.
|
|
||||||
|
|
||||||
The last element of the memory map is a sentinel where base == 0 and size == 0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Include the proper code for reading the segment map */
|
/* Include the proper code for reading the segment map */
|
||||||
|
|
||||||
#ifdef WAD_SOLARIS
|
#ifdef WAD_SOLARIS
|
||||||
|
|
@ -39,90 +27,44 @@
|
||||||
#include "plat/segment_linux.c"
|
#include "plat/segment_linux.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static WadSegment *segments = 0; /* Linked list of segments */
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
* wad_segment_read()
|
* wad_segment_read()
|
||||||
*
|
*
|
||||||
* Read all of the memory segments.
|
* Read all of the memory segments into a linked list. Any previous segment
|
||||||
|
* map is simply lost. The only way to reclaim this memory is to call
|
||||||
|
* wad_release_memory().
|
||||||
* ----------------------------------------------------------------------------- */
|
* ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
WadSegment *
|
int
|
||||||
wad_segment_read() {
|
wad_segment_read() {
|
||||||
int fs;
|
int fs;
|
||||||
int dz;
|
int n;
|
||||||
int offset = 0;
|
WadSegment *s, *lasts;
|
||||||
int i;
|
|
||||||
int n = 0;
|
|
||||||
int nsegments;
|
|
||||||
WadSegment *segments;
|
|
||||||
WadSegment *s;
|
|
||||||
WadSegment ws;
|
|
||||||
|
|
||||||
/* Try to load the virtual address map */
|
segments = 0;
|
||||||
|
lasts = 0;
|
||||||
fs = segment_open();
|
fs = segment_open();
|
||||||
if (fs < 0) return 0;
|
|
||||||
nsegments = 0;
|
|
||||||
while (1) {
|
|
||||||
n = segment_read(fs,&ws);
|
|
||||||
if (n <= 0) break;
|
|
||||||
nsegments++;
|
|
||||||
}
|
|
||||||
nsegments+=3;
|
|
||||||
close(fs);
|
|
||||||
|
|
||||||
dz = open("/dev/zero", O_RDWR, 0644);
|
|
||||||
if (dz < 0) {
|
|
||||||
puts("Couldn't open /dev/zero\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
segments = (WadSegment *) mmap(NULL, nsegments*sizeof(WadSegment), PROT_READ | PROT_WRITE, MAP_PRIVATE, dz, 0);
|
|
||||||
close(dz);
|
|
||||||
|
|
||||||
fs = segment_open();
|
|
||||||
i = 0;
|
|
||||||
s = segments;
|
|
||||||
|
|
||||||
/* First segment is a map to the segment list */
|
|
||||||
s->base = (char *) segments;
|
|
||||||
s->vaddr = (char *) segments;
|
|
||||||
s->size = nsegments*sizeof(WadSegment);
|
|
||||||
s->mapname[0] = 0;
|
|
||||||
s->mappath[0] = 0;
|
|
||||||
s++;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
n = segment_read(fs,&ws);
|
s = (WadSegment *) wad_malloc(sizeof(WadSegment));
|
||||||
|
n = segment_read(fs,s);
|
||||||
if (n <= 0) break;
|
if (n <= 0) break;
|
||||||
strcpy(s->mapname, ws.mapname);
|
s->next = 0;
|
||||||
strcpy(s->mappath, ws.mappath);
|
if (!lasts) {
|
||||||
s->vaddr = ws.vaddr;
|
segments = s;
|
||||||
s->base = ws.base;
|
lasts = s;
|
||||||
s->size = ws.size;
|
} else {
|
||||||
s->offset = ws.offset;
|
lasts->next = s;
|
||||||
|
lasts = s;
|
||||||
|
}
|
||||||
if (wad_debug_mode & DEBUG_SEGMENT) {
|
if (wad_debug_mode & DEBUG_SEGMENT) {
|
||||||
printf("wad_segment: read : %08x-%08x, base=%x in %s\n", s->vaddr, ((char *) s->vaddr) + s->size, s->base, s->mappath);
|
printf("wad_segment: read : %08x-%08x, base=%x in %s\n", s->vaddr, ((char *) s->vaddr) + s->size, s->base, s->mappath);
|
||||||
}
|
}
|
||||||
s++;
|
|
||||||
}
|
}
|
||||||
/* Create sentinel */
|
|
||||||
s->base = 0;
|
|
||||||
s->vaddr = 0;
|
|
||||||
s->size = 0;
|
|
||||||
s->offset = 0;
|
|
||||||
s->mapname[0] =0;
|
|
||||||
s->mappath[0] = 0;
|
|
||||||
close(fs);
|
close(fs);
|
||||||
return segments;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
|
||||||
* wad_segment_release()
|
|
||||||
*
|
|
||||||
* This function releases all of the segments.
|
|
||||||
* ----------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
void
|
|
||||||
wad_segment_release(WadSegment *s) {
|
|
||||||
munmap((void *)s, s->size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
|
|
@ -135,13 +77,14 @@ wad_segment_release(WadSegment *s) {
|
||||||
* ----------------------------------------------------------------------------- */
|
* ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
WadSegment *
|
WadSegment *
|
||||||
wad_segment_find(WadSegment *s, void *vaddr) {
|
wad_segment_find(void *vaddr) {
|
||||||
WadSegment *ls;
|
WadSegment *ls;
|
||||||
|
WadSegment *s;
|
||||||
char *addr = (char *)vaddr;
|
char *addr = (char *)vaddr;
|
||||||
|
|
||||||
ls = s;
|
s = segments;
|
||||||
while (s->size || s->base) {
|
ls = segments;
|
||||||
/* printf("s = %x, %x(%d) %x\n",s, s->vaddr, s->size,addr); */
|
while (s) {
|
||||||
if (strcmp(s->mapname,ls->mapname)) {
|
if (strcmp(s->mapname,ls->mapname)) {
|
||||||
ls = s; /* First segment for a given name */
|
ls = s; /* First segment for a given name */
|
||||||
}
|
}
|
||||||
|
|
@ -151,9 +94,13 @@ wad_segment_find(WadSegment *s, void *vaddr) {
|
||||||
}
|
}
|
||||||
return ls;
|
return ls;
|
||||||
}
|
}
|
||||||
s++;
|
s = s->next;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,10 @@ wad_stack_trace(unsigned long pc, unsigned long sp, unsigned long fp) {
|
||||||
|
|
||||||
/* Read the segments */
|
/* Read the segments */
|
||||||
|
|
||||||
segments = wad_segment_read();
|
if (wad_segment_read() < 0) {
|
||||||
|
printf("WAD: Unable to read segment map\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Open the frame file for output */
|
/* Open the frame file for output */
|
||||||
tmpnam(framefile);
|
tmpnam(framefile);
|
||||||
|
|
@ -97,7 +100,7 @@ wad_stack_trace(unsigned long pc, unsigned long sp, unsigned long fp) {
|
||||||
|
|
||||||
while (p_sp) {
|
while (p_sp) {
|
||||||
/* Add check for stack validity here */
|
/* Add check for stack validity here */
|
||||||
ws = wad_segment_find(segments, (void *) p_sp);
|
ws = wad_segment_find((void *) p_sp);
|
||||||
|
|
||||||
if (!ws) {
|
if (!ws) {
|
||||||
/* If the stack is bad, we are really hosed here */
|
/* If the stack is bad, we are really hosed here */
|
||||||
|
|
@ -105,7 +108,7 @@ wad_stack_trace(unsigned long pc, unsigned long sp, unsigned long fp) {
|
||||||
exit(1);
|
exit(1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ws = wad_segment_find(segments, (void *) p_pc);
|
ws = wad_segment_find((void *) p_pc);
|
||||||
{
|
{
|
||||||
int symsize = 0;
|
int symsize = 0;
|
||||||
int srcsize = 0;
|
int srcsize = 0;
|
||||||
|
|
@ -272,7 +275,6 @@ wad_stack_trace(unsigned long pc, unsigned long sp, unsigned long fp) {
|
||||||
lseek(ffile,0,SEEK_SET);
|
lseek(ffile,0,SEEK_SET);
|
||||||
trace_addr = mmap(NULL, trace_len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, ffile, 0);
|
trace_addr = mmap(NULL, trace_len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, ffile, 0);
|
||||||
close(ffile);
|
close(ffile);
|
||||||
wad_segment_release(segments);
|
|
||||||
return (WadFrame *) trace_addr;
|
return (WadFrame *) trace_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue