gdb/ChangeLog:
* solib-svr4.h (struct link_map_offsets): Add l_ld_offset and l_ld_size fields. * solib-svr4.c (struct lm_info): Add l_addr field. (LM_ADDR_FROM_LINK_MAP): Renamed from LM_ADDR. (HAS_LM_DYNAMIC_FROM_LINK_MAP): New. (LM_DYNAMIC_FROM_LINK_MAP): New. (LM_ADDR_CHECK): New. Use it instead of LM_ADDR. (svr4_current_sos): Initialize l_addr. Adjust. (svr4_relocate_section_addresses): Adjust. (svr4_ilp32_fetch_link_map_offsets): Define new members. (svr4_lp64_fetch_link_map_offsets): Likewise. * solib-legacy.c (legacy_svr4_fetch_link_map_offsets): Likewise. * mipsnbsd-tdep.c (mipsnbsd_ilp32_fetch_link_map_offsets): Likewise. (mipsnbsd_lp64_fetch_link_map_offsets): Likewise. * Makefile.in (solib-svr4.o): Depend on $(elf_bfd_h). gdb/testsuite/ChangeLog: * gdb.base/prelink.exp: New test. * gdb.base/prelink.c, gdb.base/prelink-lib.c: New sources.
This commit is contained in:
parent
10cd14b412
commit
cc10cae34a
@ -1,3 +1,21 @@
|
||||
2006-02-28 Alexandre Oliva <aoliva@redhat.com>
|
||||
|
||||
* solib-svr4.h (struct link_map_offsets): Add l_ld_offset and
|
||||
l_ld_size fields.
|
||||
* solib-svr4.c (struct lm_info): Add l_addr field.
|
||||
(LM_ADDR_FROM_LINK_MAP): Renamed from LM_ADDR.
|
||||
(HAS_LM_DYNAMIC_FROM_LINK_MAP): New.
|
||||
(LM_DYNAMIC_FROM_LINK_MAP): New.
|
||||
(LM_ADDR_CHECK): New. Use it instead of LM_ADDR.
|
||||
(svr4_current_sos): Initialize l_addr. Adjust.
|
||||
(svr4_relocate_section_addresses): Adjust.
|
||||
(svr4_ilp32_fetch_link_map_offsets): Define new members.
|
||||
(svr4_lp64_fetch_link_map_offsets): Likewise.
|
||||
* solib-legacy.c (legacy_svr4_fetch_link_map_offsets): Likewise.
|
||||
* mipsnbsd-tdep.c (mipsnbsd_ilp32_fetch_link_map_offsets): Likewise.
|
||||
(mipsnbsd_lp64_fetch_link_map_offsets): Likewise.
|
||||
* Makefile.in (solib-svr4.o): Depend on $(elf_bfd_h).
|
||||
|
||||
2006-02-26 David S. Miller <davem@sunset.davemloft.net>
|
||||
|
||||
* config/sparc/linux.mt (TDEPFILES): Add sol2-tdep.o.
|
||||
|
@ -2582,7 +2582,8 @@ solib-sunos.o: solib-sunos.c $(defs_h) $(gdb_string_h) $(symtab_h) $(bfd_h) \
|
||||
solib-svr4.o: solib-svr4.c $(defs_h) $(elf_external_h) $(elf_common_h) \
|
||||
$(elf_mips_h) $(symtab_h) $(bfd_h) $(symfile_h) $(objfiles_h) \
|
||||
$(gdbcore_h) $(target_h) $(inferior_h) $(gdb_assert_h) \
|
||||
$(solist_h) $(solib_h) $(solib_svr4_h) $(bfd_target_h) $(exec_h)
|
||||
$(solist_h) $(solib_h) $(solib_svr4_h) $(bfd_target_h) $(elf_bfd_h) \
|
||||
$(exec_h)
|
||||
sol-thread.o: sol-thread.c $(defs_h) $(gdbthread_h) $(target_h) \
|
||||
$(inferior_h) $(gdb_stat_h) $(gdbcmd_h) $(gdbcore_h) $(regcache_h) \
|
||||
$(solib_h) $(symfile_h) $(gdb_string_h) $(gregset_h)
|
||||
|
@ -339,6 +339,8 @@ mipsnbsd_ilp32_fetch_link_map_offsets (void)
|
||||
lmo.l_addr_size = 4;
|
||||
lmo.l_name_offset = 8;
|
||||
lmo.l_name_size = 4;
|
||||
lmo.l_ld_offset = 12;
|
||||
lmo.l_ld_size = 4;
|
||||
lmo.l_next_offset = 16;
|
||||
lmo.l_next_size = 4;
|
||||
lmo.l_prev_offset = 20;
|
||||
@ -369,6 +371,8 @@ mipsnbsd_lp64_fetch_link_map_offsets (void)
|
||||
lmo.l_addr_size = 8;
|
||||
lmo.l_name_offset = 16;
|
||||
lmo.l_name_size = 8;
|
||||
lmo.l_ld_offset = 24;
|
||||
lmo.l_ld_size = 8;
|
||||
lmo.l_next_offset = 32;
|
||||
lmo.l_next_size = 8;
|
||||
lmo.l_prev_offset = 40;
|
||||
|
@ -69,6 +69,9 @@ legacy_svr4_fetch_link_map_offsets (void)
|
||||
lmo.l_next_offset = offsetof (struct link_map, l_next);
|
||||
lmo.l_next_size = fieldsize (struct link_map, l_next);
|
||||
|
||||
lmo.l_ld_offset = offsetof (struct link_map, l_ld);
|
||||
lmo.l_ld_size = fieldsize (struct link_map, l_ld);
|
||||
|
||||
lmo.l_prev_offset = offsetof (struct link_map, l_prev);
|
||||
lmo.l_prev_size = fieldsize (struct link_map, l_prev);
|
||||
|
||||
@ -84,6 +87,10 @@ legacy_svr4_fetch_link_map_offsets (void)
|
||||
lmo.l_next_offset = offsetof (struct link_map, lm_next);
|
||||
lmo.l_next_size = fieldsize (struct link_map, lm_next);
|
||||
|
||||
/* FIXME: Is this the right field name, or is it available at all? */
|
||||
lmo.l_ld_offset = offsetof (struct link_map, lm_ld);
|
||||
lmo.l_ld_size = fieldsize (struct link_map, lm_ld);
|
||||
|
||||
lmo.l_name_offset = offsetof (struct link_map, lm_name);
|
||||
lmo.l_name_size = fieldsize (struct link_map, lm_name);
|
||||
#else /* !defined(HAVE_STRUCT_LINK_MAP_WITH_LM_MEMBERS) */
|
||||
@ -98,6 +105,10 @@ legacy_svr4_fetch_link_map_offsets (void)
|
||||
|
||||
lmo.l_name_offset = offsetof (struct so_map, som_path);
|
||||
lmo.l_name_size = fieldsize (struct so_map, som_path);
|
||||
|
||||
/* FIXME: Is the address of the dynamic table available? */
|
||||
lmo.l_ld_offset = 0;
|
||||
lmo.l_ld_size = 0;
|
||||
#endif /* HAVE_STRUCT_SO_MAP_WITH_SOM_MEMBERS */
|
||||
#endif /* HAVE_STRUCT_LINK_MAP_WITH_LM_MEMBERS */
|
||||
#endif /* HAVE_STRUCT_LINK_MAP_WITH_L_MEMBERS */
|
||||
|
113
gdb/solib-svr4.c
113
gdb/solib-svr4.c
@ -42,6 +42,7 @@
|
||||
#include "solib-svr4.h"
|
||||
|
||||
#include "bfd-target.h"
|
||||
#include "elf-bfd.h"
|
||||
#include "exec.h"
|
||||
|
||||
static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
|
||||
@ -59,6 +60,13 @@ struct lm_info
|
||||
rather than void *, so that we may use byte offsets to find the
|
||||
various fields without the need for a cast. */
|
||||
gdb_byte *lm;
|
||||
|
||||
/* Amount by which addresses in the binary should be relocated to
|
||||
match the inferior. This could most often be taken directly
|
||||
from lm, but when prelinking is involved and the prelink base
|
||||
address changes, we may need a different offset, we want to
|
||||
warn about the difference and compute it only once. */
|
||||
CORE_ADDR l_addr;
|
||||
};
|
||||
|
||||
/* On SVR4 systems, a list of symbols in the dynamic linker where
|
||||
@ -127,14 +135,101 @@ static char *main_name_list[] =
|
||||
/* link map access functions */
|
||||
|
||||
static CORE_ADDR
|
||||
LM_ADDR (struct so_list *so)
|
||||
LM_ADDR_FROM_LINK_MAP (struct so_list *so)
|
||||
{
|
||||
struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
|
||||
|
||||
return (CORE_ADDR) extract_signed_integer (so->lm_info->lm + lmo->l_addr_offset,
|
||||
return (CORE_ADDR) extract_signed_integer (so->lm_info->lm
|
||||
+ lmo->l_addr_offset,
|
||||
lmo->l_addr_size);
|
||||
}
|
||||
|
||||
static int
|
||||
HAS_LM_DYNAMIC_FROM_LINK_MAP ()
|
||||
{
|
||||
struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
|
||||
|
||||
return (lmo->l_ld_size != 0);
|
||||
}
|
||||
|
||||
static CORE_ADDR
|
||||
LM_DYNAMIC_FROM_LINK_MAP (struct so_list *so)
|
||||
{
|
||||
struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
|
||||
|
||||
gdb_assert (lmo->l_ld_size != 0);
|
||||
|
||||
return (CORE_ADDR) extract_signed_integer (so->lm_info->lm
|
||||
+ lmo->l_ld_offset,
|
||||
lmo->l_ld_size);
|
||||
}
|
||||
|
||||
static CORE_ADDR
|
||||
LM_ADDR_CHECK (struct so_list *so, bfd *abfd)
|
||||
{
|
||||
if (so->lm_info->l_addr == (CORE_ADDR)-1)
|
||||
{
|
||||
struct bfd_section *dyninfo_sect;
|
||||
CORE_ADDR l_addr, l_dynaddr, dynaddr, align = 0x1000;
|
||||
|
||||
l_addr = LM_ADDR_FROM_LINK_MAP (so);
|
||||
|
||||
if (! abfd || ! HAS_LM_DYNAMIC_FROM_LINK_MAP ())
|
||||
goto set_addr;
|
||||
|
||||
l_dynaddr = LM_DYNAMIC_FROM_LINK_MAP (so);
|
||||
|
||||
dyninfo_sect = bfd_get_section_by_name (abfd, ".dynamic");
|
||||
if (dyninfo_sect == NULL)
|
||||
goto set_addr;
|
||||
|
||||
dynaddr = bfd_section_vma (abfd, dyninfo_sect);
|
||||
|
||||
if (dynaddr + l_addr != l_dynaddr)
|
||||
{
|
||||
warning (_(".dynamic section for \"%s\" "
|
||||
"is not at the expected address"), so->so_name);
|
||||
|
||||
if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
|
||||
{
|
||||
Elf_Internal_Ehdr *ehdr = elf_tdata (abfd)->elf_header;
|
||||
Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr;
|
||||
int i;
|
||||
|
||||
align = 1;
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++)
|
||||
if (phdr[i].p_type == PT_LOAD && phdr[i].p_align > align)
|
||||
align = phdr[i].p_align;
|
||||
}
|
||||
|
||||
/* Turn it into a mask. */
|
||||
align--;
|
||||
|
||||
/* If the changes match the alignment requirements, we
|
||||
assume we're using a core file that was generated by the
|
||||
same binary, just prelinked with a different base offset.
|
||||
If it doesn't match, we may have a different binary, the
|
||||
same binary with the dynamic table loaded at an unrelated
|
||||
location, or anything, really. To avoid regressions,
|
||||
don't adjust the base offset in the latter case, although
|
||||
odds are that, if things really changed, debugging won't
|
||||
quite work. */
|
||||
if ((l_addr & align) == 0 && ((dynaddr - l_dynaddr) & align) == 0)
|
||||
{
|
||||
l_addr = l_dynaddr - dynaddr;
|
||||
warning (_("difference appears to be caused by prelink, "
|
||||
"adjusting expectations"));
|
||||
}
|
||||
}
|
||||
|
||||
set_addr:
|
||||
so->lm_info->l_addr = l_addr;
|
||||
}
|
||||
|
||||
return so->lm_info->l_addr;
|
||||
}
|
||||
|
||||
static CORE_ADDR
|
||||
LM_NEXT (struct so_list *so)
|
||||
{
|
||||
@ -649,6 +744,8 @@ svr4_current_sos (void)
|
||||
free_so (new);
|
||||
else
|
||||
{
|
||||
new->lm_info->l_addr = (CORE_ADDR)-1;
|
||||
|
||||
new->next = 0;
|
||||
*link_ptr = new;
|
||||
link_ptr = &new->next;
|
||||
@ -912,7 +1009,7 @@ enable_break (void)
|
||||
if (strcmp (buf, so->so_original_name) == 0)
|
||||
{
|
||||
load_addr_found = 1;
|
||||
load_addr = LM_ADDR (so);
|
||||
load_addr = LM_ADDR_CHECK (so, tmp_bfd);
|
||||
break;
|
||||
}
|
||||
so = so->next;
|
||||
@ -1272,8 +1369,10 @@ static void
|
||||
svr4_relocate_section_addresses (struct so_list *so,
|
||||
struct section_table *sec)
|
||||
{
|
||||
sec->addr = svr4_truncate_ptr (sec->addr + LM_ADDR (so));
|
||||
sec->endaddr = svr4_truncate_ptr (sec->endaddr + LM_ADDR (so));
|
||||
sec->addr = svr4_truncate_ptr (sec->addr + LM_ADDR_CHECK (so,
|
||||
sec->bfd));
|
||||
sec->endaddr = svr4_truncate_ptr (sec->endaddr + LM_ADDR_CHECK (so,
|
||||
sec->bfd));
|
||||
}
|
||||
|
||||
|
||||
@ -1362,6 +1461,8 @@ svr4_ilp32_fetch_link_map_offsets (void)
|
||||
lmo.l_addr_size = 4;
|
||||
lmo.l_name_offset = 4;
|
||||
lmo.l_name_size = 4;
|
||||
lmo.l_ld_offset = 8;
|
||||
lmo.l_ld_size = 4;
|
||||
lmo.l_next_offset = 12;
|
||||
lmo.l_next_size = 4;
|
||||
lmo.l_prev_offset = 16;
|
||||
@ -1395,6 +1496,8 @@ svr4_lp64_fetch_link_map_offsets (void)
|
||||
lmo.l_addr_size = 8;
|
||||
lmo.l_name_offset = 8;
|
||||
lmo.l_name_size = 8;
|
||||
lmo.l_ld_offset = 16;
|
||||
lmo.l_ld_size = 8;
|
||||
lmo.l_next_offset = 24;
|
||||
lmo.l_next_size = 8;
|
||||
lmo.l_prev_offset = 32;
|
||||
|
@ -50,6 +50,12 @@ struct link_map_offsets
|
||||
/* Size of l_addr field in struct link_map. */
|
||||
int l_addr_size;
|
||||
|
||||
/* Offset to l_ld field in struct link_map. */
|
||||
int l_ld_offset;
|
||||
|
||||
/* Size of l_ld field in struct link_map. */
|
||||
int l_ld_size;
|
||||
|
||||
/* Offset to l_next field in struct link_map. */
|
||||
int l_next_offset;
|
||||
|
||||
|
@ -1,3 +1,8 @@
|
||||
2006-02-28 Alexandre Oliva <aoliva@redhat.com>
|
||||
|
||||
* gdb.base/prelink.exp: New test.
|
||||
* gdb.base/prelink.c, gdb.base/prelink-lib.c: New sources.
|
||||
|
||||
2006-02-24 Wu Zhou <woodzltc@cn.ibm.com>
|
||||
|
||||
* gdb.fortran/derived-type.f90: New file.
|
||||
|
34
gdb/testsuite/gdb.base/prelink-lib.c
Normal file
34
gdb/testsuite/gdb.base/prelink-lib.c
Normal file
@ -0,0 +1,34 @@
|
||||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2006 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
int
|
||||
g (void (*p)(void))
|
||||
{
|
||||
p ();
|
||||
}
|
||||
|
||||
void
|
||||
f(void (*p)(void)) {
|
||||
g (p);
|
||||
}
|
||||
|
||||
void (*h (void)) (void (*p)(void))
|
||||
{
|
||||
return f;
|
||||
}
|
30
gdb/testsuite/gdb.base/prelink.c
Normal file
30
gdb/testsuite/gdb.base/prelink.c
Normal file
@ -0,0 +1,30 @@
|
||||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2006 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern void (*h (void)) (void (*)(void));
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
void (*f) (void (*)(void)) = h ();
|
||||
printf ("%p\n", f);
|
||||
f (0);
|
||||
}
|
128
gdb/testsuite/gdb.base/prelink.exp
Normal file
128
gdb/testsuite/gdb.base/prelink.exp
Normal file
@ -0,0 +1,128 @@
|
||||
# Copyright 2006 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
# Please email any bugs, comments, and/or additions to this file to:
|
||||
# bug-gdb@prep.ai.mit.edu
|
||||
|
||||
# This file was written by Alexandre Oliva <aoliva@redhat.com>
|
||||
|
||||
if $tracelevel then {
|
||||
strace $tracelevel
|
||||
}
|
||||
|
||||
set prms_id 0
|
||||
set bug_id 0
|
||||
|
||||
# are we on a target board
|
||||
if ![isnative] then {
|
||||
return
|
||||
}
|
||||
|
||||
if [get_compiler_info "ignored"] {
|
||||
return -1
|
||||
}
|
||||
|
||||
if {$gcc_compiled == 0} {
|
||||
return -1
|
||||
}
|
||||
|
||||
set testfile "prelink"
|
||||
set srcfile ${testfile}.c
|
||||
set binfile ${objdir}/${subdir}/${testfile}
|
||||
|
||||
set libsrcfile ${testfile}-lib.c
|
||||
set libfile ${objdir}/${subdir}/${testfile}.so
|
||||
if { [gdb_compile "${srcdir}/${subdir}/${libsrcfile}" "${libfile}" executable [list debug "additional_flags=-fpic -shared -nodefaultlibs"]] != ""} {
|
||||
# If creating the shared library fails, maybe we don't have the right tools
|
||||
return -1
|
||||
}
|
||||
|
||||
if {[catch "system \"prelink -NR ${libfile}\""] != 0} {
|
||||
# Maybe we don't have prelink.
|
||||
return -1
|
||||
}
|
||||
|
||||
set srcfile ${testfile}.c
|
||||
set binfile ${objdir}/${subdir}/${testfile}
|
||||
if { [gdb_compile "${srcdir}/${subdir}/${srcfile} ${libfile}" "${binfile}" executable [list debug "additional_flags=-Wl,-rpath,${objdir}/${subdir}"]] != ""} {
|
||||
return -1;
|
||||
}
|
||||
|
||||
set found 0
|
||||
set coredir "${objdir}/${subdir}/coredir.[getpid]"
|
||||
file mkdir $coredir
|
||||
catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile}; true) >/dev/null 2>&1\""
|
||||
|
||||
foreach i "${coredir}/core ${coredir}/core.coremaker.c ${binfile}.core" {
|
||||
if [remote_file build exists $i] {
|
||||
remote_exec build "mv $i ${objdir}/${subdir}/prelink.core"
|
||||
set found 1
|
||||
}
|
||||
}
|
||||
# Check for "core.PID".
|
||||
if { $found == 0 } {
|
||||
set names [glob -nocomplain -directory $coredir core.*]
|
||||
if {[llength $names] == 1} {
|
||||
set corefile [file join $coredir [lindex $names 0]]
|
||||
remote_exec build "mv $corefile ${objdir}/${subdir}/prelink.core"
|
||||
set found 1
|
||||
}
|
||||
}
|
||||
|
||||
catch "system \"prelink -u ${libfile}\""
|
||||
catch "system \"prelink -NR ${libfile}\""
|
||||
|
||||
# Try to clean up after ourselves.
|
||||
remote_file build delete [file join $coredir coremmap.data]
|
||||
remote_exec build "rmdir $coredir"
|
||||
|
||||
if { $found == 0 } {
|
||||
warning "can't generate a core file - prelink tests suppressed - check ulimit -c"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Start with a fresh gdb
|
||||
|
||||
gdb_exit
|
||||
gdb_start
|
||||
gdb_reinitialize_dir $srcdir/$subdir
|
||||
gdb_load ${binfile}
|
||||
|
||||
set oldtimeout $timeout
|
||||
set timeout [expr "$timeout + 60"]
|
||||
verbose "Timeout is now $timeout seconds" 2
|
||||
send_gdb "core-file $objdir/$subdir/prelink.core\n"
|
||||
gdb_expect {
|
||||
-re "warning: \.dynamic section.*not at the expected address" {
|
||||
pass "changed base address"
|
||||
}
|
||||
-re ".*$gdb_prompt $" { fail "changed base address" }
|
||||
timeout { fail "(timeout) changed base address" }
|
||||
}
|
||||
gdb_expect {
|
||||
-re "warning: difference.*caused by prelink, adjusting" {
|
||||
pass "prelink adjustment"
|
||||
}
|
||||
-re ".*$gdb_prompt $" { fail "prelink adjustment" }
|
||||
timeout { fail "(timeout) prelink adjustment" }
|
||||
}
|
||||
set timeout $oldtimeout
|
||||
verbose "Timeout is now $timeout seconds" 2
|
||||
|
||||
gdb_exit
|
||||
|
||||
return 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user