X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/43387ee1abcd83c3c7586b7f7aa86e838d239aac..6af091cddde7717ea89d9dd2196f18d5f4431198:/util.c diff --git a/util.c b/util.c index 1fd1e00..4666233 100644 --- a/util.c +++ b/util.c @@ -26,7 +26,7 @@ #include "perl.h" #include "reentr.h" -#ifdef USE_PERLIO +#if defined(USE_PERLIO) #include "perliol.h" /* For PerlIOUnix_refcnt */ #endif @@ -37,6 +37,9 @@ #endif #endif +#include +#include + #ifdef __Lynx__ /* Missing protos on LynxOS */ int putenv(char *); @@ -48,12 +51,30 @@ int putenv(char *); # endif #endif -#define FLUSH +#ifdef USE_C_BACKTRACE +# ifdef I_BFD +# define USE_BFD +# ifdef PERL_DARWIN +# undef USE_BFD /* BFD is useless in OS X. */ +# endif +# ifdef USE_BFD +# include +# endif +# endif +# ifdef I_DLFCN +# include +# endif +# ifdef I_EXECINFO +# include +# endif +#endif -#if defined(HAS_FCNTL) && defined(F_SETFD) && !defined(FD_CLOEXEC) -# define FD_CLOEXEC 1 /* NeXT needs this */ +#ifdef PERL_DEBUG_READONLY_COW +# include #endif +#define FLUSH + /* NOTE: Do not call the next three routines directly. Use the macros * in handy.h, so that we can easily redefine everything to do tracking of * allocated hunks back to the original New to track down any memory leaks. @@ -64,6 +85,40 @@ int putenv(char *); # define ALWAYS_NEED_THX #endif +#if defined(PERL_TRACK_MEMPOOL) && defined(PERL_DEBUG_READONLY_COW) +static void +S_maybe_protect_rw(pTHX_ struct perl_memory_debug_header *header) +{ + if (header->readonly + && mprotect(header, header->size, PROT_READ|PROT_WRITE)) + Perl_warn(aTHX_ "mprotect for COW string %p %lu failed with %d", + header, header->size, errno); +} + +static void +S_maybe_protect_ro(pTHX_ struct perl_memory_debug_header *header) +{ + if (header->readonly + && mprotect(header, header->size, PROT_READ)) + Perl_warn(aTHX_ "mprotect RW for COW string %p %lu failed with %d", + header, header->size, errno); +} +# define maybe_protect_rw(foo) S_maybe_protect_rw(aTHX_ foo) +# define maybe_protect_ro(foo) S_maybe_protect_ro(aTHX_ foo) +#else +# define maybe_protect_rw(foo) NOOP +# define maybe_protect_ro(foo) NOOP +#endif + +#if defined(PERL_TRACK_MEMPOOL) || defined(PERL_DEBUG_READONLY_COW) + /* Use memory_debug_header */ +# define USE_MDH +# if (defined(PERL_POISON) && defined(PERL_TRACK_MEMPOOL)) \ + || defined(PERL_DEBUG_READONLY_COW) +# define MDH_HAS_SIZE +# endif +#endif + /* paranoid version of system's malloc() */ Malloc_t @@ -73,24 +128,24 @@ Perl_safesysmalloc(MEM_SIZE size) dTHX; #endif Malloc_t ptr; -#ifdef HAS_64K_LIMIT - if (size > 0xffff) { - PerlIO_printf(Perl_error_log, - "Allocation too large: %lx\n", size) FLUSH; - my_exit(1); - } -#endif /* HAS_64K_LIMIT */ -#ifdef PERL_TRACK_MEMPOOL - size += sTHX; -#endif + size += PERL_MEMORY_DEBUG_HEADER_SIZE; #ifdef DEBUGGING if ((SSize_t)size < 0) Perl_croak_nocontext("panic: malloc, size=%"UVuf, (UV) size); #endif - ptr = (Malloc_t)PerlMem_malloc(size?size:1); /* malloc(0) is NASTY on our system */ + if (!size) size = 1; /* malloc(0) is NASTY on our system */ +#ifdef PERL_DEBUG_READONLY_COW + if ((ptr = mmap(0, size, PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) { + perror("mmap failed"); + abort(); + } +#else + ptr = (Malloc_t)PerlMem_malloc(size?size:1); +#endif PERL_ALLOC_CHECK(ptr); if (ptr != NULL) { -#ifdef PERL_TRACK_MEMPOOL +#ifdef USE_MDH struct perl_memory_debug_header *const header = (struct perl_memory_debug_header *)ptr; #endif @@ -105,12 +160,17 @@ Perl_safesysmalloc(MEM_SIZE size) header->prev = &PL_memory_debug_header; header->next = PL_memory_debug_header.next; PL_memory_debug_header.next = header; + maybe_protect_rw(header->next); header->next->prev = header; -# ifdef PERL_POISON - header->size = size; + maybe_protect_ro(header->next); +# ifdef PERL_DEBUG_READONLY_COW + header->readonly = 0; # endif - ptr = (Malloc_t)((char*)ptr+sTHX); #endif +#ifdef MDH_HAS_SIZE + header->size = size; +#endif + ptr = (Malloc_t)((char*)ptr+PERL_MEMORY_DEBUG_HEADER_SIZE); DEBUG_m(PerlIO_printf(Perl_debug_log, "0x%"UVxf": (%05ld) malloc %ld bytes\n",PTR2UV(ptr),(long)PL_an++,(long)size)); return ptr; } @@ -136,17 +196,15 @@ Perl_safesysrealloc(Malloc_t where,MEM_SIZE size) dTHX; #endif Malloc_t ptr; +#ifdef PERL_DEBUG_READONLY_COW + const MEM_SIZE oldsize = where + ? ((struct perl_memory_debug_header *)((char *)where - PERL_MEMORY_DEBUG_HEADER_SIZE))->size + : 0; +#endif #if !defined(STANDARD_C) && !defined(HAS_REALLOC_PROTOTYPE) && !defined(PERL_MICRO) Malloc_t PerlMem_realloc(); #endif /* !defined(STANDARD_C) && !defined(HAS_REALLOC_PROTOTYPE) */ -#ifdef HAS_64K_LIMIT - if (size > 0xffff) { - PerlIO_printf(Perl_error_log, - "Reallocation too large: %lx\n", size) FLUSH; - my_exit(1); - } -#endif /* HAS_64K_LIMIT */ if (!size) { safesysfree(where); return NULL; @@ -154,13 +212,14 @@ Perl_safesysrealloc(Malloc_t where,MEM_SIZE size) if (!where) return safesysmalloc(size); -#ifdef PERL_TRACK_MEMPOOL - where = (Malloc_t)((char*)where-sTHX); - size += sTHX; +#ifdef USE_MDH + where = (Malloc_t)((char*)where-PERL_MEMORY_DEBUG_HEADER_SIZE); + size += PERL_MEMORY_DEBUG_HEADER_SIZE; { struct perl_memory_debug_header *const header = (struct perl_memory_debug_header *)where; +# ifdef PERL_TRACK_MEMPOOL if (header->interpreter != aTHX) { Perl_croak_nocontext("panic: realloc from wrong pool, %p!=%p", header->interpreter, aTHX); @@ -173,22 +232,38 @@ Perl_safesysrealloc(Malloc_t where,MEM_SIZE size) char *start_of_freed = ((char *)where) + size; PoisonFree(start_of_freed, freed_up, char); } - header->size = size; # endif +# endif +# ifdef MDH_HAS_SIZE + header->size = size; +# endif } #endif #ifdef DEBUGGING if ((SSize_t)size < 0) Perl_croak_nocontext("panic: realloc, size=%"UVuf, (UV)size); #endif +#ifdef PERL_DEBUG_READONLY_COW + if ((ptr = mmap(0, size, PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) { + perror("mmap failed"); + abort(); + } + Copy(where,ptr,oldsize < size ? oldsize : size,char); + if (munmap(where, oldsize)) { + perror("munmap failed"); + abort(); + } +#else ptr = (Malloc_t)PerlMem_realloc(where,size); +#endif PERL_ALLOC_CHECK(ptr); /* MUST do this fixup first, before doing ANYTHING else, as anything else might allocate memory/free/move memory, and until we do the fixup, it may well be chasing (and writing to) free memory. */ -#ifdef PERL_TRACK_MEMPOOL if (ptr != NULL) { +#ifdef PERL_TRACK_MEMPOOL struct perl_memory_debug_header *const header = (struct perl_memory_debug_header *)ptr; @@ -200,12 +275,15 @@ Perl_safesysrealloc(Malloc_t where,MEM_SIZE size) } # endif + maybe_protect_rw(header->next); header->next->prev = header; + maybe_protect_ro(header->next); + maybe_protect_rw(header->prev); header->prev->next = header; - - ptr = (Malloc_t)((char*)ptr+sTHX); - } + maybe_protect_ro(header->prev); #endif + ptr = (Malloc_t)((char*)ptr+PERL_MEMORY_DEBUG_HEADER_SIZE); + } /* In particular, must do that fixup above before logging anything via *printf(), as it can reallocate memory, which can cause SEGVs. */ @@ -242,12 +320,16 @@ Perl_safesysfree(Malloc_t where) #endif DEBUG_m( PerlIO_printf(Perl_debug_log, "0x%"UVxf": (%05ld) free\n",PTR2UV(where),(long)PL_an++)); if (where) { -#ifdef PERL_TRACK_MEMPOOL - where = (Malloc_t)((char*)where-sTHX); +#ifdef USE_MDH + where = (Malloc_t)((char*)where-PERL_MEMORY_DEBUG_HEADER_SIZE); { struct perl_memory_debug_header *const header = (struct perl_memory_debug_header *)where; +# ifdef MDH_HAS_SIZE + const MEM_SIZE size = header->size; +# endif +# ifdef PERL_TRACK_MEMPOOL if (header->interpreter != aTHX) { Perl_croak_nocontext("panic: free from wrong pool, %p!=%p", header->interpreter, aTHX); @@ -264,16 +346,30 @@ Perl_safesysfree(Malloc_t where) header->prev->next); } /* Unlink us from the chain. */ + maybe_protect_rw(header->next); header->next->prev = header->prev; + maybe_protect_ro(header->next); + maybe_protect_rw(header->prev); header->prev->next = header->next; + maybe_protect_ro(header->prev); + maybe_protect_rw(header); # ifdef PERL_POISON - PoisonNew(where, header->size, char); + PoisonNew(where, size, char); # endif /* Trigger the duplicate free warning. */ header->next = NULL; +# endif +# ifdef PERL_DEBUG_READONLY_COW + if (munmap(where, size)) { + perror("munmap failed"); + abort(); + } +# endif } #endif +#ifndef PERL_DEBUG_READONLY_COW PerlMem_free(where); +#endif } } @@ -286,37 +382,36 @@ Perl_safesyscalloc(MEM_SIZE count, MEM_SIZE size) dTHX; #endif Malloc_t ptr; -#if defined(PERL_TRACK_MEMPOOL) || defined(HAS_64K_LIMIT) || defined(DEBUGGING) +#if defined(USE_MDH) || defined(DEBUGGING) MEM_SIZE total_size = 0; #endif /* Even though calloc() for zero bytes is strange, be robust. */ if (size && (count <= MEM_SIZE_MAX / size)) { -#if defined(PERL_TRACK_MEMPOOL) || defined(HAS_64K_LIMIT) || defined(DEBUGGING) +#if defined(USE_MDH) || defined(DEBUGGING) total_size = size * count; #endif } else - Perl_croak_memory_wrap(); -#ifdef PERL_TRACK_MEMPOOL - if (sTHX <= MEM_SIZE_MAX - (MEM_SIZE)total_size) - total_size += sTHX; + croak_memory_wrap(); +#ifdef USE_MDH + if (PERL_MEMORY_DEBUG_HEADER_SIZE <= MEM_SIZE_MAX - (MEM_SIZE)total_size) + total_size += PERL_MEMORY_DEBUG_HEADER_SIZE; else - Perl_croak_memory_wrap(); + croak_memory_wrap(); #endif -#ifdef HAS_64K_LIMIT - if (total_size > 0xffff) { - PerlIO_printf(Perl_error_log, - "Allocation too large: %lx\n", total_size) FLUSH; - my_exit(1); - } -#endif /* HAS_64K_LIMIT */ #ifdef DEBUGGING if ((SSize_t)size < 0 || (SSize_t)count < 0) Perl_croak_nocontext("panic: calloc, size=%"UVuf", count=%"UVuf, (UV)size, (UV)count); #endif -#ifdef PERL_TRACK_MEMPOOL +#ifdef PERL_DEBUG_READONLY_COW + if ((ptr = mmap(0, total_size ? total_size : 1, PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) { + perror("mmap failed"); + abort(); + } +#elif defined(PERL_TRACK_MEMPOOL) /* Have to use malloc() because we've added some space for our tracking header. */ /* malloc(0) is non-portable. */ @@ -332,22 +427,31 @@ Perl_safesyscalloc(MEM_SIZE count, MEM_SIZE size) PERL_ALLOC_CHECK(ptr); DEBUG_m(PerlIO_printf(Perl_debug_log, "0x%"UVxf": (%05ld) calloc %ld x %ld bytes\n",PTR2UV(ptr),(long)PL_an++,(long)count,(long)total_size)); if (ptr != NULL) { -#ifdef PERL_TRACK_MEMPOOL +#ifdef USE_MDH { struct perl_memory_debug_header *const header = (struct perl_memory_debug_header *)ptr; +# ifndef PERL_DEBUG_READONLY_COW memset((void*)ptr, 0, total_size); +# endif +# ifdef PERL_TRACK_MEMPOOL header->interpreter = aTHX; /* Link us into the list. */ header->prev = &PL_memory_debug_header; header->next = PL_memory_debug_header.next; PL_memory_debug_header.next = header; + maybe_protect_rw(header->next); header->next->prev = header; -# ifdef PERL_POISON + maybe_protect_ro(header->next); +# ifdef PERL_DEBUG_READONLY_COW + header->readonly = 0; +# endif +# endif +# ifdef MDH_HAS_SIZE header->size = total_size; # endif - ptr = (Malloc_t)((char*)ptr+sTHX); + ptr = (Malloc_t)((char*)ptr+PERL_MEMORY_DEBUG_HEADER_SIZE); } #endif return ptr; @@ -431,7 +535,8 @@ Perl_instr(const char *big, const char *little) PERL_ARGS_ASSERT_INSTR; - /* libc prior to 4.6.27 did not work properly on a NULL 'little' */ + /* libc prior to 4.6.27 (late 1994) did not work properly on a NULL + * 'little' */ if (!little) return (char*)big; return strstr((char*)big, (char*)little); @@ -521,13 +626,13 @@ Perl_fbm_compile(pTHX_ SV *sv, U32 flags) const U8 *s; STRLEN i; STRLEN len; - STRLEN rarest = 0; U32 frequency = 256; MAGIC *mg; + PERL_DEB( STRLEN rarest = 0 ); PERL_ARGS_ASSERT_FBM_COMPILE; - if (isGV_with_GP(sv)) + if (isGV_with_GP(sv) || SvROK(sv)) return; if (SvVALID(sv)) @@ -539,7 +644,9 @@ Perl_fbm_compile(pTHX_ SV *sv, U32 flags) if (mg && mg->mg_len >= 0) mg->mg_len++; } - s = (U8*)SvPV_force_mutable(sv, len); + if (!SvPOK(sv) || SvNIOKp(sv)) + s = (U8*)SvPV_force_mutable(sv, len); + else s = (U8 *)SvPV_mutable(sv, len); if (len == 0) /* TAIL might be on a zero-length string. */ return; SvUPGRADE(sv, SVt_PVMG); @@ -589,17 +696,15 @@ Perl_fbm_compile(pTHX_ SV *sv, U32 flags) s = (const unsigned char*)(SvPVX_const(sv)); /* deeper magic */ for (i = 0; i < len; i++) { if (PL_freq[s[i]] < frequency) { - rarest = i; + PERL_DEB( rarest = i ); frequency = PL_freq[s[i]]; } } - BmRARE(sv) = s[rarest]; - BmPREVIOUS(sv) = rarest; BmUSEFUL(sv) = 100; /* Initial value */ if (flags & FBMcf_TAIL) SvTAIL_on(sv); DEBUG_r(PerlIO_printf(Perl_debug_log, "rarest char %c at %"UVuf"\n", - BmRARE(sv), BmPREVIOUS(sv))); + s[rarest], (UV)rarest)); } /* If SvTAIL(littlestr), it has a fake '\n' at end. */ @@ -759,15 +864,17 @@ Perl_fbm_instr(pTHX_ unsigned char *big, unsigned char *bigend, SV *littlestr, U { const MAGIC *const mg = mg_find(littlestr, PERL_MAGIC_bm); - const unsigned char * const table = (const unsigned char *) mg->mg_ptr; const unsigned char *oldlittle; + assert(mg); + --littlelen; /* Last char found by table lookup */ s = big + littlelen; little += littlelen; /* last char */ oldlittle = little; if (s < bigend) { + const unsigned char * const table = (const unsigned char *) mg->mg_ptr; I32 tmp; top2: @@ -818,7 +925,7 @@ Perl_screaminstr(pTHX_ SV *bigstr, SV *littlestr, I32 start_shift, I32 end_shift /* This function must only ever be called on a scalar with study magic, but those do not happen any more. */ Perl_croak(aTHX_ "panic: screaminstr"); - return NULL; + NORETURN_FUNCTION_END; } /* @@ -909,10 +1016,15 @@ Perl_foldEQ_locale(const char *s1, const char *s2, I32 len) =for apidoc savepv -Perl's version of C. Returns a pointer to a newly allocated -string which is a duplicate of C. The size of the string is -determined by C. The memory allocated for the new string can -be freed with the C function. +Perl's version of C. Returns a pointer to a newly allocated +string which is a duplicate of C. The size of the string is +determined by C, which means it may not contain embedded C +characters and must have a trailing C. The memory allocated for the new +string can be freed with the C function. + +On some platforms, Windows for example, all allocated memory owned by a thread +is deallocated when that thread ends. So if you need that not to happen, you +need to use the shared memory functions, such as C>. =cut */ @@ -936,11 +1048,16 @@ Perl_savepv(pTHX_ const char *pv) /* =for apidoc savepvn -Perl's version of what C would be if it existed. Returns a +Perl's version of what C would be if it existed. Returns a pointer to a newly allocated string which is a duplicate of the first -C bytes from C, plus a trailing NUL byte. The memory allocated for +C bytes from C, plus a trailing +C byte. The memory allocated for the new string can be freed with the C function. +On some platforms, Windows for example, all allocated memory owned by a thread +is deallocated when that thread ends. So if you need that not to happen, you +need to use the shared memory functions, such as C>. + =cut */ @@ -977,6 +1094,9 @@ Perl_savesharedpv(pTHX_ const char *pv) { char *newaddr; STRLEN pvlen; + + PERL_UNUSED_CONTEXT; + if (!pv) return NULL; @@ -992,7 +1112,7 @@ Perl_savesharedpv(pTHX_ const char *pv) =for apidoc savesharedpvn A version of C which allocates the duplicate string in memory -which is shared between threads. (With the specific difference that a NULL +which is shared between threads. (With the specific difference that a NULL pointer is not acceptable) =cut @@ -1002,6 +1122,7 @@ Perl_savesharedpvn(pTHX_ const char *const pv, const STRLEN len) { char *const newaddr = (char*)PerlMemShared_malloc(len + 1); + PERL_UNUSED_CONTEXT; /* PERL_ARGS_ASSERT_SAVESHAREDPVN; */ if (!newaddr) { @@ -1017,6 +1138,10 @@ Perl_savesharedpvn(pTHX_ const char *const pv, const STRLEN len) A version of C/C which gets the string to duplicate from the passed in SV using C +On some platforms, Windows for example, all allocated memory owned by a thread +is deallocated when that thread ends. So if you need that not to happen, you +need to use the shared memory functions, such as C>. + =cut */ @@ -1178,15 +1303,20 @@ Perl_mess(pTHX_ const char *pat, ...) return retval; } -STATIC const COP* -S_closest_cop(pTHX_ const COP *cop, const OP *o) +const COP* +Perl_closest_cop(pTHX_ const COP *cop, const OP *o, const OP *curop, + bool opnext) { dVAR; - /* Look for PL_op starting from o. cop is the last COP we've seen. */ + /* Look for curop starting from o. cop is the last COP we've seen. */ + /* opnext means that curop is actually the ->op_next of the op we are + seeking. */ PERL_ARGS_ASSERT_CLOSEST_COP; - if (!o || o == PL_op) + if (!o || !curop || ( + opnext ? o->op_next == curop && o->op_type != OP_SCOPE : o == curop + )) return cop; if (o->op_flags & OPf_KIDS) { @@ -1202,7 +1332,7 @@ S_closest_cop(pTHX_ const COP *cop, const OP *o) /* Keep searching, and return when we've found something. */ - new_cop = closest_cop(cop, kid); + new_cop = closest_cop(cop, kid, curop, opnext); if (new_cop) return new_cop; } @@ -1243,6 +1373,18 @@ Perl_mess_sv(pTHX_ SV *basemsg, bool consume) dVAR; SV *sv; +#if defined(USE_C_BACKTRACE) && defined(USE_C_BACKTRACE_ON_ERROR) + { + char *ws; + int wi; + /* The PERL_C_BACKTRACE_ON_WARN must be an integer of one or more. */ + if ((ws = PerlEnv_getenv("PERL_C_BACKTRACE_ON_ERROR")) && + (wi = atoi(ws)) > 0) { + Perl_dump_c_backtrace(aTHX_ Perl_debug_log, wi, 1); + } + } +#endif + PERL_ARGS_ASSERT_MESS_SV; if (SvROK(basemsg)) { @@ -1272,7 +1414,8 @@ Perl_mess_sv(pTHX_ SV *basemsg, bool consume) * from the sibling of PL_curcop. */ - const COP *cop = closest_cop(PL_curcop, PL_curcop->op_sibling); + const COP *cop = + closest_cop(PL_curcop, PL_curcop->op_sibling, PL_op, FALSE); if (!cop) cop = PL_curcop; @@ -1340,20 +1483,13 @@ Perl_write_to_stderr(pTHX_ SV* msv) if (PL_stderrgv && SvREFCNT(PL_stderrgv) && (io = GvIO(PL_stderrgv)) && (mg = SvTIED_mg((const SV *)io, PERL_MAGIC_tiedscalar))) - Perl_magic_methcall(aTHX_ MUTABLE_SV(io), mg, "PRINT", + Perl_magic_methcall(aTHX_ MUTABLE_SV(io), mg, SV_CONST(PRINT), G_SCALAR | G_DISCARD | G_WRITING_TO_STDERR, 1, msv); else { -#ifdef USE_SFIO - /* SFIO can really mess with your errno */ - dSAVED_ERRNO; -#endif PerlIO * const serr = Perl_error_log; do_print(msv, serr); (void)PerlIO_flush(serr); -#ifdef USE_SFIO - RESTORE_ERRNO; -#endif } } @@ -1436,7 +1572,7 @@ Perl_die_sv(pTHX_ SV *baseex) PERL_ARGS_ASSERT_DIE_SV; croak_sv(baseex); assert(0); /* NOTREACHED */ - return NULL; + NORETURN_FUNCTION_END; } /* @@ -1459,7 +1595,7 @@ Perl_die_nocontext(const char* pat, ...) vcroak(pat, &args); assert(0); /* NOTREACHED */ va_end(args); - return NULL; + NORETURN_FUNCTION_END; } #endif /* PERL_IMPLICIT_CONTEXT */ @@ -1471,7 +1607,7 @@ Perl_die(pTHX_ const char* pat, ...) vcroak(pat, &args); assert(0); /* NOTREACHED */ va_end(args); - return NULL; + NORETURN_FUNCTION_END; } /* @@ -1588,14 +1724,14 @@ Perl_croak(pTHX_ const char *pat, ...) =for apidoc Am|void|croak_no_modify Exactly equivalent to C, but generates -terser object code than using C. Less code used on exception code +terser object code than using C. Less code used on exception code paths reduces CPU cache pressure. =cut */ void -Perl_croak_no_modify() +Perl_croak_no_modify(void) { Perl_croak_nocontext( "%s", PL_no_modify); } @@ -1604,24 +1740,20 @@ Perl_croak_no_modify() This is typically called when malloc returns NULL. */ void -Perl_croak_no_mem() +Perl_croak_no_mem(void) { dTHX; - /* Can't use PerlIO to write as it allocates memory */ - PerlLIO_write(PerlIO_fileno(Perl_error_log), - PL_no_mem, sizeof(PL_no_mem)-1); + int fd = PerlIO_fileno(Perl_error_log); + if (fd < 0) + SETERRNO(EBADF,RMS_IFI); + else { + /* Can't use PerlIO to write as it allocates memory */ + PERL_UNUSED_RESULT(PerlLIO_write(fd, PL_no_mem, sizeof(PL_no_mem)-1)); + } my_exit(1); } -/* saves machine code for a common noreturn idiom typically used in Newx*() */ -void -Perl_croak_memory_wrap(void) -{ - Perl_croak_nocontext("%s",PL_memory_wrap); -} - - /* does not return, used only in POPSTACK */ void Perl_croak_popstack(void) @@ -1894,54 +2026,54 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val) { #ifndef PERL_USE_SAFE_PUTENV if (!PL_use_safe_putenv) { - /* most putenv()s leak, so we manipulate environ directly */ - I32 i; - const I32 len = strlen(nam); - int nlen, vlen; - - /* where does it go? */ - for (i = 0; environ[i]; i++) { - if (strnEQ(environ[i],nam,len) && environ[i][len] == '=') - break; - } - - if (environ == PL_origenviron) { /* need we copy environment? */ - I32 j; - I32 max; - char **tmpenv; - - max = i; - while (environ[max]) - max++; - tmpenv = (char**)safesysmalloc((max+2) * sizeof(char*)); - for (j=0; j> 8) & 255); - return result; -#else - return s; -#endif -} - -long -Perl_my_htonl(pTHX_ long l) -{ - union { - long result; - char c[sizeof(long)]; - } u; - -#if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678 -#if BYTEORDER == 0x12345678 - u.result = 0; -#endif - u.c[0] = (l >> 24) & 255; - u.c[1] = (l >> 16) & 255; - u.c[2] = (l >> 8) & 255; - u.c[3] = l & 255; - return u.result; -#else -#if ((BYTEORDER - 0x1111) & 0x444) || !(BYTEORDER & 0xf) - Perl_croak(aTHX_ "Unknown BYTEORDER\n"); -#else - I32 o; - I32 s; - - for (o = BYTEORDER - 0x1111, s = 0; s < (sizeof(long)*8); o >>= 4, s += 8) { - u.c[o & 0xf] = (l >> s) & 255; - } - return u.result; -#endif -#endif -} - -long -Perl_my_ntohl(pTHX_ long l) -{ - union { - long l; - char c[sizeof(long)]; - } u; - -#if BYTEORDER == 0x1234 - u.c[0] = (l >> 24) & 255; - u.c[1] = (l >> 16) & 255; - u.c[2] = (l >> 8) & 255; - u.c[3] = l & 255; - return u.l; -#else -#if ((BYTEORDER - 0x1111) & 0x444) || !(BYTEORDER & 0xf) - Perl_croak(aTHX_ "Unknown BYTEORDER\n"); -#else - I32 o; - I32 s; - - u.l = l; - l = 0; - for (o = BYTEORDER - 0x1111, s = 0; s < (sizeof(long)*8); o >>= 4, s += 8) { - l |= (u.c[o & 0xf] & 255) << s; - } - return l; -#endif -#endif -} - -#endif /* BYTEORDER != 0x4321 */ -#endif /* MYSWAP */ - -/* - * Little-endian byte order functions - 'v' for 'VAX', or 'reVerse'. - * If these functions are defined, - * the BYTEORDER is neither 0x1234 nor 0x4321. - * However, this is not assumed. - * -DWS - */ - -#define HTOLE(name,type) \ - type \ - name (type n) \ - { \ - union { \ - type value; \ - char c[sizeof(type)]; \ - } u; \ - U32 i; \ - U32 s = 0; \ - for (i = 0; i < sizeof(u.c); i++, s += 8) { \ - u.c[i] = (n >> s) & 0xFF; \ - } \ - return u.value; \ - } - -#define LETOH(name,type) \ - type \ - name (type n) \ - { \ - union { \ - type value; \ - char c[sizeof(type)]; \ - } u; \ - U32 i; \ - U32 s = 0; \ - u.value = n; \ - n = 0; \ - for (i = 0; i < sizeof(u.c); i++, s += 8) { \ - n |= ((type)(u.c[i] & 0xFF)) << s; \ - } \ - return n; \ - } - -/* - * Big-endian byte order functions. - */ - -#define HTOBE(name,type) \ - type \ - name (type n) \ - { \ - union { \ - type value; \ - char c[sizeof(type)]; \ - } u; \ - U32 i; \ - U32 s = 8*(sizeof(u.c)-1); \ - for (i = 0; i < sizeof(u.c); i++, s -= 8) { \ - u.c[i] = (n >> s) & 0xFF; \ - } \ - return u.value; \ - } - -#define BETOH(name,type) \ - type \ - name (type n) \ - { \ - union { \ - type value; \ - char c[sizeof(type)]; \ - } u; \ - U32 i; \ - U32 s = 8*(sizeof(u.c)-1); \ - u.value = n; \ - n = 0; \ - for (i = 0; i < sizeof(u.c); i++, s -= 8) { \ - n |= ((type)(u.c[i] & 0xFF)) << s; \ - } \ - return n; \ - } - -/* - * If we just can't do it... - */ - -#define NOT_AVAIL(name,type) \ - type \ - name (type n) \ - { \ - Perl_croak_nocontext(#name "() not available"); \ - return n; /* not reached */ \ - } - - -#if defined(HAS_HTOVS) && !defined(htovs) -HTOLE(htovs,short) -#endif -#if defined(HAS_HTOVL) && !defined(htovl) -HTOLE(htovl,long) -#endif -#if defined(HAS_VTOHS) && !defined(vtohs) -LETOH(vtohs,short) -#endif -#if defined(HAS_VTOHL) && !defined(vtohl) -LETOH(vtohl,long) -#endif - -#ifdef PERL_NEED_MY_HTOLE16 -# if U16SIZE == 2 -HTOLE(Perl_my_htole16,U16) -# else -NOT_AVAIL(Perl_my_htole16,U16) -# endif -#endif -#ifdef PERL_NEED_MY_LETOH16 -# if U16SIZE == 2 -LETOH(Perl_my_letoh16,U16) -# else -NOT_AVAIL(Perl_my_letoh16,U16) -# endif -#endif -#ifdef PERL_NEED_MY_HTOBE16 -# if U16SIZE == 2 -HTOBE(Perl_my_htobe16,U16) -# else -NOT_AVAIL(Perl_my_htobe16,U16) -# endif -#endif -#ifdef PERL_NEED_MY_BETOH16 -# if U16SIZE == 2 -BETOH(Perl_my_betoh16,U16) -# else -NOT_AVAIL(Perl_my_betoh16,U16) -# endif -#endif - -#ifdef PERL_NEED_MY_HTOLE32 -# if U32SIZE == 4 -HTOLE(Perl_my_htole32,U32) -# else -NOT_AVAIL(Perl_my_htole32,U32) -# endif -#endif -#ifdef PERL_NEED_MY_LETOH32 -# if U32SIZE == 4 -LETOH(Perl_my_letoh32,U32) -# else -NOT_AVAIL(Perl_my_letoh32,U32) -# endif -#endif -#ifdef PERL_NEED_MY_HTOBE32 -# if U32SIZE == 4 -HTOBE(Perl_my_htobe32,U32) -# else -NOT_AVAIL(Perl_my_htobe32,U32) -# endif -#endif -#ifdef PERL_NEED_MY_BETOH32 -# if U32SIZE == 4 -BETOH(Perl_my_betoh32,U32) -# else -NOT_AVAIL(Perl_my_betoh32,U32) -# endif -#endif - -#ifdef PERL_NEED_MY_HTOLE64 -# if U64SIZE == 8 -HTOLE(Perl_my_htole64,U64) -# else -NOT_AVAIL(Perl_my_htole64,U64) -# endif -#endif -#ifdef PERL_NEED_MY_LETOH64 -# if U64SIZE == 8 -LETOH(Perl_my_letoh64,U64) -# else -NOT_AVAIL(Perl_my_letoh64,U64) -# endif -#endif -#ifdef PERL_NEED_MY_HTOBE64 -# if U64SIZE == 8 -HTOBE(Perl_my_htobe64,U64) -# else -NOT_AVAIL(Perl_my_htobe64,U64) -# endif -#endif -#ifdef PERL_NEED_MY_BETOH64 -# if U64SIZE == 8 -BETOH(Perl_my_betoh64,U64) -# else -NOT_AVAIL(Perl_my_betoh64,U64) -# endif -#endif - -#ifdef PERL_NEED_MY_HTOLES -HTOLE(Perl_my_htoles,short) -#endif -#ifdef PERL_NEED_MY_LETOHS -LETOH(Perl_my_letohs,short) -#endif -#ifdef PERL_NEED_MY_HTOBES -HTOBE(Perl_my_htobes,short) -#endif -#ifdef PERL_NEED_MY_BETOHS -BETOH(Perl_my_betohs,short) -#endif - -#ifdef PERL_NEED_MY_HTOLEI -HTOLE(Perl_my_htolei,int) -#endif -#ifdef PERL_NEED_MY_LETOHI -LETOH(Perl_my_letohi,int) -#endif -#ifdef PERL_NEED_MY_HTOBEI -HTOBE(Perl_my_htobei,int) -#endif -#ifdef PERL_NEED_MY_BETOHI -BETOH(Perl_my_betohi,int) -#endif - -#ifdef PERL_NEED_MY_HTOLEL -HTOLE(Perl_my_htolel,long) -#endif -#ifdef PERL_NEED_MY_LETOHL -LETOH(Perl_my_letohl,long) -#endif -#ifdef PERL_NEED_MY_HTOBEL -HTOBE(Perl_my_htobel,long) -#endif -#ifdef PERL_NEED_MY_BETOHL -BETOH(Perl_my_betohl,long) -#endif - -void -Perl_my_swabn(void *ptr, int n) -{ - char *s = (char *)ptr; - char *e = s + (n-1); - char tc; - - PERL_ARGS_ASSERT_MY_SWABN; - - for (n /= 2; n > 0; s++, e--, n--) { - tc = *s; - *s = *e; - *e = tc; - } -} - PerlIO * Perl_my_popen_list(pTHX_ const char *mode, int n, SV **args) { @@ -2538,7 +2342,8 @@ Perl_my_popen_list(pTHX_ const char *mode, int n, SV **args) PerlLIO_close(pp[0]); #if defined(HAS_FCNTL) && defined(F_SETFD) /* Close error pipe automatically if exec works */ - fcntl(pp[1], F_SETFD, FD_CLOEXEC); + if (fcntl(pp[1], F_SETFD, FD_CLOEXEC) < 0) + return NULL; #endif } /* Now dup our end of _the_ pipe to right position */ @@ -2683,7 +2488,8 @@ Perl_my_popen(pTHX_ const char *cmd, const char *mode) if (did_pipes) { PerlLIO_close(pp[0]); #if defined(HAS_FCNTL) && defined(F_SETFD) - fcntl(pp[1], F_SETFD, FD_CLOEXEC); + if (fcntl(pp[1], F_SETFD, FD_CLOEXEC) < 0) + return NULL; #endif } if (p[THIS] != (*mode == 'r')) { @@ -2855,25 +2661,6 @@ Perl_my_fork(void) #endif /* HAS_FORK */ } -#ifdef DUMP_FDS -void -Perl_dump_fds(pTHX_ const char *const s) -{ - int fd; - Stat_t tmpstatbuf; - - PERL_ARGS_ASSERT_DUMP_FDS; - - PerlIO_printf(Perl_debug_log,"%s", s); - for (fd = 0; fd < 32; fd++) { - if (PerlLIO_fstat(fd,&tmpstatbuf) >= 0) - PerlIO_printf(Perl_debug_log," %d",fd); - } - PerlIO_printf(Perl_debug_log,"\n"); - return; -} -#endif /* DUMP_FDS */ - #ifndef HAS_DUP2 int dup2(int oldfd, int newfd) @@ -3066,7 +2853,6 @@ I32 Perl_my_pclose(pTHX_ PerlIO *ptr) { dVAR; - Sigsave_t hstat, istat, qstat; int status; SV **svp; Pid_t pid; @@ -3074,19 +2860,21 @@ Perl_my_pclose(pTHX_ PerlIO *ptr) bool close_failed; dSAVEDERRNO; const int fd = PerlIO_fileno(ptr); + bool should_wait; + + svp = av_fetch(PL_fdpid,fd,TRUE); + pid = (SvTYPE(*svp) == SVt_IV) ? SvIVX(*svp) : -1; + SvREFCNT_dec(*svp); + *svp = NULL; -#ifdef USE_PERLIO +#if defined(USE_PERLIO) /* Find out whether the refcount is low enough for us to wait for the child proc without blocking. */ - const bool should_wait = PerlIOUnix_refcnt(fd) == 1; + should_wait = PerlIOUnix_refcnt(fd) == 1 && pid > 0; #else - const bool should_wait = 1; + should_wait = pid > 0; #endif - svp = av_fetch(PL_fdpid,fd,TRUE); - pid = (SvTYPE(*svp) == SVt_IV) ? SvIVX(*svp) : -1; - SvREFCNT_dec(*svp); - *svp = &PL_sv_undef; #ifdef OS2 if (pid == -1) { /* Opened by popen. */ return my_syspclose(ptr); @@ -3094,19 +2882,9 @@ Perl_my_pclose(pTHX_ PerlIO *ptr) #endif close_failed = (PerlIO_close(ptr) == EOF); SAVE_ERRNO; -#ifndef PERL_MICRO - rsignal_save(SIGHUP, (Sighandler_t) SIG_IGN, &hstat); - rsignal_save(SIGINT, (Sighandler_t) SIG_IGN, &istat); - rsignal_save(SIGQUIT, (Sighandler_t) SIG_IGN, &qstat); -#endif if (should_wait) do { pid2 = wait4pid(pid, &status, 0); } while (pid2 == -1 && errno == EINTR); -#ifndef PERL_MICRO - rsignal_restore(SIGHUP, &hstat); - rsignal_restore(SIGINT, &istat); - rsignal_restore(SIGQUIT, &qstat); -#endif if (close_failed) { RESTORE_ERRNO; return -1; @@ -3134,9 +2912,16 @@ Perl_wait4pid(pTHX_ Pid_t pid, int *statusp, int flags) dVAR; I32 result = 0; PERL_ARGS_ASSERT_WAIT4PID; - if (!pid) - return -1; #ifdef PERL_USES_PL_PIDSTATUS + if (!pid) { + /* PERL_USES_PL_PIDSTATUS is only defined when neither + waitpid() nor wait4() is available, or on OS/2, which + doesn't appear to support waiting for a progress group + member, so we can only treat a 0 pid as an unknown child. + */ + errno = ECHILD; + return -1; + } { if (pid > 0) { /* The keys in PL_pidstatus are now the raw 4 (or 8) bytes of the @@ -3183,7 +2968,7 @@ Perl_wait4pid(pTHX_ Pid_t pid, int *statusp, int flags) goto finish; #endif #if !defined(HAS_WAITPID) && defined(HAS_WAIT4) - result = wait4((pid==-1)?0:pid,statusp,flags,NULL); + result = wait4(pid,statusp,flags,NULL); goto finish; #endif #ifdef PERL_USES_PL_PIDSTATUS @@ -3267,7 +3052,7 @@ Perl_repeatcpy(char *to, const char *from, I32 len, IV count) assert(len >= 0); if (count < 0) - Perl_croak_memory_wrap(); + croak_memory_wrap(); if (len == 1) memset(to, *from, count); @@ -3671,7 +3456,7 @@ Perl_get_vtbl(pTHX_ int vtbl_id) I32 Perl_my_fflush_all(pTHX) { -#if defined(USE_PERLIO) || defined(FFLUSH_NULL) || defined(USE_SFIO) +#if defined(USE_PERLIO) || defined(FFLUSH_NULL) return PerlIO_flush(NULL); #else # if defined(HAS__FWALK) @@ -3731,7 +3516,7 @@ Perl_report_wrongway_fh(pTHX_ const GV *gv, const char have) if (name && HEK_LEN(name)) Perl_warner(aTHX_ packWARN(WARN_IO), "Filehandle %"HEKf" opened only for %sput", - name, direction); + HEKfARG(name), direction); else Perl_warner(aTHX_ packWARN(WARN_IO), "Filehandle opened only for %sput", direction); @@ -3763,7 +3548,8 @@ Perl_report_evil_fh(pTHX_ const GV *gv) (const char *)(OP_IS_FILETEST(op) ? "" : "()"); const char * const func = (const char *) - (op == OP_READLINE ? "readline" : /* "" not nice */ + (op == OP_READLINE || op == OP_RCATLINE + ? "readline" : /* "" not nice */ op == OP_LEAVEWRITE ? "write" : /* "write exit" not nice */ PL_op_desc[op]); const char * const type = @@ -3795,7 +3581,7 @@ Perl_report_evil_fh(pTHX_ const GV *gv) * */ -#ifdef HAS_GNULIBC +#ifdef __GLIBC__ # ifndef STRUCT_TM_HASZONE # define STRUCT_TM_HASZONE # endif @@ -3813,12 +3599,14 @@ Perl_init_tm(pTHX_ struct tm *ptm) /* see mktime, strftime and asctime */ #ifdef HAS_TM_TM_ZONE Time_t now; const struct tm* my_tm; + PERL_UNUSED_CONTEXT; PERL_ARGS_ASSERT_INIT_TM; (void)time(&now); my_tm = localtime(&now); if (my_tm) Copy(my_tm, ptm, 1, struct tm); #else + PERL_UNUSED_CONTEXT; PERL_ARGS_ASSERT_INIT_TM; PERL_UNUSED_ARG(ptm); #endif @@ -4052,7 +3840,11 @@ Perl_my_strftime(pTHX_ const char *fmt, int sec, int min, int hour, int mday, in #endif buflen = 64; Newx(buf, buflen, char); + + GCC_DIAG_IGNORE(-Wformat-nonliteral); /* fmt checked by caller */ len = strftime(buf, buflen, fmt, &mytm); + GCC_DIAG_RESTORE; + /* ** The following is needed to handle to the situation where ** tmpbuf overflows. Basically we want to allocate a buffer @@ -4076,7 +3868,11 @@ Perl_my_strftime(pTHX_ const char *fmt, int sec, int min, int hour, int mday, in Renew(buf, bufsize, char); while (buf) { + + GCC_DIAG_IGNORE(-Wformat-nonliteral); /* fmt checked by caller */ buflen = strftime(buf, bufsize, fmt, &mytm); + GCC_DIAG_RESTORE; + if (buflen > 0 && buflen < bufsize) break; /* heuristic to prevent out-of-memory errors */ @@ -4128,9 +3924,7 @@ Perl_getcwd_sv(pTHX_ SV *sv) { #ifndef PERL_MICRO dVAR; -#ifndef INCOMPLETE_TAINTS SvTAINTED_on(sv); -#endif PERL_ARGS_ASSERT_GETCWD_SV; @@ -4272,938 +4066,7 @@ Perl_getcwd_sv(pTHX_ SV *sv) #endif } -#define VERSION_MAX 0x7FFFFFFF - -/* -=for apidoc prescan_version - -Validate that a given string can be parsed as a version object, but doesn't -actually perform the parsing. Can use either strict or lax validation rules. -Can optionally set a number of hint variables to save the parsing code -some time when tokenizing. - -=cut -*/ -const char * -Perl_prescan_version(pTHX_ const char *s, bool strict, - const char **errstr, - bool *sqv, int *ssaw_decimal, int *swidth, bool *salpha) { - bool qv = (sqv ? *sqv : FALSE); - int width = 3; - int saw_decimal = 0; - bool alpha = FALSE; - const char *d = s; - - PERL_ARGS_ASSERT_PRESCAN_VERSION; - - if (qv && isDIGIT(*d)) - goto dotted_decimal_version; - - if (*d == 'v') { /* explicit v-string */ - d++; - if (isDIGIT(*d)) { - qv = TRUE; - } - else { /* degenerate v-string */ - /* requires v1.2.3 */ - BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)"); - } - -dotted_decimal_version: - if (strict && d[0] == '0' && isDIGIT(d[1])) { - /* no leading zeros allowed */ - BADVERSION(s,errstr,"Invalid version format (no leading zeros)"); - } - - while (isDIGIT(*d)) /* integer part */ - d++; - - if (*d == '.') - { - saw_decimal++; - d++; /* decimal point */ - } - else - { - if (strict) { - /* require v1.2.3 */ - BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)"); - } - else { - goto version_prescan_finish; - } - } - - { - int i = 0; - int j = 0; - while (isDIGIT(*d)) { /* just keep reading */ - i++; - while (isDIGIT(*d)) { - d++; j++; - /* maximum 3 digits between decimal */ - if (strict && j > 3) { - BADVERSION(s,errstr,"Invalid version format (maximum 3 digits between decimals)"); - } - } - if (*d == '_') { - if (strict) { - BADVERSION(s,errstr,"Invalid version format (no underscores)"); - } - if ( alpha ) { - BADVERSION(s,errstr,"Invalid version format (multiple underscores)"); - } - d++; - alpha = TRUE; - } - else if (*d == '.') { - if (alpha) { - BADVERSION(s,errstr,"Invalid version format (underscores before decimal)"); - } - saw_decimal++; - d++; - } - else if (!isDIGIT(*d)) { - break; - } - j = 0; - } - - if (strict && i < 2) { - /* requires v1.2.3 */ - BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)"); - } - } - } /* end if dotted-decimal */ - else - { /* decimal versions */ - int j = 0; /* may need this later */ - /* special strict case for leading '.' or '0' */ - if (strict) { - if (*d == '.') { - BADVERSION(s,errstr,"Invalid version format (0 before decimal required)"); - } - if (*d == '0' && isDIGIT(d[1])) { - BADVERSION(s,errstr,"Invalid version format (no leading zeros)"); - } - } - - /* and we never support negative versions */ - if ( *d == '-') { - BADVERSION(s,errstr,"Invalid version format (negative version number)"); - } - - /* consume all of the integer part */ - while (isDIGIT(*d)) - d++; - - /* look for a fractional part */ - if (*d == '.') { - /* we found it, so consume it */ - saw_decimal++; - d++; - } - else if (!*d || *d == ';' || isSPACE(*d) || *d == '{' || *d == '}') { - if ( d == s ) { - /* found nothing */ - BADVERSION(s,errstr,"Invalid version format (version required)"); - } - /* found just an integer */ - goto version_prescan_finish; - } - else if ( d == s ) { - /* didn't find either integer or period */ - BADVERSION(s,errstr,"Invalid version format (non-numeric data)"); - } - else if (*d == '_') { - /* underscore can't come after integer part */ - if (strict) { - BADVERSION(s,errstr,"Invalid version format (no underscores)"); - } - else if (isDIGIT(d[1])) { - BADVERSION(s,errstr,"Invalid version format (alpha without decimal)"); - } - else { - BADVERSION(s,errstr,"Invalid version format (misplaced underscore)"); - } - } - else { - /* anything else after integer part is just invalid data */ - BADVERSION(s,errstr,"Invalid version format (non-numeric data)"); - } - - /* scan the fractional part after the decimal point*/ - - if (!isDIGIT(*d) && (strict || ! (!*d || *d == ';' || isSPACE(*d) || *d == '{' || *d == '}') )) { - /* strict or lax-but-not-the-end */ - BADVERSION(s,errstr,"Invalid version format (fractional part required)"); - } - - while (isDIGIT(*d)) { - d++; j++; - if (*d == '.' && isDIGIT(d[-1])) { - if (alpha) { - BADVERSION(s,errstr,"Invalid version format (underscores before decimal)"); - } - if (strict) { - BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions must begin with 'v')"); - } - d = (char *)s; /* start all over again */ - qv = TRUE; - goto dotted_decimal_version; - } - if (*d == '_') { - if (strict) { - BADVERSION(s,errstr,"Invalid version format (no underscores)"); - } - if ( alpha ) { - BADVERSION(s,errstr,"Invalid version format (multiple underscores)"); - } - if ( ! isDIGIT(d[1]) ) { - BADVERSION(s,errstr,"Invalid version format (misplaced underscore)"); - } - width = j; - d++; - alpha = TRUE; - } - } - } - -version_prescan_finish: - while (isSPACE(*d)) - d++; - - if (!isDIGIT(*d) && (! (!*d || *d == ';' || *d == '{' || *d == '}') )) { - /* trailing non-numeric data */ - BADVERSION(s,errstr,"Invalid version format (non-numeric data)"); - } - - if (sqv) - *sqv = qv; - if (swidth) - *swidth = width; - if (ssaw_decimal) - *ssaw_decimal = saw_decimal; - if (salpha) - *salpha = alpha; - return d; -} - -/* -=for apidoc scan_version - -Returns a pointer to the next character after the parsed -version string, as well as upgrading the passed in SV to -an RV. - -Function must be called with an already existing SV like - - sv = newSV(0); - s = scan_version(s, SV *sv, bool qv); - -Performs some preprocessing to the string to ensure that -it has the correct characteristics of a version. Flags the -object if it contains an underscore (which denotes this -is an alpha version). The boolean qv denotes that the version -should be interpreted as if it had multiple decimals, even if -it doesn't. - -=cut -*/ - -const char * -Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv) -{ - const char *start = s; - const char *pos; - const char *last; - const char *errstr = NULL; - int saw_decimal = 0; - int width = 3; - bool alpha = FALSE; - bool vinf = FALSE; - AV * av; - SV * hv; - - PERL_ARGS_ASSERT_SCAN_VERSION; - - while (isSPACE(*s)) /* leading whitespace is OK */ - s++; - - last = prescan_version(s, FALSE, &errstr, &qv, &saw_decimal, &width, &alpha); - if (errstr) { - /* "undef" is a special case and not an error */ - if ( ! ( *s == 'u' && strEQ(s,"undef")) ) { - Safefree(start); - Perl_croak(aTHX_ "%s", errstr); - } - } - - start = s; - if (*s == 'v') - s++; - pos = s; - - /* Now that we are through the prescan, start creating the object */ - av = newAV(); - hv = newSVrv(rv, "version"); /* create an SV and upgrade the RV */ - (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */ - -#ifndef NODEFAULT_SHAREKEYS - HvSHAREKEYS_on(hv); /* key-sharing on by default */ -#endif - - if ( qv ) - (void)hv_stores(MUTABLE_HV(hv), "qv", newSViv(qv)); - if ( alpha ) - (void)hv_stores(MUTABLE_HV(hv), "alpha", newSViv(alpha)); - if ( !qv && width < 3 ) - (void)hv_stores(MUTABLE_HV(hv), "width", newSViv(width)); - - while (isDIGIT(*pos)) - pos++; - if (!isALPHA(*pos)) { - I32 rev; - - for (;;) { - rev = 0; - { - /* this is atoi() that delimits on underscores */ - const char *end = pos; - I32 mult = 1; - I32 orev; - - /* the following if() will only be true after the decimal - * point of a version originally created with a bare - * floating point number, i.e. not quoted in any way - */ - if ( !qv && s > start && saw_decimal == 1 ) { - mult *= 100; - while ( s < end ) { - orev = rev; - rev += (*s - '0') * mult; - mult /= 10; - if ( (PERL_ABS(orev) > PERL_ABS(rev)) - || (PERL_ABS(rev) > VERSION_MAX )) { - Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW), - "Integer overflow in version %d",VERSION_MAX); - s = end - 1; - rev = VERSION_MAX; - vinf = 1; - } - s++; - if ( *s == '_' ) - s++; - } - } - else { - while (--end >= s) { - orev = rev; - rev += (*end - '0') * mult; - mult *= 10; - if ( (PERL_ABS(orev) > PERL_ABS(rev)) - || (PERL_ABS(rev) > VERSION_MAX )) { - Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW), - "Integer overflow in version"); - end = s - 1; - rev = VERSION_MAX; - vinf = 1; - } - } - } - } - - /* Append revision */ - av_push(av, newSViv(rev)); - if ( vinf ) { - s = last; - break; - } - else if ( *pos == '.' ) - s = ++pos; - else if ( *pos == '_' && isDIGIT(pos[1]) ) - s = ++pos; - else if ( *pos == ',' && isDIGIT(pos[1]) ) - s = ++pos; - else if ( isDIGIT(*pos) ) - s = pos; - else { - s = pos; - break; - } - if ( qv ) { - while ( isDIGIT(*pos) ) - pos++; - } - else { - int digits = 0; - while ( ( isDIGIT(*pos) || *pos == '_' ) && digits < 3 ) { - if ( *pos != '_' ) - digits++; - pos++; - } - } - } - } - if ( qv ) { /* quoted versions always get at least three terms*/ - I32 len = av_len(av); - /* This for loop appears to trigger a compiler bug on OS X, as it - loops infinitely. Yes, len is negative. No, it makes no sense. - Compiler in question is: - gcc version 3.3 20030304 (Apple Computer, Inc. build 1640) - for ( len = 2 - len; len > 0; len-- ) - av_push(MUTABLE_AV(sv), newSViv(0)); - */ - len = 2 - len; - while (len-- > 0) - av_push(av, newSViv(0)); - } - - /* need to save off the current version string for later */ - if ( vinf ) { - SV * orig = newSVpvn("v.Inf", sizeof("v.Inf")-1); - (void)hv_stores(MUTABLE_HV(hv), "original", orig); - (void)hv_stores(MUTABLE_HV(hv), "vinf", newSViv(1)); - } - else if ( s > start ) { - SV * orig = newSVpvn(start,s-start); - if ( qv && saw_decimal == 1 && *start != 'v' ) { - /* need to insert a v to be consistent */ - sv_insert(orig, 0, 0, "v", 1); - } - (void)hv_stores(MUTABLE_HV(hv), "original", orig); - } - else { - (void)hv_stores(MUTABLE_HV(hv), "original", newSVpvs("0")); - av_push(av, newSViv(0)); - } - - /* And finally, store the AV in the hash */ - (void)hv_stores(MUTABLE_HV(hv), "version", newRV_noinc(MUTABLE_SV(av))); - - /* fix RT#19517 - special case 'undef' as string */ - if ( *s == 'u' && strEQ(s,"undef") ) { - s += 5; - } - - return s; -} - -/* -=for apidoc new_version - -Returns a new version object based on the passed in SV: - - SV *sv = new_version(SV *ver); - -Does not alter the passed in ver SV. See "upg_version" if you -want to upgrade the SV. - -=cut -*/ - -SV * -Perl_new_version(pTHX_ SV *ver) -{ - dVAR; - SV * const rv = newSV(0); - PERL_ARGS_ASSERT_NEW_VERSION; - if ( sv_isobject(ver) && sv_derived_from(ver, "version") ) - /* can just copy directly */ - { - I32 key; - AV * const av = newAV(); - AV *sav; - /* This will get reblessed later if a derived class*/ - SV * const hv = newSVrv(rv, "version"); - (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */ -#ifndef NODEFAULT_SHAREKEYS - HvSHAREKEYS_on(hv); /* key-sharing on by default */ -#endif - - if ( SvROK(ver) ) - ver = SvRV(ver); - - /* Begin copying all of the elements */ - if ( hv_exists(MUTABLE_HV(ver), "qv", 2) ) - (void)hv_stores(MUTABLE_HV(hv), "qv", newSViv(1)); - - if ( hv_exists(MUTABLE_HV(ver), "alpha", 5) ) - (void)hv_stores(MUTABLE_HV(hv), "alpha", newSViv(1)); - - if ( hv_exists(MUTABLE_HV(ver), "width", 5 ) ) - { - const I32 width = SvIV(*hv_fetchs(MUTABLE_HV(ver), "width", FALSE)); - (void)hv_stores(MUTABLE_HV(hv), "width", newSViv(width)); - } - - if ( hv_exists(MUTABLE_HV(ver), "original", 8 ) ) - { - SV * pv = *hv_fetchs(MUTABLE_HV(ver), "original", FALSE); - (void)hv_stores(MUTABLE_HV(hv), "original", newSVsv(pv)); - } - - sav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(ver), "version", FALSE))); - /* This will get reblessed later if a derived class*/ - for ( key = 0; key <= av_len(sav); key++ ) - { - const I32 rev = SvIV(*av_fetch(sav, key, FALSE)); - av_push(av, newSViv(rev)); - } - - (void)hv_stores(MUTABLE_HV(hv), "version", newRV_noinc(MUTABLE_SV(av))); - return rv; - } -#ifdef SvVOK - { - const MAGIC* const mg = SvVSTRING_mg(ver); - if ( mg ) { /* already a v-string */ - const STRLEN len = mg->mg_len; - char * const version = savepvn( (const char*)mg->mg_ptr, len); - sv_setpvn(rv,version,len); - /* this is for consistency with the pure Perl class */ - if ( isDIGIT(*version) ) - sv_insert(rv, 0, 0, "v", 1); - Safefree(version); - } - else { -#endif - sv_setsv(rv,ver); /* make a duplicate */ -#ifdef SvVOK - } - } -#endif - return upg_version(rv, FALSE); -} - -/* -=for apidoc upg_version - -In-place upgrade of the supplied SV to a version object. - - SV *sv = upg_version(SV *sv, bool qv); - -Returns a pointer to the upgraded SV. Set the boolean qv if you want -to force this SV to be interpreted as an "extended" version. - -=cut -*/ - -SV * -Perl_upg_version(pTHX_ SV *ver, bool qv) -{ - const char *version, *s; -#ifdef SvVOK - const MAGIC *mg; -#endif - - PERL_ARGS_ASSERT_UPG_VERSION; - - if ( SvNOK(ver) && !( SvPOK(ver) && sv_len(ver) == 3 ) ) - { - STRLEN len; - - /* may get too much accuracy */ - char tbuf[64]; - SV *sv = SvNVX(ver) > 10e50 ? newSV(64) : 0; - char *buf; -#ifdef USE_LOCALE_NUMERIC - char *loc = savepv(setlocale(LC_NUMERIC, NULL)); - setlocale(LC_NUMERIC, "C"); -#endif - if (sv) { - Perl_sv_setpvf(aTHX_ sv, "%.9"NVff, SvNVX(ver)); - buf = SvPV(sv, len); - } - else { - len = my_snprintf(tbuf, sizeof(tbuf), "%.9"NVff, SvNVX(ver)); - buf = tbuf; - } -#ifdef USE_LOCALE_NUMERIC - setlocale(LC_NUMERIC, loc); - Safefree(loc); -#endif - while (buf[len-1] == '0' && len > 0) len--; - if ( buf[len-1] == '.' ) len--; /* eat the trailing decimal */ - version = savepvn(buf, len); - SvREFCNT_dec(sv); - } -#ifdef SvVOK - else if ( (mg = SvVSTRING_mg(ver)) ) { /* already a v-string */ - version = savepvn( (const char*)mg->mg_ptr,mg->mg_len ); - qv = TRUE; - } -#endif - else /* must be a string or something like a string */ - { - STRLEN len; - version = savepv(SvPV(ver,len)); -#ifndef SvVOK -# if PERL_VERSION > 5 - /* This will only be executed for 5.6.0 - 5.8.0 inclusive */ - if ( len >= 3 && !instr(version,".") && !instr(version,"_")) { - /* may be a v-string */ - char *testv = (char *)version; - STRLEN tlen = len; - for (tlen=0; tlen < len; tlen++, testv++) { - /* if one of the characters is non-text assume v-string */ - if (testv[0] < ' ') { - SV * const nsv = sv_newmortal(); - const char *nver; - const char *pos; - int saw_decimal = 0; - sv_setpvf(nsv,"v%vd",ver); - pos = nver = savepv(SvPV_nolen(nsv)); - - /* scan the resulting formatted string */ - pos++; /* skip the leading 'v' */ - while ( *pos == '.' || isDIGIT(*pos) ) { - if ( *pos == '.' ) - saw_decimal++ ; - pos++; - } - - /* is definitely a v-string */ - if ( saw_decimal >= 2 ) { - Safefree(version); - version = nver; - } - break; - } - } - } -# endif -#endif - } - - s = scan_version(version, ver, qv); - if ( *s != '\0' ) - Perl_ck_warner(aTHX_ packWARN(WARN_MISC), - "Version string '%s' contains invalid data; " - "ignoring: '%s'", version, s); - Safefree(version); - return ver; -} - -/* -=for apidoc vverify - -Validates that the SV contains valid internal structure for a version object. -It may be passed either the version object (RV) or the hash itself (HV). If -the structure is valid, it returns the HV. If the structure is invalid, -it returns NULL. - - SV *hv = vverify(sv); - -Note that it only confirms the bare minimum structure (so as not to get -confused by derived classes which may contain additional hash entries): - -=over 4 - -=item * The SV is an HV or a reference to an HV - -=item * The hash contains a "version" key - -=item * The "version" key has a reference to an AV as its value - -=back - -=cut -*/ - -SV * -Perl_vverify(pTHX_ SV *vs) -{ - SV *sv; - - PERL_ARGS_ASSERT_VVERIFY; - - if ( SvROK(vs) ) - vs = SvRV(vs); - - /* see if the appropriate elements exist */ - if ( SvTYPE(vs) == SVt_PVHV - && hv_exists(MUTABLE_HV(vs), "version", 7) - && (sv = SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE))) - && SvTYPE(sv) == SVt_PVAV ) - return vs; - else - return NULL; -} - -/* -=for apidoc vnumify - -Accepts a version object and returns the normalized floating -point representation. Call like: - - sv = vnumify(rv); - -NOTE: you can pass either the object directly or the SV -contained within the RV. - -The SV returned has a refcount of 1. - -=cut -*/ - -SV * -Perl_vnumify(pTHX_ SV *vs) -{ - I32 i, len, digit; - int width; - bool alpha = FALSE; - SV *sv; - AV *av; - - PERL_ARGS_ASSERT_VNUMIFY; - - /* extract the HV from the object */ - vs = vverify(vs); - if ( ! vs ) - Perl_croak(aTHX_ "Invalid version object"); - - /* see if various flags exist */ - if ( hv_exists(MUTABLE_HV(vs), "alpha", 5 ) ) - alpha = TRUE; - if ( hv_exists(MUTABLE_HV(vs), "width", 5 ) ) - width = SvIV(*hv_fetchs(MUTABLE_HV(vs), "width", FALSE)); - else - width = 3; - - - /* attempt to retrieve the version array */ - if ( !(av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE))) ) ) { - return newSVpvs("0"); - } - - len = av_len(av); - if ( len == -1 ) - { - return newSVpvs("0"); - } - - digit = SvIV(*av_fetch(av, 0, 0)); - sv = Perl_newSVpvf(aTHX_ "%d.", (int)PERL_ABS(digit)); - for ( i = 1 ; i < len ; i++ ) - { - digit = SvIV(*av_fetch(av, i, 0)); - if ( width < 3 ) { - const int denom = (width == 2 ? 10 : 100); - const div_t term = div((int)PERL_ABS(digit),denom); - Perl_sv_catpvf(aTHX_ sv, "%0*d_%d", width, term.quot, term.rem); - } - else { - Perl_sv_catpvf(aTHX_ sv, "%0*d", width, (int)digit); - } - } - - if ( len > 0 ) - { - digit = SvIV(*av_fetch(av, len, 0)); - if ( alpha && width == 3 ) /* alpha version */ - sv_catpvs(sv,"_"); - Perl_sv_catpvf(aTHX_ sv, "%0*d", width, (int)digit); - } - else /* len == 0 */ - { - sv_catpvs(sv, "000"); - } - return sv; -} - -/* -=for apidoc vnormal - -Accepts a version object and returns the normalized string -representation. Call like: - - sv = vnormal(rv); - -NOTE: you can pass either the object directly or the SV -contained within the RV. - -The SV returned has a refcount of 1. - -=cut -*/ - -SV * -Perl_vnormal(pTHX_ SV *vs) -{ - I32 i, len, digit; - bool alpha = FALSE; - SV *sv; - AV *av; - - PERL_ARGS_ASSERT_VNORMAL; - - /* extract the HV from the object */ - vs = vverify(vs); - if ( ! vs ) - Perl_croak(aTHX_ "Invalid version object"); - - if ( hv_exists(MUTABLE_HV(vs), "alpha", 5 ) ) - alpha = TRUE; - av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE))); - - len = av_len(av); - if ( len == -1 ) - { - return newSVpvs(""); - } - digit = SvIV(*av_fetch(av, 0, 0)); - sv = Perl_newSVpvf(aTHX_ "v%"IVdf, (IV)digit); - for ( i = 1 ; i < len ; i++ ) { - digit = SvIV(*av_fetch(av, i, 0)); - Perl_sv_catpvf(aTHX_ sv, ".%"IVdf, (IV)digit); - } - - if ( len > 0 ) - { - /* handle last digit specially */ - digit = SvIV(*av_fetch(av, len, 0)); - if ( alpha ) - Perl_sv_catpvf(aTHX_ sv, "_%"IVdf, (IV)digit); - else - Perl_sv_catpvf(aTHX_ sv, ".%"IVdf, (IV)digit); - } - - if ( len <= 2 ) { /* short version, must be at least three */ - for ( len = 2 - len; len != 0; len-- ) - sv_catpvs(sv,".0"); - } - return sv; -} - -/* -=for apidoc vstringify - -In order to maintain maximum compatibility with earlier versions -of Perl, this function will return either the floating point -notation or the multiple dotted notation, depending on whether -the original version contained 1 or more dots, respectively. - -The SV returned has a refcount of 1. - -=cut -*/ - -SV * -Perl_vstringify(pTHX_ SV *vs) -{ - PERL_ARGS_ASSERT_VSTRINGIFY; - - /* extract the HV from the object */ - vs = vverify(vs); - if ( ! vs ) - Perl_croak(aTHX_ "Invalid version object"); - - if (hv_exists(MUTABLE_HV(vs), "original", sizeof("original") - 1)) { - SV *pv; - pv = *hv_fetchs(MUTABLE_HV(vs), "original", FALSE); - if ( SvPOK(pv) ) - return newSVsv(pv); - else - return &PL_sv_undef; - } - else { - if ( hv_exists(MUTABLE_HV(vs), "qv", 2) ) - return vnormal(vs); - else - return vnumify(vs); - } -} - -/* -=for apidoc vcmp - -Version object aware cmp. Both operands must already have been -converted into version objects. - -=cut -*/ - -int -Perl_vcmp(pTHX_ SV *lhv, SV *rhv) -{ - I32 i,l,m,r,retval; - bool lalpha = FALSE; - bool ralpha = FALSE; - I32 left = 0; - I32 right = 0; - AV *lav, *rav; - - PERL_ARGS_ASSERT_VCMP; - - /* extract the HVs from the objects */ - lhv = vverify(lhv); - rhv = vverify(rhv); - if ( ! ( lhv && rhv ) ) - Perl_croak(aTHX_ "Invalid version object"); - - /* get the left hand term */ - lav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(lhv), "version", FALSE))); - if ( hv_exists(MUTABLE_HV(lhv), "alpha", 5 ) ) - lalpha = TRUE; - - /* and the right hand term */ - rav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(rhv), "version", FALSE))); - if ( hv_exists(MUTABLE_HV(rhv), "alpha", 5 ) ) - ralpha = TRUE; - - l = av_len(lav); - r = av_len(rav); - m = l < r ? l : r; - retval = 0; - i = 0; - while ( i <= m && retval == 0 ) - { - left = SvIV(*av_fetch(lav,i,0)); - right = SvIV(*av_fetch(rav,i,0)); - if ( left < right ) - retval = -1; - if ( left > right ) - retval = +1; - i++; - } - - /* tiebreaker for alpha with identical terms */ - if ( retval == 0 && l == r && left == right && ( lalpha || ralpha ) ) - { - if ( lalpha && !ralpha ) - { - retval = -1; - } - else if ( ralpha && !lalpha) - { - retval = +1; - } - } - - if ( l != r && retval == 0 ) /* possible match except for trailing 0's */ - { - if ( l < r ) - { - while ( i <= r && retval == 0 ) - { - if ( SvIV(*av_fetch(rav,i,0)) != 0 ) - retval = -1; /* not a match after all */ - i++; - } - } - else - { - while ( i <= l && retval == 0 ) - { - if ( SvIV(*av_fetch(lav,i,0)) != 0 ) - retval = +1; /* not a match after all */ - i++; - } - } - } - return retval; -} +#include "vutil.c" #if !defined(HAS_SOCKETPAIR) && defined(HAS_SOCKET) && defined(AF_INET) && defined(PF_INET) && defined(SOCK_DGRAM) && defined(HAS_SELECT) # define EMULATE_SOCKETPAIR_UDP @@ -5473,7 +4336,8 @@ Perl_my_socketpair (int family, int type, int protocol, int fd[2]) { =for apidoc sv_nosharing Dummy routine which "shares" an SV when there is no sharing module present. -Or "locks" it. Or "unlocks" it. In other words, ignores its single SV argument. +Or "locks" it. Or "unlocks" it. In other +words, ignores its single SV argument. Exists to avoid test for a NULL function pointer and because it could potentially warn under some level of strict-ness. @@ -5660,58 +4524,84 @@ Perl_seed(pTHX) } void -Perl_get_hash_seed(pTHX_ unsigned char *seed_buffer) +Perl_get_hash_seed(pTHX_ unsigned char * const seed_buffer) { dVAR; - const char *s; - const unsigned char * const end= seed_buffer + PERL_HASH_SEED_BYTES; + const char *env_pv; + unsigned long i; PERL_ARGS_ASSERT_GET_HASH_SEED; - s= PerlEnv_getenv("PERL_HASH_SEED"); + env_pv= PerlEnv_getenv("PERL_HASH_SEED"); - if ( s ) + if ( env_pv ) #ifndef USE_HASH_SEED_EXPLICIT { - while (isSPACE(*s)) - s++; - if (s[0] == '0' && s[1] == 'x') - s += 2; - - while (isXDIGIT(*s) && seed_buffer < end) { - *seed_buffer = READ_XDIGIT(s) << 4; - if (isXDIGIT(*s)) { - *seed_buffer |= READ_XDIGIT(s); + /* ignore leading spaces */ + while (isSPACE(*env_pv)) + env_pv++; +#ifdef USE_PERL_PERTURB_KEYS + /* if they set it to "0" we disable key traversal randomization completely */ + if (strEQ(env_pv,"0")) { + PL_hash_rand_bits_enabled= 0; + } else { + /* otherwise switch to deterministic mode */ + PL_hash_rand_bits_enabled= 2; + } +#endif + /* ignore a leading 0x... if it is there */ + if (env_pv[0] == '0' && env_pv[1] == 'x') + env_pv += 2; + + for( i = 0; isXDIGIT(*env_pv) && i < PERL_HASH_SEED_BYTES; i++ ) { + seed_buffer[i] = READ_XDIGIT(env_pv) << 4; + if ( isXDIGIT(*env_pv)) { + seed_buffer[i] |= READ_XDIGIT(env_pv); } - seed_buffer++; } - while (isSPACE(*s)) - s++; - if (*s && !isXDIGIT(*s)) { + while (isSPACE(*env_pv)) + env_pv++; + + if (*env_pv && !isXDIGIT(*env_pv)) { Perl_warn(aTHX_ "perl: warning: Non hex character in '$ENV{PERL_HASH_SEED}', seed only partially set\n"); } /* should we check for unparsed crap? */ + /* should we warn about unused hex? */ + /* should we warn about insufficient hex? */ } else #endif { - unsigned char *ptr= seed_buffer; (void)seedDrand01((Rand_seed_t)seed()); - while (ptr < end) { - *ptr++ = (unsigned char)(Drand01() * (U8_MAX+1)); + for( i = 0; i < PERL_HASH_SEED_BYTES; i++ ) { + seed_buffer[i] = (unsigned char)(Drand01() * (U8_MAX+1)); } } +#ifdef USE_PERL_PERTURB_KEYS { /* initialize PL_hash_rand_bits from the hash seed. * This value is highly volatile, it is updated every * hash insert, and is used as part of hash bucket chain * randomization and hash iterator randomization. */ - unsigned long i; - PL_hash_rand_bits= 0; + PL_hash_rand_bits= 0xbe49d17f; /* I just picked a number */ for( i = 0; i < sizeof(UV) ; i++ ) { - PL_hash_rand_bits = (PL_hash_rand_bits << 8) | seed_buffer[i % PERL_HASH_SEED_BYTES]; + PL_hash_rand_bits += seed_buffer[i % PERL_HASH_SEED_BYTES]; + PL_hash_rand_bits = ROTL_UV(PL_hash_rand_bits,8); + } + } + env_pv= PerlEnv_getenv("PERL_PERTURB_KEYS"); + if (env_pv) { + if (strEQ(env_pv,"0") || strEQ(env_pv,"NO")) { + PL_hash_rand_bits_enabled= 0; + } else if (strEQ(env_pv,"1") || strEQ(env_pv,"RANDOM")) { + PL_hash_rand_bits_enabled= 1; + } else if (strEQ(env_pv,"2") || strEQ(env_pv,"DETERMINISTIC")) { + PL_hash_rand_bits_enabled= 2; + } else { + Perl_warn(aTHX_ "perl: warning: strange setting in '$ENV{PERL_PERTURB_KEYS}': '%s'\n", env_pv); } } +#endif } #ifdef PERL_GLOBAL_STRUCT @@ -5724,8 +4614,8 @@ Perl_init_global_struct(pTHX) { struct perl_vars *plvarsp = NULL; # ifdef PERL_GLOBAL_STRUCT - const IV nppaddr = sizeof(Gppaddr)/sizeof(Perl_ppaddr_t); - const IV ncheck = sizeof(Gcheck) /sizeof(Perl_check_t); + const IV nppaddr = C_ARRAY_LENGTH(Gppaddr); + const IV ncheck = C_ARRAY_LENGTH(Gcheck); # ifdef PERL_GLOBAL_STRUCT_PRIVATE /* PerlMem_malloc() because can't use even safesysmalloc() this early. */ plvarsp = (struct perl_vars*)PerlMem_malloc(sizeof(struct perl_vars)); @@ -5764,6 +4654,10 @@ Perl_init_global_struct(pTHX) # ifdef PERL_SET_VARS PERL_SET_VARS(plvarsp); # endif +# ifdef PERL_GLOBAL_STRUCT_PRIVATE + plvarsp->Gsv_placeholder.sv_flags = 0; + memset(plvarsp->Ghash_seed, 0, sizeof(plvarsp->Ghash_seed)); +# endif # undef PERL_GLOBAL_STRUCT_INIT # endif return plvarsp; @@ -5776,11 +4670,15 @@ Perl_init_global_struct(pTHX) void Perl_free_global_struct(pTHX_ struct perl_vars *plvarsp) { + int veto = plvarsp->Gveto_cleanup; + PERL_ARGS_ASSERT_FREE_GLOBAL_STRUCT; # ifdef PERL_GLOBAL_STRUCT # ifdef PERL_UNSET_VARS PERL_UNSET_VARS(plvarsp); # endif + if (veto) + return; free(plvarsp->Gppaddr); free(plvarsp->Gcheck); # ifdef PERL_GLOBAL_STRUCT_PRIVATE @@ -5875,7 +4773,7 @@ S_mem_log_common(enum mem_log_type mlt, const UV n, if (strchr(pmlenv, 't')) { len = my_snprintf(buf, sizeof(buf), MEM_LOG_TIME_FMT, MEM_LOG_TIME_ARG); - PerlLIO_write(fd, buf, len); + PERL_UNUSED_RESULT(PerlLIO_write(fd, buf, len)); } switch (mlt) { case MLT_ALLOC: @@ -5910,7 +4808,7 @@ S_mem_log_common(enum mem_log_type mlt, const UV n, default: len = 0; } - PerlLIO_write(fd, buf, len); + PERL_UNUSED_RESULT(PerlLIO_write(fd, buf, len)); } } } @@ -5987,7 +4885,7 @@ Perl_mem_log_del_sv(const SV *sv, =for apidoc my_sprintf The C library C, wrapped if necessary, to ensure that it will return -the length of the string written to the buffer. Only rare pre-ANSI systems +the length of the string written to the buffer. Only rare pre-ANSI systems need the wrapper function - usually this is a direct call to C. =cut @@ -6068,6 +4966,7 @@ Perl_my_vsnprintf(char *buffer, const Size_t len, const char *format, va_list ap # else retval = vsprintf(buffer, format, apc); # endif + va_end(apc); #else # ifdef HAS_VSNPRINTF retval = vsnprintf(buffer, len, format, ap); @@ -6116,15 +5015,14 @@ Perl_my_clearenv(pTHX) (void)clearenv(); # elif defined(HAS_UNSETENV) int bsiz = 80; /* Most envvar names will be shorter than this. */ - int bufsiz = bsiz * sizeof(char); /* sizeof(char) paranoid? */ - char *buf = (char*)safesysmalloc(bufsiz); + char *buf = (char*)safesysmalloc(bsiz); while (*environ != NULL) { char *e = strchr(*environ, '='); int l = e ? e - *environ : (int)strlen(*environ); if (bsiz < l + 1) { (void)safesysfree(buf); bsiz = l + 1; /* + 1 for the \0. */ - buf = (char*)safesysmalloc(bufsiz); + buf = (char*)safesysmalloc(bsiz); } memcpy(buf, *environ, l); buf[l] = '\0'; @@ -6275,10 +5173,10 @@ Perl_xs_version_bootcheck(pTHX_ U32 items, U32 ax, const char *xs_p, else { /* XXX GV_ADDWARN */ vn = "XS_VERSION"; - sv = get_sv(Perl_form(aTHX_ "%"SVf"::%s", module, vn), 0); + sv = get_sv(Perl_form(aTHX_ "%"SVf"::%s", SVfARG(module), vn), 0); if (!sv || !SvOK(sv)) { vn = "VERSION"; - sv = get_sv(Perl_form(aTHX_ "%"SVf"::%s", module, vn), 0); + sv = get_sv(Perl_form(aTHX_ "%"SVf"::%s", SVfARG(module), vn), 0); } } if (sv) { @@ -6289,16 +5187,16 @@ Perl_xs_version_bootcheck(pTHX_ U32 items, U32 ax, const char *xs_p, if ( vcmp(pmsv,xssv) ) { SV *string = vstringify(xssv); SV *xpt = Perl_newSVpvf(aTHX_ "%"SVf" object version %"SVf - " does not match ", module, string); + " does not match ", SVfARG(module), SVfARG(string)); SvREFCNT_dec(string); string = vstringify(pmsv); if (vn) { - Perl_sv_catpvf(aTHX_ xpt, "$%"SVf"::%s %"SVf, module, vn, - string); + Perl_sv_catpvf(aTHX_ xpt, "$%"SVf"::%s %"SVf, SVfARG(module), vn, + SVfARG(string)); } else { - Perl_sv_catpvf(aTHX_ xpt, "bootstrap parameter %"SVf, string); + Perl_sv_catpvf(aTHX_ xpt, "bootstrap parameter %"SVf, SVfARG(string)); } SvREFCNT_dec(string); @@ -6327,7 +5225,8 @@ Perl_xs_apiversion_bootcheck(pTHX_ SV *module, const char *api_p, SV *runver_string = vstringify(runver); xpt = Perl_newSVpvf(aTHX_ "Perl API version %"SVf " of %"SVf" does not match %"SVf, - compver_string, module, runver_string); + SVfARG(compver_string), SVfARG(module), + SVfARG(runver_string)); Perl_sv_2mortal(aTHX_ xpt); SvREFCNT_dec(compver_string); @@ -6338,6 +5237,26 @@ Perl_xs_apiversion_bootcheck(pTHX_ SV *module, const char *api_p, Perl_croak_sv(aTHX_ xpt); } +/* +=for apidoc my_strlcat + +The C library C if available, or a Perl implementation of it. +This operates on C C-terminated strings. + +C appends string C to the end of C. It will append at +most S> characters. It will then C-terminate, +unless C is 0 or the original C string was longer than C (in +practice this should not happen as it means that either C is incorrect or +that C is not a proper C-terminated string). + +Note that C is the full size of the destination buffer and +the result is guaranteed to be C-terminated if there is room. Note that +room for the C should be included in C. + +=cut + +Description stolen from http://www.openbsd.org/cgi-bin/man.cgi?query=strlcat +*/ #ifndef HAS_STRLCAT Size_t Perl_my_strlcat(char *dst, const char *src, Size_t size) @@ -6355,6 +5274,20 @@ Perl_my_strlcat(char *dst, const char *src, Size_t size) } #endif + +/* +=for apidoc my_strlcpy + +The C library C if available, or a Perl implementation of it. +This operates on C C-terminated strings. + +C copies up to S> characters from the string C +to C, C-terminating the result if C is not 0. + +=cut + +Description stolen from http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy +*/ #ifndef HAS_STRLCPY Size_t Perl_my_strlcpy(char *dst, const char *src, Size_t size) @@ -6383,9 +5316,8 @@ S_gv_has_usable_name(pTHX_ GV *gv) GV **gvp; return GvSTASH(gv) && HvENAME(GvSTASH(gv)) - && (gvp = (GV **)hv_fetch( - GvSTASH(gv), GvNAME(gv), - GvNAMEUTF8(gv) ? -GvNAMELEN(gv) : GvNAMELEN(gv), 0 + && (gvp = (GV **)hv_fetchhek( + GvSTASH(gv), GvNAME_HEK(gv), 0 )) && *gvp == gv; } @@ -6395,7 +5327,7 @@ Perl_get_db_sub(pTHX_ SV **svp, CV *cv) { dVAR; SV * const dbsv = GvSVn(PL_DBsub); - const bool save_taint = TAINT_get; /* Accepted unused var warning under NO_TAINT_SUPPORT */ + const bool save_taint = TAINT_get; /* When we are called from pp_goto (svp is null), * we do not care about using dbsv to call CV; @@ -6445,7 +5377,11 @@ Perl_get_db_sub(pTHX_ SV **svp, CV *cv) (void)SvIOK_on(dbsv); SvIV_set(dbsv, PTR2IV(cv)); /* Do it the quickest way */ } + SvSETMAGIC(dbsv); TAINT_IF(save_taint); +#ifdef NO_TAINT_SUPPORT + PERL_UNUSED_VAR(save_taint); +#endif } int @@ -6455,8 +5391,10 @@ Perl_my_dirfd(pTHX_ DIR * dir) { if(!dir) return -1; #ifdef HAS_DIRFD + PERL_UNUSED_CONTEXT; return dirfd(dir); #elif defined(HAS_DIR_DD_FD) + PERL_UNUSED_CONTEXT; return dir->dd_fd; #else Perl_die(aTHX_ PL_no_func, "dirfd"); @@ -6481,6 +5419,759 @@ Perl_get_re_arg(pTHX_ SV *sv) { } /* + * This code is derived from drand48() implementation from FreeBSD, + * found in lib/libc/gen/_rand48.c. + * + * The U64 implementation is original, based on the POSIX + * specification for drand48(). + */ + +/* +* Copyright (c) 1993 Martin Birgmeier +* All rights reserved. +* +* You may redistribute unmodified or modified versions of this source +* code provided that the above copyright notice and this and the +* following conditions are retained. +* +* This software is provided ``as is'', and comes with no warranties +* of any kind. I shall in no event be liable for anything that happens +* to anyone/anything when using this software. +*/ + +#define FREEBSD_DRAND48_SEED_0 (0x330e) + +#ifdef PERL_DRAND48_QUAD + +#define DRAND48_MULT U64_CONST(0x5deece66d) +#define DRAND48_ADD 0xb +#define DRAND48_MASK U64_CONST(0xffffffffffff) + +#else + +#define FREEBSD_DRAND48_SEED_1 (0xabcd) +#define FREEBSD_DRAND48_SEED_2 (0x1234) +#define FREEBSD_DRAND48_MULT_0 (0xe66d) +#define FREEBSD_DRAND48_MULT_1 (0xdeec) +#define FREEBSD_DRAND48_MULT_2 (0x0005) +#define FREEBSD_DRAND48_ADD (0x000b) + +const unsigned short _rand48_mult[3] = { + FREEBSD_DRAND48_MULT_0, + FREEBSD_DRAND48_MULT_1, + FREEBSD_DRAND48_MULT_2 +}; +const unsigned short _rand48_add = FREEBSD_DRAND48_ADD; + +#endif + +void +Perl_drand48_init_r(perl_drand48_t *random_state, U32 seed) +{ + PERL_ARGS_ASSERT_DRAND48_INIT_R; + +#ifdef PERL_DRAND48_QUAD + *random_state = FREEBSD_DRAND48_SEED_0 + ((U64TYPE)seed << 16); +#else + random_state->seed[0] = FREEBSD_DRAND48_SEED_0; + random_state->seed[1] = (U16) seed; + random_state->seed[2] = (U16) (seed >> 16); +#endif +} + +double +Perl_drand48_r(perl_drand48_t *random_state) +{ + PERL_ARGS_ASSERT_DRAND48_R; + +#ifdef PERL_DRAND48_QUAD + *random_state = (*random_state * DRAND48_MULT + DRAND48_ADD) + & DRAND48_MASK; + + return ldexp((double)*random_state, -48); +#else + { + U32 accu; + U16 temp[2]; + + accu = (U32) _rand48_mult[0] * (U32) random_state->seed[0] + + (U32) _rand48_add; + temp[0] = (U16) accu; /* lower 16 bits */ + accu >>= sizeof(U16) * 8; + accu += (U32) _rand48_mult[0] * (U32) random_state->seed[1] + + (U32) _rand48_mult[1] * (U32) random_state->seed[0]; + temp[1] = (U16) accu; /* middle 16 bits */ + accu >>= sizeof(U16) * 8; + accu += _rand48_mult[0] * random_state->seed[2] + + _rand48_mult[1] * random_state->seed[1] + + _rand48_mult[2] * random_state->seed[0]; + random_state->seed[0] = temp[0]; + random_state->seed[1] = temp[1]; + random_state->seed[2] = (U16) accu; + + return ldexp((double) random_state->seed[0], -48) + + ldexp((double) random_state->seed[1], -32) + + ldexp((double) random_state->seed[2], -16); + } +#endif +} + +#ifdef USE_C_BACKTRACE + +/* Possibly move all this USE_C_BACKTRACE code into a new file. */ + +#ifdef USE_BFD + +typedef struct { + /* abfd is the BFD handle. */ + bfd* abfd; + /* bfd_syms is the BFD symbol table. */ + asymbol** bfd_syms; + /* bfd_text is handle to the the ".text" section of the object file. */ + asection* bfd_text; + /* Since opening the executable and scanning its symbols is quite + * heavy operation, we remember the filename we used the last time, + * and do the opening and scanning only if the filename changes. + * This removes most (but not all) open+scan cycles. */ + const char* fname_prev; +} bfd_context; + +/* Given a dl_info, update the BFD context if necessary. */ +static void bfd_update(bfd_context* ctx, Dl_info* dl_info) +{ + /* BFD open and scan only if the filename changed. */ + if (ctx->fname_prev == NULL || + strNE(dl_info->dli_fname, ctx->fname_prev)) { + ctx->abfd = bfd_openr(dl_info->dli_fname, 0); + if (ctx->abfd) { + if (bfd_check_format(ctx->abfd, bfd_object)) { + IV symbol_size = bfd_get_symtab_upper_bound(ctx->abfd); + if (symbol_size > 0) { + Safefree(ctx->bfd_syms); + Newx(ctx->bfd_syms, symbol_size, asymbol*); + ctx->bfd_text = + bfd_get_section_by_name(ctx->abfd, ".text"); + } + else + ctx->abfd = NULL; + } + else + ctx->abfd = NULL; + } + ctx->fname_prev = dl_info->dli_fname; + } +} + +/* Given a raw frame, try to symbolize it and store + * symbol information (source file, line number) away. */ +static void bfd_symbolize(bfd_context* ctx, + void* raw_frame, + char** symbol_name, + STRLEN* symbol_name_size, + char** source_name, + STRLEN* source_name_size, + STRLEN* source_line) +{ + *symbol_name = NULL; + *symbol_name_size = 0; + if (ctx->abfd) { + IV offset = PTR2IV(raw_frame) - PTR2IV(ctx->bfd_text->vma); + if (offset > 0 && + bfd_canonicalize_symtab(ctx->abfd, ctx->bfd_syms) > 0) { + const char *file; + const char *func; + unsigned int line = 0; + if (bfd_find_nearest_line(ctx->abfd, ctx->bfd_text, + ctx->bfd_syms, offset, + &file, &func, &line) && + file && func && line > 0) { + /* Size and copy the source file, use only + * the basename of the source file. + * + * NOTE: the basenames are fine for the + * Perl source files, but may not always + * be the best idea for XS files. */ + const char *p, *b = NULL; + /* Look for the last slash. */ + for (p = file; *p; p++) { + if (*p == '/') + b = p + 1; + } + if (b == NULL || *b == 0) { + b = file; + } + *source_name_size = p - b + 1; + Newx(*source_name, *source_name_size + 1, char); + Copy(b, *source_name, *source_name_size + 1, char); + + *symbol_name_size = strlen(func); + Newx(*symbol_name, *symbol_name_size + 1, char); + Copy(func, *symbol_name, *symbol_name_size + 1, char); + + *source_line = line; + } + } + } +} + +#endif /* #ifdef USE_BFD */ + +#ifdef PERL_DARWIN + +/* OS X has no public API for for 'symbolicating' (Apple official term) + * stack addresses to {function_name, source_file, line_number}. + * Good news: there is command line utility atos(1) which does that. + * Bad news 1: it's a command line utility. + * Bad news 2: one needs to have the Developer Tools installed. + * Bad news 3: in newer releases it needs to be run as 'xcrun atos'. + * + * To recap: we need to open a pipe for reading for a utility which + * might not exist, or exists in different locations, and then parse + * the output. And since this is all for a low-level API, we cannot + * use high-level stuff. Thanks, Apple. */ + +typedef struct { + /* tool is set to the absolute pathname of the tool to use: + * xcrun or atos. */ + const char* tool; + /* format is set to a printf format string used for building + * the external command to run. */ + const char* format; + /* unavail is set if e.g. xcrun cannot be found, or something + * else happens that makes getting the backtrace dubious. Note, + * however, that the context isn't persistent, the next call to + * get_c_backtrace() will start from scratch. */ + bool unavail; + /* fname is the current object file name. */ + const char* fname; + /* object_base_addr is the base address of the shared object. */ + void* object_base_addr; +} atos_context; + +/* Given |dl_info|, updates the context. If the context has been + * marked unavailable, return immediately. If not but the tool has + * not been set, set it to either "xcrun atos" or "atos" (also set the + * format to use for creating commands for piping), or if neither is + * unavailable (one needs the Developer Tools installed), mark the context + * an unavailable. Finally, update the filename (object name), + * and its base address. */ + +static void atos_update(atos_context* ctx, + Dl_info* dl_info) +{ + if (ctx->unavail) + return; + if (ctx->tool == NULL) { + const char* tools[] = { + "/usr/bin/xcrun", + "/usr/bin/atos" + }; + const char* formats[] = { + "/usr/bin/xcrun atos -o '%s' -l %08x %08x 2>&1", + "/usr/bin/atos -d -o '%s' -l %08x %08x 2>&1" + }; + struct stat st; + UV i; + for (i = 0; i < C_ARRAY_LENGTH(tools); i++) { + if (stat(tools[i], &st) == 0 && S_ISREG(st.st_mode)) { + ctx->tool = tools[i]; + ctx->format = formats[i]; + break; + } + } + if (ctx->tool == NULL) { + ctx->unavail = TRUE; + return; + } + } + if (ctx->fname == NULL || + strNE(dl_info->dli_fname, ctx->fname)) { + ctx->fname = dl_info->dli_fname; + ctx->object_base_addr = dl_info->dli_fbase; + } +} + +/* Given an output buffer end |p| and its |start|, matches + * for the atos output, extracting the source code location + * if possible, returning NULL otherwise. */ +static const char* atos_parse(const char* p, + const char* start, + STRLEN* source_name_size, + STRLEN* source_line) { + /* atos() outputs is something like: + * perl_parse (in miniperl) (perl.c:2314)\n\n". + * We cannot use Perl regular expressions, because we need to + * stay low-level. Therefore here we have a rolled-out version + * of a state machine which matches _backwards_from_the_end_ and + * if there's a success, returns the starts of the filename, + * also setting the filename size and the source line number. + * The matched regular expression is roughly "\(.*:\d+\)\s*$" */ + const char* source_number_start; + const char* source_name_end; + /* Skip trailing whitespace. */ + while (p > start && isspace(*p)) p--; + /* Now we should be at the close paren. */ + if (p == start || *p != ')') + return NULL; + p--; + /* Now we should be in the line number. */ + if (p == start || !isdigit(*p)) + return NULL; + /* Skip over the digits. */ + while (p > start && isdigit(*p)) + p--; + /* Now we should be at the colon. */ + if (p == start || *p != ':') + return NULL; + source_number_start = p + 1; + source_name_end = p; /* Just beyond the end. */ + p--; + /* Look for the open paren. */ + while (p > start && *p != '(') + p--; + if (p == start) + return NULL; + p++; + *source_name_size = source_name_end - p; + *source_line = atoi(source_number_start); + return p; +} + +/* Given a raw frame, read a pipe from the symbolicator (that's the + * technical term) atos, reads the result, and parses the source code + * location. We must stay low-level, so we use snprintf(), pipe(), + * and fread(), and then also parse the output ourselves. */ +static void atos_symbolize(atos_context* ctx, + void* raw_frame, + char** source_name, + STRLEN* source_name_size, + STRLEN* source_line) +{ + char cmd[1024]; + const char* p; + Size_t cnt; + + if (ctx->unavail) + return; + /* Simple security measure: if there's any funny business with + * the object name (used as "-o '%s'" ), leave since at least + * partially the user controls it. */ + for (p = ctx->fname; *p; p++) { + if (*p == '\'' || iscntrl(*p)) { + ctx->unavail = TRUE; + return; + } + } + cnt = snprintf(cmd, sizeof(cmd), ctx->format, + ctx->fname, ctx->object_base_addr, raw_frame); + if (cnt < sizeof(cmd)) { + /* Undo nostdio.h #defines that disable stdio. + * This is somewhat naughty, but is used elsewhere + * in the core, and affects only OS X. */ +#undef FILE +#undef popen +#undef fread +#undef pclose + FILE* fp = popen(cmd, "r"); + /* At the moment we open a new pipe for each stack frame. + * This is naturally somewhat slow, but hopefully generating + * stack traces is never going to in a performance critical path. + * + * We could play tricks with atos by batching the stack + * addresses to be resolved: atos can either take multiple + * addresses from the command line, or read addresses from + * a file (though the mess of creating temporary files would + * probably negate much of any possible speedup). + * + * Normally there are only two objects present in the backtrace: + * perl itself, and the libdyld.dylib. (Note that the object + * filenames contain the full pathname, so perl may not always + * be in the same place.) Whenever the object in the + * backtrace changes, the base address also changes. + * + * The problem with batching the addresses, though, would be + * matching the results with the addresses: the parsing of + * the results is already painful enough with a single address. */ + if (fp) { + char out[1024]; + UV cnt = fread(out, 1, sizeof(out), fp); + if (cnt < sizeof(out)) { + const char* p = atos_parse(out + cnt, out, + source_name_size, + source_line); + if (p) { + Newx(*source_name, + *source_name_size + 1, char); + Copy(p, *source_name, + *source_name_size + 1, char); + } + } + pclose(fp); + } + } +} + +#endif /* #ifdef PERL_DARWIN */ + +/* +=for apidoc get_c_backtrace + +Collects the backtrace (aka "stacktrace") into a single linear +malloced buffer, which the caller B Perl_free_c_backtrace(). + +Scans the frames back by depth + skip, then drops the skip innermost, +returning at most depth frames. + +=cut +*/ + +Perl_c_backtrace* +Perl_get_c_backtrace(pTHX_ int depth, int skip) +{ + /* Note that here we must stay as low-level as possible: Newx(), + * Copy(), Safefree(); since we may be called from anywhere, + * so we should avoid higher level constructs like SVs or AVs. + * + * Since we are using safesysmalloc() via Newx(), don't try + * getting backtrace() there, unless you like deep recursion. */ + + /* Currently only implemented with backtrace() and dladdr(), + * for other platforms NULL is returned. */ + +#if defined(HAS_BACKTRACE) && defined(HAS_DLADDR) + /* backtrace() is available via in glibc and in most + * modern BSDs; dladdr() is available via . */ + + /* We try fetching this many frames total, but then discard + * the |skip| first ones. For the remaining ones we will try + * retrieving more information with dladdr(). */ + int try_depth = skip + depth; + + /* The addresses (program counters) returned by backtrace(). */ + void** raw_frames; + + /* Retrieved with dladdr() from the addresses returned by backtrace(). */ + Dl_info* dl_infos; + + /* Sizes _including_ the terminating \0 of the object name + * and symbol name strings. */ + STRLEN* object_name_sizes; + STRLEN* symbol_name_sizes; + +#ifdef USE_BFD + /* The symbol names comes either from dli_sname, + * or if using BFD, they can come from BFD. */ + char** symbol_names; +#endif + + /* The source code location information. Dug out with e.g. BFD. */ + char** source_names; + STRLEN* source_name_sizes; + STRLEN* source_lines; + + Perl_c_backtrace* bt = NULL; /* This is what will be returned. */ + int got_depth; /* How many frames were returned from backtrace(). */ + UV frame_count = 0; /* How many frames we return. */ + UV total_bytes = 0; /* The size of the whole returned backtrace. */ + +#ifdef USE_BFD + bfd_context bfd_ctx; +#endif +#ifdef PERL_DARWIN + atos_context atos_ctx; +#endif + + /* Here are probably possibilities for optimizing. We could for + * example have a struct that contains most of these and then + * allocate |try_depth| of them, saving a bunch of malloc calls. + * Note, however, that |frames| could not be part of that struct + * because backtrace() will want an array of just them. Also be + * careful about the name strings. */ + Newx(raw_frames, try_depth, void*); + Newx(dl_infos, try_depth, Dl_info); + Newx(object_name_sizes, try_depth, STRLEN); + Newx(symbol_name_sizes, try_depth, STRLEN); + Newx(source_names, try_depth, char*); + Newx(source_name_sizes, try_depth, STRLEN); + Newx(source_lines, try_depth, STRLEN); +#ifdef USE_BFD + Newx(symbol_names, try_depth, char*); +#endif + + /* Get the raw frames. */ + got_depth = (int)backtrace(raw_frames, try_depth); + + /* We use dladdr() instead of backtrace_symbols() because we want + * the full details instead of opaque strings. This is useful for + * two reasons: () the details are needed for further symbolic + * digging, for example in OS X (2) by having the details we fully + * control the output, which in turn is useful when more platforms + * are added: we can keep out output "portable". */ + + /* We want a single linear allocation, which can then be freed + * with a single swoop. We will do the usual trick of first + * walking over the structure and seeing how much we need to + * allocate, then allocating, and then walking over the structure + * the second time and populating it. */ + + /* First we must compute the total size of the buffer. */ + total_bytes = sizeof(Perl_c_backtrace_header); + if (got_depth > skip) { + int i; +#ifdef USE_BFD + bfd_init(); /* Is this safe to call multiple times? */ + Zero(&bfd_ctx, 1, bfd_context); +#endif +#ifdef PERL_DARWIN + Zero(&atos_ctx, 1, atos_context); +#endif + for (i = skip; i < try_depth; i++) { + Dl_info* dl_info = &dl_infos[i]; + + total_bytes += sizeof(Perl_c_backtrace_frame); + + source_names[i] = NULL; + source_name_sizes[i] = 0; + source_lines[i] = 0; + + /* Yes, zero from dladdr() is failure. */ + if (dladdr(raw_frames[i], dl_info)) { + object_name_sizes[i] = + dl_info->dli_fname ? strlen(dl_info->dli_fname) : 0; + symbol_name_sizes[i] = + dl_info->dli_sname ? strlen(dl_info->dli_sname) : 0; +#ifdef USE_BFD + bfd_update(&bfd_ctx, dl_info); + bfd_symbolize(&bfd_ctx, raw_frames[i], + &symbol_names[i], + &symbol_name_sizes[i], + &source_names[i], + &source_name_sizes[i], + &source_lines[i]); +#endif +#if PERL_DARWIN + atos_update(&atos_ctx, dl_info); + atos_symbolize(&atos_ctx, + raw_frames[i], + &source_names[i], + &source_name_sizes[i], + &source_lines[i]); +#endif + + /* Plus ones for the terminating \0. */ + total_bytes += object_name_sizes[i] + 1; + total_bytes += symbol_name_sizes[i] + 1; + total_bytes += source_name_sizes[i] + 1; + + frame_count++; + } else { + break; + } + } +#ifdef USE_BFD + Safefree(bfd_ctx.bfd_syms); +#endif + } + + /* Now we can allocate and populate the result buffer. */ + Newxc(bt, total_bytes, char, Perl_c_backtrace); + Zero(bt, total_bytes, char); + bt->header.frame_count = frame_count; + bt->header.total_bytes = total_bytes; + if (frame_count > 0) { + Perl_c_backtrace_frame* frame = bt->frame_info; + char* name_base = (char *)(frame + frame_count); + char* name_curr = name_base; /* Outputting the name strings here. */ + UV i; + for (i = skip; i < skip + frame_count; i++) { + Dl_info* dl_info = &dl_infos[i]; + + frame->addr = raw_frames[i]; + frame->object_base_addr = dl_info->dli_fbase; + frame->symbol_addr = dl_info->dli_saddr; + + /* Copies a string, including the \0, and advances the name_curr. + * Also copies the start and the size to the frame. */ +#define PERL_C_BACKTRACE_STRCPY(frame, doffset, src, dsize, size) \ + if (size && src) \ + Copy(src, name_curr, size, char); \ + frame->doffset = name_curr - (char*)bt; \ + frame->dsize = size; \ + name_curr += size; \ + *name_curr++ = 0; + + PERL_C_BACKTRACE_STRCPY(frame, object_name_offset, + dl_info->dli_fname, + object_name_size, object_name_sizes[i]); + +#ifdef USE_BFD + PERL_C_BACKTRACE_STRCPY(frame, symbol_name_offset, + symbol_names[i], + symbol_name_size, symbol_name_sizes[i]); + Safefree(symbol_names[i]); +#else + PERL_C_BACKTRACE_STRCPY(frame, symbol_name_offset, + dl_info->dli_sname, + symbol_name_size, symbol_name_sizes[i]); +#endif + + PERL_C_BACKTRACE_STRCPY(frame, source_name_offset, + source_names[i], + source_name_size, source_name_sizes[i]); + Safefree(source_names[i]); + +#undef PERL_C_BACKTRACE_STRCPY + + frame->source_line_number = source_lines[i]; + + frame++; + } + assert(total_bytes == + (UV)(sizeof(Perl_c_backtrace_header) + + frame_count * sizeof(Perl_c_backtrace_frame) + + name_curr - name_base)); + } +#ifdef USE_BFD + Safefree(symbol_names); +#endif + Safefree(source_lines); + Safefree(source_name_sizes); + Safefree(source_names); + Safefree(symbol_name_sizes); + Safefree(object_name_sizes); + /* Assuming the strings returned by dladdr() are pointers + * to read-only static memory (the object file), so that + * they do not need freeing (and cannot be). */ + Safefree(dl_infos); + Safefree(raw_frames); + return bt; +#else + PERL_UNUSED_ARGV(depth); + PERL_UNUSED_ARGV(skip); + return NULL; +#endif +} + +/* +=for apidoc free_c_backtrace + +Deallocates a backtrace received from get_c_bracktrace. + +=cut +*/ + +/* +=for apidoc get_c_backtrace_dump + +Returns a SV a dump of |depth| frames of the call stack, skipping +the |skip| innermost ones. depth of 20 is usually enough. + +The appended output looks like: + +... +1 10e004812:0082 Perl_croak util.c:1716 /usr/bin/perl +2 10df8d6d2:1d72 perl_parse perl.c:3975 /usr/bin/perl +... + +The fields are tab-separated. The first column is the depth (zero +being the innermost non-skipped frame). In the hex:offset, the hex is +where the program counter was in S_parse_body, and the :offset (might +be missing) tells how much inside the S_parse_body the program counter was. + +The util.c:1716 is the source code file and line number. + +The /usr/bin/perl is obvious (hopefully). + +Unknowns are C<"-">. Unknowns can happen unfortunately quite easily: +if the platform doesn't support retrieving the information; +if the binary is missing the debug information; +if the optimizer has transformed the code by for example inlining. + +=cut +*/ + +SV* +Perl_get_c_backtrace_dump(pTHX_ int depth, int skip) +{ + Perl_c_backtrace* bt; + + bt = get_c_backtrace(depth, skip + 1 /* Hide ourselves. */); + if (bt) { + Perl_c_backtrace_frame* frame; + SV* dsv = newSVpvs(""); + UV i; + for (i = 0, frame = bt->frame_info; + i < bt->header.frame_count; i++, frame++) { + Perl_sv_catpvf(aTHX_ dsv, "%d", (int)i); + Perl_sv_catpvf(aTHX_ dsv, "\t%p", frame->addr ? frame->addr : "-"); + /* Symbol (function) names might disappear without debug info. + * + * The source code location might disappear in case of the + * optimizer inlining or otherwise rearranging the code. */ + if (frame->symbol_addr) { + Perl_sv_catpvf(aTHX_ dsv, ":%04x", + (int) + ((char*)frame->addr - (char*)frame->symbol_addr)); + } + Perl_sv_catpvf(aTHX_ dsv, "\t%s", + frame->symbol_name_size && + frame->symbol_name_offset ? + (char*)bt + frame->symbol_name_offset : "-"); + if (frame->source_name_size && + frame->source_name_offset && + frame->source_line_number) { + Perl_sv_catpvf(aTHX_ dsv, "\t%s:%"UVuf, + (char*)bt + frame->source_name_offset, + (UV)frame->source_line_number); + } else { + Perl_sv_catpvf(aTHX_ dsv, "\t-"); + } + Perl_sv_catpvf(aTHX_ dsv, "\t%s", + frame->object_name_size && + frame->object_name_offset ? + (char*)bt + frame->object_name_offset : "-"); + /* The frame->object_base_addr is not output, + * but it is used for symbolizing/symbolicating. */ + sv_catpvs(dsv, "\n"); + } + + Perl_free_c_backtrace(aTHX_ bt); + + return dsv; + } + + return NULL; +} + +/* +=for apidoc dump_c_backtrace + +Dumps the C backtrace to the given fp. + +Returns true if a backtrace could be retrieved, false if not. + +=cut +*/ + +bool +Perl_dump_c_backtrace(pTHX_ PerlIO* fp, int depth, int skip) +{ + SV* sv; + + PERL_ARGS_ASSERT_DUMP_C_BACKTRACE; + + sv = Perl_get_c_backtrace_dump(aTHX_ depth, skip); + if (sv) { + sv_2mortal(sv); + PerlIO_printf(fp, "%s", SvPV_nolen(sv)); + return TRUE; + } + return FALSE; +} + +#endif /* #ifdef USE_C_BACKTRACE */ + +/* * Local variables: * c-indentation-style: bsd * c-basic-offset: 4