diff --git a/NEWS b/NEWS index 1813c24dcd393..250d7017ee6d6 100644 --- a/NEWS +++ b/NEWS @@ -96,10 +96,6 @@ PHP NEWS . Fixed bug #49169 (SoapServer calls wrong function, although "SOAP action" header is correct). (nielsdos) -- Sodium: - . Fix overall theorical overflows on zend_string buffer allocations. - (David Carlier/nielsdos) - - Sockets: . Added IPPROTO_ICMP/IPPROTO_ICMPV6 to create raw socket for ICMP usage. (David Carlier) @@ -124,6 +120,12 @@ PHP NEWS . socket_set_option with the options MCAST_LEAVE_GROUP/MCAST_LEAVE_SOURCE_GROUP will throw an exception if its value is not a valid array/object. (David Carlier) + . socket_getsockname/socket_create/socket_bind handled AF_PACKET family socket. + (David Carlier) + +- Sodium: + . Fix overall theorical overflows on zend_string buffer allocations. + (David Carlier/nielsdos) - Standard: . Fixed crypt() tests on musl when using --with-external-libcrypt diff --git a/UPGRADING b/UPGRADING index 65db4791a2ae5..fcf1703fa5d92 100644 --- a/UPGRADING +++ b/UPGRADING @@ -164,6 +164,9 @@ PHP 8.5 UPGRADE NOTES . socket_set_option with MCAST_LEAVE_GROUP/MCAST_LEAVE_SOURCE_GROUP options will throw an exception if the value isn't a valid object or array. + . socket_create/socket_bind can create AF_PACKET family sockets. + . socket_getsockname gets the interface index and its string + representation with AF_PACKET socket. - Zlib: . The "use_include_path" argument for the @@ -241,6 +244,7 @@ PHP 8.5 UPGRADE NOTES . TCP_REUSPORT_LB_NUMA_NODOM (FreeBSD only). . TCP_REUSPORT_LB_NUMA_CURDOM (FreeBSD only). . TCP_BBR_ALGORITHM (FreeBSD only). + . AF_PACKET (Linux only). ======================================== 11. Changes to INI File Handling diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c index d1d33287f75fa..41624bfe172fd 100644 --- a/ext/dom/html_document.c +++ b/ext/dom/html_document.c @@ -571,7 +571,7 @@ static bool dom_decode_encode_fast_path( parser, buf_ptr - buf, buf, - buf_ptr - buf, + buf_ref - *buf_ref_ref, tokenizer_error_offset, tree_error_offset )) { diff --git a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_08.phpt b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_08.phpt index 68f0708bab780..305b78f4b6123 100644 --- a/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_08.phpt +++ b/ext/dom/tests/modern/html/encoding/HTMLDocument_encoding_edge_case_08.phpt @@ -9,9 +9,9 @@ function test(string $str) { $dom = Dom\HTMLDocument::createFromString($str); var_dump($dom->body->textContent); - file_put_contents(__DIR__ . '/HTMLDocument_encoding_edge_case_07.tmp', $str); + file_put_contents(__DIR__ . '/HTMLDocument_encoding_edge_case_08.tmp', $str); - $dom = Dom\HTMLDocument::createFromFile(__DIR__ . '/HTMLDocument_encoding_edge_case_07.tmp'); + $dom = Dom\HTMLDocument::createFromFile(__DIR__ . '/HTMLDocument_encoding_edge_case_08.tmp'); var_dump($dom->body->textContent); } @@ -36,7 +36,7 @@ test($str); ?> --CLEAN-- --EXPECT-- string(4198) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" diff --git a/ext/sockets/config.m4 b/ext/sockets/config.m4 index e6d1de4c0c70b..37f927a78186a 100644 --- a/ext/sockets/config.m4 +++ b/ext/sockets/config.m4 @@ -5,7 +5,7 @@ PHP_ARG_ENABLE([sockets], if test "$PHP_SOCKETS" != "no"; then AC_CHECK_FUNCS([hstrerror if_nametoindex if_indextoname sockatmark]) - AC_CHECK_HEADERS([sys/sockio.h linux/filter.h]) + AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h]) AC_DEFINE([HAVE_SOCKETS], [1], [Define to 1 if the PHP extension 'sockets' is available.]) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index f7a32894257b5..88a26f19b4f82 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -64,6 +64,12 @@ # else # undef SO_BPF_EXTENSIONS # endif +# if defined(HAVE_LINUX_IF_PACKET_H) +# include +# endif +# if defined(HAVE_LINUX_IF_ETHER_H) +# include +# endif #endif #include @@ -91,6 +97,18 @@ ZEND_DECLARE_MODULE_GLOBALS(sockets) #define PF_INET AF_INET #endif +#if defined(AF_PACKET) +#define PHP_ETH_PROTO_CHECK(protocol, family) \ + do { \ + /* We ll let EINVAL errno warning about miusage, too many protocols conflicts */ \ + if (protocol <= USHRT_MAX && family == AF_PACKET) { \ + protocol = htons(protocol); \ + } \ + } while (0) +#else +#define PHP_ETH_PROTO_CHECK(protocol, family) (0) +#endif + static PHP_GINIT_FUNCTION(sockets); static PHP_GSHUTDOWN_FUNCTION(sockets); static PHP_MINIT_FUNCTION(sockets); @@ -960,13 +978,16 @@ PHP_FUNCTION(socket_read) /* {{{ Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type. */ PHP_FUNCTION(socket_getsockname) { - zval *arg1, *addr, *port = NULL; + zval *arg1, *addr, *objint = NULL; php_sockaddr_storage sa_storage = {0}; php_socket *php_sock; struct sockaddr *sa; struct sockaddr_in *sin; #ifdef HAVE_IPV6 struct sockaddr_in6 *sin6; +#endif +#ifdef AF_PACKET + struct sockaddr_ll *sll; #endif char addrbuf[INET6_ADDRSTRLEN]; struct sockaddr_un *s_un; @@ -977,7 +998,7 @@ PHP_FUNCTION(socket_getsockname) Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce) Z_PARAM_ZVAL(addr) Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(port) + Z_PARAM_ZVAL(objint) ZEND_PARSE_PARAMETERS_END(); php_sock = Z_SOCKET_P(arg1); @@ -997,8 +1018,8 @@ PHP_FUNCTION(socket_getsockname) inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf, sizeof(addrbuf)); ZEND_TRY_ASSIGN_REF_STRING(addr, addrbuf); - if (port != NULL) { - ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin6->sin6_port)); + if (objint != NULL) { + ZEND_TRY_ASSIGN_REF_LONG(objint, htons(sin6->sin6_port)); } RETURN_TRUE; break; @@ -1008,8 +1029,8 @@ PHP_FUNCTION(socket_getsockname) addr_string = inet_ntop(AF_INET, &sin->sin_addr, addrbuf, sizeof(addrbuf)); ZEND_TRY_ASSIGN_REF_STRING(addr, addr_string); - if (port != NULL) { - ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin->sin_port)); + if (objint != NULL) { + ZEND_TRY_ASSIGN_REF_LONG(objint, htons(sin->sin_port)); } RETURN_TRUE; break; @@ -1020,9 +1041,26 @@ PHP_FUNCTION(socket_getsockname) ZEND_TRY_ASSIGN_REF_STRING(addr, s_un->sun_path); RETURN_TRUE; break; +#ifdef AF_PACKET + case AF_PACKET: + sll = (struct sockaddr_ll *) sa; + char ifrname[IFNAMSIZ]; + + if (UNEXPECTED(!if_indextoname(sll->sll_ifindex, ifrname))) { + zend_throw_error(NULL, "invalid interface index"); + RETURN_THROWS(); + } + + ZEND_TRY_ASSIGN_REF_STRING(addr, ifrname); + if (objint != NULL) { + ZEND_TRY_ASSIGN_REF_LONG(objint, sll->sll_ifindex); + } + RETURN_TRUE; + break; +#endif default: - zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6"); RETURN_THROWS(); } } @@ -1117,9 +1155,12 @@ PHP_FUNCTION(socket_create) if (domain != AF_UNIX #ifdef HAVE_IPV6 && domain != AF_INET6 +#endif +#ifdef AF_PACKET + && domain != AF_PACKET #endif && domain != AF_INET) { - zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET6, or AF_INET"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET6, or AF_INET"); RETURN_THROWS(); } @@ -1138,6 +1179,8 @@ PHP_FUNCTION(socket_create) RETURN_THROWS(); } + PHP_ETH_PROTO_CHECK(protocol, domain); + object_init_ex(return_value, socket_ce); php_sock = Z_SOCKET_P(return_value); @@ -1275,20 +1318,20 @@ PHP_FUNCTION(socket_bind) php_socket *php_sock; char *addr; size_t addr_len; - zend_long port = 0; + zend_long objint = 0; zend_long retval = 0; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce) Z_PARAM_STRING(addr, addr_len) Z_PARAM_OPTIONAL - Z_PARAM_LONG(port) + Z_PARAM_LONG(objint) ZEND_PARSE_PARAMETERS_END(); php_sock = Z_SOCKET_P(arg1); ENSURE_SOCKET_VALID(php_sock); - if (port < 0 || port > USHRT_MAX) { + if (objint < 0 || objint > USHRT_MAX) { zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX); RETURN_THROWS(); } @@ -1316,7 +1359,7 @@ PHP_FUNCTION(socket_bind) struct sockaddr_in *sa = (struct sockaddr_in *) sock_type; sa->sin_family = AF_INET; - sa->sin_port = htons((unsigned short) port); + sa->sin_port = htons((unsigned short) objint); if (! php_set_inet_addr(sa, addr, php_sock)) { RETURN_FALSE; @@ -1331,7 +1374,7 @@ PHP_FUNCTION(socket_bind) struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type; sa->sin6_family = AF_INET6; - sa->sin6_port = htons((unsigned short) port); + sa->sin6_port = htons((unsigned short) objint); if (! php_set_inet6_addr(sa, addr, php_sock)) { RETURN_FALSE; @@ -1340,9 +1383,26 @@ PHP_FUNCTION(socket_bind) retval = bind(php_sock->bsd_socket, (struct sockaddr *)sa, sizeof(struct sockaddr_in6)); break; } +#endif +#ifdef AF_PACKET + case AF_PACKET: + { + struct sockaddr_ll *sa = (struct sockaddr_ll *) sock_type; + socklen_t sa_len = sizeof(sa); + + if (getsockname(php_sock->bsd_socket, sock_type, &sa_len) < 0) { + zend_value_error("invalid AF_PACKET socket"); + RETURN_THROWS(); + } + + sa->sll_ifindex = if_nametoindex(addr); + + retval = bind(php_sock->bsd_socket, sock_type, sizeof(struct sockaddr_ll)); + break; + } #endif default: - zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6"); RETURN_THROWS(); } @@ -1443,6 +1503,9 @@ PHP_FUNCTION(socket_recvfrom) struct sockaddr_in sin; #ifdef HAVE_IPV6 struct sockaddr_in6 sin6; +#endif +#ifdef AF_PACKET + //struct sockaddr_ll sll; #endif char addrbuf[INET6_ADDRSTRLEN]; socklen_t slen; @@ -1547,6 +1610,38 @@ PHP_FUNCTION(socket_recvfrom) ZEND_TRY_ASSIGN_REF_STRING(arg5, addrbuf[0] ? addrbuf : "::"); ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin6.sin6_port)); break; +#endif +#ifdef AF_PACKET + /* + case AF_PACKET: + // TODO expose and use proper ethernet frame type instead i.e. src mac, dst mac and payload to userland + // ditto for socket_sendto + slen = sizeof(sll); + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + char ifrname[IFNAMSIZ]; + + retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sll, (socklen_t *)&slen); + + if (retval < 0) { + PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno); + zend_string_efree(recv_buf); + RETURN_FALSE; + } + ZSTR_LEN(recv_buf) = retval; + ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0'; + + if (UNEXPECTED(!if_indextoname(sll.sll_ifindex, ifrname))) { + PHP_SOCKET_ERROR(php_sock, "unable to get the interface name", errno); + zend_string_efree(recv_buf); + RETURN_FALSE; + } + + ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf); + ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname); + ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex); + break; + */ #endif default: zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); @@ -1566,6 +1661,9 @@ PHP_FUNCTION(socket_sendto) struct sockaddr_in sin; #ifdef HAVE_IPV6 struct sockaddr_in6 sin6; +#endif +#ifdef AF_PACKET + //struct sockaddr_ll sll; #endif int retval; size_t buf_len, addr_len; @@ -1639,6 +1737,22 @@ PHP_FUNCTION(socket_sendto) retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin6, sizeof(sin6)); break; +#endif +#ifdef AF_PACKET + /* + case AF_PACKET: + if (port_is_null) { + zend_argument_value_error(6, "cannot be null when the socket type is AF_PACKET"); + RETURN_THROWS(); + } + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = port; + + retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin)); + break; + */ #endif default: zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); @@ -2702,6 +2816,8 @@ PHP_FUNCTION(socket_addrinfo_bind) ai = Z_ADDRESS_INFO_P(arg1); + PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family); + object_init_ex(return_value, socket_ce); php_sock = Z_SOCKET_P(return_value); @@ -2765,6 +2881,8 @@ PHP_FUNCTION(socket_addrinfo_connect) ai = Z_ADDRESS_INFO_P(arg1); + PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family); + object_init_ex(return_value, socket_ce); php_sock = Z_SOCKET_P(return_value); diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 325ade65c9f88..142772fc7cdb7 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -26,6 +26,13 @@ */ const AF_DIVERT = UNKNOWN; #endif +#ifdef AF_PACKET +/** + * @var int + * @cvalue AF_PACKET + */ +const AF_PACKET = UNKNOWN; +#endif /** * @var int * @cvalue SOCK_STREAM @@ -1978,6 +1985,28 @@ */ const UDPLITE_RECV_CSCOV = UNKNOWN; #endif +#if defined(ETH_P_ALL) +/** + * @var int + * @cvalue ETH_P_IP + */ +const ETH_P_IP = UNKNOWN; +/** + * @var int + * @cvalue ETH_P_IPV6 + */ +const ETH_P_IPV6 = UNKNOWN; +/** + * @var int + * @cvalue ETH_P_LOOP + */ +const ETH_P_LOOP = UNKNOWN; +/** + * @var int + * @cvalue ETH_P_ALL + */ +const ETH_P_ALL = UNKNOWN; +#endif /** * @strict-properties diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index fdaa537b41daf..56d6a280bbafe 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2c5f4685d5dab42426d4fe0553dd17cb9935a572 */ + * Stub hash: 0be24cb2f268ab3d43121637ae451d8da4b50410 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -319,6 +319,9 @@ static void register_sockets_symbols(int module_number) #endif #if defined(AF_DIVERT) REGISTER_LONG_CONSTANT("AF_DIVERT", AF_DIVERT, CONST_PERSISTENT); +#endif +#if defined(AF_PACKET) + REGISTER_LONG_CONSTANT("AF_PACKET", AF_PACKET, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("SOCK_STREAM", SOCK_STREAM, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SOCK_DGRAM", SOCK_DGRAM, CONST_PERSISTENT); @@ -1087,6 +1090,18 @@ static void register_sockets_symbols(int module_number) #if defined(UDPLITE_RECV_CSCOV) REGISTER_LONG_CONSTANT("UDPLITE_RECV_CSCOV", UDPLITE_RECV_CSCOV, CONST_PERSISTENT); #endif +#if defined(ETH_P_ALL) + REGISTER_LONG_CONSTANT("ETH_P_IP", ETH_P_IP, CONST_PERSISTENT); +#endif +#if defined(ETH_P_ALL) + REGISTER_LONG_CONSTANT("ETH_P_IPV6", ETH_P_IPV6, CONST_PERSISTENT); +#endif +#if defined(ETH_P_ALL) + REGISTER_LONG_CONSTANT("ETH_P_LOOP", ETH_P_LOOP, CONST_PERSISTENT); +#endif +#if defined(ETH_P_ALL) + REGISTER_LONG_CONSTANT("ETH_P_ALL", ETH_P_ALL, CONST_PERSISTENT); +#endif } static zend_class_entry *register_class_Socket(void) diff --git a/ext/sockets/tests/socket_afpacket.phpt b/ext/sockets/tests/socket_afpacket.phpt new file mode 100644 index 0000000000000..d0de69f7b1a19 --- /dev/null +++ b/ext/sockets/tests/socket_afpacket.phpt @@ -0,0 +1,37 @@ +--TEST-- +socket_getsockname from AF_PACKET socket +--EXTENSIONS-- +sockets +posix +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +string(2) "lo" +int(%i) + +Warning: socket_getpeername(): unable to retrieve peer name [95]: Operation not supported in %s on line %d diff --git a/main/debug_gdb_scripts.c b/main/debug_gdb_scripts.c index 590a4f42abb97..eef618bd03301 100644 --- a/main/debug_gdb_scripts.c +++ b/main/debug_gdb_scripts.c @@ -144,7 +144,7 @@ asm( ".ascii \"\\n\"\n" ".ascii \" printf \\\") \\\"\\n\"\n" ".ascii \" else\\n\"\n" - ".ascii \" printf \\\"??? \\\"\\n\"\n" + ".ascii \" printf \\\"\?\?\? \\\"\\n\"\n" ".ascii \" end\\n\"\n" ".ascii \" if $func != 0\\n\"\n" ".ascii \" if $func->type == 2\\n\"\n" @@ -688,7 +688,7 @@ asm( ".ascii \"\\n\"\n" ".ascii \"Use |set print max-depth| to control the maximum print depth for nested\\n\"\n" ".ascii \"structures:\\n\"\n" - ".ascii \" (gdb) set print pretty on\\n\"\n" + ".ascii \" (gdb) set print max-depth 5\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"To interactively type Python for development of the printers, use\\n\"\n" ".ascii \" (gdb) python foo = gdb.parse_and_eval('bar')\\n\"\n" @@ -699,6 +699,8 @@ asm( ".ascii \"\\n\"\n" ".ascii \"import gdb\\n\"\n" ".ascii \"import re\\n\"\n" + ".ascii \"import traceback\\n\"\n" + ".ascii \"import os\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"pp_set = gdb.printing.RegexpCollectionPrettyPrinter(\\\"php\\\")\\n\"\n" ".ascii \"\\n\"\n" @@ -709,53 +711,65 @@ asm( ".ascii \" self.val = val\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def to_string(self):\\n\"\n" - ".ascii \" return self.format_string()\\n\"\n" + ".ascii \" return '((zend_string*) 0x%x) %s' % (self.val.address, format_zstr(self.val))\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def children(self):\\n\"\n" ".ascii \" for field in self.val.type.fields():\\n\"\n" ".ascii \" if field.name == 'val':\\n\"\n" - ".ascii \" yield ('val', self.format_string())\\n\"\n" + ".ascii \" yield ('val', format_zstr(self.val))\\n\"\n" ".ascii \" else:\\n\"\n" - ".ascii \" yield (field.name, self.val[field.name])\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" def format_string(self):\\n\"\n" - ".ascii \" len = int(self.val['len'])\\n\"\n" - ".ascii \" truncated = False\\n\"\n" - ".ascii \" if len > 50:\\n\"\n" - ".ascii \" len = 50\\n\"\n" - ".ascii \" truncated = True\\n\"\n" + ".ascii \"pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter)\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" ptr_type = gdb.lookup_type('char').pointer()\\n\"\n" - ".ascii \" ary_type = gdb.lookup_type('char').array(len)\\n\"\n" - ".ascii \" str = self.val['val'].cast(ptr_type).dereference().cast(ary_type)\\n\"\n" - ".ascii \" str = str.format_string()\\n\"\n" - ".ascii \" if truncated:\\n\"\n" - ".ascii \" str += ' (%d bytes total)' % int(self.val['len'])\\n\"\n" + ".ascii \"class ZendObjectPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" + ".ascii \" \\\"Print a zend_object\\\"\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" return str\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" ".ascii \"\\n\"\n" + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" return 'object(%s)' % format_zstr(self.val['ce']['name'])\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \"pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter)\\n\"\n" + ".ascii \" def children(self):\\n\"\n" + ".ascii \" for field in self.val.type.fields():\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"pp_set.add_printer('zend_object', '^_zend_object$', ZendObjectPrettyPrinter)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendArrayPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" + ".ascii \" \\\"Print a zend_array\\\"\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \"def zendStringPointerPrinter(ptr):\\n\"\n" - ".ascii \" \\\"Given a pointer to a zend_string, show the contents (if non-NULL)\\\"\\n\"\n" - ".ascii \" if int(ptr) == 0:\\n\"\n" - ".ascii \" return '0x0'\\n\"\n" - ".ascii \" return ZendStringPrettyPrinter(ptr.dereference()).to_string()\\n\"\n" + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" return 'array(%d)' % self.val['nNumOfElements']\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def children(self):\\n\"\n" + ".ascii \" for field in self.val.type.fields():\\n\"\n" + ".ascii \" if field.name is None:\\n\"\n" + ".ascii \" name = ''\\n\"\n" + ".ascii \" val = self.val[field]\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" name = field.name\\n\"\n" + ".ascii \" val = self.val[field.name]\\n\"\n" + ".ascii \" yield (name, format_nested(val))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"pp_set.add_printer('zend_array', '^_zend_array$', ZendArrayPrettyPrinter)\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" ".ascii \" \\\"Print a zend_type\\\"\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def __init__(self, val):\\n\"\n" ".ascii \" self.val = val\\n\"\n" - ".ascii \" load_type_bits()\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def to_string(self):\\n\"\n" - ".ascii \" return self.format_type(self.val)\\n\"\n" + ".ascii \" return '((zend_type*) 0x%x) %s' % (self.val.address, self.format_type(self.val))\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def children(self):\\n\"\n" ".ascii \" for field in self.val.type.fields():\\n\"\n" - ".ascii \" yield (field.name, self.val[field.name])\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def format_type(self, t):\\n\"\n" ".ascii \" type_mask = int(t['type_mask'])\\n\"\n" @@ -765,7 +779,7 @@ asm( ".ascii \" meta = []\\n\"\n" ".ascii \" for bit in range(0, type_mask_size):\\n\"\n" ".ascii \" if type_mask & (1 << bit):\\n\"\n" - ".ascii \" type_name = type_bit_to_name.get(bit)\\n\"\n" + ".ascii \" type_name = ZendTypeBits.zendTypeName(bit)\\n\"\n" ".ascii \" match type_name:\\n\"\n" ".ascii \" case None:\\n\"\n" ".ascii \" parts.append('(1<<%d)' % bit)\\n\"\n" @@ -785,7 +799,7 @@ asm( ".ascii \" separator = '&'\\n\"\n" ".ascii \" case 'name':\\n\"\n" ".ascii \" str = t['ptr'].cast(gdb.lookup_type('zend_string').pointer())\\n\"\n" - ".ascii \" parts.append(ZendStringPrettyPrinter(str).to_string())\\n\"\n" + ".ascii \" parts.append(format_zstr(str))\\n\"\n" ".ascii \" case _:\\n\"\n" ".ascii \" parts.append(type_name)\\n\"\n" ".ascii \"\\n\"\n" @@ -836,11 +850,11 @@ asm( ".ascii \" c = c.dereference()\\n\"\n" ".ascii \" yield ('child[%d]' % i, c)\\n\"\n" ".ascii \" elif field.name == 'name':\\n\"\n" - ".ascii \" yield (field.name, ZendStringPrettyPrinter(val[field.name].dereference()).to_string())\\n\"\n" + ".ascii \" yield (field.name, format_zstr(val[field.name]))\\n\"\n" ".ascii \" elif field.name == 'val':\\n\"\n" ".ascii \" yield (field.name, ZvalPrettyPrinter(val[field.name]).to_string())\\n\"\n" ".ascii \" else:\\n\"\n" - ".ascii \" yield (field.name, val[field.name])\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def is_special(self):\\n\"\n" ".ascii \" special_shift = 6 # ZEND_AST_SPECIAL_SHIFT\\n\"\n" @@ -893,37 +907,46 @@ asm( ".ascii \"\\n\"\n" ".ascii \" def __init__(self, val):\\n\"\n" ".ascii \" self.val = val\\n\"\n" - ".ascii \" load_type_bits()\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def to_string(self):\\n\"\n" - ".ascii \" return self.value_to_string()\\n\"\n" + ".ascii \" return '((zval*) 0x%x) %s' % (self.val.address, self.value_to_string())\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def value_to_string(self):\\n\"\n" ".ascii \" t = int(self.val['u1']['v']['type'])\\n\"\n" - ".ascii \" if t == type_name_to_bit['undef']:\\n\"\n" + ".ascii \" if t == ZendTypeBits.bit('undef'):\\n\"\n" ".ascii \" return 'undef'\\n\"\n" - ".ascii \" elif t == type_name_to_bit['null']:\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('null'):\\n\"\n" ".ascii \" return 'null'\\n\"\n" - ".ascii \" elif t == type_name_to_bit['false']:\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('false'):\\n\"\n" ".ascii \" return 'false'\\n\"\n" - ".ascii \" elif t == type_name_to_bit['true']:\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('true'):\\n\"\n" ".ascii \" return 'true'\\n\"\n" - ".ascii \" elif t == type_name_to_bit['long']:\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('long'):\\n\"\n" ".ascii \" return str(self.val['value']['lval'])\\n\"\n" - ".ascii \" elif t == type_name_to_bit['double']:\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('double'):\\n\"\n" ".ascii \" return str(self.val['value']['dval'])\\n\"\n" - ".ascii \" elif t == type_name_to_bit['string']:\\n\"\n" - ".ascii \" return ZendStringPrettyPrinter(self.val['value']['str'].dereference()).to_string()\\n\"\n" - ".ascii \" elif t == type_name_to_bit['array']:\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('string'):\\n\"\n" + ".ascii \" return format_zstr(self.val['value']['str'])\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('array'):\\n\"\n" ".ascii \" return 'array'\\n\"\n" - ".ascii \" elif t == type_name_to_bit['object']:\\n\"\n" - ".ascii \" return 'object(%s)' % ZendStringPrettyPrinter(self.val['value']['obj']['ce']['name'].dereference()).to_string()\\n\"\n" - ".ascii \" elif t == type_name_to_bit['resource']:\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('object'):\\n\"\n" + ".ascii \" return 'object(%s)' % format_zstr(self.val['value']['obj']['ce']['name'])\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('resource'):\\n\"\n" ".ascii \" return 'resource'\\n\"\n" - ".ascii \" elif t == type_name_to_bit['reference']:\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('reference'):\\n\"\n" ".ascii \" return 'reference'\\n\"\n" - ".ascii \" elif t == type_name_to_bit['constant_ast']:\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('constant_ast'):\\n\"\n" ".ascii \" return 'constant_ast'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('indirect'):\\n\"\n" + ".ascii \" value = self.val['value']['zv']\\n\"\n" + ".ascii \" valuestr = ZvalPrettyPrinter(value).to_string()\\n\"\n" + ".ascii \" return 'indirect: ((zval*) 0x%x) %s' % (int(value), valuestr)\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('ptr'):\\n\"\n" + ".ascii \" value = int(self.val['value']['ptr'])\\n\"\n" + ".ascii \" return 'ptr: ((void*) 0x%x)' % (value)\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('alias_ptr'):\\n\"\n" + ".ascii \" value = int(self.val['value']['ptr'])\\n\"\n" + ".ascii \" return 'alias_ptr: ((void*) 0x%x)' % (value)\\n\"\n" ".ascii \" else:\\n\"\n" ".ascii \" return 'zval of type %d' % int(self.val['u1']['v']['type'])\\n\"\n" ".ascii \"\\n\"\n" @@ -931,38 +954,42 @@ asm( ".ascii \" for field in self.val.type.fields():\\n\"\n" ".ascii \" if field.name == 'value':\\n\"\n" ".ascii \" value = self.val['value']\\n\"\n" + ".ascii \" sub_field = 'ptr'\\n\"\n" ".ascii \" t = int(self.val['u1']['v']['type'])\\n\"\n" - ".ascii \" if t == type_name_to_bit['undef']:\\n\"\n" - ".ascii \" value = value['lval']\\n\"\n" - ".ascii \" elif t == type_name_to_bit['null']:\\n\"\n" - ".ascii \" value = value['lval']\\n\"\n" - ".ascii \" elif t == type_name_to_bit['false']:\\n\"\n" - ".ascii \" value = value['lval']\\n\"\n" - ".ascii \" elif t == type_name_to_bit['true']:\\n\"\n" - ".ascii \" value = value['lval']\\n\"\n" - ".ascii \" elif t == type_name_to_bit['long']:\\n\"\n" - ".ascii \" value = value['lval']\\n\"\n" - ".ascii \" elif t == type_name_to_bit['double']:\\n\"\n" - ".ascii \" value = value['dval']\\n\"\n" - ".ascii \" elif t == type_name_to_bit['string']:\\n\"\n" - ".ascii \" value = value['str'].dereference()\\n\"\n" - ".ascii \" elif t == type_name_to_bit['array']:\\n\"\n" - ".ascii \" value = value['ht'].dereference()\\n\"\n" - ".ascii \" elif t == type_name_to_bit['object']:\\n\"\n" - ".ascii \" value = value['obj'].dereference()\\n\"\n" - ".ascii \" elif t == type_name_to_bit['resource']:\\n\"\n" - ".ascii \" value = value['res'].dereference()\\n\"\n" - ".ascii \" elif t == type_name_to_bit['reference']:\\n\"\n" - ".ascii \" value = value['ref'].dereference()\\n\"\n" - ".ascii \" elif t == type_name_to_bit['constant_ast']:\\n\"\n" - ".ascii \" value = value['ast'].dereference()\\n\"\n" - ".ascii \" else:\\n\"\n" - ".ascii \" value = value['ptr']\\n\"\n" - ".ascii \" yield (field.name, value)\\n\"\n" + ".ascii \" if t == ZendTypeBits.bit('undef'):\\n\"\n" + ".ascii \" sub_field = 'lval'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('null'):\\n\"\n" + ".ascii \" sub_field = 'lval'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('false'):\\n\"\n" + ".ascii \" sub_field = 'lval'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('true'):\\n\"\n" + ".ascii \" sub_field = 'lval'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('long'):\\n\"\n" + ".ascii \" sub_field = 'lval'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('double'):\\n\"\n" + ".ascii \" sub_field = 'dval'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('string'):\\n\"\n" + ".ascii \" sub_field = 'str'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('array'):\\n\"\n" + ".ascii \" sub_field = 'arr'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('object'):\\n\"\n" + ".ascii \" sub_field = 'obj'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('resource'):\\n\"\n" + ".ascii \" sub_field = 'res'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('reference'):\\n\"\n" + ".ascii \" sub_field = 'ref'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('constant_ast'):\\n\"\n" + ".ascii \" sub_field = 'ast'\\n\"\n" + ".ascii \" elif t == ZendTypeBits.bit('indirect'):\\n\"\n" + ".ascii \" sub_field = 'zv'\\n\"\n" + ".ascii \" value = value[sub_field]\\n\"\n" + ".ascii \" if sub_field != 'ptr' and value.type.code == gdb.TYPE_CODE_PTR:\\n\"\n" + ".ascii \" value = value.dereference()\\n\"\n" + ".ascii \" yield ('%s.%s' % (field.name, sub_field), value)\\n\"\n" ".ascii \" elif field.name == 'u2':\\n\"\n" ".ascii \" yield ('u2', self.val[field.name]['extra'])\\n\"\n" ".ascii \" else:\\n\"\n" - ".ascii \" yield (field.name, self.val[field.name])\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"pp_set.add_printer('zval', '^_zval_struct$', ZvalPrettyPrinter)\\n\"\n" @@ -970,22 +997,20 @@ asm( ".ascii \"class ZendClassEntryPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" ".ascii \" \\\"Print a zend_class_entry\\\"\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" # String pointers, show the string contents if possible\\n\"\n" - ".ascii \" STRING_FIELDS = [ 'name', 'doc_comment' ]\\n\"\n" - ".ascii \"\\n\"\n" ".ascii \" def __init__(self, val):\\n\"\n" ".ascii \" self.val = val\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def to_string(self):\\n\"\n" - ".ascii \" return zendStringPointerPrinter(self.val['name'])\\n\"\n" + ".ascii \" return '((zend_class_entry*) 0x%x) %s' % (self.val.address, format_zstr(self.val['name']))\\n\"\n" ".ascii \"\\n\"\n" ".ascii \" def children(self):\\n\"\n" ".ascii \" for field in self.val.type.fields():\\n\"\n" ".ascii \" if field.name is not None:\\n\"\n" - ".ascii \" if field.name in self.STRING_FIELDS:\\n\"\n" - ".ascii \" yield (field.name, zendStringPointerPrinter(self.val[field.name]))\\n\"\n" + ".ascii \" if field.name == 'ce_flags':\\n\"\n" + ".ascii \" flags = self.val[field.name]\\n\"\n" + ".ascii \" yield (field.name, '%d (%s)' % (flags, ZendAccFlags.format_ce_flags(flags)))\\n\"\n" ".ascii \" else:\\n\"\n" - ".ascii \" yield (field.name, self.val[field.name])\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" ".ascii \" else:\\n\"\n" ".ascii \" # Don't break on the union fields. Unfortunately, pretty\\n\"\n" ".ascii \" # printers done in python cannot match the default formatting of\\n\"\n" @@ -1004,52 +1029,592 @@ asm( ".ascii \"\\n\"\n" ".ascii \" def children(self):\\n\"\n" ".ascii \" for field in self.val.type.fields():\\n\"\n" - ".ascii \" if field.name == 'doc_comment':\\n\"\n" - ".ascii \" yield ('doc_comment', zendStringPointerPrinter(self.val['doc_comment']))\\n\"\n" - ".ascii \" elif field.name == 'ce':\\n\"\n" - ".ascii \" yield ('ce', zendStringPointerPrinter(self.val['ce']['name']))\\n\"\n" - ".ascii \" else:\\n\"\n" + ".ascii \" if field.name == 'value':\\n\"\n" + ".ascii \" flags = self.val[field.name]['u2']['constant_flags']\\n\"\n" + ".ascii \" yield ('value.u2.constant_flags', '%d (%s)' % (flags, ZendAccFlags.format_const_flags(flags)))\\n\"\n" ".ascii \" yield (field.name, self.val[field.name])\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"pp_set.add_printer('zend_class_constant', '^_zend_class_constant$', ZendClassConstantPrettyPrinter)\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \"type_bit_to_name = None\\n\"\n" - ".ascii \"type_name_to_bit = None\\n\"\n" + ".ascii \"class ZendPropertyInfoPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" + ".ascii \" \\\"Print a zend_property_info\\\"\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def children(self):\\n\"\n" + ".ascii \" for field in self.val.type.fields():\\n\"\n" + ".ascii \" if field.name == 'flags':\\n\"\n" + ".ascii \" flags = self.val[field.name]\\n\"\n" + ".ascii \" yield ('flags', '%d (%s)' % (flags, ZendAccFlags.format_prop_flags(flags)))\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \"def load_type_bits():\\n\"\n" - ".ascii \" global type_bit_to_name\\n\"\n" - ".ascii \" global type_name_to_bit\\n\"\n" + ".ascii \"pp_set.add_printer('zend_property_info', '^_zend_property_info$', ZendPropertyInfoPrettyPrinter)\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" if type_bit_to_name != None:\\n\"\n" - ".ascii \" return\\n\"\n" + ".ascii \"class ZendFunctionPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" + ".ascii \" \\\"Print a zend_function\\\"\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" (symbol,_) = gdb.lookup_symbol(\\\"zend_gc_refcount\\\")\\n\"\n" - ".ascii \" if symbol == None:\\n\"\n" - ".ascii \" raise Exception(\\\"Could not find zend_types.h: symbol zend_gc_refcount not found\\\")\\n\"\n" - ".ascii \" filename = symbol.symtab.fullname()\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" match int(self.val['type']):\\n\"\n" + ".ascii \" case ZendFnTypes.ZEND_INTERNAL_FUNCTION:\\n\"\n" + ".ascii \" typestr = 'internal'\\n\"\n" + ".ascii \" case ZendFnTypes.ZEND_USER_FUNCTION:\\n\"\n" + ".ascii \" typestr = 'user'\\n\"\n" + ".ascii \" case ZendFnTypes.ZEND_EVAL_CODE:\\n\"\n" + ".ascii \" typestr = 'eval'\\n\"\n" + ".ascii \" case _:\\n\"\n" + ".ascii \" typestr = '\?\?\?'\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" if self.val['common']['function_name']:\\n\"\n" + ".ascii \" namestr = format_zstr(self.val['common']['function_name'])\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" namestr = '{main}'\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" if self.val['common']['scope']:\\n\"\n" + ".ascii \" str = '%s method %s::%s' % (typestr, format_zstr(self.val['common']['scope']['name']), namestr)\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" str = '%s function %s' % (typestr, namestr)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" if int(self.val['type']) == ZendFnTypes.ZEND_USER_FUNCTION or int(self.val['type']) == ZendFnTypes.ZEND_EVAL_CODE:\\n\"\n" + ".ascii \" str = '%s %s:%d' % (str, format_zstr(self.val['op_array']['filename']), int(self.val['op_array']['line_start']))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" return '((zend_function*) 0x%x) %s' % (self.val.address, str)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def children(self):\\n\"\n" + ".ascii \" for field in self.val.type.fields():\\n\"\n" + ".ascii \" if field.name == 'common' or field.name == 'type' or field.name == 'quick_arg_flags':\\n\"\n" + ".ascii \" # Redundant with op_array / internal_function\\n\"\n" + ".ascii \" continue\\n\"\n" + ".ascii \" elif field.name == 'op_array':\\n\"\n" + ".ascii \" if int(self.val['type']) == ZendFnTypes.ZEND_USER_FUNCTION or int(self.val['type']) == ZendFnTypes.ZEND_EVAL_CODE:\\n\"\n" + ".ascii \" yield (field.name, self.val[field.name])\\n\"\n" + ".ascii \" elif field.name == 'internal_function':\\n\"\n" + ".ascii \" if int(self.val['type']) == ZendFnTypes.ZEND_INTERNAL_FUNCTION:\\n\"\n" + ".ascii \" yield (field.name, self.val[field.name])\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"pp_set.add_printer('zend_function', '^_zend_function$', ZendFunctionPrettyPrinter)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendOpArrayPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" + ".ascii \" \\\"Print a zend_op_array\\\"\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" if self.val['function_name']:\\n\"\n" + ".ascii \" namestr = format_zstr(self.val['function_name'])\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" namestr = '{main}'\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" if self.val['scope']:\\n\"\n" + ".ascii \" str = 'method %s::%s' % (format_zstr(self.val['scope']['name']), namestr)\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" str = 'function %s' % (namestr)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" str = '%s %s:%d' % (str, format_zstr(self.val['filename']), int(self.val['line_start']))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" return '((zend_op_array*) 0x%x) %s' % (self.val.address, str)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def children(self):\\n\"\n" + ".ascii \" for field in self.val.type.fields():\\n\"\n" + ".ascii \" if field.name == 'fn_flags':\\n\"\n" + ".ascii \" value = self.val[field.name]\\n\"\n" + ".ascii \" yield (field.name, '%d (%s)' % (value, ZendAccFlags.format_fn_flags(value)))\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"pp_set.add_printer('zend_op_array', '^_zend_op_array$', ZendOpArrayPrettyPrinter)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendOpPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" + ".ascii \" \\\"Print a zend_op\\\"\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def children(self):\\n\"\n" + ".ascii \" for field in self.val.type.fields():\\n\"\n" + ".ascii \" if field.name == 'opcode':\\n\"\n" + ".ascii \" opcode = int(self.val[field.name])\\n\"\n" + ".ascii \" yield (field.name, '%d (%s)' % (opcode, ZendOpcodes.name(opcode)))\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"pp_set.add_printer('zend_op', '^_zend_op$', ZendOpPrettyPrinter)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendInternalFunctionPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" + ".ascii \" \\\"Print a zend_internal_function\\\"\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" if self.val['function_name']:\\n\"\n" + ".ascii \" namestr = format_zstr(self.val['function_name'])\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" namestr = '\?\?\?'\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" if self.val['scope']:\\n\"\n" + ".ascii \" str = 'method %s::%s' % (format_zstr(self.val['scope']['name']), namestr)\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" str = 'function %s' % (namestr)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" return '((zend_internal_function*) 0x%x) %s' % (self.val.address, str)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def children(self):\\n\"\n" + ".ascii \" for field in self.val.type.fields():\\n\"\n" + ".ascii \" if field.name == 'fn_flags':\\n\"\n" + ".ascii \" yield ('fn_flags', ('%d (%s)' % (self.val[field.name], ZendAccFlags.format_fn_flags(self.val[field.name]))))\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"pp_set.add_printer('zend_internal_function', '^_zend_internal_function$', ZendInternalFunctionPrettyPrinter)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendRefcountedHPrettyPrinter(gdb.printing.PrettyPrinter):\\n\"\n" + ".ascii \" \\\"Print a zend_refcounted_h\\\"\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def children(self):\\n\"\n" + ".ascii \" for field in self.val.type.fields():\\n\"\n" + ".ascii \" if field.name == 'u':\\n\"\n" + ".ascii \" val = self.val[field.name]\\n\"\n" + ".ascii \" if val == None:\\n\"\n" + ".ascii \" val = self.val\\n\"\n" + ".ascii \" for subfield in val.type.fields():\\n\"\n" + ".ascii \" if subfield.name == 'type_info':\\n\"\n" + ".ascii \" flags = int(val[subfield.name])\\n\"\n" + ".ascii \" yield (('%s.%s' % (field.name, subfield.name)), '%d (%s)' % (flags, ZendRefTypeInfo.format(flags)))\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield (('%s.%s' % (field.name, subfield.name)), format_nested(val[subfield.name]))\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield (('%s' % field.name), format_nested(self.val[field.name]))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"pp_set.add_printer('zend_refcounted_h', '^_zend_refcounted_h$', ZendRefcountedHPrettyPrinter)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class PrintAccFlagsCommand(gdb.Command):\\n\"\n" + ".ascii \" \\\"Pretty print ACC flags\\\"\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def __init__ (self, type):\\n\"\n" + ".ascii \" self.type = type\\n\"\n" + ".ascii \" name = 'print_%s_flags' % type\\n\"\n" + ".ascii \" super(PrintAccFlagsCommand, self).__init__(name, gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" bits = {}\\n\"\n" + ".ascii \" def invoke (self, arg, from_tty):\\n\"\n" + ".ascii \" arg = int(gdb.parse_and_eval(arg))\\n\"\n" + ".ascii \" print(ZendAccFlags.format_flags(arg, self.type))\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" with open(filename, 'r') as file:\\n\"\n" - ".ascii \" content = file.read()\\n\"\n" + ".ascii \"PrintAccFlagsCommand('fn')\\n\"\n" + ".ascii \"PrintAccFlagsCommand('ce')\\n\"\n" + ".ascii \"PrintAccFlagsCommand('prop')\\n\"\n" + ".ascii \"PrintAccFlagsCommand('const')\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" pattern = re.compile(r'#define _ZEND_TYPE_([^\\\\s]+)_BIT\\\\s+\\\\(1u << (\\\\d+)\\\\)')\\n\"\n" - ".ascii \" matches = pattern.findall(content)\\n\"\n" - ".ascii \" for name, bit in matches:\\n\"\n" - ".ascii \" bits[int(bit)] = name.lower()\\n\"\n" + ".ascii \"class PrintOpcodeCommand(gdb.Command):\\n\"\n" + ".ascii \" \\\"Pretty print opcode\\\"\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" pattern = re.compile(r'#define IS_([^\\\\s]+)\\\\s+(\\\\d+)')\\n\"\n" - ".ascii \" matches = pattern.findall(content)\\n\"\n" - ".ascii \" for name, bit in matches:\\n\"\n" - ".ascii \" if not int(bit) in bits:\\n\"\n" - ".ascii \" bits[int(bit)] = name.lower()\\n\"\n" + ".ascii \" def __init__ (self):\\n\"\n" + ".ascii \" super(PrintOpcodeCommand, self).__init__(\\\"print_opcode\\\", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" types = {}\\n\"\n" - ".ascii \" for bit in bits:\\n\"\n" - ".ascii \" types[bits[bit]] = bit\\n\"\n" + ".ascii \" def invoke (self, arg, from_tty):\\n\"\n" + ".ascii \" arg = int(gdb.parse_and_eval(arg))\\n\"\n" + ".ascii \" print(ZendOpcodes.name(arg))\\n\"\n" ".ascii \"\\n\"\n" - ".ascii \" type_bit_to_name = bits\\n\"\n" - ".ascii \" type_name_to_bit = types\\n\"\n" + ".ascii \"PrintOpcodeCommand()\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class PrintRefTypeInfoCommand(gdb.Command):\\n\"\n" + ".ascii \" \\\"Pretty print zend_refcounted.gc.u.type_info\\\"\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def __init__ (self):\\n\"\n" + ".ascii \" super(PrintRefTypeInfoCommand, self).__init__(\\\"print_ref_type_info\\\", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def invoke (self, arg, from_tty):\\n\"\n" + ".ascii \" arg = int(gdb.parse_and_eval(arg))\\n\"\n" + ".ascii \" print(ZendRefTypeInfo.format(arg))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"PrintRefTypeInfoCommand()\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class DumpOpArrayCommand(gdb.Command):\\n\"\n" + ".ascii \" \\\"Dump an op_array\\\"\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def __init__ (self):\\n\"\n" + ".ascii \" super(DumpOpArrayCommand, self).__init__(\\\"dump_op_array\\\", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def invoke (self, arg, from_tty):\\n\"\n" + ".ascii \" op_array = gdb.parse_and_eval(arg)\\n\"\n" + ".ascii \" if op_array.type.code != gdb.TYPE_CODE_PTR:\\n\"\n" + ".ascii \" print(\\\"Must pass a zend_op_array* (got a %s)\\\" % op_array.type)\\n\"\n" + ".ascii \" return\\n\"\n" + ".ascii \" if str(gdb.types.get_basic_type(op_array.type.target())) != 'struct _zend_op_array':\\n\"\n" + ".ascii \" print(\\\"Must pass a zend_op_array* (got a %s)\\\" % op_array.type)\\n\"\n" + ".ascii \" return\\n\"\n" + ".ascii \" if int(op_array) == 0:\\n\"\n" + ".ascii \" print(\\\"NULL\\\")\\n\"\n" + ".ascii \" return\\n\"\n" + ".ascii \" gdb.execute(\\\"call zend_dump_op_array((zend_op_array*)0x%x, 0, 0, 0)\\\" % (int(op_array)))\\n\"\n" + ".ascii \" return\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"DumpOpArrayCommand()\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendTypeBits:\\n\"\n" + ".ascii \" _bits = None\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def zendTypeName(self, bit):\\n\"\n" + ".ascii \" self._load()\\n\"\n" + ".ascii \" for name in self._bits:\\n\"\n" + ".ascii \" if bit == self._bits[name]:\\n\"\n" + ".ascii \" return name\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def zvalTypeName(self, bit):\\n\"\n" + ".ascii \" # Same as zendTypeName, but return the last matching one\\n\"\n" + ".ascii \" # e.g. 13 is IS_PTR, not IS_ITERABLE\\n\"\n" + ".ascii \" self._load()\\n\"\n" + ".ascii \" ret = None\\n\"\n" + ".ascii \" for name in self._bits:\\n\"\n" + ".ascii \" if bit == self._bits[name]:\\n\"\n" + ".ascii \" ret = name\\n\"\n" + ".ascii \" return ret\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def bit(self, name):\\n\"\n" + ".ascii \" self._load()\\n\"\n" + ".ascii \" return self._bits.get(name)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def _load(self):\\n\"\n" + ".ascii \" if self._bits != None:\\n\"\n" + ".ascii \" return\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" dirname = detect_source_dir()\\n\"\n" + ".ascii \" filename = os.path.join(dirname, 'zend_types.h')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" bits = {}\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" with open(filename, 'r') as file:\\n\"\n" + ".ascii \" content = file.read()\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" pattern = re.compile(r'#define _ZEND_TYPE_([^\\\\s]+)_BIT\\\\s+\\\\(1u << (\\\\d+)\\\\)')\\n\"\n" + ".ascii \" matches = pattern.findall(content)\\n\"\n" + ".ascii \" for name, bit in matches:\\n\"\n" + ".ascii \" bits[name.lower()] = int(bit)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" pattern = re.compile(r'#define IS_([^\\\\s]+)\\\\s+(\\\\d+)')\\n\"\n" + ".ascii \" matches = pattern.findall(content)\\n\"\n" + ".ascii \" for name, bit in matches:\\n\"\n" + ".ascii \" bits[name.lower()] = int(bit)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" self._bits = bits\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendFnTypes:\\n\"\n" + ".ascii \" ZEND_INTERNAL_FUNCTION = 1\\n\"\n" + ".ascii \" ZEND_USER_FUNCTION = 2\\n\"\n" + ".ascii \" ZEND_EVAL_CODE = 4\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendAccFlag:\\n\"\n" + ".ascii \" ce = False\\n\"\n" + ".ascii \" fn = False\\n\"\n" + ".ascii \" prop = False\\n\"\n" + ".ascii \" const = False\\n\"\n" + ".ascii \" bit = 0\\n\"\n" + ".ascii \" def __init__(self, ce, fn, prop, const, bit):\\n\"\n" + ".ascii \" self.ce = ce\\n\"\n" + ".ascii \" self.fn = fn\\n\"\n" + ".ascii \" self.prop = prop\\n\"\n" + ".ascii \" self.const = const\\n\"\n" + ".ascii \" self.bit = bit\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" def applies_to(self, type):\\n\"\n" + ".ascii \" return getattr(self, type)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendAccFlags:\\n\"\n" + ".ascii \" _flags = None\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def fn_flag_name(self, bit):\\n\"\n" + ".ascii \" self.flag_name(bit, 'fn')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def ce_flag_name(self, bit):\\n\"\n" + ".ascii \" self.flag_name(bit, 'ce')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def prop_flag_name(self, bit):\\n\"\n" + ".ascii \" self.flag_name(bit, 'prop')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def const_flag_name(self, bit):\\n\"\n" + ".ascii \" self.flag_name(bit, 'const')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def flag_name(self, bit, type):\\n\"\n" + ".ascii \" self._load()\\n\"\n" + ".ascii \" for name in self._flags:\\n\"\n" + ".ascii \" flag = self._flags[name]\\n\"\n" + ".ascii \" if flag.applies_to(type) and bit == flag.bit:\\n\"\n" + ".ascii \" return name\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def flag_bit(self, name):\\n\"\n" + ".ascii \" self._load()\\n\"\n" + ".ascii \" return self._flags[name]\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def format_flags(self, flags, type):\\n\"\n" + ".ascii \" flags = int(flags)\\n\"\n" + ".ascii \" names = []\\n\"\n" + ".ascii \" for i in range(0, 31):\\n\"\n" + ".ascii \" if (flags & (1 << i)) != 0:\\n\"\n" + ".ascii \" name = self.flag_name(i, type)\\n\"\n" + ".ascii \" if name == None:\\n\"\n" + ".ascii \" names.append('(1 << %d)' % (i))\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" names.append(name)\\n\"\n" + ".ascii \" return ' | '.join(names)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def format_fn_flags(self, flags):\\n\"\n" + ".ascii \" return self.format_flags(flags, 'fn')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def format_ce_flags(self, flags):\\n\"\n" + ".ascii \" return self.format_flags(flags, 'ce')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def format_prop_flags(self, flags):\\n\"\n" + ".ascii \" return self.format_flags(flags, 'prop')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def format_const_flags(self, flags):\\n\"\n" + ".ascii \" return self.format_flags(flags, 'const')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def _load(self):\\n\"\n" + ".ascii \" if self._flags != None:\\n\"\n" + ".ascii \" return\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" dirname = detect_source_dir()\\n\"\n" + ".ascii \" filename = os.path.join(dirname, 'zend_compile.h')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" flags = {}\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" with open(filename, 'r') as file:\\n\"\n" + ".ascii \" content = file.read()\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" pattern = re.compile(r'#define (ZEND_ACC_[^\\\\s]+)\\\\s+\\\\(1U\?\\\\s+<<\\\\s+(\\\\d+)\\\\)\\\\s+/\\\\*\\\\s+(X\?)\\\\s+\\\\|\\\\s+(X\?)\\\\s+\\\\|\\\\s+(X\?)\\\\s+\\\\|\\\\s+(X\?)\\\\s+\\\\*/')\\n\"\n" + ".ascii \" matches = pattern.findall(content)\\n\"\n" + ".ascii \" for name, bit, cls, func, prop, const in matches:\\n\"\n" + ".ascii \" flags[name] = ZendAccFlag(cls == 'X', func == 'X', prop == 'X', const == 'X', int(bit))\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" self._flags = flags\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendOpcodes:\\n\"\n" + ".ascii \" _opcodes = None\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def name(self, number):\\n\"\n" + ".ascii \" self._load()\\n\"\n" + ".ascii \" number = int(number)\\n\"\n" + ".ascii \" for name in self._opcodes:\\n\"\n" + ".ascii \" if number == self._opcodes[name]:\\n\"\n" + ".ascii \" return name\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def number(self, name):\\n\"\n" + ".ascii \" self._load()\\n\"\n" + ".ascii \" return self._opcodes[name]\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def _load(self):\\n\"\n" + ".ascii \" if self._opcodes != None:\\n\"\n" + ".ascii \" return\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" dirname = detect_source_dir()\\n\"\n" + ".ascii \" filename = os.path.join(dirname, 'zend_vm_opcodes.h')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" opcodes = {}\\n\"\n" + ".ascii \" found_nop = False\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" with open(filename, 'r') as file:\\n\"\n" + ".ascii \" content = file.read()\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" pattern = re.compile(r'#define (ZEND_[^\\\\s]+)\\\\s+([0-9]+)')\\n\"\n" + ".ascii \" matches = pattern.findall(content)\\n\"\n" + ".ascii \" for name, number in matches:\\n\"\n" + ".ascii \" if not found_nop:\\n\"\n" + ".ascii \" if name == 'ZEND_NOP':\\n\"\n" + ".ascii \" found_nop = True\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" continue\\n\"\n" + ".ascii \" if name == 'ZEND_VM_LAST_OPCODE':\\n\"\n" + ".ascii \" break\\n\"\n" + ".ascii \" opcodes[name] = int(number)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" self._opcodes = opcodes\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"class ZendRefTypeInfo:\\n\"\n" + ".ascii \" _bits = None\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def flag_name(self, bit):\\n\"\n" + ".ascii \" return self._flag_name_in(bit, self._bits)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def _flag_name_in(self, bit, bits):\\n\"\n" + ".ascii \" for name in bits:\\n\"\n" + ".ascii \" if bit == bits[name]:\\n\"\n" + ".ascii \" return name\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def bit(self, name):\\n\"\n" + ".ascii \" return self._bits.get(name)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def format(self, flags):\\n\"\n" + ".ascii \" self._load()\\n\"\n" + ".ascii \" names = []\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" type = flags & self._type_mask\\n\"\n" + ".ascii \" type_name = ZendTypeBits.zvalTypeName(type)\\n\"\n" + ".ascii \" if type_name is not None:\\n\"\n" + ".ascii \" names.append('IS_%s' % type_name.upper())\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" bits = self._bits\\n\"\n" + ".ascii \" type_bits = None\\n\"\n" + ".ascii \" match type_name:\\n\"\n" + ".ascii \" case 'string':\\n\"\n" + ".ascii \" type_bits = self._str_bits\\n\"\n" + ".ascii \" case 'array':\\n\"\n" + ".ascii \" type_bits = self._array_bits\\n\"\n" + ".ascii \" case 'object':\\n\"\n" + ".ascii \" type_bits = self._obj_bits\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" type_flags = flags & self._flags_mask\\n\"\n" + ".ascii \" for i in range(0, 31):\\n\"\n" + ".ascii \" if (1< type_flags:\\n\"\n" + ".ascii \" break\\n\"\n" + ".ascii \" if (type_flags & (1<> self._info_shift) & self._gc_color\\n\"\n" + ".ascii \" match gc_color:\\n\"\n" + ".ascii \" case self._gc_black:\\n\"\n" + ".ascii \" names.append('GC_BLACK')\\n\"\n" + ".ascii \" case self._gc_white:\\n\"\n" + ".ascii \" names.append('GC_WHITE')\\n\"\n" + ".ascii \" case self._gc_grey:\\n\"\n" + ".ascii \" names.append('GC_GREY')\\n\"\n" + ".ascii \" case self._gc_purple:\\n\"\n" + ".ascii \" names.append('GC_PURPLE')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" gc_address = (flags >> self._info_shift) & self._gc_address\\n\"\n" + ".ascii \" if gc_address != 0:\\n\"\n" + ".ascii \" names.append('GC_ADDRESS(%d)' % gc_address)\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" info = flags & self._info_mask\\n\"\n" + ".ascii \" if info != 0:\\n\"\n" + ".ascii \" names.append('GC_INFO(%d)' % info)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" return ' | '.join(names)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" @classmethod\\n\"\n" + ".ascii \" def _load(self):\\n\"\n" + ".ascii \" if self._bits != None:\\n\"\n" + ".ascii \" return\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" dirname = detect_source_dir()\\n\"\n" + ".ascii \" filename = os.path.join(dirname, 'zend_types.h')\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" bits = {}\\n\"\n" + ".ascii \" str_bits = {}\\n\"\n" + ".ascii \" array_bits = {}\\n\"\n" + ".ascii \" obj_bits = {}\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" with open(filename, 'r') as file:\\n\"\n" + ".ascii \" content = file.read()\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" # GC_NOT_COLLECTABLE (1<<4)\\n\"\n" + ".ascii \" pattern = re.compile(r'#define (GC_[^\\\\s]+)\\\\s+\\\\(\\\\s*1\\\\s*<<\\\\s*([0-9]+)\\\\s*\\\\)')\\n\"\n" + ".ascii \" matches = pattern.findall(content)\\n\"\n" + ".ascii \" for name, bit in matches:\\n\"\n" + ".ascii \" bits[name] = int(bit)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" # GC_TYPE_MASK 0x0000000f\\n\"\n" + ".ascii \" # GC_INFO_SHIFT 10\\n\"\n" + ".ascii \" pattern = re.compile(r'#define (GC_[^\\\\s]+)\\\\s+((0x)\?[0-9a-f]+)')\\n\"\n" + ".ascii \" matches = pattern.findall(content)\\n\"\n" + ".ascii \" for name, bit, _ in matches:\\n\"\n" + ".ascii \" match name:\\n\"\n" + ".ascii \" case 'GC_TYPE_MASK':\\n\"\n" + ".ascii \" self._type_mask = int(bit, 0)\\n\"\n" + ".ascii \" case 'GC_FLAGS_MASK':\\n\"\n" + ".ascii \" self._flags_mask = int(bit, 0)\\n\"\n" + ".ascii \" case 'GC_INFO_MASK':\\n\"\n" + ".ascii \" self._info_mask = int(bit, 0)\\n\"\n" + ".ascii \" case 'GC_INFO_SHIFT':\\n\"\n" + ".ascii \" self._info_shift = int(bit, 0)\\n\"\n" + ".ascii \" case 'GC_FLAGS_SHIFT':\\n\"\n" + ".ascii \" self._flags_shift = int(bit, 0)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" # IS_STR_INTERNED GC_IMMUTABLE\\n\"\n" + ".ascii \" # IS_STR_PERMANENT (1<<8)\\n\"\n" + ".ascii \" pattern = re.compile(r'#define (IS_(STR|ARRAY|OBJ)_[^\\\\s]+)\\\\s+(\\\\(\\\\s*1\\\\s*<<\\\\s*([0-9]+)\\\\s*\\\\)|GC_[a-zA-Z_]+)')\\n\"\n" + ".ascii \" matches = pattern.findall(content)\\n\"\n" + ".ascii \" for name, type, val, bit in matches:\\n\"\n" + ".ascii \" if bit == '':\\n\"\n" + ".ascii \" bit = bits.get(val)\\n\"\n" + ".ascii \" if bit == None:\\n\"\n" + ".ascii \" continue\\n\"\n" + ".ascii \" match type:\\n\"\n" + ".ascii \" case 'STR':\\n\"\n" + ".ascii \" target = str_bits\\n\"\n" + ".ascii \" case 'ARRAY':\\n\"\n" + ".ascii \" target = array_bits\\n\"\n" + ".ascii \" case 'OBJ':\\n\"\n" + ".ascii \" target = obj_bits\\n\"\n" + ".ascii \" target[name] = int(bit)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" # Hard coded because these are not exposed in header files\\n\"\n" + ".ascii \" self._gc_address = 0x0fffff\\n\"\n" + ".ascii \" self._gc_color = 0x300000\\n\"\n" + ".ascii \" self._gc_black = 0x000000\\n\"\n" + ".ascii \" self._gc_white = 0x100000\\n\"\n" + ".ascii \" self._gc_grey = 0x200000\\n\"\n" + ".ascii \" self._gc_purple = 0x300000\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" self._bits = bits\\n\"\n" + ".ascii \" self._str_bits = str_bits\\n\"\n" + ".ascii \" self._array_bits = array_bits\\n\"\n" + ".ascii \" self._obj_bits = obj_bits\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"def detect_source_dir():\\n\"\n" + ".ascii \" (symbol,_) = gdb.lookup_symbol(\\\"zend_visibility_to_set_visibility\\\")\\n\"\n" + ".ascii \" if symbol == None:\\n\"\n" + ".ascii \" raise Exception(\\\"Could not find zend_compile.h: symbol zend_visibility_to_set_visibility not found\\\")\\n\"\n" + ".ascii \" filename = symbol.symtab.fullname()\\n\"\n" + ".ascii \" dirname = os.path.dirname(filename)\\n\"\n" + ".ascii \" return dirname\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"def lookup_symbol(name):\\n\"\n" ".ascii \" (symbol, _) = gdb.lookup_symbol(name)\\n\"\n" @@ -1065,6 +1630,51 @@ asm( ".ascii \" # array types have a single field whose type represents its range\\n\"\n" ".ascii \" return ary_type.fields()[0].type.range()[1]+1\\n\"\n" ".ascii \"\\n\"\n" + ".ascii \"def format_zstr(zstr):\\n\"\n" + ".ascii \" len = int(zstr['len'])\\n\"\n" + ".ascii \" truncated = False\\n\"\n" + ".ascii \" if len > 200:\\n\"\n" + ".ascii \" len = 200\\n\"\n" + ".ascii \" truncated = True\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" ptr_type = gdb.lookup_type('char').pointer()\\n\"\n" + ".ascii \" ary_type = gdb.lookup_type('char').array(len)\\n\"\n" + ".ascii \" str = zstr['val'].cast(ptr_type).dereference().cast(ary_type)\\n\"\n" + ".ascii \" str = str.format_string()\\n\"\n" + ".ascii \" if truncated:\\n\"\n" + ".ascii \" str += ' (%d bytes total)' % int(zstr['len'])\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" return str\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \"def format_nested(value):\\n\"\n" + ".ascii \" orig_value = value\\n\"\n" + ".ascii \" type = value.type\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" # Null pointers\\n\"\n" + ".ascii \" if type.code == gdb.TYPE_CODE_PTR and int(value) == 0:\\n\"\n" + ".ascii \" return orig_value\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" addr = orig_value.address\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" while type.code == gdb.TYPE_CODE_PTR:\\n\"\n" + ".ascii \" addr = int(value)\\n\"\n" + ".ascii \" type = type.target()\\n\"\n" + ".ascii \" try:\\n\"\n" + ".ascii \" value = value.dereference()\\n\"\n" + ".ascii \" except:\\n\"\n" + ".ascii \" pass\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" type = gdb.types.get_basic_type(type)\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" if type.tag and re.match(r'^_zend_string$', type.tag):\\n\"\n" + ".ascii \" return format_zstr(value)\\n\"\n" + ".ascii \" elif type.tag and re.match(r'^_zend_class_entry$', type.tag):\\n\"\n" + ".ascii \" return '((zend_class_entry*)0x%x) %s' % (addr, format_zstr(value['name']))\\n\"\n" + ".ascii \" elif type.tag and re.match(r'^_zend_array$', type.tag):\\n\"\n" + ".ascii \" return '((zend_array*)0x%x) array(%d)' % (addr, value['nNumOfElements'])\\n\"\n" + ".ascii \"\\n\"\n" + ".ascii \" return orig_value\\n\"\n" + ".ascii \"\\n\"\n" ".ascii \"gdb.printing.register_pretty_printer(gdb, pp_set, replace=True)\\n\"\n" ".ascii \"\\n\"\n" ".byte 0\n" diff --git a/scripts/gdb/debug_gdb_scripts_gen.php b/scripts/gdb/debug_gdb_scripts_gen.php index 1b33d9a63aa6e..85cf06b70d640 100755 --- a/scripts/gdb/debug_gdb_scripts_gen.php +++ b/scripts/gdb/debug_gdb_scripts_gen.php @@ -40,7 +40,7 @@ basename(__FILE__), implode("\n ", array_map(function ($line) { $escapedPy = addcslashes($line."\n", "\"\n\\"); - $escapedAsm = addcslashes(sprintf(".ascii \"%s\"\n", $escapedPy), "\"\n\\"); + $escapedAsm = addcslashes(sprintf(".ascii \"%s\"\n", $escapedPy), "\"\n\\?"); return sprintf('"%s"', $escapedAsm); }, explode("\n", $pyscript))), ); diff --git a/scripts/gdb/php_gdb.py b/scripts/gdb/php_gdb.py index 97f7e976b75c0..3bd66c8d88fb8 100644 --- a/scripts/gdb/php_gdb.py +++ b/scripts/gdb/php_gdb.py @@ -18,7 +18,7 @@ Use |set print max-depth| to control the maximum print depth for nested structures: - (gdb) set print pretty on + (gdb) set print max-depth 5 To interactively type Python for development of the printers, use (gdb) python foo = gdb.parse_and_eval('bar') @@ -29,6 +29,8 @@ import gdb import re +import traceback +import os pp_set = gdb.printing.RegexpCollectionPrettyPrinter("php") @@ -39,53 +41,65 @@ def __init__(self, val): self.val = val def to_string(self): - return self.format_string() + return '((zend_string*) 0x%x) %s' % (self.val.address, format_zstr(self.val)) def children(self): for field in self.val.type.fields(): if field.name == 'val': - yield ('val', self.format_string()) + yield ('val', format_zstr(self.val)) else: - yield (field.name, self.val[field.name]) + yield (field.name, format_nested(self.val[field.name])) - def format_string(self): - len = int(self.val['len']) - truncated = False - if len > 50: - len = 50 - truncated = True +pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter) - ptr_type = gdb.lookup_type('char').pointer() - ary_type = gdb.lookup_type('char').array(len) - str = self.val['val'].cast(ptr_type).dereference().cast(ary_type) - str = str.format_string() - if truncated: - str += ' (%d bytes total)' % int(self.val['len']) +class ZendObjectPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_object" - return str + def __init__(self, val): + self.val = val + def to_string(self): + return 'object(%s)' % format_zstr(self.val['ce']['name']) -pp_set.add_printer('zend_string', '^_zend_string$', ZendStringPrettyPrinter) + def children(self): + for field in self.val.type.fields(): + yield (field.name, format_nested(self.val[field.name])) + +pp_set.add_printer('zend_object', '^_zend_object$', ZendObjectPrettyPrinter) + +class ZendArrayPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_array" + + def __init__(self, val): + self.val = val -def zendStringPointerPrinter(ptr): - "Given a pointer to a zend_string, show the contents (if non-NULL)" - if int(ptr) == 0: - return '0x0' - return ZendStringPrettyPrinter(ptr.dereference()).to_string() + def to_string(self): + return 'array(%d)' % self.val['nNumOfElements'] + + def children(self): + for field in self.val.type.fields(): + if field.name is None: + name = '' + val = self.val[field] + else: + name = field.name + val = self.val[field.name] + yield (name, format_nested(val)) + +pp_set.add_printer('zend_array', '^_zend_array$', ZendArrayPrettyPrinter) class ZendTypePrettyPrinter(gdb.printing.PrettyPrinter): "Print a zend_type" def __init__(self, val): self.val = val - load_type_bits() def to_string(self): - return self.format_type(self.val) + return '((zend_type*) 0x%x) %s' % (self.val.address, self.format_type(self.val)) def children(self): for field in self.val.type.fields(): - yield (field.name, self.val[field.name]) + yield (field.name, format_nested(self.val[field.name])) def format_type(self, t): type_mask = int(t['type_mask']) @@ -95,7 +109,7 @@ def format_type(self, t): meta = [] for bit in range(0, type_mask_size): if type_mask & (1 << bit): - type_name = type_bit_to_name.get(bit) + type_name = ZendTypeBits.zendTypeName(bit) match type_name: case None: parts.append('(1<<%d)' % bit) @@ -115,7 +129,7 @@ def format_type(self, t): separator = '&' case 'name': str = t['ptr'].cast(gdb.lookup_type('zend_string').pointer()) - parts.append(ZendStringPrettyPrinter(str).to_string()) + parts.append(format_zstr(str)) case _: parts.append(type_name) @@ -166,11 +180,11 @@ def children(self): c = c.dereference() yield ('child[%d]' % i, c) elif field.name == 'name': - yield (field.name, ZendStringPrettyPrinter(val[field.name].dereference()).to_string()) + yield (field.name, format_zstr(val[field.name])) elif field.name == 'val': yield (field.name, ZvalPrettyPrinter(val[field.name]).to_string()) else: - yield (field.name, val[field.name]) + yield (field.name, format_nested(self.val[field.name])) def is_special(self): special_shift = 6 # ZEND_AST_SPECIAL_SHIFT @@ -223,37 +237,46 @@ class ZvalPrettyPrinter(gdb.printing.PrettyPrinter): def __init__(self, val): self.val = val - load_type_bits() def to_string(self): - return self.value_to_string() + return '((zval*) 0x%x) %s' % (self.val.address, self.value_to_string()) def value_to_string(self): t = int(self.val['u1']['v']['type']) - if t == type_name_to_bit['undef']: + if t == ZendTypeBits.bit('undef'): return 'undef' - elif t == type_name_to_bit['null']: + elif t == ZendTypeBits.bit('null'): return 'null' - elif t == type_name_to_bit['false']: + elif t == ZendTypeBits.bit('false'): return 'false' - elif t == type_name_to_bit['true']: + elif t == ZendTypeBits.bit('true'): return 'true' - elif t == type_name_to_bit['long']: + elif t == ZendTypeBits.bit('long'): return str(self.val['value']['lval']) - elif t == type_name_to_bit['double']: + elif t == ZendTypeBits.bit('double'): return str(self.val['value']['dval']) - elif t == type_name_to_bit['string']: - return ZendStringPrettyPrinter(self.val['value']['str'].dereference()).to_string() - elif t == type_name_to_bit['array']: + elif t == ZendTypeBits.bit('string'): + return format_zstr(self.val['value']['str']) + elif t == ZendTypeBits.bit('array'): return 'array' - elif t == type_name_to_bit['object']: - return 'object(%s)' % ZendStringPrettyPrinter(self.val['value']['obj']['ce']['name'].dereference()).to_string() - elif t == type_name_to_bit['resource']: + elif t == ZendTypeBits.bit('object'): + return 'object(%s)' % format_zstr(self.val['value']['obj']['ce']['name']) + elif t == ZendTypeBits.bit('resource'): return 'resource' - elif t == type_name_to_bit['reference']: + elif t == ZendTypeBits.bit('reference'): return 'reference' - elif t == type_name_to_bit['constant_ast']: + elif t == ZendTypeBits.bit('constant_ast'): return 'constant_ast' + elif t == ZendTypeBits.bit('indirect'): + value = self.val['value']['zv'] + valuestr = ZvalPrettyPrinter(value).to_string() + return 'indirect: ((zval*) 0x%x) %s' % (int(value), valuestr) + elif t == ZendTypeBits.bit('ptr'): + value = int(self.val['value']['ptr']) + return 'ptr: ((void*) 0x%x)' % (value) + elif t == ZendTypeBits.bit('alias_ptr'): + value = int(self.val['value']['ptr']) + return 'alias_ptr: ((void*) 0x%x)' % (value) else: return 'zval of type %d' % int(self.val['u1']['v']['type']) @@ -261,38 +284,42 @@ def children(self): for field in self.val.type.fields(): if field.name == 'value': value = self.val['value'] + sub_field = 'ptr' t = int(self.val['u1']['v']['type']) - if t == type_name_to_bit['undef']: - value = value['lval'] - elif t == type_name_to_bit['null']: - value = value['lval'] - elif t == type_name_to_bit['false']: - value = value['lval'] - elif t == type_name_to_bit['true']: - value = value['lval'] - elif t == type_name_to_bit['long']: - value = value['lval'] - elif t == type_name_to_bit['double']: - value = value['dval'] - elif t == type_name_to_bit['string']: - value = value['str'].dereference() - elif t == type_name_to_bit['array']: - value = value['ht'].dereference() - elif t == type_name_to_bit['object']: - value = value['obj'].dereference() - elif t == type_name_to_bit['resource']: - value = value['res'].dereference() - elif t == type_name_to_bit['reference']: - value = value['ref'].dereference() - elif t == type_name_to_bit['constant_ast']: - value = value['ast'].dereference() - else: - value = value['ptr'] - yield (field.name, value) + if t == ZendTypeBits.bit('undef'): + sub_field = 'lval' + elif t == ZendTypeBits.bit('null'): + sub_field = 'lval' + elif t == ZendTypeBits.bit('false'): + sub_field = 'lval' + elif t == ZendTypeBits.bit('true'): + sub_field = 'lval' + elif t == ZendTypeBits.bit('long'): + sub_field = 'lval' + elif t == ZendTypeBits.bit('double'): + sub_field = 'dval' + elif t == ZendTypeBits.bit('string'): + sub_field = 'str' + elif t == ZendTypeBits.bit('array'): + sub_field = 'arr' + elif t == ZendTypeBits.bit('object'): + sub_field = 'obj' + elif t == ZendTypeBits.bit('resource'): + sub_field = 'res' + elif t == ZendTypeBits.bit('reference'): + sub_field = 'ref' + elif t == ZendTypeBits.bit('constant_ast'): + sub_field = 'ast' + elif t == ZendTypeBits.bit('indirect'): + sub_field = 'zv' + value = value[sub_field] + if sub_field != 'ptr' and value.type.code == gdb.TYPE_CODE_PTR: + value = value.dereference() + yield ('%s.%s' % (field.name, sub_field), value) elif field.name == 'u2': yield ('u2', self.val[field.name]['extra']) else: - yield (field.name, self.val[field.name]) + yield (field.name, format_nested(self.val[field.name])) pp_set.add_printer('zval', '^_zval_struct$', ZvalPrettyPrinter) @@ -300,22 +327,20 @@ def children(self): class ZendClassEntryPrettyPrinter(gdb.printing.PrettyPrinter): "Print a zend_class_entry" - # String pointers, show the string contents if possible - STRING_FIELDS = [ 'name', 'doc_comment' ] - def __init__(self, val): self.val = val def to_string(self): - return zendStringPointerPrinter(self.val['name']) + return '((zend_class_entry*) 0x%x) %s' % (self.val.address, format_zstr(self.val['name'])) def children(self): for field in self.val.type.fields(): if field.name is not None: - if field.name in self.STRING_FIELDS: - yield (field.name, zendStringPointerPrinter(self.val[field.name])) + if field.name == 'ce_flags': + flags = self.val[field.name] + yield (field.name, '%d (%s)' % (flags, ZendAccFlags.format_ce_flags(flags))) else: - yield (field.name, self.val[field.name]) + yield (field.name, format_nested(self.val[field.name])) else: # Don't break on the union fields. Unfortunately, pretty # printers done in python cannot match the default formatting of @@ -334,52 +359,592 @@ def __init__(self, val): def children(self): for field in self.val.type.fields(): - if field.name == 'doc_comment': - yield ('doc_comment', zendStringPointerPrinter(self.val['doc_comment'])) - elif field.name == 'ce': - yield ('ce', zendStringPointerPrinter(self.val['ce']['name'])) - else: + if field.name == 'value': + flags = self.val[field.name]['u2']['constant_flags'] + yield ('value.u2.constant_flags', '%d (%s)' % (flags, ZendAccFlags.format_const_flags(flags))) yield (field.name, self.val[field.name]) + else: + yield (field.name, format_nested(self.val[field.name])) pp_set.add_printer('zend_class_constant', '^_zend_class_constant$', ZendClassConstantPrettyPrinter) -type_bit_to_name = None -type_name_to_bit = None +class ZendPropertyInfoPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_property_info" + + def __init__(self, val): + self.val = val + + def children(self): + for field in self.val.type.fields(): + if field.name == 'flags': + flags = self.val[field.name] + yield ('flags', '%d (%s)' % (flags, ZendAccFlags.format_prop_flags(flags))) + else: + yield (field.name, format_nested(self.val[field.name])) -def load_type_bits(): - global type_bit_to_name - global type_name_to_bit +pp_set.add_printer('zend_property_info', '^_zend_property_info$', ZendPropertyInfoPrettyPrinter) - if type_bit_to_name != None: - return +class ZendFunctionPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_function" - (symbol,_) = gdb.lookup_symbol("zend_gc_refcount") - if symbol == None: - raise Exception("Could not find zend_types.h: symbol zend_gc_refcount not found") - filename = symbol.symtab.fullname() + def __init__(self, val): + self.val = val + + def to_string(self): + match int(self.val['type']): + case ZendFnTypes.ZEND_INTERNAL_FUNCTION: + typestr = 'internal' + case ZendFnTypes.ZEND_USER_FUNCTION: + typestr = 'user' + case ZendFnTypes.ZEND_EVAL_CODE: + typestr = 'eval' + case _: + typestr = '???' + + if self.val['common']['function_name']: + namestr = format_zstr(self.val['common']['function_name']) + else: + namestr = '{main}' + + if self.val['common']['scope']: + str = '%s method %s::%s' % (typestr, format_zstr(self.val['common']['scope']['name']), namestr) + else: + str = '%s function %s' % (typestr, namestr) + + if int(self.val['type']) == ZendFnTypes.ZEND_USER_FUNCTION or int(self.val['type']) == ZendFnTypes.ZEND_EVAL_CODE: + str = '%s %s:%d' % (str, format_zstr(self.val['op_array']['filename']), int(self.val['op_array']['line_start'])) + + return '((zend_function*) 0x%x) %s' % (self.val.address, str) + + def children(self): + for field in self.val.type.fields(): + if field.name == 'common' or field.name == 'type' or field.name == 'quick_arg_flags': + # Redundant with op_array / internal_function + continue + elif field.name == 'op_array': + if int(self.val['type']) == ZendFnTypes.ZEND_USER_FUNCTION or int(self.val['type']) == ZendFnTypes.ZEND_EVAL_CODE: + yield (field.name, self.val[field.name]) + elif field.name == 'internal_function': + if int(self.val['type']) == ZendFnTypes.ZEND_INTERNAL_FUNCTION: + yield (field.name, self.val[field.name]) + else: + yield (field.name, format_nested(self.val[field.name])) + +pp_set.add_printer('zend_function', '^_zend_function$', ZendFunctionPrettyPrinter) + +class ZendOpArrayPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_op_array" + + def __init__(self, val): + self.val = val + + def to_string(self): + if self.val['function_name']: + namestr = format_zstr(self.val['function_name']) + else: + namestr = '{main}' + + if self.val['scope']: + str = 'method %s::%s' % (format_zstr(self.val['scope']['name']), namestr) + else: + str = 'function %s' % (namestr) + + str = '%s %s:%d' % (str, format_zstr(self.val['filename']), int(self.val['line_start'])) + + return '((zend_op_array*) 0x%x) %s' % (self.val.address, str) + + def children(self): + for field in self.val.type.fields(): + if field.name == 'fn_flags': + value = self.val[field.name] + yield (field.name, '%d (%s)' % (value, ZendAccFlags.format_fn_flags(value))) + else: + yield (field.name, format_nested(self.val[field.name])) + +pp_set.add_printer('zend_op_array', '^_zend_op_array$', ZendOpArrayPrettyPrinter) + +class ZendOpPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_op" + + def __init__(self, val): + self.val = val + + def children(self): + for field in self.val.type.fields(): + if field.name == 'opcode': + opcode = int(self.val[field.name]) + yield (field.name, '%d (%s)' % (opcode, ZendOpcodes.name(opcode))) + else: + yield (field.name, format_nested(self.val[field.name])) + +pp_set.add_printer('zend_op', '^_zend_op$', ZendOpPrettyPrinter) + +class ZendInternalFunctionPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_internal_function" + + def __init__(self, val): + self.val = val + + def to_string(self): + if self.val['function_name']: + namestr = format_zstr(self.val['function_name']) + else: + namestr = '???' + + if self.val['scope']: + str = 'method %s::%s' % (format_zstr(self.val['scope']['name']), namestr) + else: + str = 'function %s' % (namestr) + + return '((zend_internal_function*) 0x%x) %s' % (self.val.address, str) + + def children(self): + for field in self.val.type.fields(): + if field.name == 'fn_flags': + yield ('fn_flags', ('%d (%s)' % (self.val[field.name], ZendAccFlags.format_fn_flags(self.val[field.name])))) + else: + yield (field.name, format_nested(self.val[field.name])) + +pp_set.add_printer('zend_internal_function', '^_zend_internal_function$', ZendInternalFunctionPrettyPrinter) + +class ZendRefcountedHPrettyPrinter(gdb.printing.PrettyPrinter): + "Print a zend_refcounted_h" + + def __init__(self, val): + self.val = val + + def children(self): + for field in self.val.type.fields(): + if field.name == 'u': + val = self.val[field.name] + if val == None: + val = self.val + for subfield in val.type.fields(): + if subfield.name == 'type_info': + flags = int(val[subfield.name]) + yield (('%s.%s' % (field.name, subfield.name)), '%d (%s)' % (flags, ZendRefTypeInfo.format(flags))) + else: + yield (('%s.%s' % (field.name, subfield.name)), format_nested(val[subfield.name])) + else: + yield (('%s' % field.name), format_nested(self.val[field.name])) + +pp_set.add_printer('zend_refcounted_h', '^_zend_refcounted_h$', ZendRefcountedHPrettyPrinter) + +class PrintAccFlagsCommand(gdb.Command): + "Pretty print ACC flags" + + def __init__ (self, type): + self.type = type + name = 'print_%s_flags' % type + super(PrintAccFlagsCommand, self).__init__(name, gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION) - bits = {} + def invoke (self, arg, from_tty): + arg = int(gdb.parse_and_eval(arg)) + print(ZendAccFlags.format_flags(arg, self.type)) - with open(filename, 'r') as file: - content = file.read() +PrintAccFlagsCommand('fn') +PrintAccFlagsCommand('ce') +PrintAccFlagsCommand('prop') +PrintAccFlagsCommand('const') - pattern = re.compile(r'#define _ZEND_TYPE_([^\s]+)_BIT\s+\(1u << (\d+)\)') - matches = pattern.findall(content) - for name, bit in matches: - bits[int(bit)] = name.lower() +class PrintOpcodeCommand(gdb.Command): + "Pretty print opcode" - pattern = re.compile(r'#define IS_([^\s]+)\s+(\d+)') - matches = pattern.findall(content) - for name, bit in matches: - if not int(bit) in bits: - bits[int(bit)] = name.lower() + def __init__ (self): + super(PrintOpcodeCommand, self).__init__("print_opcode", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION) - types = {} - for bit in bits: - types[bits[bit]] = bit + def invoke (self, arg, from_tty): + arg = int(gdb.parse_and_eval(arg)) + print(ZendOpcodes.name(arg)) - type_bit_to_name = bits - type_name_to_bit = types +PrintOpcodeCommand() + +class PrintRefTypeInfoCommand(gdb.Command): + "Pretty print zend_refcounted.gc.u.type_info" + + def __init__ (self): + super(PrintRefTypeInfoCommand, self).__init__("print_ref_type_info", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION) + + def invoke (self, arg, from_tty): + arg = int(gdb.parse_and_eval(arg)) + print(ZendRefTypeInfo.format(arg)) + +PrintRefTypeInfoCommand() + +class DumpOpArrayCommand(gdb.Command): + "Dump an op_array" + + def __init__ (self): + super(DumpOpArrayCommand, self).__init__("dump_op_array", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION) + + def invoke (self, arg, from_tty): + op_array = gdb.parse_and_eval(arg) + if op_array.type.code != gdb.TYPE_CODE_PTR: + print("Must pass a zend_op_array* (got a %s)" % op_array.type) + return + if str(gdb.types.get_basic_type(op_array.type.target())) != 'struct _zend_op_array': + print("Must pass a zend_op_array* (got a %s)" % op_array.type) + return + if int(op_array) == 0: + print("NULL") + return + gdb.execute("call zend_dump_op_array((zend_op_array*)0x%x, 0, 0, 0)" % (int(op_array))) + return + +DumpOpArrayCommand() + +class ZendTypeBits: + _bits = None + + @classmethod + def zendTypeName(self, bit): + self._load() + for name in self._bits: + if bit == self._bits[name]: + return name + + @classmethod + def zvalTypeName(self, bit): + # Same as zendTypeName, but return the last matching one + # e.g. 13 is IS_PTR, not IS_ITERABLE + self._load() + ret = None + for name in self._bits: + if bit == self._bits[name]: + ret = name + return ret + + @classmethod + def bit(self, name): + self._load() + return self._bits.get(name) + + @classmethod + def _load(self): + if self._bits != None: + return + + dirname = detect_source_dir() + filename = os.path.join(dirname, 'zend_types.h') + + bits = {} + + with open(filename, 'r') as file: + content = file.read() + + pattern = re.compile(r'#define _ZEND_TYPE_([^\s]+)_BIT\s+\(1u << (\d+)\)') + matches = pattern.findall(content) + for name, bit in matches: + bits[name.lower()] = int(bit) + + pattern = re.compile(r'#define IS_([^\s]+)\s+(\d+)') + matches = pattern.findall(content) + for name, bit in matches: + bits[name.lower()] = int(bit) + + self._bits = bits + +class ZendFnTypes: + ZEND_INTERNAL_FUNCTION = 1 + ZEND_USER_FUNCTION = 2 + ZEND_EVAL_CODE = 4 + +class ZendAccFlag: + ce = False + fn = False + prop = False + const = False + bit = 0 + def __init__(self, ce, fn, prop, const, bit): + self.ce = ce + self.fn = fn + self.prop = prop + self.const = const + self.bit = bit + + def applies_to(self, type): + return getattr(self, type) + +class ZendAccFlags: + _flags = None + + @classmethod + def fn_flag_name(self, bit): + self.flag_name(bit, 'fn') + + @classmethod + def ce_flag_name(self, bit): + self.flag_name(bit, 'ce') + + @classmethod + def prop_flag_name(self, bit): + self.flag_name(bit, 'prop') + + @classmethod + def const_flag_name(self, bit): + self.flag_name(bit, 'const') + + @classmethod + def flag_name(self, bit, type): + self._load() + for name in self._flags: + flag = self._flags[name] + if flag.applies_to(type) and bit == flag.bit: + return name + + @classmethod + def flag_bit(self, name): + self._load() + return self._flags[name] + + @classmethod + def format_flags(self, flags, type): + flags = int(flags) + names = [] + for i in range(0, 31): + if (flags & (1 << i)) != 0: + name = self.flag_name(i, type) + if name == None: + names.append('(1 << %d)' % (i)) + else: + names.append(name) + return ' | '.join(names) + + @classmethod + def format_fn_flags(self, flags): + return self.format_flags(flags, 'fn') + + @classmethod + def format_ce_flags(self, flags): + return self.format_flags(flags, 'ce') + + @classmethod + def format_prop_flags(self, flags): + return self.format_flags(flags, 'prop') + + @classmethod + def format_const_flags(self, flags): + return self.format_flags(flags, 'const') + + @classmethod + def _load(self): + if self._flags != None: + return + + dirname = detect_source_dir() + filename = os.path.join(dirname, 'zend_compile.h') + + flags = {} + + with open(filename, 'r') as file: + content = file.read() + + pattern = re.compile(r'#define (ZEND_ACC_[^\s]+)\s+\(1U?\s+<<\s+(\d+)\)\s+/\*\s+(X?)\s+\|\s+(X?)\s+\|\s+(X?)\s+\|\s+(X?)\s+\*/') + matches = pattern.findall(content) + for name, bit, cls, func, prop, const in matches: + flags[name] = ZendAccFlag(cls == 'X', func == 'X', prop == 'X', const == 'X', int(bit)) + + self._flags = flags + +class ZendOpcodes: + _opcodes = None + + @classmethod + def name(self, number): + self._load() + number = int(number) + for name in self._opcodes: + if number == self._opcodes[name]: + return name + + @classmethod + def number(self, name): + self._load() + return self._opcodes[name] + + @classmethod + def _load(self): + if self._opcodes != None: + return + + dirname = detect_source_dir() + filename = os.path.join(dirname, 'zend_vm_opcodes.h') + + opcodes = {} + found_nop = False + + with open(filename, 'r') as file: + content = file.read() + + pattern = re.compile(r'#define (ZEND_[^\s]+)\s+([0-9]+)') + matches = pattern.findall(content) + for name, number in matches: + if not found_nop: + if name == 'ZEND_NOP': + found_nop = True + else: + continue + if name == 'ZEND_VM_LAST_OPCODE': + break + opcodes[name] = int(number) + + self._opcodes = opcodes + +class ZendRefTypeInfo: + _bits = None + + @classmethod + def flag_name(self, bit): + return self._flag_name_in(bit, self._bits) + + @classmethod + def _flag_name_in(self, bit, bits): + for name in bits: + if bit == bits[name]: + return name + + @classmethod + def bit(self, name): + return self._bits.get(name) + + @classmethod + def format(self, flags): + self._load() + names = [] + + type = flags & self._type_mask + type_name = ZendTypeBits.zvalTypeName(type) + if type_name is not None: + names.append('IS_%s' % type_name.upper()) + + bits = self._bits + type_bits = None + match type_name: + case 'string': + type_bits = self._str_bits + case 'array': + type_bits = self._array_bits + case 'object': + type_bits = self._obj_bits + + type_flags = flags & self._flags_mask + for i in range(0, 31): + if (1< type_flags: + break + if (type_flags & (1<> self._info_shift) & self._gc_color + match gc_color: + case self._gc_black: + names.append('GC_BLACK') + case self._gc_white: + names.append('GC_WHITE') + case self._gc_grey: + names.append('GC_GREY') + case self._gc_purple: + names.append('GC_PURPLE') + + gc_address = (flags >> self._info_shift) & self._gc_address + if gc_address != 0: + names.append('GC_ADDRESS(%d)' % gc_address) + else: + info = flags & self._info_mask + if info != 0: + names.append('GC_INFO(%d)' % info) + + return ' | '.join(names) + + @classmethod + def _load(self): + if self._bits != None: + return + + dirname = detect_source_dir() + filename = os.path.join(dirname, 'zend_types.h') + + bits = {} + str_bits = {} + array_bits = {} + obj_bits = {} + + with open(filename, 'r') as file: + content = file.read() + + # GC_NOT_COLLECTABLE (1<<4) + pattern = re.compile(r'#define (GC_[^\s]+)\s+\(\s*1\s*<<\s*([0-9]+)\s*\)') + matches = pattern.findall(content) + for name, bit in matches: + bits[name] = int(bit) + + # GC_TYPE_MASK 0x0000000f + # GC_INFO_SHIFT 10 + pattern = re.compile(r'#define (GC_[^\s]+)\s+((0x)?[0-9a-f]+)') + matches = pattern.findall(content) + for name, bit, _ in matches: + match name: + case 'GC_TYPE_MASK': + self._type_mask = int(bit, 0) + case 'GC_FLAGS_MASK': + self._flags_mask = int(bit, 0) + case 'GC_INFO_MASK': + self._info_mask = int(bit, 0) + case 'GC_INFO_SHIFT': + self._info_shift = int(bit, 0) + case 'GC_FLAGS_SHIFT': + self._flags_shift = int(bit, 0) + + # IS_STR_INTERNED GC_IMMUTABLE + # IS_STR_PERMANENT (1<<8) + pattern = re.compile(r'#define (IS_(STR|ARRAY|OBJ)_[^\s]+)\s+(\(\s*1\s*<<\s*([0-9]+)\s*\)|GC_[a-zA-Z_]+)') + matches = pattern.findall(content) + for name, type, val, bit in matches: + if bit == '': + bit = bits.get(val) + if bit == None: + continue + match type: + case 'STR': + target = str_bits + case 'ARRAY': + target = array_bits + case 'OBJ': + target = obj_bits + target[name] = int(bit) + + # Hard coded because these are not exposed in header files + self._gc_address = 0x0fffff + self._gc_color = 0x300000 + self._gc_black = 0x000000 + self._gc_white = 0x100000 + self._gc_grey = 0x200000 + self._gc_purple = 0x300000 + + self._bits = bits + self._str_bits = str_bits + self._array_bits = array_bits + self._obj_bits = obj_bits + +def detect_source_dir(): + (symbol,_) = gdb.lookup_symbol("zend_visibility_to_set_visibility") + if symbol == None: + raise Exception("Could not find zend_compile.h: symbol zend_visibility_to_set_visibility not found") + filename = symbol.symtab.fullname() + dirname = os.path.dirname(filename) + return dirname def lookup_symbol(name): (symbol, _) = gdb.lookup_symbol(name) @@ -395,4 +960,49 @@ def array_size(ary_type): # array types have a single field whose type represents its range return ary_type.fields()[0].type.range()[1]+1 +def format_zstr(zstr): + len = int(zstr['len']) + truncated = False + if len > 200: + len = 200 + truncated = True + + ptr_type = gdb.lookup_type('char').pointer() + ary_type = gdb.lookup_type('char').array(len) + str = zstr['val'].cast(ptr_type).dereference().cast(ary_type) + str = str.format_string() + if truncated: + str += ' (%d bytes total)' % int(zstr['len']) + + return str + +def format_nested(value): + orig_value = value + type = value.type + + # Null pointers + if type.code == gdb.TYPE_CODE_PTR and int(value) == 0: + return orig_value + + addr = orig_value.address + + while type.code == gdb.TYPE_CODE_PTR: + addr = int(value) + type = type.target() + try: + value = value.dereference() + except: + pass + + type = gdb.types.get_basic_type(type) + + if type.tag and re.match(r'^_zend_string$', type.tag): + return format_zstr(value) + elif type.tag and re.match(r'^_zend_class_entry$', type.tag): + return '((zend_class_entry*)0x%x) %s' % (addr, format_zstr(value['name'])) + elif type.tag and re.match(r'^_zend_array$', type.tag): + return '((zend_array*)0x%x) array(%d)' % (addr, value['nNumOfElements']) + + return orig_value + gdb.printing.register_pretty_printer(gdb, pp_set, replace=True)