diff --git a/src/arch/i386/include/bits/compiler.h b/src/arch/i386/include/bits/compiler.h index db512dbd..87201135 100644 --- a/src/arch/i386/include/bits/compiler.h +++ b/src/arch/i386/include/bits/compiler.h @@ -3,6 +3,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_386_NONE + #ifndef ASSEMBLY /** Declare a function with standard calling conventions */ diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index 9703733c..10431366 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -346,6 +346,7 @@ int pxe_start_nbp ( void ) { return 0; } +REQUIRING_SYMBOL ( pxe_api_call ); REQUIRE_OBJECT ( pxe_preboot ); REQUIRE_OBJECT ( pxe_undi ); REQUIRE_OBJECT ( pxe_udp ); diff --git a/src/arch/i386/prefix/kkkpxeprefix.S b/src/arch/i386/prefix/kkkpxeprefix.S index 999fe1bf..6e43cd26 100644 --- a/src/arch/i386/prefix/kkkpxeprefix.S +++ b/src/arch/i386/prefix/kkkpxeprefix.S @@ -8,6 +8,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Provide the PXENV_FILE_EXIT_HOOK API call */ +REQUIRING_SYMBOL ( _kkkpxe_start ) REQUIRE_OBJECT ( pxe_exit_hook ) #define PXELOADER_KEEP_UNDI diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S index 08fcf73b..777b9e04 100644 --- a/src/arch/i386/prefix/romprefix.S +++ b/src/arch/i386/prefix/romprefix.S @@ -875,5 +875,8 @@ wait_for_tick: ret .size wait_for_tick, . - wait_for_tick +/* Drag in objects via _rom_start */ +REQUIRING_SYMBOL ( _rom_start ) + /* Drag in ROM configuration */ REQUIRE_OBJECT ( config_romprefix ) diff --git a/src/arch/i386/transitions/librm_test.c b/src/arch/i386/transitions/librm_test.c index af7f0470..f1a517ed 100644 --- a/src/arch/i386/transitions/librm_test.c +++ b/src/arch/i386/transitions/librm_test.c @@ -118,4 +118,5 @@ struct self_test librm_test __self_test = { .exec = librm_test_exec, }; +REQUIRING_SYMBOL ( librm_test ); REQUIRE_OBJECT ( test ); diff --git a/src/arch/x86/drivers/hyperv/hyperv.c b/src/arch/x86/drivers/hyperv/hyperv.c index c3c0e86c..f73829bd 100644 --- a/src/arch/x86/drivers/hyperv/hyperv.c +++ b/src/arch/x86/drivers/hyperv/hyperv.c @@ -590,5 +590,8 @@ struct root_device hv_root_device __root_device = { .driver = &hv_root_driver, }; +/* Drag in objects via hv_root_device */ +REQUIRING_SYMBOL ( hv_root_device ); + /* Drag in netvsc driver */ REQUIRE_OBJECT ( netvsc ); diff --git a/src/arch/x86/drivers/xen/hvm.c b/src/arch/x86/drivers/xen/hvm.c index 6f3be8e2..7ac32d54 100644 --- a/src/arch/x86/drivers/xen/hvm.c +++ b/src/arch/x86/drivers/xen/hvm.c @@ -496,5 +496,8 @@ struct pci_driver hvm_driver __pci_driver = { .remove = hvm_remove, }; +/* Drag in objects via hvm_driver */ +REQUIRING_SYMBOL ( hvm_driver ); + /* Drag in netfront driver */ REQUIRE_OBJECT ( netfront ); diff --git a/src/arch/x86_64/include/bits/compiler.h b/src/arch/x86_64/include/bits/compiler.h index 51a7eaae..f70b2e51 100644 --- a/src/arch/x86_64/include/bits/compiler.h +++ b/src/arch/x86_64/include/bits/compiler.h @@ -1,6 +1,9 @@ #ifndef _BITS_COMPILER_H #define _BITS_COMPILER_H +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_X86_64_NONE + #ifndef ASSEMBLY /** Declare a function with standard calling conventions */ diff --git a/src/config/config.c b/src/config/config.c index e4d378f0..47008388 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -44,6 +44,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * in the final iPXE executable built. */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in all requested console types * diff --git a/src/config/config_ethernet.c b/src/config/config_ethernet.c index e9239955..372ec993 100644 --- a/src/config/config_ethernet.c +++ b/src/config/config_ethernet.c @@ -29,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in Ethernet-specific protocols */ diff --git a/src/config/config_fc.c b/src/config/config_fc.c index 2b317935..33fc9462 100644 --- a/src/config/config_fc.c +++ b/src/config/config_fc.c @@ -29,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in Fibre Channel-specific commands * diff --git a/src/config/config_infiniband.c b/src/config/config_infiniband.c index 13ed25a8..a742e755 100644 --- a/src/config/config_infiniband.c +++ b/src/config/config_infiniband.c @@ -29,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in Infiniband-specific protocols */ diff --git a/src/config/config_net80211.c b/src/config/config_net80211.c index c89988a2..34361754 100644 --- a/src/config/config_net80211.c +++ b/src/config/config_net80211.c @@ -25,6 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in 802.11-specific commands * diff --git a/src/config/config_romprefix.c b/src/config/config_romprefix.c index ad6f31f8..21921b86 100644 --- a/src/config/config_romprefix.c +++ b/src/config/config_romprefix.c @@ -29,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Provide UNDI loader if PXE stack is requested * diff --git a/src/config/config_route.c b/src/config/config_route.c index 38812ebb..c0b4ee91 100644 --- a/src/config/config_route.c +++ b/src/config/config_route.c @@ -29,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in routing management for relevant protocols * diff --git a/src/config/config_usb.c b/src/config/config_usb.c index 55730235..30aa06c1 100644 --- a/src/config/config_usb.c +++ b/src/config/config_usb.c @@ -29,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in USB controllers */ diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 0f114b84..49a1bce7 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -1765,5 +1765,8 @@ int x509_validate_chain ( struct x509_chain *chain, time_t time, return -EACCES_USELESS; } +/* Drag in objects via x509_validate() */ +REQUIRING_SYMBOL ( x509_validate ); + /* Drag in certificate store */ REQUIRE_OBJECT ( certstore ); diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index 91d52b0c..22a57e6e 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -1903,6 +1903,9 @@ struct usb_port * usb_root_hub_port ( struct usb_device *usb ) { return usb->port; } +/* Drag in objects via register_usb_bus() */ +REQUIRING_SYMBOL ( register_usb_bus ); + /* Drag in USB configuration */ REQUIRE_OBJECT ( config_usb ); diff --git a/src/drivers/net/rtl818x/rtl8180.c b/src/drivers/net/rtl818x/rtl8180.c index 8851d1bf..0c7d0775 100644 --- a/src/drivers/net/rtl818x/rtl8180.c +++ b/src/drivers/net/rtl818x/rtl8180.c @@ -4,6 +4,7 @@ FILE_LICENCE(GPL2_OR_LATER); #include +PROVIDE_REQUIRING_SYMBOL(); REQUIRE_OBJECT(rtl818x); REQUIRE_OBJECT(rtl8180_grf5101); REQUIRE_OBJECT(rtl8180_max2820); diff --git a/src/drivers/net/rtl818x/rtl8185.c b/src/drivers/net/rtl818x/rtl8185.c index fd27e5c8..438c5f24 100644 --- a/src/drivers/net/rtl818x/rtl8185.c +++ b/src/drivers/net/rtl818x/rtl8185.c @@ -4,6 +4,7 @@ FILE_LICENCE(GPL2_OR_LATER); #include +PROVIDE_REQUIRING_SYMBOL(); REQUIRE_OBJECT(rtl818x); REQUIRE_OBJECT(rtl8185_rtl8225); diff --git a/src/drivers/net/vxge/vxge.c b/src/drivers/net/vxge/vxge.c index bf20ec43..9c587722 100644 --- a/src/drivers/net/vxge/vxge.c +++ b/src/drivers/net/vxge/vxge.c @@ -9,6 +9,7 @@ FILE_LICENCE(GPL2_OR_LATER); #include +PROVIDE_REQUIRING_SYMBOL(); REQUIRE_OBJECT(vxge_main); /** vxge PCI IDs for util/parserom.pl which are put into bin/NIC */ diff --git a/src/hci/commands/image_trust_cmd.c b/src/hci/commands/image_trust_cmd.c index 094b5afa..f9d6b5b3 100644 --- a/src/hci/commands/image_trust_cmd.c +++ b/src/hci/commands/image_trust_cmd.c @@ -173,6 +173,9 @@ struct command image_trust_commands[] __command = { }, }; +/* Drag in objects via command list */ +REQUIRING_SYMBOL ( image_trust_commands ); + /* Drag in objects typically required for signature verification */ REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( md5 ); diff --git a/src/include/compiler.h b/src/include/compiler.h index f877ebce..ca82f952 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -57,54 +57,101 @@ * @{ */ -/** Provide a symbol within this object file */ -#ifdef ASSEMBLY -#define PROVIDE_SYMBOL( _sym ) \ - .section ".provided", "a", @nobits ; \ - .hidden _sym ; \ - .globl _sym ; \ - _sym: ; \ - .previous -#else /* ASSEMBLY */ -#define PROVIDE_SYMBOL( _sym ) \ - char _sym[0] \ - __attribute__ (( section ( ".provided" ) )) -#endif /* ASSEMBLY */ - -/** Require a symbol within this object file +/** + * Provide a symbol within this object file * - * The symbol is referenced by a relocation in a discarded section, so - * if it is not available at link time the link will fail. + * @v symbol Symbol name */ #ifdef ASSEMBLY -#define REQUIRE_SYMBOL( _sym ) \ - .section ".discard", "a", @progbits ; \ - .extern _sym ; \ - .long _sym ; \ +#define PROVIDE_SYMBOL( symbol ) \ + .section ".provided", "a", @nobits ; \ + .hidden symbol ; \ + .globl symbol ; \ + symbol: ; \ .previous -#else /* ASSEMBLY */ -#define REQUIRE_SYMBOL( _sym ) \ - extern char _sym; \ - static char * _C2 ( _C2 ( __require_, _sym ), _C2 ( _, __LINE__ ) ) \ - __attribute__ (( section ( ".discard" ), used )) \ - = &_sym +#else +#define PROVIDE_SYMBOL( symbol ) \ + char symbol[0] \ + __attribute__ (( section ( ".provided" ) )) #endif -/** Request that a symbol be available at runtime +/** + * Request a symbol * - * The requested symbol is entered as undefined into the symbol table - * for this object, so the linker will pull in other object files as - * necessary to satisfy the reference. However, the undefined symbol - * is not referenced in any relocations, so the link can still succeed - * if no file contains it. + * @v symbol Symbol name + * + * Request a symbol to be included within the link. If the symbol + * cannot be found, the link will succeed anyway. */ #ifdef ASSEMBLY -#define REQUEST_SYMBOL( _sym ) \ - .equ __need_ ## _sym, _sym -#else /* ASSEMBLY */ -#define REQUEST_SYMBOL( _sym ) \ - __asm__ ( ".equ\t__need_" #_sym ", " #_sym ) -#endif /* ASSEMBLY */ +#define REQUEST_SYMBOL( symbol ) \ + .equ __request_ ## symbol, symbol +#else +#define REQUEST_SYMBOL( symbol ) \ + __asm__ ( ".equ __request_" #symbol ", " #symbol ) +#endif + +/** + * Require a symbol + * + * @v symbol Symbol name + * + * Require a symbol to be included within the link. If the symbol + * cannot be found, the link will fail. + * + * To use this macro within a file, you must also specify the file's + * "requiring symbol" using the REQUIRING_SYMBOL() or + * PROVIDE_REQUIRING_SYMBOL() macros. + */ +#ifdef ASSEMBLY +#define REQUIRE_SYMBOL( symbol ) \ + .reloc __requiring_symbol__, RELOC_TYPE_NONE, symbol +#else +#define REQUIRE_SYMBOL( symbol ) \ + __asm__ ( ".reloc __requiring_symbol__, " \ + _S2 ( RELOC_TYPE_NONE ) ", " #symbol ) +#endif + +/** + * Specify the file's requiring symbol + * + * @v symbol Symbol name + * + * REQUIRE_SYMBOL() works by defining a dummy relocation record + * against a nominated "requiring symbol". The presence of the + * nominated requiring symbol will drag in all of the symbols + * specified using REQUIRE_SYMBOL(). + */ +#ifdef ASSEMBLY +#define REQUIRING_SYMBOL( symbol ) \ + .equ __requiring_symbol__, symbol +#else +#define REQUIRING_SYMBOL( symbol ) \ + __asm__ ( ".equ __requiring_symbol__, " #symbol ) +#endif + +/** + * Provide a file's requiring symbol + * + * If the file contains no symbols that can be used as the requiring + * symbol, you can provide a dummy one-byte-long symbol using + * PROVIDE_REQUIRING_SYMBOL(). + */ +#ifdef ASSEMBLY +#define PROVIDE_REQUIRING_SYMBOL() \ + .section ".tbl.requiring_symbols", "a", @progbits ; \ + __requiring_symbol__: .byte 0 ; \ + .size __requiring_symbol__, . - __requiring_symbol__ ; \ + .previous +#else +#define PROVIDE_REQUIRING_SYMBOL() \ + __asm__ ( ".section \".tbl.requiring_symbols\", " \ + " \"a\", @progbits\n" \ + "__requiring_symbol__:\t.byte 0\n" \ + ".size __requiring_symbol__, " \ + " . - __requiring_symbol__\n" \ + ".previous" ) +#endif /** @} */ @@ -119,11 +166,29 @@ /** Always provide the symbol for the current object (defined by -DOBJECT) */ PROVIDE_SYMBOL ( OBJECT_SYMBOL ); -/** Explicitly require another object */ -#define REQUIRE_OBJECT( _obj ) REQUIRE_SYMBOL ( obj_ ## _obj ) +/** + * Request an object + * + * @v object Object name + * + * Request an object to be included within the link. If the object + * cannot be found, the link will succeed anyway. + */ +#define REQUEST_OBJECT( object ) REQUEST_SYMBOL ( obj_ ## object ) -/** Pull in another object if it exists */ -#define REQUEST_OBJECT( _obj ) REQUEST_SYMBOL ( obj_ ## _obj ) +/** + * Require an object + * + * @v object Object name + * + * Require an object to be included within the link. If the object + * cannot be found, the link will fail. + * + * To use this macro within a file, you must also specify the file's + * "requiring symbol" using the REQUIRING_SYMBOL() or + * PROVIDE_REQUIRING_SYMBOL() macros. + */ +#define REQUIRE_OBJECT( object ) REQUIRE_SYMBOL ( obj_ ## object ) /** @} */ @@ -685,8 +750,8 @@ int __debug_disable; /** @} */ -/* This file itself is under GPLv2-or-later */ -FILE_LICENCE ( GPL2_OR_LATER ); +/* This file itself is under GPLv2+/UBDL */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include diff --git a/src/include/nic.h b/src/include/nic.h index d242333e..4c91f57a 100644 --- a/src/include/nic.h +++ b/src/include/nic.h @@ -276,6 +276,7 @@ static inline void * legacy_isa_get_drvdata ( void *hwdev ) { _name ## _isa_legacy_remove ( struct isa_device *isa ) { \ return legacy_remove ( isa, legacy_isa_get_drvdata, \ _name ## _disable ); \ - } + } \ + PROVIDE_REQUIRING_SYMBOL() #endif /* NIC_H */ diff --git a/src/net/80211/net80211.c b/src/net/80211/net80211.c index d2051f64..00794592 100644 --- a/src/net/80211/net80211.c +++ b/src/net/80211/net80211.c @@ -2827,5 +2827,8 @@ struct errortab common_wireless_errors[] __errortab = { __einfo_errortab ( EINFO_ECONNREFUSED_AUTH_ALGO_UNSUPP ), }; +/* Drag in objects via net80211_ll_protocol */ +REQUIRING_SYMBOL ( net80211_ll_protocol ); + /* Drag in 802.11 configuration */ REQUIRE_OBJECT ( config_net80211 ); diff --git a/src/net/80211/wpa.c b/src/net/80211/wpa.c index e2c4945f..77f66d82 100644 --- a/src/net/80211/wpa.c +++ b/src/net/80211/wpa.c @@ -912,4 +912,5 @@ struct eapol_handler eapol_key_handler __eapol_handler = { }; /* WPA always needs EAPOL in order to be useful */ +REQUIRING_SYMBOL ( eapol_key_handler ); REQUIRE_OBJECT ( eapol ); diff --git a/src/net/ethernet.c b/src/net/ethernet.c index a4bdfd4e..33e05725 100644 --- a/src/net/ethernet.c +++ b/src/net/ethernet.c @@ -239,6 +239,9 @@ struct net_device * alloc_etherdev ( size_t priv_size ) { return netdev; } +/* Drag in objects via ethernet_protocol */ +REQUIRING_SYMBOL ( ethernet_protocol ); + /* Drag in Ethernet configuration */ REQUIRE_OBJECT ( config_ethernet ); diff --git a/src/net/fc.c b/src/net/fc.c index 459f6fc3..2e807027 100644 --- a/src/net/fc.c +++ b/src/net/fc.c @@ -1940,5 +1940,8 @@ struct fc_ulp * fc_ulp_get_port_id_type ( struct fc_port *port, return NULL; } +/* Drag in objects via fc_ports */ +REQUIRING_SYMBOL ( fc_ports ); + /* Drag in Fibre Channel configuration */ REQUIRE_OBJECT ( config_fc ); diff --git a/src/net/infiniband.c b/src/net/infiniband.c index 7e545f74..8ba031f1 100644 --- a/src/net/infiniband.c +++ b/src/net/infiniband.c @@ -999,6 +999,9 @@ struct ib_device * last_opened_ibdev ( void ) { return ibdev; } +/* Drag in objects via register_ibdev() */ +REQUIRING_SYMBOL ( register_ibdev ); + /* Drag in Infiniband configuration */ REQUIRE_OBJECT ( config_infiniband ); diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 85ee5a68..ed22c4d3 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -841,5 +841,8 @@ struct settings_applicator ipv4_settings_applicator __settings_applicator = { .apply = ipv4_create_routes, }; +/* Drag in objects via ipv4_protocol */ +REQUIRING_SYMBOL ( ipv4_protocol ); + /* Drag in ICMPv4 */ REQUIRE_OBJECT ( icmpv4 ); diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 3c374168..c5bead1c 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1104,6 +1104,9 @@ struct net_driver ipv6_driver __net_driver = { .remove = ipv6_remove, }; +/* Drag in objects via ipv6_protocol */ +REQUIRING_SYMBOL ( ipv6_protocol ); + /* Drag in ICMPv6 */ REQUIRE_OBJECT ( icmpv6 ); diff --git a/src/tests/cms_test.c b/src/tests/cms_test.c index 8962e2ab..b805a997 100644 --- a/src/tests/cms_test.c +++ b/src/tests/cms_test.c @@ -1474,6 +1474,7 @@ struct self_test cms_test __self_test = { }; /* Drag in algorithms required for tests */ +REQUIRING_SYMBOL ( cms_test ); REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( md5 ); REQUIRE_OBJECT ( sha1 ); diff --git a/src/tests/ocsp_test.c b/src/tests/ocsp_test.c index a2f1f339..c6d45859 100644 --- a/src/tests/ocsp_test.c +++ b/src/tests/ocsp_test.c @@ -1861,5 +1861,6 @@ struct self_test ocsp_test __self_test = { }; /* Drag in algorithms required for tests */ +REQUIRING_SYMBOL ( ocsp_test ); REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( sha1 ); diff --git a/src/tests/tests.c b/src/tests/tests.c index a79ddfa0..adb90f20 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /* Drag in all applicable self-tests */ +PROVIDE_REQUIRING_SYMBOL(); REQUIRE_OBJECT ( memset_test ); REQUIRE_OBJECT ( memcpy_test ); REQUIRE_OBJECT ( string_test ); diff --git a/src/tests/x509_test.c b/src/tests/x509_test.c index 67c8b39d..658d5247 100644 --- a/src/tests/x509_test.c +++ b/src/tests/x509_test.c @@ -1109,6 +1109,7 @@ struct self_test x509_test __self_test = { }; /* Drag in algorithms required for tests */ +REQUIRING_SYMBOL ( x509_test ); REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( sha1 ); REQUIRE_OBJECT ( sha256 ); diff --git a/src/usr/route.c b/src/usr/route.c index eb50b5bb..690ba3b6 100644 --- a/src/usr/route.c +++ b/src/usr/route.c @@ -48,4 +48,5 @@ void route ( void ) { } /* Drag in routing management configuration */ +REQUIRING_SYMBOL ( route ); REQUIRE_OBJECT ( config_route ); diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 1e7a99b7..e68fa5d1 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -478,6 +478,9 @@ static void process_reloc ( bfd *bfd __attribute__ (( unused )), /* Skip absolute symbols; the symbol value won't * change when the object is loaded. */ + } else if ( ( strcmp ( howto->name, "R_386_NONE" ) == 0 ) || + ( strcmp ( howto->name, "R_X86_64_NONE" ) == 0 ) ) { + /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) { /* Generate an 8-byte PE relocation */ generate_pe_reloc ( pe_reltab, offset, 8 );