forked from KolibriOS/kolibrios
newlib: dynamic loader
git-svn-id: svn://kolibrios.org@3809 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
89c2639856
commit
1707fbc555
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
|
|
||||||
CFLAGS = -c -O2 -fomit-frame-pointer
|
CFLAGS = -c -O2 -fomit-frame-pointer -DBUILD_DLL
|
||||||
LDFLAGS = -nostdlib -shared -s -T libcdll.lds --out-implib libcimp.a --image-base 0
|
LDFLAGS = -nostdlib -shared -s -T libcdll.lds --out-implib libcimp.a --image-base 0
|
||||||
|
|
||||||
LIBC_TOPDIR = .
|
LIBC_TOPDIR = .
|
||||||
@ -21,11 +21,12 @@ STATIC_SRCS:= \
|
|||||||
crt/exit.S \
|
crt/exit.S \
|
||||||
pe/crtloader.c
|
pe/crtloader.c
|
||||||
|
|
||||||
DLL_SRCS:= \
|
LIBDLL_SRCS:= \
|
||||||
crt/crtbegin.c \
|
crt/dllstart.c \
|
||||||
crt/pseudo-reloc.c \
|
|
||||||
crt/chkstk.S \
|
crt/chkstk.S \
|
||||||
crt/exit.S
|
crt/exit.S \
|
||||||
|
crt/pseudo-reloc.c \
|
||||||
|
crt/setjmp.S
|
||||||
|
|
||||||
|
|
||||||
LIBCDLL_SRCS:= \
|
LIBCDLL_SRCS:= \
|
||||||
@ -39,7 +40,6 @@ LIBCRT_SRCS:= \
|
|||||||
crt/start.S \
|
crt/start.S \
|
||||||
crt/chkstk.S \
|
crt/chkstk.S \
|
||||||
crt/crt3.c \
|
crt/crt3.c \
|
||||||
crt/crtbegin.o \
|
|
||||||
pe/crtloader.c
|
pe/crtloader.c
|
||||||
|
|
||||||
CORE_SRCS:= \
|
CORE_SRCS:= \
|
||||||
@ -306,7 +306,7 @@ STATIC_OBJS = $(patsubst %.S, %.o, $(patsubst %.c, %.o, $(STATIC_SRCS)))
|
|||||||
|
|
||||||
LIBCRT_OBJS = $(patsubst %.S, %.o, $(patsubst %.c, %.o, $(LIBCRT_SRCS)))
|
LIBCRT_OBJS = $(patsubst %.S, %.o, $(patsubst %.c, %.o, $(LIBCRT_SRCS)))
|
||||||
|
|
||||||
DLL_OBJS = $(patsubst %.S, %.o, $(patsubst %.c, %.o, $(DLL_SRCS)))
|
LIBDLL_OBJS = $(patsubst %.S, %.o, $(patsubst %.c, %.o, $(LIBDLL_SRCS)))
|
||||||
|
|
||||||
LIBCDLL_OBJS = $(patsubst %.S, %.o, $(patsubst %.c, %.o, $(LIBCDLL_SRCS)))
|
LIBCDLL_OBJS = $(patsubst %.S, %.o, $(patsubst %.c, %.o, $(LIBCDLL_SRCS)))
|
||||||
|
|
||||||
@ -374,11 +374,8 @@ $(NAME).dll: $(LIB_OBJS) $(SRC_DEP) Makefile
|
|||||||
libcrt.a: $(LIBCRT_OBJS) Makefile
|
libcrt.a: $(LIBCRT_OBJS) Makefile
|
||||||
ar rc libcrt.a $(LIBCRT_OBJS)
|
ar rc libcrt.a $(LIBCRT_OBJS)
|
||||||
|
|
||||||
libdll.a: $(DLL_OBJS) Makefile
|
libdll.a: $(LIBDLL_OBJS) Makefile
|
||||||
ar rc libdll.a $(DLL_OBJS)
|
ar rc libdll.a $(LIBDLL_OBJS)
|
||||||
|
|
||||||
libc.obj: $(NAME).dll
|
|
||||||
fasm pe/libc.asm ./libc.obj
|
|
||||||
|
|
||||||
|
|
||||||
static: $(NAME).a
|
static: $(NAME).a
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
|
|
||||||
extern void _pei386_runtime_relocator (void);
|
extern void _pei386_runtime_relocator (void);
|
||||||
|
|
||||||
int DllStartup(void *module, int reason);// __attribute__ ((dllexport));
|
|
||||||
|
|
||||||
|
|
||||||
int DllStartup(void *module, int reason)
|
int DllStartup(void *module, int reason)
|
||||||
{
|
{
|
||||||
_pei386_runtime_relocator();
|
_pei386_runtime_relocator();
|
||||||
|
11
programs/develop/libraries/newlib/crt/dllstart.c
Normal file
11
programs/develop/libraries/newlib/crt/dllstart.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
void _pei386_runtime_relocator (void);
|
||||||
|
|
||||||
|
int DllStartup(void *module, int reason);
|
||||||
|
|
||||||
|
|
||||||
|
int DllStartup(void *module, int reason)
|
||||||
|
{
|
||||||
|
_pei386_runtime_relocator();
|
||||||
|
return 1;
|
||||||
|
};
|
@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
OUTPUT_FORMAT(pei-i386)
|
OUTPUT_FORMAT(pei-i386)
|
||||||
|
|
||||||
ENTRY("____dll_start")
|
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -209,20 +209,18 @@ void* load_libc()
|
|||||||
raw_img = uf.data;
|
raw_img = uf.data;
|
||||||
raw_size = uf.size;
|
raw_size = uf.size;
|
||||||
|
|
||||||
|
|
||||||
if(raw_img == NULL)
|
if(raw_img == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// printf("libc.dll raw %p, size %d\n", raw_img, raw_size);
|
// printf("libc.dll raw %p, size %d\n", raw_img, raw_size);
|
||||||
|
|
||||||
if( validate_pe(raw_img, raw_size, 0) == 0)
|
if(validate_pe(raw_img, raw_size, 0) != 0)
|
||||||
{
|
{
|
||||||
// printf("invalide libc.dll\n");
|
// printf("invalide libc.dll\n");
|
||||||
user_free(raw_img);
|
img_base = create_image(raw_img);
|
||||||
};
|
};
|
||||||
|
|
||||||
img_base = create_image(raw_img);
|
user_free(raw_img);
|
||||||
|
|
||||||
|
|
||||||
return img_base;
|
return img_base;
|
||||||
|
|
||||||
|
@ -20,10 +20,8 @@
|
|||||||
|
|
||||||
|
|
||||||
void init_loader(void *libc_image);
|
void init_loader(void *libc_image);
|
||||||
void* __fastcall create_image(void *raw);
|
void* create_image(void *raw);
|
||||||
int __fastcall link_image(void *img_base);
|
int link_image(void *img_base, PIMAGE_IMPORT_DESCRIPTOR imp);
|
||||||
int __fastcall do_exec(uint32_t my_app, uint32_t *params);
|
|
||||||
|
|
||||||
|
|
||||||
extern char* __appenv;
|
extern char* __appenv;
|
||||||
extern int __appenv_size;
|
extern int __appenv_size;
|
||||||
@ -148,6 +146,8 @@ void init_loader(void *libc_image)
|
|||||||
PIMAGE_EXPORT_DIRECTORY exp;
|
PIMAGE_EXPORT_DIRECTORY exp;
|
||||||
|
|
||||||
struct app_hdr *header;
|
struct app_hdr *header;
|
||||||
|
dll_path_t *path;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
dll_path_t *path;
|
dll_path_t *path;
|
||||||
int len;
|
int len;
|
||||||
@ -212,16 +212,14 @@ void init_loader(void *libc_image)
|
|||||||
DBG("add libraries path %s\n", path->path);
|
DBG("add libraries path %s\n", path->path);
|
||||||
list_add_tail(&path->list, &path_list);
|
list_add_tail(&path->list, &path_list);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#if 0
|
|
||||||
path = (dll_path_t*)malloc(sizeof(dll_path_t));
|
path = (dll_path_t*)malloc(sizeof(dll_path_t));
|
||||||
INIT_LIST_HEAD(&path->list);
|
INIT_LIST_HEAD(&path->list);
|
||||||
path->path = "/sys/lib/";
|
path->path = "/kolibrios/lib/";
|
||||||
path->path_len = 9; /* FIXME */
|
path->path_len = 15; /* FIXME */
|
||||||
DBG("add libraries path %s\n", path->path);
|
DBG("add libraries path %s\n", path->path);
|
||||||
list_add_tail(&path->list, &path_list);
|
list_add_tail(&path->list, &path_list);
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
INIT_LIST_HEAD(&libc_dll.list);
|
INIT_LIST_HEAD(&libc_dll.list);
|
||||||
|
|
||||||
@ -273,7 +271,7 @@ static inline void sec_copy(void *dst, void *src, size_t len)
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void* __fastcall create_image(void *raw)
|
void* create_image(void *raw)
|
||||||
{
|
{
|
||||||
PIMAGE_DOS_HEADER dos;
|
PIMAGE_DOS_HEADER dos;
|
||||||
PIMAGE_NT_HEADERS32 nt;
|
PIMAGE_NT_HEADERS32 nt;
|
||||||
@ -361,16 +359,13 @@ void* __fastcall create_image(void *raw)
|
|||||||
return img_base;
|
return img_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
static jmp_buf loader_env;
|
//static jmp_buf loader_env;
|
||||||
static loader_recursion;
|
//static loader_recursion;
|
||||||
|
|
||||||
int __fastcall link_image(void *img_base)
|
int link_image(void *img_base, PIMAGE_IMPORT_DESCRIPTOR imp)
|
||||||
{
|
{
|
||||||
static jmp_buf loader_env;
|
static jmp_buf loader_env;
|
||||||
static recursion = -1;
|
static recursion = -1;
|
||||||
|
|
||||||
PIMAGE_DOS_HEADER dos;
|
|
||||||
PIMAGE_NT_HEADERS32 nt;
|
|
||||||
int warn = 0;
|
int warn = 0;
|
||||||
|
|
||||||
recursion++;
|
recursion++;
|
||||||
@ -383,16 +378,6 @@ int __fastcall link_image(void *img_base)
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
dos = (PIMAGE_DOS_HEADER)img_base;
|
|
||||||
nt = MakePtr( PIMAGE_NT_HEADERS32, dos, dos->e_lfanew);
|
|
||||||
|
|
||||||
if(nt->OptionalHeader.DataDirectory[1].Size)
|
|
||||||
{
|
|
||||||
PIMAGE_IMPORT_DESCRIPTOR imp;
|
|
||||||
|
|
||||||
imp = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, img_base,
|
|
||||||
nt->OptionalHeader.DataDirectory[1].VirtualAddress);
|
|
||||||
|
|
||||||
while ( imp->Name )
|
while ( imp->Name )
|
||||||
{
|
{
|
||||||
PIMAGE_DOS_HEADER expdos;
|
PIMAGE_DOS_HEADER expdos;
|
||||||
@ -540,7 +525,6 @@ int __fastcall link_image(void *img_base)
|
|||||||
}
|
}
|
||||||
imp++; // advance to next IMAGE_IMPORT_DESCRIPTOR
|
imp++; // advance to next IMAGE_IMPORT_DESCRIPTOR
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
recursion--;
|
recursion--;
|
||||||
|
|
||||||
@ -552,175 +536,15 @@ int __fastcall link_image(void *img_base)
|
|||||||
|
|
||||||
int link_app()
|
int link_app()
|
||||||
{
|
{
|
||||||
PIMAGE_IMPORT_DESCRIPTOR imp;
|
|
||||||
|
|
||||||
struct app_hdr *header = NULL;
|
struct app_hdr *header = NULL;
|
||||||
|
PIMAGE_IMPORT_DESCRIPTOR imp;
|
||||||
int warn = 0;
|
|
||||||
|
|
||||||
if( unlikely(setjmp(loader_env) != 0))
|
|
||||||
{
|
|
||||||
loader_recursion = 0;
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
imp = (PIMAGE_IMPORT_DESCRIPTOR)header->__idata_start;
|
imp = (PIMAGE_IMPORT_DESCRIPTOR)header->__idata_start;
|
||||||
|
|
||||||
while ( imp->Name )
|
return link_image(NULL, imp);
|
||||||
{
|
|
||||||
PIMAGE_DOS_HEADER expdos;
|
|
||||||
PIMAGE_NT_HEADERS32 expnt;
|
|
||||||
PIMAGE_EXPORT_DIRECTORY exp;
|
|
||||||
PIMAGE_THUNK_DATA32 thunk;
|
|
||||||
|
|
||||||
void **iat;
|
|
||||||
char *libname;
|
|
||||||
uint32_t *exp_functions;
|
|
||||||
uint16_t *exp_ordinals;
|
|
||||||
char **exp_names;
|
|
||||||
|
|
||||||
const module_t *api;
|
|
||||||
|
|
||||||
libname=MakePtr(char*,imp->Name, NULL);
|
|
||||||
|
|
||||||
DBG("import from %s\n",libname);
|
|
||||||
|
|
||||||
api = find_module(libname);
|
|
||||||
if(unlikely(api == NULL))
|
|
||||||
{
|
|
||||||
printf("library %s not found\n", libname);
|
|
||||||
longjmp(loader_env, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iat = MakePtr(void**,imp->FirstThunk, NULL);
|
|
||||||
|
|
||||||
if(imp->OriginalFirstThunk !=0 )
|
|
||||||
{
|
|
||||||
thunk = MakePtr(PIMAGE_THUNK_DATA32,imp->OriginalFirstThunk, NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
thunk = MakePtr(PIMAGE_THUNK_DATA32,imp->FirstThunk, NULL);
|
|
||||||
};
|
|
||||||
|
|
||||||
exp = api->img_exp;
|
|
||||||
|
|
||||||
exp_functions = MakePtr(uint32_t*,exp->AddressOfFunctions,api->start);
|
|
||||||
exp_ordinals = MakePtr(uint16_t*, exp->AddressOfNameOrdinals,api->start);
|
|
||||||
exp_names = MakePtr(char**, exp->AddressOfNames,api->start);
|
|
||||||
|
|
||||||
while ( thunk->u1.AddressOfData != 0 )
|
|
||||||
{
|
|
||||||
PIMAGE_IMPORT_BY_NAME imp_name;
|
|
||||||
|
|
||||||
if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
|
|
||||||
{
|
|
||||||
// ordinal = (*func_list) & 0x7fffffff;
|
|
||||||
// *ImportAddressList = LdrGetExportByOrdinal(ImportedModule->DllBase, Ordinal);
|
|
||||||
// if ((*ImportAddressList) == NULL)
|
|
||||||
// {
|
|
||||||
// DPRINT1("Failed to import #%ld from %wZ\n", Ordinal, &ImportedModule->FullDllName);
|
|
||||||
// RtlpRaiseImportNotFound(NULL, Ordinal, &ImportedModule->FullDllName);
|
|
||||||
// return STATUS_ENTRYPOINT_NOT_FOUND;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char *export_name;
|
|
||||||
uint16_t ordinal;
|
|
||||||
void *function;
|
|
||||||
uint32_t minn;
|
|
||||||
uint32_t maxn;
|
|
||||||
|
|
||||||
imp_name = MakePtr(PIMAGE_IMPORT_BY_NAME,
|
|
||||||
thunk->u1.AddressOfData, NULL);
|
|
||||||
*iat = NULL;
|
|
||||||
|
|
||||||
DBG("import %s", imp_name->Name);
|
|
||||||
|
|
||||||
if(imp_name->Hint < exp->NumberOfNames)
|
|
||||||
{
|
|
||||||
export_name = MakePtr(char*,exp_names[imp_name->Hint],
|
|
||||||
api->start);
|
|
||||||
if(strcmp(imp_name->Name, export_name) == 0)
|
|
||||||
{
|
|
||||||
ordinal = exp_ordinals[imp_name->Hint];
|
|
||||||
function = MakePtr(void*,exp_functions[ordinal], api->start);
|
|
||||||
if((uint32_t)function >= (uint32_t)exp)
|
|
||||||
{
|
|
||||||
printf("forward %s\n", function);
|
|
||||||
warn=1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DBG(" \t\tat %x\n", function);
|
|
||||||
*iat = function;
|
|
||||||
};
|
|
||||||
thunk++; // Advance to next thunk
|
|
||||||
iat++;
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
minn = 0;
|
|
||||||
maxn = exp->NumberOfNames - 1;
|
|
||||||
while (minn <= maxn)
|
|
||||||
{
|
|
||||||
int mid;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
mid = (minn + maxn) / 2;
|
|
||||||
|
|
||||||
export_name = MakePtr(char*,exp_names[mid],api->start);
|
|
||||||
|
|
||||||
res = strcmp(export_name, imp_name->Name);
|
|
||||||
if (res == 0)
|
|
||||||
{
|
|
||||||
ordinal = exp_ordinals[mid];
|
|
||||||
function = MakePtr(void*,exp_functions[ordinal], api->start);
|
|
||||||
|
|
||||||
if((uint32_t)function >= (uint32_t)exp)
|
|
||||||
{
|
|
||||||
DBG("forward %s\n", function);
|
|
||||||
warn=1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DBG(" \t\tat %x\n", function);
|
|
||||||
*iat = function;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (minn == maxn)
|
|
||||||
{
|
|
||||||
printf(" unresolved %s\n",imp_name->Name);
|
|
||||||
warn=1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (res > 0)
|
|
||||||
{
|
|
||||||
maxn = mid - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
minn = mid + 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
thunk++; // Advance to next thunk
|
|
||||||
iat++;
|
|
||||||
}
|
|
||||||
imp++; // advance to next IMAGE_IMPORT_DESCRIPTOR
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( !warn )
|
|
||||||
return 1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void* get_entry_point(void *raw)
|
void* get_entry_point(void *raw)
|
||||||
{
|
{
|
||||||
@ -804,13 +628,13 @@ void *get_proc_address(module_t *module, char *proc_name)
|
|||||||
|
|
||||||
module_t* load_module(const char *name)
|
module_t* load_module(const char *name)
|
||||||
{
|
{
|
||||||
|
dll_path_t *dllpath;
|
||||||
|
|
||||||
char *path;
|
char *path;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = strlen(name);
|
len = strlen(name);
|
||||||
|
|
||||||
dll_path_t *dllpath;
|
|
||||||
|
|
||||||
list_for_each_entry(dllpath, &path_list, list)
|
list_for_each_entry(dllpath, &path_list, list)
|
||||||
{
|
{
|
||||||
PIMAGE_DOS_HEADER dos;
|
PIMAGE_DOS_HEADER dos;
|
||||||
@ -818,6 +642,7 @@ module_t* load_module(const char *name)
|
|||||||
PIMAGE_EXPORT_DIRECTORY exp;
|
PIMAGE_EXPORT_DIRECTORY exp;
|
||||||
|
|
||||||
module_t *module;
|
module_t *module;
|
||||||
|
ufile_t uf;
|
||||||
void *raw_img;
|
void *raw_img;
|
||||||
size_t raw_size;
|
size_t raw_size;
|
||||||
void *img_base;
|
void *img_base;
|
||||||
@ -828,10 +653,16 @@ module_t* load_module(const char *name)
|
|||||||
memcpy(path+dllpath->path_len, name, len);
|
memcpy(path+dllpath->path_len, name, len);
|
||||||
path[len+dllpath->path_len]=0;
|
path[len+dllpath->path_len]=0;
|
||||||
|
|
||||||
// raw_img = load_file(path, &raw_size);
|
// printf("%s %s\n", path);
|
||||||
|
|
||||||
|
uf = load_file(path);
|
||||||
|
raw_img = uf.data;
|
||||||
|
raw_size = uf.size;
|
||||||
|
|
||||||
if(raw_img == NULL)
|
if(raw_img == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
if( validate_pe(raw_img, raw_size, 0) == 0)
|
if( validate_pe(raw_img, raw_size, 0) == 0)
|
||||||
{
|
{
|
||||||
printf("invalide module %s\n", path);
|
printf("invalide module %s\n", path);
|
||||||
@ -854,7 +685,7 @@ module_t* load_module(const char *name)
|
|||||||
{
|
{
|
||||||
printf("%s epic fail: no enough memory\n",__FUNCTION__);
|
printf("%s epic fail: no enough memory\n",__FUNCTION__);
|
||||||
user_free(img_base);
|
user_free(img_base);
|
||||||
return 0;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_LIST_HEAD(&module->list);
|
INIT_LIST_HEAD(&module->list);
|
||||||
@ -879,22 +710,24 @@ module_t* load_module(const char *name)
|
|||||||
|
|
||||||
list_add_tail(&module->list, &libc_dll.list);
|
list_add_tail(&module->list, &libc_dll.list);
|
||||||
|
|
||||||
if( link_image(img_base))
|
if(nt->OptionalHeader.DataDirectory[1].Size)
|
||||||
{
|
{
|
||||||
|
PIMAGE_IMPORT_DESCRIPTOR imp;
|
||||||
int (*dll_startup)(module_t *mod, uint32_t reason);
|
int (*dll_startup)(module_t *mod, uint32_t reason);
|
||||||
|
|
||||||
|
imp = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, img_base,
|
||||||
|
nt->OptionalHeader.DataDirectory[1].VirtualAddress);
|
||||||
|
|
||||||
|
if(link_image(img_base, imp) == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
dll_startup = get_proc_address(module, "DllStartup");
|
dll_startup = get_proc_address(module, "DllStartup");
|
||||||
if( dll_startup )
|
if( dll_startup )
|
||||||
{
|
|
||||||
if( 0 == dll_startup(module, 1))
|
if( 0 == dll_startup(module, 1))
|
||||||
return 0;
|
return NULL;
|
||||||
}
|
};
|
||||||
return module;
|
return module;
|
||||||
};
|
};
|
||||||
|
|
||||||
return NULL;
|
|
||||||
};
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user