From 89f4c76703ac1b50e8ee9bea6045757187f35e81 Mon Sep 17 00:00:00 2001 From: BharatDBPG Date: Wed, 3 Dec 2025 17:18:56 +0530 Subject: [PATCH v4] libpq: centralize exit() check logic and unify Makefile/Meson behavior Move the libpq exit() reference check into a shared Perl script (libpq-exit-check) and use it for both autoconf/Makefile and Meson builds. This avoids duplicated logic and ensures consistent platform-specific handling of whitelisted symbols. The script now receives the full path to `nm`, detected at the top-level configure using AC_PATH_PROG and propagated via NM_PROG. Both Makefile and Meson invoke the script with --nm=. Platform-specific behavior (Solaris skip, Windows skip, whitelisted symbols, nm availability checks) is centralized inside the script. This makes the two build systems apply the same exit() policy and keeps all rules in one place. --- configure.ac | 2 + meson.build | 1 + src/Makefile.global.in | 2 +- src/interfaces/libpq/Makefile | 9 ++- src/interfaces/libpq/libpq-exit-check | 100 ++++++++++++-------------- src/interfaces/libpq/meson.build | 10 ++- 6 files changed, 62 insertions(+), 62 deletions(-) diff --git a/configure.ac b/configure.ac index c241372..7284f1f 100644 --- a/configure.ac +++ b/configure.ac @@ -1214,6 +1214,8 @@ case $MKDIR_P in *install-sh*) MKDIR_P='\${SHELL} \${top_srcdir}/config/install-sh -c -d';; esac +AC_PATH_PROG(NM, nm) +AC_SUBST(NM) PGAC_PATH_BISON PGAC_PATH_FLEX diff --git a/meson.build b/meson.build index c1e17aa..273cb1f 100644 --- a/meson.build +++ b/meson.build @@ -350,6 +350,7 @@ missing = find_program('config/missing', native: true) cp = find_program('cp', required: false, native: true) xmllint_bin = find_program(get_option('XMLLINT'), native: true, required: false) xsltproc_bin = find_program(get_option('XSLTPROC'), native: true, required: false) +nm = find_program('nm', required: false, native: true) bison_flags = [] if bison.found() diff --git a/src/Makefile.global.in b/src/Makefile.global.in index 0aa389b..9d12cdb 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -285,7 +285,7 @@ LLVM_CFLAGS = @LLVM_CFLAGS@ LLVM_CXXFLAGS = @LLVM_CXXFLAGS@ # Kind-of compilers - +NM = @NM@ BISON = @BISON@ BISONFLAGS = @BISONFLAGS@ $(YFLAGS) FLEX = @FLEX@ diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile index 305361f..6e1fb30 100644 --- a/src/interfaces/libpq/Makefile +++ b/src/interfaces/libpq/Makefile @@ -130,12 +130,15 @@ $(stlib): override OBJS += $(OBJS_STATIC) $(stlib): $(OBJS_STATIC) # Check for functions that libpq must not call, currently just exit(). +# (Ideally we'd reject abort() too, but there are various scenarios where +# build toolchains insert abort() calls, e.g. to implement assert().) +# Skip the test when profiling, as gcc may insert exit() calls for that. libpq-refs-stamp: $(shlib) ifneq ($(enable_coverage), yes) - # See libpq-exit-check for full platform rules and whitelisting. - $(PERL) libpq-exit-check --input_file $< +# See libpq-exit-check for full platform rules and whitelisting. + $(PERL) libpq-exit-check --input_file $< --nm='$(NM)' endif - touch $@ + touch $@ # Make dependencies on pg_config_paths.h visible in all builds. fe-connect.o: fe-connect.c $(top_builddir)/src/port/pg_config_paths.h diff --git a/src/interfaces/libpq/libpq-exit-check b/src/interfaces/libpq/libpq-exit-check index f500cef..bfac4ca 100755 --- a/src/interfaces/libpq/libpq-exit-check +++ b/src/interfaces/libpq/libpq-exit-check @@ -6,93 +6,83 @@ use warnings FATAL => 'all'; use Getopt::Long; use Config; -# -# Platform & tool notes: -# # - Purpose: ensure libpq never references exit(), because client libraries # must not terminate the host application. -# -# - Whitelisted injected symbols: -# __cxa_atexit - injected by some libcs (e.g., OpenBSD) -# __tsan_func_exit - ThreadSanitizer instrumentation -# pthread_exit - legitimate thread cleanup -# -# - Solaris: libpq infrastructure may be statically linked -> skip check. -# -# - nm availability: -# If nm does not exist or cannot be executed, skip the scan. -# This matches PostgreSQL behavior in Makefile builds. -# # - Makefile and Meson both rely on this script for full logic. -# +my $nm_path; my $input_file; my $stamp_file; my @problematic_lines; Getopt::Long::GetOptions( - 'input_file:s' => \$input_file, - 'stamp_file:s' => \$stamp_file) or die "$0: wrong arguments\n"; + 'nm:s' => \$nm_path, + 'input_file:s' => \$input_file, + 'stamp_file:s' => \$stamp_file) or die "$0: wrong arguments\n"; die "$0: --input_file must be specified\n" unless defined $input_file; +die "$0: --nm must be specified\n" unless defined $nm_path and -x $nm_path; -# ---- Skip entirely on Solaris ---- -if ($Config{osname} =~ /solaris/i) { - exit 0; +# ---- Skip Windows ---- +if ($Config{osname} =~ /MSWin32|cygwin|msys/i) +{ + exit 0; } -# ---- Check if 'nm' exists on the system ---- -my $nm_path = `which nm 2>/dev/null`; -chomp($nm_path); - -if (!$nm_path || ! -x $nm_path) { - # nm not available → skip check gracefully - exit 0; +# ---- Skip entirely on Solaris ---- +if ($Config{osname} =~ /solaris/i) +{ + exit 0; } # ---- Run nm to scan undefined symbols ---- open my $fh, '-|', "$nm_path -A -u $input_file 2>/dev/null" - or exit 0; # If nm fails at runtime, skip also + or exit 0; # If nm fails at runtime, skip also while (<$fh>) { - # Allowed symbols - next if /__cxa_atexit/; - next if /__tsan_func_exit/; - next if /pthread_exit/; - - # Anything containing "exit" is suspicious - if (/exit/) - { - push @problematic_lines, $_; - } + # Whitelisted injected symbols: + # __cxa_atexit - injected by some libcs (e.g., OpenBSD) + # __tsan_func_exit - ThreadSanitizer instrumentation + # pthread_exit - legitimate thread cleanup + + next if /__cxa_atexit/; + next if /__tsan_func_exit/; + next if /pthread_exit/; + + # Anything containing "exit" is suspicious + if (/exit/) + { + push @problematic_lines, $_; + } } +close $fh; if (@problematic_lines) { - print "libpq must not be calling any function which invokes exit\n"; - print "Problematic symbol references:\n"; - print @problematic_lines; + print "libpq must not be calling any function which invokes exit\n"; + print "Problematic symbol references:\n"; + print @problematic_lines; - exit 1; + exit 1; } else { - # Meson optional stamp file - if ($stamp_file) - { - create_stamp_file(); - } + # Meson optional stamp file + if ($stamp_file) + { + create_stamp_file(); + } - exit 0; + exit 0; } sub create_stamp_file { - if (!(-f $stamp_file)) - { - open my $fh, '>', $stamp_file - or die "can't open $stamp_file: $!"; - close $fh; - } + if (!(-f $stamp_file)) + { + open my $fh, '>', $stamp_file + or die "can't open $stamp_file: $!"; + close $fh; + } } diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build index 1b32eed..6f97f49 100644 --- a/src/interfaces/libpq/meson.build +++ b/src/interfaces/libpq/meson.build @@ -85,9 +85,12 @@ libpq = declare_dependency( include_directories: [include_directories('.')] ) -# Run the unified exit() check script for libpq. +# Check for functions that libpq must not call, currently just exit(). +# (Ideally we'd reject abort() too, but there are various scenarios where +# build toolchains insert abort() calls, e.g. to implement assert().) +# Skip the test when profiling, as gcc may insert exit() calls for that. # All platform-specific rules are implemented inside libpq-exit-check. -if find_program('nm', required: false, native: true).found() and not get_option('b_coverage') +if nm.found() and not get_option('b_coverage') custom_target( 'libpq-exit-check', input: libpq_so, @@ -96,7 +99,8 @@ if find_program('nm', required: false, native: true).found() and not get_option( perl, files('libpq-exit-check'), '--input_file', '@INPUT@', - '--stamp_file', '@OUTPUT@' + '--stamp_file', '@OUTPUT@', + '--nm', nm.full_path() ], build_by_default: true, ) -- 2.43.0