From ad685afc57f271d4d78b588f073e0cbbda32d295 Mon Sep 17 00:00:00 2001 From: Mohamed Date: Thu, 3 Sep 2026 15:07:37 +0200 Subject: [PATCH] build: build the vendored Capstone from the autotools build Callgrind's cycle estimation links a static Capstone, which until now had to be built by hand before Valgrind: configure hard-failed unless it was given --with-capstone=PATH or CAPSTONE_DIR. That put the same cmake invocation in a wrapper script and in a CI action, where the two copies of the required compiler flags could drift apart, and every new consumer had to repeat it. Compile the submodule as an automake convenience library instead, so a plain `./autogen.sh && ./configure && make && make install` is the whole build. --with-capstone (or CAPSTONE_DIR) stays as an override for a prebuilt decoder, which the Debian packaging still wires through, but nothing needs it any more: the Nix dev shell no longer sets it either. The submodule sources are compiled through one-line stub units in third_party/capstone-stubs/ rather than where they live. Automake is configured with subdir-objects, so it writes an object next to its source, and Valgrind only builds in-tree: compiling them in place would drop objects, .deps directories and .dirstamp files into the capstone checkout and leave the submodule permanently reported as dirty. The stubs keep every build artefact in this repository, where .gitignore covers it, instead of asking git to look away from a submodule that is genuinely being written to. The CodSpeed logic lives in files of its own (m4/codspeed_capstone.m4 and third_party/Makefile.am), leaving three one-line touch points in upstream files -- the macro call, an AC_CONFIG_FILES entry and a SUBDIRS entry -- in place of the 28-line block this removes from configure.ac, so rebasing onto upstream conflicts on single lines. Two things the wrapper script was hiding, now handled by the build system: - The tool CFLAGS pass -fno-builtin, so GCC no longer folds Capstone's fprintf(stderr, "...") guard into fwrite. The tool shimmed only fwrite and thus only linked by accident of that optimisation; shim fprintf and strcat explicitly, and compile Capstone -DNDEBUG as its own Release build does so assert() does not pull in __assert_fail. - CFLAGS=-std=gnu17 was passed by the script alone, so builds through debian/rules or nix never got it. AC_PROG_CC picks gnu23 on GCC 15+, under which glibc 2.42+ defines strchr/strrchr/strstr as _Generic macros that clash with Callgrind's own definitions. Pin it in configure instead. Co-Authored-By: Claude Opus 5 (1M context) build(callgrind): relink the tool when Capstone is rebuilt CAPSTONE_LIBS is named in callgrind's _LDADD but not in its _DEPENDENCIES, and automake cannot tell that a configure substitution expands to a file, so it derived no dependency of its own either. Rebuilding the decoder therefore left callgrind- linked against the previous archive, silently, until something else forced a relink. Add it to the _DEPENDENCIES line the fork already sets for the primary platform. The secondary platform has no Capstone and needs nothing. Co-Authored-By: Claude Opus 5 (1M context) build: compile the vendored Capstone without stub sources Review feedback on #40. The submodule sources are compiled by explicit rules that write objects and dependency files under third_party/capstone-obj/ in the build directory, so the committed stub translation units are gone and the submodule checkout is left untouched by a build. - autogen.sh initialises the submodule only when CAPSTONE_DIR is unset and the checkout is empty, so a build against a prebuilt Capstone makes no network access and prints no warning. - flake.nix no longer ships Capstone; the vendored build covers the dev shell. - New CI job covering --with-capstone, and a check that the vendored build leaves the submodule clean. The job builds Capstone with cmake rather than installing libcapstone-dev: a distribution build is compiled with stack-protector (unresolved __stack_chk_guard) and with every architecture enabled (unresolved strtol from the other instruction printers), so it cannot be linked into a tool. - configure accepts a multiarch libdir for --with-capstone. Co-Authored-By: Claude Opus 5 (1M context) build: pin the C dialect on the Callgrind tool only -std=gnu17 was appended to CFLAGS, so it reached the test programs too and changed the debug info gcc 11 emits for them, which made the inline-crossfile callgrind test fail on ubuntu-22.04. The pin exists for the strchr/strrchr/strstr definitions cycledecode_capstone.c makes under C23, so configure exports it as CODSPEED_C_STD and only the primary Callgrind tool is built with it. Co-Authored-By: Claude Opus 5 (1M context) build: trim the comments in the Capstone build files Co-Authored-By: Claude Opus 5 (1M context) ci: restrict the CI workflow token to contents: read The workflow relied on the default token permissions, which CodeQL flags. Co-Authored-By: Claude Opus 5 (1M context) --- .github/actions/build-capstone/action.yml | 25 ------ .github/workflows/ci.yml | 67 ++++++++++++++- .github/workflows/codspeed.yml | 5 -- .github/workflows/release.yml | 8 +- .gitignore | 8 +- Makefile.am | 1 + autogen.sh | 5 ++ callgrind/Makefile.am | 4 +- callgrind/cycledecode_capstone.c | 20 ++++- configure.ac | 36 ++------- debian/rules | 5 +- flake.nix | 34 +------- m4/codspeed_capstone.m4 | 72 +++++++++++++++++ third_party/Makefile.am | 99 +++++++++++++++++++++++ 14 files changed, 278 insertions(+), 111 deletions(-) delete mode 100644 .github/actions/build-capstone/action.yml create mode 100644 m4/codspeed_capstone.m4 create mode 100644 third_party/Makefile.am diff --git a/.github/actions/build-capstone/action.yml b/.github/actions/build-capstone/action.yml deleted file mode 100644 index ef837a772..000000000 --- a/.github/actions/build-capstone/action.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Build Capstone (cycle-estimation decoder) -description: >- - Build the static Capstone that Callgrind cycle estimation links against, and - export its install prefix as CAPSTONE_DIR for later steps. - -runs: - using: composite - steps: - - shell: bash - run: | - CS_SRC="$GITHUB_WORKSPACE/third_party/capstone" - CS_PREFIX="$GITHUB_WORKSPACE/.capstone" - cmake -S "$CS_SRC" -B "$CS_SRC/build" \ - -DCMAKE_BUILD_TYPE=Release \ - -DCAPSTONE_ARCHITECTURE_DEFAULT=OFF \ - -DCAPSTONE_X86_SUPPORT=ON \ - -DCAPSTONE_ARM64_SUPPORT=ON \ - -DCAPSTONE_BUILD_SHARED_LIBS=OFF \ - -DCAPSTONE_BUILD_CSTOOL=OFF \ - -DCMAKE_INSTALL_LIBDIR=lib \ - -DCMAKE_INSTALL_PREFIX="$CS_PREFIX" \ - -DCMAKE_C_FLAGS="-fno-stack-protector -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -fPIC" - cmake --build "$CS_SRC/build" -j"$(nproc)" - cmake --install "$CS_SRC/build" - echo "CAPSTONE_DIR=$CS_PREFIX" >> "$GITHUB_ENV" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56254b955..a1ca43ae4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,9 @@ on: branches: [ master ] workflow_dispatch: +permissions: + contents: read + jobs: test-callgrind: strategy: @@ -44,7 +47,6 @@ jobs: build-essential \ automake \ autoconf \ - cmake \ libc6-dev \ gdb \ docbook \ @@ -52,9 +54,6 @@ jobs: docbook-xml \ xsltproc - - name: Build Capstone (cycle-estimation decoder) - uses: ./.github/actions/build-capstone - - name: Run autogen run: ./autogen.sh @@ -67,6 +66,10 @@ jobs: - name: Build Valgrind run: make -j$(nproc) + # The vendored build must not write anything into the submodule checkout. + - name: Check the Capstone submodule is clean + run: test -z "$(git -C third_party/capstone status --porcelain --untracked-files=all)" + - name: Build test dependencies run: | make -C tests arch_test os_test true @@ -84,3 +87,59 @@ jobs: with: name: callgrind-test-logs-${{ matrix.runner.ubuntu-version }} path: callgrind/tests/*.log + + # `make` builds the vendored submodule; this guards the other supported path, + # --with-capstone=PATH, which debian/rules forwards CAPSTONE_DIR to. + build-with-prebuilt-capstone: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Update apt-get cache + run: sudo apt-get update + + - name: Install build dependencies + run: | + sudo apt-get install -y \ + build-essential \ + automake \ + autoconf \ + cmake \ + libc6-dev + + # Not the distribution's libcapstone-dev: a Valgrind tool links + # -nodefaultlibs and runs without glibc's %fs TLS, so the decoder has to + # be built without stack-protector and without fortify, which no distro + # package is. + - name: Build Capstone outside the source tree + run: | + cmake -S third_party/capstone -B "$RUNNER_TEMP/capstone-build" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCAPSTONE_ARCHITECTURE_DEFAULT=OFF \ + -DCAPSTONE_X86_SUPPORT=ON \ + -DCAPSTONE_ARM64_SUPPORT=ON \ + -DCAPSTONE_BUILD_SHARED_LIBS=OFF \ + -DCAPSTONE_BUILD_CSTOOL=OFF \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_INSTALL_PREFIX="$RUNNER_TEMP/capstone" \ + -DCMAKE_C_FLAGS="-fno-stack-protector -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -fPIC" + cmake --build "$RUNNER_TEMP/capstone-build" -j"$(nproc)" + cmake --install "$RUNNER_TEMP/capstone-build" + + - name: Run autogen + run: ./autogen.sh + + - name: Configure + run: ./configure --enable-only64bit --with-capstone="$RUNNER_TEMP/capstone" + + - name: Build Valgrind + run: make -j$(nproc) + + - name: Check cycle estimation + run: | + ./vg-in-place --tool=callgrind --cycle-estimation=yes \ + --callgrind-out-file=/dev/null /bin/true 2>&1 | grep -q 'Events : Ir Ct Cl' + diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index fc41edf9d..4a0b5628a 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -72,17 +72,12 @@ jobs: build-essential \ automake \ autoconf \ - cmake \ gdb \ docbook \ docbook-xsl \ docbook-xml \ xsltproc - - name: Build Capstone (cycle-estimation decoder) - if: steps.valgrind-cache.outputs.cache-hit != 'true' && matrix.valgrind == 'local' - uses: ./.github/actions/build-capstone - - name: Build Valgrind (${{ matrix.valgrind }}) if: steps.valgrind-cache.outputs.cache-hit != 'true' run: just build ${{ matrix.valgrind }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b3850a7da..5f639d782 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,20 +36,16 @@ jobs: - name: Install packaging deps run: sudo apt-get install -y build-essential devscripts debhelper dh-make - name: Install build deps - run: sudo apt-get install -y debhelper-compat gdb mpi-default-dev pkgconf cmake docbook docbook-xsl docbook-xml xsltproc + run: sudo apt-get install -y debhelper-compat gdb mpi-default-dev pkgconf docbook docbook-xsl docbook-xml xsltproc - name: Configure GPG Key run: echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --import env: GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} - # debian/rules forwards $CAPSTONE_DIR to configure via --with-capstone. - - name: Build Capstone (cycle-estimation decoder) - uses: ./.github/actions/build-capstone - - name: Build the deb package id: build_deb run: | - debuild -e CAPSTONE_DIR --no-tgz-check -nc + debuild --no-tgz-check -nc echo "asset-path=$(find .. -name 'valgrind_*.deb')" >> "$GITHUB_OUTPUT" env: DEBEMAIL: ${{ vars.MAINTAINER_EMAIL }} diff --git a/.gitignore b/.gitignore index 5c527eaee..4446456c3 100644 --- a/.gitignore +++ b/.gitignore @@ -2567,7 +2567,13 @@ test-suite.log # autoconf backup /configure~ -# Capstone build install prefix (built from the third_party/capstone submodule) +# /third_party/ (vendored Capstone, built by third_party/Makefile.am) +/third_party/Makefile +/third_party/Makefile.in +/third_party/libcapstone.a +/third_party/capstone-obj/ + +# install prefix of a Capstone built by hand for --with-capstone /.capstone # fake CodSpeed benchmark fixture binary (compiled from testdata/llsc_tzconvert_bench.c) diff --git a/Makefile.am b/Makefile.am index 6c5b9f5b6..176317a5d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,6 +22,7 @@ EXP_TOOLS = \ # Put docs last because building the HTML is slow and we want to get # everything else working before we try it. SUBDIRS = \ + third_party \ include \ VEX \ coregrind \ diff --git a/autogen.sh b/autogen.sh index 5de086b1a..b350bc9c9 100755 --- a/autogen.sh +++ b/autogen.sh @@ -20,6 +20,11 @@ run autoconf if git rev-parse --is-inside-work-tree > /dev/null 2>&1 ; then echo "running: git configuration" git config blame.ignoreRevsFile .git-blame-ignore-revs + # CodSpeed: check out the Capstone decoder that Callgrind's cycle estimation + # links against, unless a prebuilt one was provided or it is already there. + if test -z "$CAPSTONE_DIR" && test ! -f third_party/capstone/cs.c ; then + run git submodule update --init third_party/capstone + fi else echo "skipping: git configuration" fi diff --git a/callgrind/Makefile.am b/callgrind/Makefile.am index 1e5bcb97d..de92e16b8 100644 --- a/callgrind/Makefile.am +++ b/callgrind/Makefile.am @@ -65,9 +65,9 @@ callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CPPFLAGS = \ $(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS = $(LTO_CFLAGS) \ $(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) $(CALLGRIND_CFLAGS_COMMON) \ - @CAPSTONE_CFLAGS@ + @CAPSTONE_CFLAGS@ @CODSPEED_C_STD@ callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_DEPENDENCIES = \ - $(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_PRI_CAPS@) + $(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_PRI_CAPS@) @CAPSTONE_LIBS@ callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDADD = \ $(TOOL_LDADD_@VGCONF_PLATFORM_PRI_CAPS@) @CAPSTONE_LIBS@ callgrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS = \ diff --git a/callgrind/cycledecode_capstone.c b/callgrind/cycledecode_capstone.c index 31033bf73..6472cc485 100644 --- a/callgrind/cycledecode_capstone.c +++ b/callgrind/cycledecode_capstone.c @@ -55,6 +55,7 @@ extern char* vgPlain_strncpy(char* d, const char* s, unsigned long n); extern char* vgPlain_strchr(const char* s, char c); extern char* vgPlain_strrchr(const char* s, char c); extern char* vgPlain_strstr(const char* h, const char* n); +extern char* vgPlain_strcat(char* d, const char* s); static const char* const CLG_CD_CC = "clg.cycledecode"; @@ -91,10 +92,11 @@ int printf(const char* fmt, ...) } int puts(const char* s) { return printf("%s\n", s); } -/* Capstone's SStream references stderr/fwrite on a buffer-overflow guard in the - * op_str text path (which this code never reads). Valgrind has no FILE* layer, - * so stderr is a sentinel and fwrite routes the bytes to the Valgrind log fd, - * making such an overflow visible rather than swallowed. */ +/* Capstone's SStream buffer-overflow guard calls fprintf(stderr, ...), which + * GCC folds into fwrite only when builtins are enabled -- and Valgrind's tool + * CFLAGS pass -fno-builtin. Both are defined, so the tool links against a + * Capstone compiled either way. Valgrind has no FILE* layer, so stderr is a + * sentinel and the bytes are routed to the Valgrind log fd. */ extern int vgPlain_write(int fd, const void* buf, int count); FILE* stderr = 0; size_t fwrite(const void* p, size_t size, size_t nmemb, FILE* f) @@ -103,6 +105,15 @@ size_t fwrite(const void* p, size_t size, size_t nmemb, FILE* f) vgPlain_write(2, p, (int)(size * nmemb)); return nmemb; } +int fprintf(FILE* f, const char* fmt, ...) +{ + (void)f; + va_list ap; + va_start(ap, fmt); + unsigned int r = vgPlain_vprintf(fmt, ap); + va_end(ap); + return (int)r; +} size_t strlen(const char* s) { return vgPlain_strlen(s); } int strcmp(const char* a, const char* b) { return vgPlain_strcmp(a, b); } @@ -118,6 +129,7 @@ char* strncpy(char* d, const char* s, size_t n) char* strchr(const char* s, int c) { return vgPlain_strchr(s, (char)c); } char* strrchr(const char* s, int c) { return vgPlain_strrchr(s, (char)c); } char* strstr(const char* h, const char* n) { return vgPlain_strstr(h, n); } +char* strcat(char* d, const char* s) { return vgPlain_strcat(d, s); } /*------------------------------------------------------------*/ /*--- Capstone handle: open / decode -*/ diff --git a/configure.ac b/configure.ac index 4275bb3c5..cb61db938 100644 --- a/configure.ac +++ b/configure.ac @@ -63,6 +63,9 @@ AC_PROG_CC AS_IF([test "x$ac_cv_prog_cc_c11" = "xno"], [AC_MSG_ERROR([Valgrind relies on a C compiler supporting C11])]) +# CodSpeed: C dialect for the Callgrind tool. See m4/codspeed_capstone.m4. +CODSPEED_C_STD_GNU17 + AC_PROG_CPP AC_PROG_CXX AC_PROG_RANLIB @@ -1122,36 +1125,8 @@ AC_ARG_WITH(tmpdir, AC_DEFINE_UNQUOTED(VG_TMPDIR, "$tmpdir", [Temporary files directory]) AC_SUBST(VG_TMPDIR, [$tmpdir]) -#---------------------------------------------------------------------------- -# CodSpeed: mandatory Capstone for Callgrind per-instruction cycle estimation -#---------------------------------------------------------------------------- -# Callgrind's primary (native) tool is always built with the Capstone decoder -# and a generated cost table (x86 or arm64, selected at compile time), enabling -# --cycle-estimation=yes. The decoder location comes from --with-capstone=PATH -# or, when omitted, the CAPSTONE_DIR environment variable (`nix develop` sets -# it). A build without Capstone is not supported and fails here. -AC_ARG_WITH(capstone, - [ --with-capstone=PATH Path to the Capstone decoder install used for - Callgrind cycle estimation (Cy/Cl). Defaults to the - CAPSTONE_DIR environment variable (amd64/arm64)], - [capstone_dir="$withval"], - [capstone_dir="$CAPSTONE_DIR"]) - -if test -z "$capstone_dir"; then - AC_MSG_ERROR([Capstone is required for Callgrind cycle estimation. Pass --with-capstone=PATH or set CAPSTONE_DIR (`nix develop` provides it).]) -fi -if test ! -f "$capstone_dir/lib/libcapstone.a" \ - -o ! -f "$capstone_dir/include/capstone/capstone.h"; then - AC_MSG_ERROR([--with-capstone=$capstone_dir: libcapstone.a or capstone.h not found]) -fi -# Fortify off: the tool links -nodefaultlibs, so glibc's __*_chk fortify -# wrappers are unavailable, and our libc shims (sprintf/snprintf/...) must -# be real definitions, not fortify macro-expansions. -CAPSTONE_CFLAGS="-DCLG_WITH_CAPSTONE -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -I$capstone_dir/include" -CAPSTONE_LIBS="$capstone_dir/lib/libcapstone.a" -AC_MSG_NOTICE([Callgrind cycle estimation enabled with Capstone at $capstone_dir]) -AC_SUBST(CAPSTONE_CFLAGS) -AC_SUBST(CAPSTONE_LIBS) +# CodSpeed: Capstone decoder for Callgrind cycle estimation. See m4/codspeed_capstone.m4. +CODSPEED_CAPSTONE #---------------------------------------------------------------------------- # Detect xcode path @@ -5790,6 +5765,7 @@ CFLAGS=$safe_CFLAGS AC_CONFIG_FILES([ Makefile VEX/Makefile:Makefile.vex.in + third_party/Makefile valgrind.spec valgrind.pc glibc-2.X.supp diff --git a/debian/rules b/debian/rules index c7caefdfe..07fc9a2c8 100644 --- a/debian/rules +++ b/debian/rules @@ -28,8 +28,9 @@ CONFARGS = \ --enable-only64bit \ --with-gdbscripts-dir=/usr/share/gdb/auto-load -# Callgrind cycle estimation requires Capstone; the release workflow builds a -# static decoder and exports its prefix as CAPSTONE_DIR. +# Callgrind cycle estimation needs Capstone. The build compiles the vendored +# third_party/capstone submodule by default; CAPSTONE_DIR, when set, points at a +# prebuilt decoder to use instead. ifneq ($(CAPSTONE_DIR),) CONFARGS += --with-capstone=$(CAPSTONE_DIR) endif diff --git a/flake.nix b/flake.nix index 277c0c31d..553d4d345 100644 --- a/flake.nix +++ b/flake.nix @@ -12,39 +12,14 @@ system: let pkgs = import nixpkgs { inherit system; }; - - # Capstone for the Callgrind cycle-estimation decoder, built the same way - # as the CI workflows (see .github/workflows): x86 + arm64 only, static. - # - # Valgrind tools run without glibc's %fs TLS and link -nodefaultlibs, so - # Capstone must be built without stack-protector (its %fs:0x28 canary - # read faults at runtime) and without fortify (pulls __*_chk libc - # symbols); hardeningDisable drops both. Limiting the architectures also - # drops the non-x86/arm64 instruction printers that reference libc - # symbols (e.g. XCore's strtol) the tool does not shim. - capstone = pkgs.capstone.overrideAttrs (old: { - cmakeFlags = (old.cmakeFlags or [ ]) ++ [ - "-DCAPSTONE_ARCHITECTURE_DEFAULT=OFF" - "-DCAPSTONE_X86_SUPPORT=ON" - "-DCAPSTONE_ARM64_SUPPORT=ON" - ]; - hardeningDisable = (old.hardeningDisable or [ ]) ++ [ - "stackprotector" - "fortify" - "fortify3" - ]; - }); in { - # Expose the pinned Capstone so the autotools build and scripts can find - # it via `nix build .#capstone` or the CAPSTONE_DIR env var below. - packages.capstone = capstone; - devShells.default = pkgs.mkShell { # Valgrind tool objects link -nodefaultlibs and run without glibc's %fs # TLS, so the toolchain must not inject stack-protector or fortify # (__*_chk) into them. The compiler wrapper otherwise re-adds these - # over our -fno-stack-protector / -D_FORTIFY_SOURCE=0 flags. + # over our -fno-stack-protector / -D_FORTIFY_SOURCE=0 flags. This + # covers the vendored Capstone too, which is compiled into the tool. hardeningDisable = [ "stackprotector" "fortify" @@ -52,7 +27,6 @@ ]; packages = [ - capstone pkgs.python3 pkgs.uv pkgs.autoconf @@ -62,10 +36,6 @@ pkgs.gcc pkgs.pkg-config ]; - - # Consumed by configure (--with-capstone), the LUT generator, and the - # standalone cycledecode test. Point them at the hardening-free build. - CAPSTONE_DIR = "${capstone}"; }; } ); diff --git a/m4/codspeed_capstone.m4 b/m4/codspeed_capstone.m4 new file mode 100644 index 000000000..398ed85aa --- /dev/null +++ b/m4/codspeed_capstone.m4 @@ -0,0 +1,72 @@ +# codspeed_capstone.m4 -- CodSpeed additions to Valgrind's configure. + +# CODSPEED_CAPSTONE +# ----------------- +# Export CAPSTONE_CFLAGS / CAPSTONE_LIBS for callgrind/Makefile.am. The vendored +# third_party/capstone submodule is compiled by third_party/Makefile.am; +# --with-capstone=PATH (or CAPSTONE_DIR) selects a prebuilt install instead. +AC_DEFUN([CODSPEED_CAPSTONE], [ +AC_ARG_WITH([capstone], + [AS_HELP_STRING([--with-capstone=PATH], + [use a prebuilt Capstone install for Callgrind cycle estimation instead + of the vendored third_party/capstone submodule. Defaults to the + CAPSTONE_DIR environment variable])], + [capstone_dir="$withval"], + [capstone_dir="$CAPSTONE_DIR"]) + +AM_CONDITIONAL([BUILD_VENDORED_CAPSTONE], [test -z "$capstone_dir"]) + +if test -z "$capstone_dir"; then + # libcapstone.a is not tested for: it does not exist yet at configure time. + if test ! -f "$srcdir/third_party/capstone/cs.c"; then + AC_MSG_ERROR([third_party/capstone is empty. Run: + git submodule update --init third_party/capstone +or pass --with-capstone=PATH to use a prebuilt Capstone.]) + fi + CAPSTONE_INCLUDES='-I$(top_srcdir)/third_party/capstone/include' + CAPSTONE_LIBS='$(top_builddir)/third_party/libcapstone.a' + AC_MSG_NOTICE([Callgrind cycle estimation enabled with the vendored Capstone]) +else + capstone_lib= + for d in lib lib64 "lib/$host_cpu-linux-gnu"; do + if test -f "$capstone_dir/$d/libcapstone.a"; then + capstone_lib="$capstone_dir/$d/libcapstone.a" + break + fi + done + if test -z "$capstone_lib" -o ! -f "$capstone_dir/include/capstone/capstone.h"; then + AC_MSG_ERROR([--with-capstone=$capstone_dir: libcapstone.a or capstone.h not found]) + fi + CAPSTONE_INCLUDES="-I$capstone_dir/include" + CAPSTONE_LIBS="$capstone_lib" + AC_MSG_NOTICE([Callgrind cycle estimation enabled with Capstone at $capstone_dir]) +fi + +# Fortify off: the tool links -nodefaultlibs, so glibc's __*_chk wrappers are +# unavailable and our libc shims must be definitions, not macro-expansions. +CAPSTONE_CFLAGS="-DCLG_WITH_CAPSTONE -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 $CAPSTONE_INCLUDES" +AC_SUBST([CAPSTONE_CFLAGS]) +AC_SUBST([CAPSTONE_LIBS]) +]) + +# CODSPEED_C_STD_GNU17 +# -------------------- +# Export CODSPEED_C_STD, the dialect the Callgrind tool is compiled with. Under +# C23 -- what AC_PROG_CC picks on GCC 15+ -- glibc 2.42+ defines strchr/strrchr/ +# strstr as _Generic macros, which clash with the definitions in +# cycledecode_capstone.c. It stays out of CFLAGS so the rest of the tree, the +# test programs included, keeps the compiler's own default. +# +# Must be called after AC_PROG_CC. +AC_DEFUN([CODSPEED_C_STD_GNU17], [ +AC_MSG_CHECKING([whether $CC accepts -std=gnu17]) +codspeed_save_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -std=gnu17" +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])], + [AC_MSG_RESULT([yes]) + CODSPEED_C_STD="-std=gnu17"], + [AC_MSG_RESULT([no]) + CODSPEED_C_STD=]) +CFLAGS="$codspeed_save_CFLAGS" +AC_SUBST([CODSPEED_C_STD]) +]) diff --git a/third_party/Makefile.am b/third_party/Makefile.am new file mode 100644 index 000000000..4c75e5511 --- /dev/null +++ b/third_party/Makefile.am @@ -0,0 +1,99 @@ +include $(top_srcdir)/Makefile.all.am + +# CodSpeed: the vendored Capstone decoder for Callgrind cycle estimation. +# Skipped when configure was given a prebuilt one (--with-capstone=PATH). +if BUILD_VENDORED_CAPSTONE + +noinst_LIBRARIES = libcapstone.a + +# Only x86 and arm64: the other targets' instruction printers reference libc +# symbols a Valgrind tool does not shim, so linking them breaks the tool. +capstone_sources = \ + cs.c \ + MCInst.c \ + MCInstrDesc.c \ + MCRegisterInfo.c \ + Mapping.c \ + SStream.c \ + utils.c \ + arch/X86/X86ATTInstPrinter.c \ + arch/X86/X86Disassembler.c \ + arch/X86/X86DisassemblerDecoder.c \ + arch/X86/X86InstPrinterCommon.c \ + arch/X86/X86IntelInstPrinter.c \ + arch/X86/X86Mapping.c \ + arch/X86/X86Module.c \ + arch/AArch64/AArch64BaseInfo.c \ + arch/AArch64/AArch64Disassembler.c \ + arch/AArch64/AArch64InstPrinter.c \ + arch/AArch64/AArch64Mapping.c \ + arch/AArch64/AArch64Module.c + +# Compiled by the rules below rather than through _SOURCES: automake writes +# every object, .deps and .dirstamp next to its source, i.e. into the submodule +# checkout. These mirror the source tree under capstone-obj/ in the build dir. +capstone_objects = $(capstone_sources:%.c=capstone-obj/%.$(OBJEXT)) + +libcapstone_a_SOURCES = +libcapstone_a_LIBADD = $(capstone_objects) + +# The preprocessor state of a CMake build with -DCAPSTONE_ARCHITECTURE_DEFAULT=OFF +# -DCAPSTONE_X86_SUPPORT=ON -DCAPSTONE_ARM64_SUPPORT=ON. +capstone_cppflags = \ + -I$(srcdir)/capstone/include \ + -DCAPSTONE_X86_SUPPORT \ + -DCAPSTONE_HAS_X86 \ + -DCAPSTONE_ARM64_SUPPORT \ + -DCAPSTONE_HAS_ARM64 \ + -DCAPSTONE_USE_SYS_DYN_MEM + +# Capstone goes into the primary Callgrind tool, so it gets that platform's +# flags: AM_CFLAGS_BASE carries -fno-stack-protector (a canary read faults in a +# tool, which runs without glibc's TLS) and the right word size. -DNDEBUG is +# load bearing: assert() would pull in __assert_fail. +capstone_cflags = \ + $(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) \ + -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \ + -DNDEBUG \ + -w + +capstone-obj/%.$(OBJEXT): $(srcdir)/capstone/%.c + $(AM_V_at)$(MKDIR_P) $(@D) + $(AM_V_CC)$(CC) $(capstone_cppflags) $(capstone_cflags) \ + -MMD -MP -MF $(@:.$(OBJEXT)=.d) -c -o $@ $< + +-include $(capstone_objects:.$(OBJEXT)=.d) + +clean-local: + -rm -rf capstone-obj + +endif + +# `make dist` tarballs are unpacked outside a git checkout, so the sources above +# and everything they include are shipped explicitly. Only the compiled-in +# architectures, which keeps this at ~18M of the submodule's 76M. +EXTRA_DIST = \ + capstone/cs.c \ + capstone/MCInst.c \ + capstone/MCInstrDesc.c \ + capstone/MCRegisterInfo.c \ + capstone/Mapping.c \ + capstone/SStream.c \ + capstone/utils.c \ + capstone/include \ + capstone/arch/X86 \ + capstone/arch/AArch64 \ + capstone/LEB128.h \ + capstone/MCDisassembler.h \ + capstone/MCFixedLenDisassembler.h \ + capstone/MCInst.h \ + capstone/MCInstrDesc.h \ + capstone/MCRegisterInfo.h \ + capstone/Mapping.h \ + capstone/MathExtras.h \ + capstone/SStream.h \ + capstone/cs_priv.h \ + capstone/cs_simple_types.h \ + capstone/utils.h \ + capstone/LICENSE.TXT \ + capstone/LICENSE_LLVM.TXT