. "$BST_LIB" # libatomic-ops (libatomic_ops 7.8.4) -- portable atomic memory-update # primitives: compare-and-swap, fetch-and-add, test-and-set and ordered # load/store, each in four memory orders, over ~30 architecture backends. # # THE ONE FACT THAT SHAPES THIS WHOLE FILE: on this arm the library is # essentially HEADER-ONLY. atomic_ops.h walks a preprocessor ladder over the # compiler and architecture macros and includes exactly one sysdeps backend; # every operation it selects is an inline function or a macro. So the API does # not live in libatomic_ops.a -- the archive holds only AO_pause and the # out-of-line lock-based emulation helpers, 15 KB in total. That is the OPPOSITE # of lzo and gmp, where the archive IS the library and its symbol index is the # main evidence. Here the headers are the deliverable and the symbol index is a # small corroborating check, so the weight of this suite is deliberately on the # header tree, the selected backend, and the pkg-config contract a future recipe # actually consumes. # # WHICH BACKEND THIS ARM SELECTS was measured with the real cross compiler # (wasmcc-glibc -E -H) rather than assumed, and the answer is the good one: # # atomic_ops.h -> atomic_ops/sysdeps/gcc/generic.h # -> atomic_ops/sysdeps/test_and_set_t_is_char.h # -> atomic_ops/sysdeps/gcc/generic-small.h # -> atomic_ops/sysdeps/gcc/generic-arithm.h # atomic_ops/generalize.h # -> atomic_ops/generalize-small.h # -> atomic_ops/generalize-arithm.h # # gcc/generic.h is clang's __atomic builtin backend. wasm32 matches no # architecture arm in the ladder, so it lands on the final `# else /* etc. */` # catch-all -- and that catch-all is the BUILTIN path, not the pthread-mutex # one. AO_USE_PTHREAD_DEFS is NOT defined, generic_pthread.h is NOT included, # and the full 334-macro AO_HAVE_* capability set is available. The pessimistic # assumption for an exotic target -- that libatomic_ops degrades to a global # mutex and stops being an atomics library -- is simply false here, and several # assertions below exist to keep it that way rather than to restate it. # # WHAT IS NOT AVAILABLE, asserted as an absence so it is not rediscovered: there # is no double-width CAS. No AO_double_t is ever defined and no AO_HAVE_*double* # macro exists, so standard_ao_double_t.h is shipped but never included. A # consumer that wants a lock-free AO_stack must therefore ask # AO_stack_is_lock_free() at run time rather than assume it. That finding rests # on the header measurement below and NOT on the archive's exported symbols -- # the "almost lock-free" aux helpers are compiled unconditionally and are present # in a native x86_64 build that does have double-CAS, which a first draft of this # file got wrong and the native control run caught. # # NO EXECUTABLE COVERAGE IS POSSIBLE, and this is stated rather than papered # over. libatomic-ops ships no CLI; nothing on this image links it (its only # consumer in the layers is bdwgc, which is not ported); and there is no # on-target C compiler to build a probe against the archive. Worse than gmp's # position, even a hypothetical consumer would not execute archive code for the # common operations, because those are inline. So the functional register this # suite CAN reach is the integration contract: pkgconf actually resolving our # .pc the way a future recipe's PKG_CHECK_MODULES will. That is a real program # parsing and interpolating our real artifact, and it is the strongest execution # available -- not a substitute for running atomic code, and not claimed as one. # # PROCESS ECONOMY, per 96-mtd-utils-jffs2's recorded lesson: each header is read # into a shell variable ONCE and matched with `case`, so ~90 assertions cost # about a dozen forks rather than ninety. # Every path this file touches is derived from these two, so a host-side dry run # against the real payload only has to rewrite two lines -- which is what makes # the dry run able to exercise the size and policy checks too, rather than # leaving them to be validated for the first time on target. inc=/usr/include libdir=/usr/lib aoi=$inc/atomic_ops pc=$libdir/pkgconfig/atomic_ops.pc lib=$libdir/libatomic_ops.a gpl=$libdir/libatomic_ops_gpl.a NL=' ' has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "want '$2'" ;; esac; } hasnt() { case "$3" in *"$2"*) bst_notok "$1" "unexpectedly present: '$2'" ;; *) bst_ok "$1" ;; esac; } # ok iff begins a line of -- a DECLARATION, not a mention # inside a comment or an argument list. decl() { case "$3" in *"$NL$2"*) bst_ok "$1" ;; *) bst_notok "$1" "no line-start declaration of '$2'" ;; esac; } # --------------------------------------------------------------- payload --- # The five shipped pieces. Everything below reads one of them, so a failure here # explains every later red in this file at a glance. check libatomic-ops/inc-dir-present test -d "$aoi" check libatomic-ops/pc-present test -f "$pc" check libatomic-ops/lib-present test -f "$lib" check libatomic-ops/gpl-present test -f "$gpl" # The three PUBLIC headers sit at the TOP LEVEL of /usr/include, not inside the # atomic_ops/ subdirectory. That is the opposite of lzo, whose thirteen headers # are all under /usr/include/lzo/, and it is why this .pc says # `Cflags: -I${includedir}` with no subdirectory suffix. A consumer writes # #include , so a future recipe change that moved these under # atomic_ops/ would break every consumer while leaving the header count intact. check libatomic-ops/hdr-top-atomic-ops test -s "$inc/atomic_ops.h" check libatomic-ops/hdr-top-atomic-ops-malloc test -s "$inc/atomic_ops_malloc.h" check libatomic-ops/hdr-top-atomic-ops-stack test -s "$inc/atomic_ops_stack.h" # ...and are NOT duplicated into the subdirectory. check libatomic-ops/hdr-not-under-subdir test ! -e "$aoi/atomic_ops.h" # ------------------------------------------------------ header inventory --- # 68 headers ship: 3 public ones above plus 65 under atomic_ops/. The counts are # asserted per directory rather than only in total, so a backend family that # failed to install is localised instead of showing up as one wrong number. # These are the shipped tree, NOT the subset this arm selects -- libatomic_ops # installs every architecture's backend unconditionally, which is what makes the # -dev package 668 KB for a 15 KB archive. check_eq libatomic-ops/hdr-count-top 3 "$(ls "$inc"/atomic_ops*.h 2>/dev/null | wc -l | tr -d ' ')" check_eq libatomic-ops/hdr-count-atomicops 65 "$(find "$aoi" -name '*.h' 2>/dev/null | wc -l | tr -d ' ')" check_eq libatomic-ops/hdr-count-all 68 "$(( $(ls "$inc"/atomic_ops*.h 2>/dev/null | wc -l) + $(find "$aoi" -name '*.h' 2>/dev/null | wc -l) ))" check_eq libatomic-ops/hdr-count-gcc 21 "$(ls "$aoi/sysdeps/gcc/"*.h 2>/dev/null | wc -l | tr -d ' ')" check_eq libatomic-ops/hdr-count-loadstore 15 "$(ls "$aoi/sysdeps/loadstore/"*.h 2>/dev/null | wc -l | tr -d ' ')" check_eq libatomic-ops/hdr-count-sysdeps 13 "$(ls "$aoi/sysdeps/"*.h 2>/dev/null | wc -l | tr -d ' ')" check_eq libatomic-ops/hdr-count-generalize 4 "$(ls "$aoi/"*.h 2>/dev/null | wc -l | tr -d ' ')" # The eight headers the preprocessor ACTUALLY pulls in on this arm, named # individually because they are the ones that have to be right for a consumer to # compile at all. Measured with wasmcc-glibc -E -H; see the file header. for h in ao_version.h generalize.h generalize-small.h generalize-arithm.h; do check "libatomic-ops/hdr-$h" test -s "$aoi/$h" done check libatomic-ops/hdr-gcc-generic test -s "$aoi/sysdeps/gcc/generic.h" check libatomic-ops/hdr-gcc-generic-small test -s "$aoi/sysdeps/gcc/generic-small.h" check libatomic-ops/hdr-gcc-generic-arith test -s "$aoi/sysdeps/gcc/generic-arithm.h" check libatomic-ops/hdr-ts-t-is-char test -s "$aoi/sysdeps/test_and_set_t_is_char.h" # A representative slice of the backends this arm does NOT select. They ship # because the -dev package is the whole portability corpus; asserting a few # keeps a truncated install from passing on the strength of the count alone. for h in gcc/x86.h gcc/aarch64.h gcc/arm.h gcc/riscv.h gcc/powerpc.h \ msftc/x86_64.h sunc/sparc.h icc/ia64.h armcc/arm_v6.h ibmc/powerpc.h; do check "libatomic-ops/hdr-other-$(echo "$h" | tr / -)" test -s "$aoi/sysdeps/$h" done # ---------------------------------------------------------------- version --- # ao_version.h is the only place the version is stated in the headers, and the # .pc is the only place it is stated on target outside them. Both are asserted # and then compared to each other, so a partial upgrade cannot leave them # disagreeing silently. AV=$(cat "$aoi/ao_version.h" 2>/dev/null) check_eq libatomic-ops/version-major 7 "$(awk '/^#define AO_VERSION_MAJOR[ \t]/{print $3}' "$aoi/ao_version.h")" check_eq libatomic-ops/version-minor 8 "$(awk '/^#define AO_VERSION_MINOR[ \t]/{print $3}' "$aoi/ao_version.h")" check_eq libatomic-ops/version-micro 4 "$(awk '/^#define AO_VERSION_MICRO[ \t]/{print $3}' "$aoi/ao_version.h")" # ao_version.h refuses to be included on its own -- the guard is part of the # API contract, not decoration, because the version macros are only meaningful # after atomic_ops.h has set up the rest. has libatomic-ops/version-guard "# error This file should not be included directly." "$AV" # ------------------------------------------------- the selected backend --- # The headline fact, asserted from the ladder itself. atomic_ops.h ends its # __GNUC__ chain with a catch-all that selects the BUILTIN backend for any # architecture it does not recognise. wasm32 is such an architecture, so this # line is the reason the port is useful rather than a mutex-shaped stub. AH=$(cat "$inc/atomic_ops.h" 2>/dev/null) has libatomic-ops/ladder-gnuc-guard "#if defined(__GNUC__) && !defined(AO_USE_PTHREAD_DEFS)" "$AH" has libatomic-ops/ladder-catchall "# else /* etc. */" "$AH" has libatomic-ops/ladder-catchall-inc '# include "atomic_ops/sysdeps/gcc/generic.h"' "$AH" # The pthread fallback ships and is reachable ONLY by defining # AO_USE_PTHREAD_DEFS. Asserting both halves -- that the header is there and # that atomic_ops.h gates it behind that macro -- is what makes "we did not take # it" a checkable statement rather than an inference. check libatomic-ops/pthread-fallback-ships test -s "$aoi/sysdeps/generic_pthread.h" has libatomic-ops/pthread-fallback-gated '# include "atomic_ops/sysdeps/generic_pthread.h"' "$AH" PT=$(cat "$aoi/sysdeps/generic_pthread.h" 2>/dev/null) decl libatomic-ops/pthread-fallback-lock "AO_API pthread_mutex_t AO_pt_lock;" "$PT" # gcc/generic.h is clang's __atomic builtin backend, and these four assertions # are what distinguish it from an inline-asm backend: a signal fence for the # compiler barrier, thread fences for the memory orders, __atomic_test_and_set # for AO_TS, and __atomic_load_n for the ordered loads. If a future toolchain # lost the builtins, the ladder would still reach this header and the port would # still "build" -- these are the cases that would go red instead. GG=$(cat "$aoi/sysdeps/gcc/generic.h" 2>/dev/null) has libatomic-ops/gcc-generic-ts-macro "#define AO_GCC_ATOMIC_TEST_AND_SET" "$GG" has libatomic-ops/gcc-generic-barrier "__atomic_signal_fence(__ATOMIC_SEQ_CST)" "$GG" has libatomic-ops/gcc-generic-fence-acq "__atomic_thread_fence(__ATOMIC_ACQUIRE)" "$GG" has libatomic-ops/gcc-generic-fence-rel "__atomic_thread_fence(__ATOMIC_RELEASE)" "$GG" # NOTE the SPACE after '#'. Inside the #ifndef AO_PREFER_GENERALIZED block # upstream indents its directives as "# define", not "#define" -- the intuitive # spelling is a red on a correct header, and this is where the host dry run # caught it before the port shipped. has libatomic-ops/gcc-generic-clear "# define AO_CLEAR(addr) __atomic_clear(addr, __ATOMIC_RELEASE)" "$GG" has libatomic-ops/gcc-generic-ts-is-char '#include "../test_and_set_t_is_char.h"' "$GG" # All four memory orders of test_and_set, each an AO_INLINE wrapper over the # builtin. This is the operation the whole AO_TS vocabulary above exists for, so # the set is asserted whole rather than sampled. for o in RELAXED ACQUIRE RELEASE SEQ_CST; do has "libatomic-ops/gcc-generic-tas-$o" "__atomic_test_and_set(addr, __ATOMIC_$o)" "$GG" done for m in test_and_set test_and_set_acquire test_and_set_release test_and_set_full CLEAR; do has "libatomic-ops/gcc-generic-have-$m" "# define AO_HAVE_$m" "$GG" done # The CAS capability macros generic.h derives from the compiler's own # __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n. These are the gate on AO_compare_and_swap # existing at all, so they are named rather than left implicit. has libatomic-ops/gcc-generic-cas-char "# define AO_GCC_HAVE_char_SYNC_CAS" "$GG" has libatomic-ops/gcc-generic-cas-short "# define AO_GCC_HAVE_short_SYNC_CAS" "$GG" has libatomic-ops/gcc-generic-cas-int "# define AO_GCC_HAVE_int_SYNC_CAS" "$GG" # No wasm-specific backend exists upstream. Asserted as an absence so that the # day one appears -- which would silently change which code this port compiles # -- the suite says so instead of absorbing it. check libatomic-ops/no-wasm-sysdeps test ! -e "$aoi/sysdeps/gcc/wasm.h" check libatomic-ops/no-wasm32-sysdeps test ! -e "$aoi/sysdeps/gcc/wasm32.h" hasnt libatomic-ops/ladder-no-wasm-arm "__wasm" "$AH" # ------------------------------------------------------------------ types --- # THE VOCABULARY IS #defines, NOT typedefs -- the same trap lzo's suite went red # on before it shipped, avoided here by reading the header first. AO_t is a # macro for size_t and AO_TS_t a macro for unsigned char (from # test_and_set_t_is_char.h, which the selected backend includes). Asserting # either in the intuitive `typedef ... AO_t;` form is a red on a correct header. decl libatomic-ops/type-ao-t "#define AO_t size_t" "$AH" has libatomic-ops/type-ts-init "#define AO_TS_INITIALIZER ((AO_TS_t)AO_TS_CLEAR)" "$AH" hasnt libatomic-ops/type-ao-t-not-typedef "typedef size_t AO_t;" "$AH" TC=$(cat "$aoi/sysdeps/test_and_set_t_is_char.h" 2>/dev/null) has libatomic-ops/type-ts-t "#define AO_TS_t unsigned char" "$TC" has libatomic-ops/type-ts-val "#define AO_TS_VAL_t AO_BYTE_TS_val" "$TC" has libatomic-ops/type-ts-set "#define AO_TS_SET AO_BYTE_TS_set" "$TC" has libatomic-ops/type-char-ts "#define AO_CHAR_TS_T" "$TC" # The compiler-version helper macros every backend arm keys off. AO_CLANG_PREREQ # is the one that matters here: this toolchain is clang, and the ladder's # builtin arms are gated on it. has libatomic-ops/macro-gnuc-prereq "# define AO_GNUC_PREREQ(major, minor)" "$AH" has libatomic-ops/macro-clang-prereq "# define AO_CLANG_PREREQ(major, minor)" "$AH" has libatomic-ops/macro-header-guard "#define AO_ATOMIC_OPS_H" "$AH" # ------------------------------------------------- no double-width CAS --- # standard_ao_double_t.h SHIPS but is not reachable on this arm, because no # backend here defines the double-width type. Both halves are asserted: the file # is present (so its absence is not what a future red would mean) and # atomic_ops.h only reaches it from the emul_cas / AO_CAN_EMUL_CAS path this arm # does not take. check libatomic-ops/double-hdr-ships test -s "$aoi/sysdeps/standard_ao_double_t.h" check libatomic-ops/emul-cas-ships test -s "$aoi/sysdeps/emul_cas.h" has libatomic-ops/emul-cas-gated "AO_CAN_EMUL_CAS" "$AH" # --------------------------------------------------------- stack/malloc --- # The two GPL extras' public API. AO_stack_is_lock_free is the runtime query # that answers the double-CAS question above, and it is declared AO_API rather # than inline precisely because the answer is a property of the build. # # THE AO_API DECORATION IS 7.8-AND-LATER. In 7.6.10 these are declared bare # (`void AO_stack_push_release(...)`) and AO_stack_is_lock_free does not exist at # all, so these five cases are version assertions as much as API ones -- which is # exactly what the 7.6.10 native control run reds them as, and why they are # written in the decorated form rather than a version-agnostic one. ST=$(cat "$inc/atomic_ops_stack.h" 2>/dev/null) decl libatomic-ops/stack-push "AO_API void AO_stack_push_release(AO_stack_t *list, AO_t *new_element);" "$ST" decl libatomic-ops/stack-pop "AO_API AO_t *AO_stack_pop_acquire(AO_stack_t *list);" "$ST" decl libatomic-ops/stack-init "AO_API void AO_stack_init(AO_stack_t *list);" "$ST" decl libatomic-ops/stack-lockfree "AO_API int AO_stack_is_lock_free(void);" "$ST" has libatomic-ops/stack-initializer "#define AO_STACK_INITIALIZER" "$ST" has libatomic-ops/stack-alias-push "#define AO_stack_push(l, e) AO_stack_push_release(l, e)" "$ST" has libatomic-ops/stack-alias-pop "#define AO_stack_pop(l) AO_stack_pop_acquire(l)" "$ST" MA=$(cat "$inc/atomic_ops_malloc.h" 2>/dev/null) has libatomic-ops/malloc-decl "AO_malloc(size_t" "$MA" has libatomic-ops/malloc-free-decl "AO_free(" "$MA" has libatomic-ops/malloc-enable-mmap "AO_malloc_enable_mmap(" "$MA" # --------------------------------------------- pkg-config: the contract --- # The file first, then the tool actually resolving it. Read directly, libdir and # includedir are compared EXACTLY rather than as substrings: "libdir=/usr/lib" # is also a substring of the "/usr/lib/" a multiarch host writes, so the # substring form passes against a library in the wrong place. (gmp's native # control run is where that lesson was paid for.) PC=$(cat "$pc" 2>/dev/null) has libatomic-ops/pc-name "Name: The atomic_ops library" "$PC" has libatomic-ops/pc-version "Version: 7.8.4" "$PC" has libatomic-ops/pc-desc "Description: Atomic memory update operations portable implementation" "$PC" # Cflags carries NO subdirectory -- see the top-level-headers note above. has libatomic-ops/pc-cflags "Cflags: -I\${includedir}" "$PC" hasnt libatomic-ops/pc-cflags-no-subdir "includedir}/atomic_ops" "$PC" # Libs names ONLY the MIT archive. libatomic_ops_gpl.a is deliberately not here: # linking it changes a consumer's licence obligations, so upstream makes that an # explicit -latomic_ops_gpl the consumer must ask for. See the licence section. has libatomic-ops/pc-libs "-latomic_ops" "$PC" hasnt libatomic-ops/pc-libs-no-gpl "-latomic_ops_gpl" "$PC" check_eq libatomic-ops/pc-prefix "/usr" "$(awk -F= '/^prefix=/{print $2}' "$pc")" check_eq libatomic-ops/pc-libdir "/usr/lib" "$(awk -F= '/^libdir=/{print $2}' "$pc")" check_eq libatomic-ops/pc-includedir "/usr/include" "$(awk -F= '/^includedir=/{print $2}' "$pc")" # Now the tool. This is the closest thing to execution this port allows: pkgconf # parsing, interpolating and answering against our real artifact, which is # exactly what a future recipe's PKG_CHECK_MODULES does. # # THE EMPTY --cflags IS THE CORRECT ANSWER, not a lookup failure: pkgconf is # built with --with-system-includedir=/usr/include and --with-system-libdir= # /usr/lib, so it filters both out. 90-pkgconf.test pins that behaviour against # a hermetic fixture; here it is pinned against a REAL shipped .pc, and the # --keep-system-* pair is asserted alongside so that "empty" is distinguishable # from "found nothing". if command -v pkgconf >/dev/null 2>&1; then check libatomic-ops/pkgconf-exists pkgconf --exists atomic_ops check_eq libatomic-ops/pkgconf-modversion "7.8.4" "$(pkgconf --modversion atomic_ops 2>&1)" check_eq libatomic-ops/pkgconf-path "$pc" "$(pkgconf --path atomic_ops 2>&1)" check_eq libatomic-ops/pkgconf-var-libdir "/usr/lib" "$(pkgconf --variable=libdir atomic_ops 2>&1)" check_eq libatomic-ops/pkgconf-cflags-filtered "" "$(pkgconf --cflags atomic_ops 2>&1)" check_eq libatomic-ops/pkgconf-cflags-kept "-I/usr/include" "$(pkgconf --keep-system-cflags --cflags atomic_ops 2>&1)" check_eq libatomic-ops/pkgconf-libs "-latomic_ops" "$(pkgconf --libs atomic_ops 2>&1)" check_eq libatomic-ops/pkgconf-libs-kept "-L/usr/lib -latomic_ops" "$(pkgconf --keep-system-libs --libs atomic_ops 2>&1)" has libatomic-ops/pkgconf-listed "atomic_ops" "$(pkgconf --list-package-names 2>&1)" check libatomic-ops/pkgconf-validate pkgconf --validate atomic_ops check libatomic-ops/pkgconf-atleast pkgconf --atleast-version=7.8 atomic_ops check_rc libatomic-ops/pkgconf-max-no 1 pkgconf --max-version=7.0 atomic_ops else bst_skip libatomic-ops/pkgconf-exists "pkgconf not installed -- cannot resolve the .pc" fi # --------------------------------------------------------------- archives --- check_out libatomic-ops/lib-archive "!" head -c 8 "$lib" check_out libatomic-ops/gpl-archive "!" head -c 8 "$gpl" # Size WINDOWS, not floors. Unlike lzo (1 MB) and gmp (4.8 MB), these archives # are small BY DESIGN -- the API is inline, so a LARGE libatomic_ops.a would # mean the build fell back to out-of-line emulation, which is as much a defect # as a truncated one. The bounds bracket the measured 15104 / 25706 bytes with # an order of magnitude of headroom either way, so a compiler change that alters # code size does not red them but a change of KIND does. win() { s=$(wc -c < "$2" 2>/dev/null | tr -d ' ') if [ -n "$s" ] && [ "$s" -gt "$3" ] && [ "$s" -lt "$4" ]; then bst_ok "$1" else bst_notok "$1" "size ${s:-absent} outside [$3,$4]"; fi } win libatomic-ops/lib-size-window "$lib" 4000 200000 win libatomic-ops/gpl-size-window "$gpl" 8000 400000 # THE SYMBOL PARTITION IS THE LICENCE BOUNDARY: the MIT archive exports the five # core out-of-line entry points, the GPL archive the stack and malloc API, and # neither exports the other's. That partition is the only thing on target that # would show a build which merged the two, or a packaging change that shipped one # archive under both names. # # READ EXACTLY THE SYMBOL INDEX, NOT A FIXED BYTE WINDOW. The index is ar member # "/" -- the archive's own table of DEFINED symbols -- and its size is in its # member header, so it can be extracted precisely. A 16 KB window (what lzo's # suite reads) is fine for PRESENCE but unsound for ABSENCE, because it also # sweeps up UNDEFINED references sitting in the object bodies: the 7.6.10 native # control has "AO_pause" as an undefined reference inside its GPL archive, which # reds a window-based "not in gpl" assertion on an archive that does not export # it at all. Measured, not theorised -- that control run is what caught it. arindex() { hdr=$(dd if="$1" bs=1 skip=8 count=60 2>/dev/null | tr -c '\40-\176' ' ') case "$(printf '%s' "$hdr" | cut -c1-16 | sed 's/ *$//')" in /) ;; *) return 1 ;; esac sz=$(printf '%s' "$hdr" | cut -c49-58 | tr -d ' ') [ -n "$sz" ] || return 1 dd if="$1" bs=1 skip=68 count="$sz" 2>/dev/null | tr -c '\40-\176\n' ' ' } idx=${TMPDIR:-/tmp}/bst-ao-i.$$ gdx=${TMPDIR:-/tmp}/bst-ao-g.$$ arindex "$lib" > "$idx" 2>/dev/null arindex "$gpl" > "$gdx" 2>/dev/null check libatomic-ops/ar-core-index-read test -s "$idx" check libatomic-ops/ar-gpl-index-read test -s "$gdx" for s in AO_pause AO_pt_lock AO_store_full_emulation \ AO_fetch_compare_and_swap_emulation \ AO_compare_double_and_swap_double_emulation; do check "libatomic-ops/sym-core-$s" grep -qw "$s" "$idx" check "libatomic-ops/sym-core-not-in-gpl-$s" sh -c "! grep -qw '$s' '$gdx'" done for s in AO_stack_init AO_stack_push_release AO_stack_pop_acquire \ AO_stack_is_lock_free AO_malloc AO_free AO_malloc_enable_mmap; do check "libatomic-ops/sym-gpl-$s" grep -qw "$s" "$gdx" check "libatomic-ops/sym-gpl-not-in-core-$s" sh -c "! grep -qw '$s' '$idx'" done # The "almost lock-free" stack helpers. These are compiled UNCONDITIONALLY -- # they are present in the 7.6.10 x86_64 control too, which does have a # double-width CAS -- so they are a completeness check on atomic_ops_stack.o and # NOT, as a first draft of this file claimed, evidence for the no-double-CAS # finding. That finding rests on the header measurement above, where it belongs. for s in AO_stack_push_explicit_aux_release AO_stack_pop_explicit_aux_acquire; do check "libatomic-ops/sym-aux-$s" grep -qw "$s" "$gdx" done rm -f "$idx" "$gdx" # ------------------------------------------------------- the arm witness --- # The archive members are LLVM IR bitcode (BC C0 DE), not native objects and not # finished wasm modules -- this toolchain defers codegen to link time. It is the # one property here that a native libatomic-ops fails, so it is what establishes # that the shipped archive is wasm32 build output rather than a host artifact. # # GETTING TO THE FIRST REAL MEMBER NEEDS A WALKER, NOT ARITHMETIC. lzo's suite # could assume the first member after the symbol index is a real object because # all 67 of its member names are short. That assumption is WRONG here and was # caught by measurement before shipping: libatomic_ops_gpl.a's member # atomic_ops_stack.o is 17 characters, so ar inserts a "//" long-name string # table between the index and the first object, and the naive offset lands in # the middle of that table reading "atom" (61746f6d) as the magic. So step the # members, skipping the index "/" and the string table "//" -- but NOT a member # whose name is "/", which is a real object whose long name lives in # that table. armagic() { f=$1; off=8; n=0 while [ $n -lt 6 ]; do hdr=$(dd if="$f" bs=1 skip="$off" count=60 2>/dev/null | tr -c '\40-\176' ' ') nm=$(printf '%s' "$hdr" | cut -c1-16 | sed 's/ *$//') sz=$(printf '%s' "$hdr" | cut -c49-58 | tr -d ' ') [ -n "$sz" ] || { echo "BADHDR"; return 1; } case "$nm" in /|//) ;; *) od -An -tx1 -j $((off + 60)) -N 4 "$f" 2>/dev/null | tr -d ' \n'; return 0 ;; esac off=$((off + 60 + sz)); [ $((off % 2)) -eq 1 ] && off=$((off + 1)) n=$((n + 1)) done echo "NOMEMBER"; return 1 } for pair in "core:$lib" "gpl:$gpl"; do tag=${pair%%:*}; f=${pair#*:} m=$(armagic "$f") check_eq "libatomic-ops/ar-$tag-bitcode" 4243c0de "$m" # The two negatives make the positive above a statement about the arm rather # than a magic number nobody can interpret. if [ "$m" = "7f454c46" ]; then bst_notok "libatomic-ops/ar-$tag-not-elf" "members are ELF -- this is a NATIVE build, not wasm32" else bst_ok "libatomic-ops/ar-$tag-not-elf"; fi if [ "$m" = "0061736d" ]; then bst_notok "libatomic-ops/ar-$tag-not-wasm-module" "members are finished wasm modules, not bitcode" else bst_ok "libatomic-ops/ar-$tag-not-wasm-module"; fi done # ----------------------------------------------------------------- policy --- # Static-only is distro policy; libatomic_ops' autotools build would otherwise # produce both archives as shared objects. # $2 is deliberately UNQUOTED inside `set --` so the glob expands; with no match # it stays literal and `[ -e ]` is false, which is the answer we want. absent() { id=$1; set -- $2; if [ -e "$1" ]; then bst_notok "$id" "unexpected $1"; else bst_ok "$id"; fi; } absent libatomic-ops/no-shared-core "$libdir/libatomic_ops.so*" absent libatomic-ops/no-shared-gpl "$libdir/libatomic_ops_gpl.so*" absent libatomic-ops/no-libtool "$libdir/libatomic_ops.la" absent libatomic-ops/no-libtool-gpl "$libdir/libatomic_ops_gpl.la" # The docs package is built but deliberately not installed, in line with every # other library port on this image. check libatomic-ops/no-docs test ! -d /usr/share/doc/libatomic_ops # libatomic-ops ships no command line at all -- there is no CLI to find, which # is asserted so a future reader does not go looking for one. check libatomic-ops/no-cli sh -c '! command -v atomic_ops >/dev/null 2>&1'