. "$BST_LIB" # GNU MP is arbitrary-precision arithmetic over three numeric types -- integers # (mpz), rationals (mpq) and floats (mpf) -- layered on mpn, the raw limb-vector # primitives the other three are built from. It installs NO binaries at all, so # unlike every tool suite here there is nothing to run, and there is no on-target # C compiler to link a probe against the library either. The assertions are # therefore that the shipped payload is REAL: the declared API of all four # layers, the public types they exchange, the pkg-config file describing the # library the image actually ships, and the archive's own symbol index carrying a # compiled entry point from each layer. That index is what separates a wasm32 # compile that really produced GMP from an empty or partial stub. # # THREE SHAPE FACTS decide how this file is written. Each was measured against # the shipped payload first, and each would produce a red on a perfectly good # port if it were assumed the other way: # # 1. /usr/include/gmp.h IS NOT THE API HEADER. The recipe runs # oe_multilib_header, which renames the real header to gmp-32.h and leaves a # Wind River wrapper at gmp.h that #includes and dispatches # on __WORDSIZE. The wrapper carries no declaration, no type and no version # macro. So every API assertion below reads gmp-32.h, and gmp.h is asserted # for what it actually is -- the dispatch. # # 2. mpn ALIASES THROUGH __MPN(), NOT __gmpn_. mpz/mpq/mpf each spell their # alias `#define mpz_init __gmpz_init`, but mpn spells it # `#define mpn_add_n __MPN(add_n)`. The __gmpn_ form is the right thing to # assert on the ARCHIVE, where the preprocessor has already expanded # __MPN() -- and the wrong thing to assert on the header. # # 3. THE ARCHIVE IS CHECKED THROUGH ITS SYMBOL INDEX, NOT ITS WHOLE BODY. # libgmp.a is ~4.8 MB but its ar symbol index -- member "/", the map of # exported symbol to member offset -- is the first ~12 KB. Flattening 16 KB # instead of 4.8 MB gives the same evidence for 1/300th of the target-side # temp space, which matters on a tmpfs root. The index is also the more # precise place to look: it lists what the archive EXPORTS, not merely what # bytes appear somewhere inside it. hdr=/usr/include/gmp-32.h # the real API header wrap=/usr/include/gmp.h # the multilib dispatch wrapper pc=/usr/lib/pkgconfig/gmp.pc lib=/usr/lib/libgmp.a # ---------------------------------------------------------------- payload ---- check gmp/header-present test -f "$hdr" check gmp/header-nonempty test -s "$hdr" check_out gmp/header-guard "#ifndef __GMP_H__" cat "$hdr" # The wrapper, asserted as a dispatch rather than as an API header. check gmp/wrapper-present test -f "$wrap" check_out gmp/wrapper-wordsize "#include " cat "$wrap" check_out gmp/wrapper-dispatch "#include " cat "$wrap" # If the API ever appears in the wrapper the multilib split has changed shape and # every assertion in this file is reading the wrong file -- so this guards the # suite's own premise, not the port. check gmp/wrapper-is-not-api sh -c '! grep -q __gmpz_init /usr/include/gmp.h' # ---------------------------------------------------------------- version ---- # Whitespace between the macro and its value is padded, so these are extracted # and compared rather than matched as a literal substring. check_eq gmp/version-major 6 "$(awk '/^#define __GNU_MP_VERSION[ \t]/{print $3}' "$hdr")" check_eq gmp/version-minor 3 "$(awk '/^#define __GNU_MP_VERSION_MINOR[ \t]/{print $3}' "$hdr")" check_eq gmp/version-patch 0 "$(awk '/^#define __GNU_MP_VERSION_PATCHLEVEL[ \t]/{print $3}' "$hdr")" # The arm witness. GMP_LIMB_BITS is substituted by configure from the target it # actually compiled for, so 32 here is evidence of the wasm32 build specifically # -- this is the one assertion in the file that a native x86_64 GMP fails. check_eq gmp/limb-bits 32 "$(awk '/^#define GMP_LIMB_BITS/{print $3}' "$hdr")" check_eq gmp/nail-bits 0 "$(awk '/^#define GMP_NAIL_BITS/{print $3}' "$hdr")" # ------------------------------------------------------- layer 1: integers ---- # mpz is the layer everything else is reached through: lifecycle, conversion in # and out of strings and C integers, the arithmetic, and the number-theory calls # that are the reason a port depends on GMP at all. for f in init init2 clear set set_ui set_str get_str get_ui get_d \ add sub mul mul_ui tdiv_q tdiv_r mod pow_ui powm \ gcd lcm invert cmp abs neg sqrt \ probab_prime_p nextprime fac_ui bin_ui fib_ui \ sizeinbase import export tstbit setbit; do check_out "gmp/mpz-$f" "#define mpz_$f __gmpz_$f" cat "$hdr" done # ------------------------------------------------------ layer 2: rationals ---- # mpq is a numerator/denominator pair over mpz. canonicalize is the call that # makes it a rational rather than a fraction, so it is asserted by name. for f in init clear set set_ui set_str canonicalize \ add sub mul div cmp neg inv get_str get_d set_num set_den; do check_out "gmp/mpq-$f" "#define mpq_$f __gmpq_$f" cat "$hdr" done # --------------------------------------------------------- layer 3: floats ---- # mpf is arbitrary-PRECISION binary floating point, so the precision controls are # as much a part of its API as the arithmetic. for f in init init2 clear set set_d set_str get_str get_d \ add sub mul div cmp neg abs sqrt pow_ui \ set_default_prec get_default_prec set_prec get_prec; do check_out "gmp/mpf-$f" "#define mpf_$f __gmpf_$f" cat "$hdr" done # The sign tests and the small-operand compare are NOT aliases to compiled # functions -- they are function-like macros that read the struct's _mp_size # field directly, so they need no library symbol at all and would still work # against an empty archive. They are asserted in that form deliberately: it is # the header's STRUCT LAYOUT they exercise, and a suite that expected the # __gmpz_ alias form here would go red on a correct header. mpz_cmp_ui has two # guarded spellings, so only its stable prefix is matched. check_out gmp/macro-mpz-sgn "#define mpz_sgn(Z) ((Z)->_mp_size" cat "$hdr" check_out gmp/macro-mpf-sgn "#define mpf_sgn(F) ((F)->_mp_size" cat "$hdr" check_out gmp/macro-mpq-sgn "#define mpq_sgn(Q) ((Q)->_mp_num._mp_size" cat "$hdr" check_out gmp/macro-mpz-cmp-ui "#define mpz_cmp_ui(Z,UI)" cat "$hdr" # ------------------------------------------------------ layer 4: mpn (raw) ---- # The limb-vector primitives the three typed layers are built from. Note the # __MPN() alias form -- see shape fact 2 at the top of this file. for f in add_n sub_n mul_n mul cmp lshift rshift \ gcd sqrtrem popcount scan0 scan1 zero copyi; do check_out "gmp/mpn-$f" "#define mpn_$f __MPN($f)" cat "$hdr" done # ------------------------------------------------- randomness + allocation ---- # The random-state API is a separate entry point (gmp_ rather than mp*_), and the # custom-allocator hooks are the reason GMP can be embedded in an allocator- # constrained host at all -- which is directly relevant on this target. for f in gmp_randinit_default gmp_randinit_mt gmp_randseed_ui gmp_randclear; do check_out "gmp/rand-${f#gmp_rand}" "#define $f __$f" cat "$hdr" done check_out gmp/mpz-urandomb "#define mpz_urandomb __gmpz_urandomb" cat "$hdr" check_out gmp/mem-set-functions "#define mp_set_memory_functions __gmp_set_memory_functions" cat "$hdr" check_out gmp/mem-get-functions "#define mp_get_memory_functions __gmp_get_memory_functions" cat "$hdr" # The version accessor and the printf family, which are the two things a caller # reaches for without touching a number type. check_out gmp/api-version "#define gmp_version __gmp_version" cat "$hdr" check_out gmp/api-printf "#define gmp_printf __gmp_printf" cat "$hdr" check_out gmp/api-snprintf "#define gmp_snprintf __gmp_snprintf" cat "$hdr" # ------------------------------------------------------------------ types ---- # GMP's public types are one-element ARRAY typedefs -- that is what makes an # mpz_t pass by reference without an explicit &. Asserting the [1] form checks # the calling convention, not just the name. check_out gmp/type-mpz "typedef __mpz_struct mpz_t[1];" cat "$hdr" check_out gmp/type-mpq "typedef __mpq_struct mpq_t[1];" cat "$hdr" check_out gmp/type-mpf "typedef __mpf_struct mpf_t[1];" cat "$hdr" check_out gmp/type-randstate "typedef __gmp_randstate_struct gmp_randstate_t[1];" cat "$hdr" # The pointer aliases the mpn layer and every internal signature are written in. check_out gmp/type-mpz-ptr "typedef __mpz_struct *mpz_ptr;" cat "$hdr" check_out gmp/type-mpz-srcptr "typedef const __mpz_struct *mpz_srcptr;" cat "$hdr" # The scalar types. mp_limb_t and mp_size_t each have several guarded spellings # in the header text, so they are asserted by the typedef that this build's # limb size selects plus the unconditional mp_bitcnt_t. check_out gmp/type-bitcnt "mp_bitcnt_t;" cat "$hdr" check_out gmp/type-limb "mp_limb_t;" cat "$hdr" check_out gmp/type-size "mp_size_t;" cat "$hdr" # ------------------------------------------------------------ pkg-config ---- # The one place on target that states the port's version and its link line, and # what a future recipe's PKG_CHECK_MODULES reads. libdir is asserted substituted # rather than left as an unexpanded placeholder. check gmp/pc-present test -f "$pc" check_out gmp/pc-name "Name: GNU MP" cat "$pc" check_out gmp/pc-version "Version: 6.3.0" cat "$pc" check_out gmp/pc-libs "-lgmp" cat "$pc" check_out gmp/pc-cflags "-I\${includedir}" cat "$pc" # libdir is compared EXACTLY rather than matched as a substring. A substring # check for "libdir=/usr/lib" is also satisfied by "libdir=/usr/lib/", # which is what a multiarch host writes -- measured, not hypothetical: that form # passed this assertion during validation against a native GMP. check_eq gmp/pc-libdir "/usr/lib" "$(awk -F= '/^libdir=/{print $2}' "$pc")" # --------------------------------------------------------------- archive ---- check gmp/lib-present test -f "$lib" check gmp/lib-nonempty test -s "$lib" check_out gmp/lib-archive "!" head -c 8 "$lib" # Flatten only the symbol index -- see shape fact 3 at the top of this file. idx=${TMPDIR:-/tmp}/bst-gmp.$$ head -c 16384 "$lib" | tr -c '\40-\176\n' ' ' > "$idx" 2>/dev/null # One compiled entry point per layer, matched as a whole word so that (for # example) __gmpz_add cannot be satisfied by __gmpz_addmul. A partial compile # that produced the integer layer but not the float layer fails here. for s in __gmpz_init __gmpz_add __gmpz_mul __gmpz_powm __gmpz_gcd \ __gmpz_probab_prime_p __gmpz_nextprime __gmpz_import __gmpz_export \ __gmpq_init __gmpq_canonicalize __gmpq_add __gmpq_div \ __gmpf_init __gmpf_add __gmpf_mul __gmpf_sqrt \ __gmpn_add_n __gmpn_sub_n __gmpn_mul_n __gmpn_popcount \ __gmp_version __gmp_randinit_mt __gmp_printf \ __gmp_set_memory_functions; do check "gmp/sym-$s" grep -qw "$s" "$idx" done rm -f "$idx" # Member NAMES are deliberately not asserted, and the reason is worth recording # because the obvious form of that assertion silently fails on a correct port. # Only names longer than 15 characters live in the archive's string table (member # "//", 666 bytes here); every short name -- init.o, add.o, version.o -- is stored # inline in its own member header, scattered across the whole 4.8 MB body and NOT # in the index window this suite reads. Asserting them against the window would # have reported ~16 reds on a perfectly good archive. They also add nothing the # symbol assertions above do not already prove: the index IS the archive's export # table, so a layer that failed to compile is already caught there. # # The size floor stands in for "this is a real GMP, not a stub". A stub archive # that somehow carried the right index would still be orders of magnitude smaller # than the ~4.8 MB of compiled mpn/mpz/mpq/mpf this port produces. check gmp/lib-substantial sh -c '[ "$(wc -c < /usr/lib/libgmp.a)" -gt 1000000 ]' # ---------------------------------------------------------------- policy ---- # Static-only is distro policy and GMP's autotools build defaults to a shared # library, so a stray libgmp.so here would mean the distro's --disable-shared # override was lost on this recipe. check gmp/no-shared sh -c 'set -- /usr/lib/libgmp.so*; [ ! -e "$1" ]' check gmp/no-libtool sh -c 'set -- /usr/lib/libgmp.la; [ ! -e "$1" ]' # The C++ interface is ABSENT, and this is asserted rather than left unstated # because it is a SILENT absence: the recipe passes --enable-cxx=detect, so a # missing C++ compiler selects "no" instead of failing the build. If a future # toolchain change makes C++ detectable, these two flip and the port gains # gmpxx.h + libgmpxx.a without anyone having asked for them -- which is exactly # the kind of change a suite should surface rather than absorb. check gmp/no-cxx-header sh -c '[ ! -e /usr/include/gmpxx.h ]' check gmp/no-cxx-archive sh -c '[ ! -e /usr/lib/libgmpxx.a ]'