mirror of
https://github.com/systemd/systemd
synced 2026-03-30 11:44:49 +02:00
Compare commits
7 Commits
6077791b3a
...
c6c43d677a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6c43d677a | ||
|
|
6f1f3a3917 | ||
|
|
3f0fc93219 | ||
|
|
4186aad374 | ||
|
|
8c3acba63b | ||
|
|
8c019224a1 | ||
|
|
7e8fe8e29f |
17
meson.build
17
meson.build
@ -15,7 +15,7 @@ project('systemd', 'c',
|
||||
|
||||
add_test_setup(
|
||||
'default',
|
||||
exclude_suites : ['clang-tidy', 'integration-tests'],
|
||||
exclude_suites : ['clang-tidy', 'unused-symbols', 'integration-tests'],
|
||||
is_default : true,
|
||||
)
|
||||
|
||||
@ -2227,7 +2227,7 @@ libudev = shared_library(
|
||||
implicit_include_directories : false,
|
||||
link_args : ['-shared',
|
||||
'-Wl,--version-script=' + libudev_sym_path],
|
||||
link_with : [libsystemd_static, libshared_static],
|
||||
link_with : [libsystemd_static],
|
||||
link_whole : libudev_basic,
|
||||
dependencies : [threads,
|
||||
userspace],
|
||||
@ -3000,6 +3000,19 @@ if meson.version().version_compare('>=1.4.0')
|
||||
endforeach
|
||||
endif
|
||||
|
||||
symbol_analysis_exes = []
|
||||
foreach name, exe : executables_by_name
|
||||
symbol_analysis_exes += exe
|
||||
endforeach
|
||||
|
||||
find_unused_library_symbols = find_program('tools/find-unused-library-symbols.py')
|
||||
test(
|
||||
'libshared-unused-symbols',
|
||||
find_unused_library_symbols,
|
||||
suite : 'unused-symbols',
|
||||
args : [libshared, libcore] + nss_targets + pam_targets + symbol_analysis_exes,
|
||||
)
|
||||
|
||||
run_target(
|
||||
'check-api-docs',
|
||||
depends : [man, libsystemd, libudev],
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "shared-forward.h"
|
||||
#include "basic-forward.h"
|
||||
|
||||
int encode_devnode_name(const char *str, char *str_enc, size_t len);
|
||||
int allow_listed_char_for_devnode(char c, const char *additional);
|
||||
@ -23,6 +23,7 @@ basic_sources = files(
|
||||
'compress.c',
|
||||
'conf-files.c',
|
||||
'confidential-virt.c',
|
||||
'device-nodes.c',
|
||||
'devnum-util.c',
|
||||
'dirent-util.c',
|
||||
'dlfcn-util.c',
|
||||
|
||||
@ -1233,7 +1233,7 @@ static int measured_crypt_activate_by_passphrase(
|
||||
if (keyslot < 0)
|
||||
return keyslot;
|
||||
|
||||
return measured_crypt_activate_by_volume_key(cd, mechanism, name, keyslot, vk, vks, flags);
|
||||
return measured_crypt_activate_by_volume_key(cd, name, mechanism, keyslot, vk, vks, flags);
|
||||
|
||||
shortcut:
|
||||
keyslot = crypt_activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags);
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "io-util.h"
|
||||
#include "udev-util.h"
|
||||
|
||||
/**
|
||||
* SECTION:libudev-queue
|
||||
@ -64,6 +63,11 @@ static struct udev_queue* udev_queue_free(struct udev_queue *udev_queue) {
|
||||
return mfree(udev_queue);
|
||||
}
|
||||
|
||||
static int udev_queue_is_empty(void) {
|
||||
return access("/run/udev/queue", F_OK) < 0 ?
|
||||
(errno == ENOENT ? true : -errno) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* udev_queue_ref:
|
||||
* @udev_queue: udev queue context
|
||||
|
||||
@ -57,7 +57,6 @@ shared_sources = files(
|
||||
'daemon-util.c',
|
||||
'data-fd-util.c',
|
||||
'dev-setup.c',
|
||||
'device-nodes.c',
|
||||
'discover-image.c',
|
||||
'dissect-image.c',
|
||||
'dm-util.c',
|
||||
|
||||
@ -3,15 +3,26 @@
|
||||
executables += [
|
||||
generator_template + {
|
||||
'name' : 'systemd-ssh-generator',
|
||||
'sources' : files('ssh-generator.c'),
|
||||
'sources' : files(
|
||||
'ssh-generator.c',
|
||||
'ssh-util.c',
|
||||
),
|
||||
'extract' : files(
|
||||
'ssh-util.c',
|
||||
),
|
||||
},
|
||||
libexec_template + {
|
||||
'name' : 'systemd-ssh-proxy',
|
||||
'sources' : files('ssh-proxy.c'),
|
||||
'sources' : files(
|
||||
'ssh-proxy.c',
|
||||
),
|
||||
},
|
||||
libexec_template + {
|
||||
'name' : 'systemd-ssh-issue',
|
||||
'sources' : files('ssh-issue.c'),
|
||||
'sources' : files(
|
||||
'ssh-issue.c',
|
||||
),
|
||||
'objects' : ['systemd-ssh-generator'],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include "socket-netlink.h"
|
||||
#include "socket-util.h"
|
||||
#include "special.h"
|
||||
#include "ssh-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "virt.h"
|
||||
@ -212,29 +213,15 @@ static int add_vsock_socket(
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
||||
if (vsock_fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno)) {
|
||||
log_debug("Not creating AF_VSOCK ssh listener, since AF_VSOCK is not available.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Unable to test if AF_VSOCK is available: %m");
|
||||
}
|
||||
|
||||
vsock_fd = safe_close(vsock_fd);
|
||||
r = vsock_open_or_warn(/* ret= */ NULL);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
/* Determine the local CID so that we can log it to help users to connect to this VM */
|
||||
unsigned local_cid;
|
||||
r = vsock_get_local_cid(&local_cid);
|
||||
if (r < 0) {
|
||||
if (ERRNO_IS_DEVICE_ABSENT(r)) {
|
||||
log_debug("Not creating AF_VSOCK ssh listener, since /dev/vsock is not available (even though AF_VSOCK is).");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m");
|
||||
}
|
||||
r = vsock_get_local_cid_or_warn(&local_cid);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = make_sshd_template_unit(
|
||||
dest,
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
#include "mkdir.h"
|
||||
#include "parse-argument.h"
|
||||
#include "pretty-print.h"
|
||||
#include "socket-util.h"
|
||||
#include "ssh-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "virt.h"
|
||||
@ -135,33 +135,11 @@ static int acquire_cid(unsigned *ret_cid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
||||
if (vsock_fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno)) {
|
||||
log_debug("Not creating issue file, since AF_VSOCK is not available.");
|
||||
*ret_cid = 0;
|
||||
return 0;
|
||||
}
|
||||
r = vsock_open_or_warn(/* ret= */ NULL);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return log_error_errno(errno, "Unable to test if AF_VSOCK is available: %m");
|
||||
}
|
||||
|
||||
vsock_fd = safe_close(vsock_fd);
|
||||
|
||||
unsigned local_cid;
|
||||
r = vsock_get_local_cid(&local_cid);
|
||||
if (r < 0) {
|
||||
if (ERRNO_IS_DEVICE_ABSENT(r)) {
|
||||
log_debug("Not creating issue file, since /dev/vsock is not available (even though AF_VSOCK is).");
|
||||
*ret_cid = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m");
|
||||
}
|
||||
|
||||
*ret_cid = local_cid;
|
||||
return 1;
|
||||
return vsock_get_local_cid_or_warn(ret_cid);
|
||||
}
|
||||
|
||||
static int run(int argc, char* argv[]) {
|
||||
|
||||
40
src/ssh-generator/ssh-util.c
Normal file
40
src/ssh-generator/ssh-util.c
Normal file
@ -0,0 +1,40 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "log.h"
|
||||
#include "socket-util.h"
|
||||
#include "ssh-util.h"
|
||||
|
||||
int vsock_open_or_warn(int *ret) {
|
||||
int fd = RET_NERRNO(socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0));
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(fd))
|
||||
log_debug_errno(fd, "AF_VSOCK is not available, ignoring: %m");
|
||||
else if (fd < 0)
|
||||
return log_error_errno(fd, "Unable to test if AF_VSOCK is available: %m");
|
||||
|
||||
if (ret)
|
||||
*ret = fd;
|
||||
else
|
||||
close(fd);
|
||||
|
||||
return fd >= 0;
|
||||
}
|
||||
|
||||
int vsock_get_local_cid_or_warn(unsigned *ret) {
|
||||
int r;
|
||||
|
||||
r = vsock_get_local_cid(ret);
|
||||
if (ERRNO_IS_NEG_DEVICE_ABSENT(r) || r == -EADDRNOTAVAIL) {
|
||||
if (ERRNO_IS_NEG_DEVICE_ABSENT(r))
|
||||
log_debug_errno(r, "/dev/vsock is not available (even though AF_VSOCK is), ignoring: %m");
|
||||
if (ret)
|
||||
*ret = 0; /* bogus value */
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m");
|
||||
return 1;
|
||||
}
|
||||
4
src/ssh-generator/ssh-util.h
Normal file
4
src/ssh-generator/ssh-util.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
int vsock_open_or_warn(int *ret);
|
||||
int vsock_get_local_cid_or_warn(unsigned *ret);
|
||||
300
tools/find-unused-library-symbols.py
Executable file
300
tools/find-unused-library-symbols.py
Executable file
@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
"""
|
||||
Find unused symbols in a shared library.
|
||||
|
||||
This script analyzes a shared library and a list of executables that link
|
||||
against it to determine which publicly exported symbols from the library
|
||||
are not used by any of the executables or by the library itself internally.
|
||||
|
||||
The script checks for symbol usage in three ways:
|
||||
1. Internal library references: Uses objdump -R to find relocations within
|
||||
the library that reference its own exported symbols
|
||||
2. Executable dependencies: Uses nm to find undefined symbols in executables
|
||||
that match the library's exported symbols
|
||||
3. Cross-references: Identifies symbols used across all provided binaries
|
||||
|
||||
This comprehensive approach ensures that symbols used internally by the
|
||||
library are not incorrectly marked as unused.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_exported_symbols(library_path):
|
||||
"""
|
||||
Extract all exported (public) symbols from a shared library.
|
||||
|
||||
Public API symbols (those starting with 'sd_') are excluded from the analysis
|
||||
since they cannot be removed or made private due to API compatibility requirements.
|
||||
|
||||
Returns a set of symbol names that are defined and exported by the library.
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['nm', '--dynamic', '--defined-only', '--extern-only', library_path],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: Failed to run nm on {library_path}: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except FileNotFoundError:
|
||||
print("Error: 'nm' command not found. Please install binutils.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
symbols = set()
|
||||
for line in result.stdout.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) >= 3:
|
||||
# Format: address type name
|
||||
symbol_type = parts[1]
|
||||
symbol_name = parts[2]
|
||||
# Include text (T) and data (D, B, R) symbols
|
||||
if symbol_type in ('T', 'D', 'B', 'R', 'W'):
|
||||
# Strip version information (e.g., @@SD_SHARED or @SD_SHARED)
|
||||
symbol_name = symbol_name.split('@')[0]
|
||||
# Skip public API symbols (those starting with sd_)
|
||||
if symbol_name.startswith('sd_'):
|
||||
continue
|
||||
symbols.add(symbol_name)
|
||||
|
||||
return symbols
|
||||
|
||||
|
||||
def get_undefined_symbols(executable_path):
|
||||
"""
|
||||
Extract all undefined symbols from an executable.
|
||||
|
||||
These are symbols that the executable expects to be provided by
|
||||
shared libraries it links against.
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['nm', '--dynamic', '--undefined-only', executable_path],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Warning: Failed to run nm on {executable_path}: {e}", file=sys.stderr)
|
||||
return set()
|
||||
except FileNotFoundError:
|
||||
print("Error: 'nm' command not found. Please install binutils.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
symbols = set()
|
||||
for line in result.stdout.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) >= 2:
|
||||
# Format: type name (no address for undefined symbols)
|
||||
symbol_name = parts[1]
|
||||
# Strip version information (e.g., @SD_SHARED)
|
||||
symbol_name = symbol_name.split('@')[0]
|
||||
symbols.add(symbol_name)
|
||||
|
||||
return symbols
|
||||
|
||||
|
||||
def verify_executable_links_library(executable_path, library_name):
|
||||
"""
|
||||
Verify that an executable actually links against the given library.
|
||||
|
||||
Returns True if the executable links against a library with the given name.
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['ldd', executable_path],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
# If ldd fails or doesn't exist, we'll skip the verification
|
||||
return True
|
||||
|
||||
# Check if library_name appears in the ldd output
|
||||
for line in result.stdout.splitlines():
|
||||
if library_name in line:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_library_internal_references(library_path, exported_symbols):
|
||||
"""
|
||||
Find which exported symbols are referenced internally within the library itself.
|
||||
|
||||
This uses objdump to look for relocations that reference the exported symbols.
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['objdump', '-R', library_path],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Warning: Failed to run objdump on {library_path}: {e}", file=sys.stderr)
|
||||
return set()
|
||||
except FileNotFoundError:
|
||||
print("Warning: 'objdump' command not found. Internal references won't be detected.",
|
||||
file=sys.stderr)
|
||||
return set()
|
||||
|
||||
internal_refs = set()
|
||||
for line in result.stdout.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) >= 3:
|
||||
# objdump -R format: offset type symbol
|
||||
# The symbol is typically the last field
|
||||
symbol_name = parts[-1]
|
||||
# Strip version information
|
||||
symbol_name = symbol_name.split('@')[0]
|
||||
# Only include if it's one of our exported symbols
|
||||
if symbol_name in exported_symbols:
|
||||
internal_refs.add(symbol_name)
|
||||
|
||||
return internal_refs
|
||||
|
||||
|
||||
def find_unused_symbols(library_path, executable_paths, verify_linkage=True):
|
||||
"""
|
||||
Find symbols exported by the library that are not used by any executable.
|
||||
|
||||
Args:
|
||||
library_path: Path to the shared library
|
||||
executable_paths: List of paths to executables
|
||||
verify_linkage: Whether to verify executables link against the library
|
||||
|
||||
Returns:
|
||||
Tuple of (unused_symbols, exported_symbols, used_symbols)
|
||||
"""
|
||||
library_name = Path(library_path).name
|
||||
|
||||
# Get all exported symbols from the library (excluding public API symbols)
|
||||
exported_symbols = get_exported_symbols(library_path)
|
||||
|
||||
if not exported_symbols:
|
||||
print(f"Warning: No exported symbols found in {library_path}", file=sys.stderr)
|
||||
return set(), set(), set()
|
||||
|
||||
# Collect all symbols used by the executables
|
||||
used_symbols = set()
|
||||
|
||||
# First, check if the library references its own exported symbols internally
|
||||
internal_refs = get_library_internal_references(library_path, exported_symbols)
|
||||
used_symbols.update(internal_refs)
|
||||
|
||||
for exe_path in executable_paths:
|
||||
# Optionally verify linkage
|
||||
if verify_linkage and not verify_executable_links_library(exe_path, library_name):
|
||||
print(f"Warning: {exe_path} does not appear to link against {library_name}",
|
||||
file=sys.stderr)
|
||||
|
||||
undefined_symbols = get_undefined_symbols(exe_path)
|
||||
# Only count symbols that are actually exported by our library
|
||||
used_symbols.update(undefined_symbols & exported_symbols)
|
||||
|
||||
# Find unused symbols
|
||||
unused_symbols = exported_symbols - used_symbols
|
||||
|
||||
return unused_symbols, exported_symbols, used_symbols
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Find unused exported symbols in a shared library'
|
||||
)
|
||||
parser.add_argument(
|
||||
'library',
|
||||
help='Path to the shared library to analyze'
|
||||
)
|
||||
parser.add_argument(
|
||||
'executables',
|
||||
nargs='+',
|
||||
help='Paths to executables that link against the library'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-verify-linkage',
|
||||
action='store_true',
|
||||
help='Skip verification that executables actually link against the library'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--show-used',
|
||||
action='store_true',
|
||||
help='Also show used symbols'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--stats-only',
|
||||
action='store_true',
|
||||
help='Only show statistics, not individual symbols'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Verify library exists
|
||||
library_path = Path(args.library)
|
||||
if not library_path.exists():
|
||||
print(f"Error: Library not found: {library_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Verify executables exist
|
||||
executable_paths = []
|
||||
for exe in args.executables:
|
||||
exe_path = Path(exe)
|
||||
if not exe_path.exists():
|
||||
print(f"Warning: Executable not found: {exe_path}", file=sys.stderr)
|
||||
else:
|
||||
executable_paths.append(str(exe_path))
|
||||
|
||||
if not executable_paths:
|
||||
print("Error: No valid executables provided", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Analyze symbols
|
||||
unused, exported, used = find_unused_symbols(
|
||||
str(library_path),
|
||||
executable_paths,
|
||||
verify_linkage=not args.no_verify_linkage
|
||||
)
|
||||
|
||||
# Print results
|
||||
print(f"Analysis of {library_path.name}")
|
||||
print("=" * 70)
|
||||
print(f"Total exported symbols: {len(exported)}")
|
||||
print(f" (excluding public API symbols starting with 'sd_')")
|
||||
print(f"Used symbols: {len(used)}")
|
||||
print(f"Unused symbols: {len(unused)}")
|
||||
print(f"Usage rate: {len(used)/len(exported)*100:.1f}%" if exported else "N/A")
|
||||
print()
|
||||
|
||||
if not args.stats_only:
|
||||
if unused:
|
||||
print("Unused symbols:")
|
||||
print("-" * 70)
|
||||
for symbol in sorted(unused):
|
||||
print(f" {symbol}")
|
||||
print()
|
||||
else:
|
||||
print("All exported symbols are used!")
|
||||
print()
|
||||
|
||||
if args.show_used and used:
|
||||
print("Used symbols:")
|
||||
print("-" * 70)
|
||||
for symbol in sorted(used):
|
||||
print(f" {symbol}")
|
||||
print()
|
||||
|
||||
# Exit with non-zero if there are unused symbols (useful for CI)
|
||||
sys.exit(0 if not unused else 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
x
Reference in New Issue
Block a user