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(
|
add_test_setup(
|
||||||
'default',
|
'default',
|
||||||
exclude_suites : ['clang-tidy', 'integration-tests'],
|
exclude_suites : ['clang-tidy', 'unused-symbols', 'integration-tests'],
|
||||||
is_default : true,
|
is_default : true,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2227,7 +2227,7 @@ libudev = shared_library(
|
|||||||
implicit_include_directories : false,
|
implicit_include_directories : false,
|
||||||
link_args : ['-shared',
|
link_args : ['-shared',
|
||||||
'-Wl,--version-script=' + libudev_sym_path],
|
'-Wl,--version-script=' + libudev_sym_path],
|
||||||
link_with : [libsystemd_static, libshared_static],
|
link_with : [libsystemd_static],
|
||||||
link_whole : libudev_basic,
|
link_whole : libudev_basic,
|
||||||
dependencies : [threads,
|
dependencies : [threads,
|
||||||
userspace],
|
userspace],
|
||||||
@ -3000,6 +3000,19 @@ if meson.version().version_compare('>=1.4.0')
|
|||||||
endforeach
|
endforeach
|
||||||
endif
|
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(
|
run_target(
|
||||||
'check-api-docs',
|
'check-api-docs',
|
||||||
depends : [man, libsystemd, libudev],
|
depends : [man, libsystemd, libudev],
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shared-forward.h"
|
#include "basic-forward.h"
|
||||||
|
|
||||||
int encode_devnode_name(const char *str, char *str_enc, size_t len);
|
int encode_devnode_name(const char *str, char *str_enc, size_t len);
|
||||||
int allow_listed_char_for_devnode(char c, const char *additional);
|
int allow_listed_char_for_devnode(char c, const char *additional);
|
||||||
@ -23,6 +23,7 @@ basic_sources = files(
|
|||||||
'compress.c',
|
'compress.c',
|
||||||
'conf-files.c',
|
'conf-files.c',
|
||||||
'confidential-virt.c',
|
'confidential-virt.c',
|
||||||
|
'device-nodes.c',
|
||||||
'devnum-util.c',
|
'devnum-util.c',
|
||||||
'dirent-util.c',
|
'dirent-util.c',
|
||||||
'dlfcn-util.c',
|
'dlfcn-util.c',
|
||||||
|
|||||||
@ -1233,7 +1233,7 @@ static int measured_crypt_activate_by_passphrase(
|
|||||||
if (keyslot < 0)
|
if (keyslot < 0)
|
||||||
return keyslot;
|
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:
|
shortcut:
|
||||||
keyslot = crypt_activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags);
|
keyslot = crypt_activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags);
|
||||||
|
|||||||
@ -12,7 +12,6 @@
|
|||||||
#include "errno-util.h"
|
#include "errno-util.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "io-util.h"
|
#include "io-util.h"
|
||||||
#include "udev-util.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:libudev-queue
|
* SECTION:libudev-queue
|
||||||
@ -64,6 +63,11 @@ static struct udev_queue* udev_queue_free(struct udev_queue *udev_queue) {
|
|||||||
return mfree(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_ref:
|
||||||
* @udev_queue: udev queue context
|
* @udev_queue: udev queue context
|
||||||
|
|||||||
@ -57,7 +57,6 @@ shared_sources = files(
|
|||||||
'daemon-util.c',
|
'daemon-util.c',
|
||||||
'data-fd-util.c',
|
'data-fd-util.c',
|
||||||
'dev-setup.c',
|
'dev-setup.c',
|
||||||
'device-nodes.c',
|
|
||||||
'discover-image.c',
|
'discover-image.c',
|
||||||
'dissect-image.c',
|
'dissect-image.c',
|
||||||
'dm-util.c',
|
'dm-util.c',
|
||||||
|
|||||||
@ -3,15 +3,26 @@
|
|||||||
executables += [
|
executables += [
|
||||||
generator_template + {
|
generator_template + {
|
||||||
'name' : 'systemd-ssh-generator',
|
'name' : 'systemd-ssh-generator',
|
||||||
'sources' : files('ssh-generator.c'),
|
'sources' : files(
|
||||||
|
'ssh-generator.c',
|
||||||
|
'ssh-util.c',
|
||||||
|
),
|
||||||
|
'extract' : files(
|
||||||
|
'ssh-util.c',
|
||||||
|
),
|
||||||
},
|
},
|
||||||
libexec_template + {
|
libexec_template + {
|
||||||
'name' : 'systemd-ssh-proxy',
|
'name' : 'systemd-ssh-proxy',
|
||||||
'sources' : files('ssh-proxy.c'),
|
'sources' : files(
|
||||||
|
'ssh-proxy.c',
|
||||||
|
),
|
||||||
},
|
},
|
||||||
libexec_template + {
|
libexec_template + {
|
||||||
'name' : 'systemd-ssh-issue',
|
'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-netlink.h"
|
||||||
#include "socket-util.h"
|
#include "socket-util.h"
|
||||||
#include "special.h"
|
#include "special.h"
|
||||||
|
#include "ssh-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "virt.h"
|
#include "virt.h"
|
||||||
@ -212,29 +213,15 @@ static int add_vsock_socket(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
r = vsock_open_or_warn(/* ret= */ NULL);
|
||||||
if (vsock_fd < 0) {
|
if (r <= 0)
|
||||||
if (ERRNO_IS_NOT_SUPPORTED(errno)) {
|
return r;
|
||||||
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);
|
|
||||||
|
|
||||||
/* Determine the local CID so that we can log it to help users to connect to this VM */
|
/* Determine the local CID so that we can log it to help users to connect to this VM */
|
||||||
unsigned local_cid;
|
unsigned local_cid;
|
||||||
r = vsock_get_local_cid(&local_cid);
|
r = vsock_get_local_cid_or_warn(&local_cid);
|
||||||
if (r < 0) {
|
if (r <= 0)
|
||||||
if (ERRNO_IS_DEVICE_ABSENT(r)) {
|
return 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 = make_sshd_template_unit(
|
r = make_sshd_template_unit(
|
||||||
dest,
|
dest,
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
#include "parse-argument.h"
|
#include "parse-argument.h"
|
||||||
#include "pretty-print.h"
|
#include "pretty-print.h"
|
||||||
#include "socket-util.h"
|
#include "ssh-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "tmpfile-util.h"
|
#include "tmpfile-util.h"
|
||||||
#include "virt.h"
|
#include "virt.h"
|
||||||
@ -135,33 +135,11 @@ static int acquire_cid(unsigned *ret_cid) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
r = vsock_open_or_warn(/* ret= */ NULL);
|
||||||
if (vsock_fd < 0) {
|
if (r <= 0)
|
||||||
if (ERRNO_IS_NOT_SUPPORTED(errno)) {
|
return r;
|
||||||
log_debug("Not creating issue file, since AF_VSOCK is not available.");
|
|
||||||
*ret_cid = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return log_error_errno(errno, "Unable to test if AF_VSOCK is available: %m");
|
return vsock_get_local_cid_or_warn(ret_cid);
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int run(int argc, char* argv[]) {
|
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