clang-tool-chain 1.0.47__py3-none-any.whl → 1.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- clang_tool_chain/__version__.py +1 -1
- clang_tool_chain/cli.py +13 -0
- clang_tool_chain/deployment/so_deployer.py +13 -0
- clang_tool_chain/env_utils.py +2 -0
- clang_tool_chain/execution/arg_transformers.py +57 -8
- clang_tool_chain/execution/sanitizer_env.py +62 -1
- clang_tool_chain/symbolizer/unwind_windows.c +461 -0
- clang_tool_chain/symbolizer/unwind_windows.h +112 -0
- {clang_tool_chain-1.0.47.dist-info → clang_tool_chain-1.1.2.dist-info}/METADATA +43 -8
- {clang_tool_chain-1.0.47.dist-info → clang_tool_chain-1.1.2.dist-info}/RECORD +13 -11
- {clang_tool_chain-1.0.47.dist-info → clang_tool_chain-1.1.2.dist-info}/WHEEL +0 -0
- {clang_tool_chain-1.0.47.dist-info → clang_tool_chain-1.1.2.dist-info}/entry_points.txt +0 -0
- {clang_tool_chain-1.0.47.dist-info → clang_tool_chain-1.1.2.dist-info}/licenses/LICENSE +0 -0
clang_tool_chain/__version__.py
CHANGED
clang_tool_chain/cli.py
CHANGED
|
@@ -201,6 +201,19 @@ def cmd_list_tools(args: argparse.Namespace) -> int:
|
|
|
201
201
|
for cmd, desc in sccache_tools:
|
|
202
202
|
print(f" {cmd:30s} - {desc}")
|
|
203
203
|
|
|
204
|
+
print()
|
|
205
|
+
print("Debugging and analysis:")
|
|
206
|
+
print("=" * 60)
|
|
207
|
+
print()
|
|
208
|
+
|
|
209
|
+
debug_tools = [
|
|
210
|
+
("clang-tool-chain-lldb", "LLVM debugger (lldb)"),
|
|
211
|
+
("clang-tool-chain-iwyu", "Include What You Use analyzer"),
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
for cmd, desc in debug_tools:
|
|
215
|
+
print(f" {cmd:30s} - {desc}")
|
|
216
|
+
|
|
204
217
|
print()
|
|
205
218
|
print("For more information, run:")
|
|
206
219
|
print(" clang-tool-chain info")
|
|
@@ -208,6 +208,19 @@ class SoDeployer(BaseLibraryDeployer):
|
|
|
208
208
|
if resolved.exists():
|
|
209
209
|
return resolved
|
|
210
210
|
|
|
211
|
+
# For sanitizer runtimes, try architecture-suffixed variants
|
|
212
|
+
# e.g., libclang_rt.asan.so -> libclang_rt.asan-x86_64.so
|
|
213
|
+
if lib_name.startswith("libclang_rt.") and lib_name.endswith(".so"):
|
|
214
|
+
base_name = lib_name[:-3] # Remove .so
|
|
215
|
+
arch_suffixes = ["-x86_64", "-aarch64", "-arm64"]
|
|
216
|
+
for suffix in arch_suffixes:
|
|
217
|
+
arch_lib_path = search_dir / f"{base_name}{suffix}.so"
|
|
218
|
+
if arch_lib_path.exists():
|
|
219
|
+
resolved = arch_lib_path.resolve()
|
|
220
|
+
if resolved.exists():
|
|
221
|
+
self.logger.debug(f"Found architecture-suffixed variant: {arch_lib_path}")
|
|
222
|
+
return resolved
|
|
223
|
+
|
|
211
224
|
return None
|
|
212
225
|
|
|
213
226
|
except KeyboardInterrupt as ke:
|
clang_tool_chain/env_utils.py
CHANGED
|
@@ -31,6 +31,8 @@ CONTROLLABLE_FEATURES = {
|
|
|
31
31
|
"SYSROOT": "Automatic macOS SDK detection (-isysroot)",
|
|
32
32
|
"DEPLOY_LIBS": "Cross-platform library deployment (all outputs)",
|
|
33
33
|
"DEPLOY_SHARED_LIB": "Library deployment for shared library outputs only (.dll, .so, .dylib)",
|
|
34
|
+
"BUNDLED_UNWIND": "Bundled libunwind paths on Linux",
|
|
35
|
+
"MACOS_UNWIND_FIX": "Automatic -lunwind removal on macOS (libunwind in libSystem)",
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
|
|
@@ -16,6 +16,7 @@ Architecture:
|
|
|
16
16
|
ArgumentTransformer (ABC)
|
|
17
17
|
├── DirectivesTransformer (priority=50)
|
|
18
18
|
├── MacOSSDKTransformer (priority=100)
|
|
19
|
+
├── MacOSUnwindTransformer (priority=125)
|
|
19
20
|
├── LinuxUnwindTransformer (priority=150)
|
|
20
21
|
├── LLDLinkerTransformer (priority=200)
|
|
21
22
|
├── ASANRuntimeTransformer (priority=250)
|
|
@@ -208,6 +209,52 @@ class MacOSSDKTransformer(ArgumentTransformer):
|
|
|
208
209
|
return _add_macos_sysroot_if_needed(args)
|
|
209
210
|
|
|
210
211
|
|
|
212
|
+
class MacOSUnwindTransformer(ArgumentTransformer):
|
|
213
|
+
"""
|
|
214
|
+
Transformer for handling -lunwind on macOS.
|
|
215
|
+
|
|
216
|
+
Priority: 125 (runs after SDK but before Linux unwind transformer)
|
|
217
|
+
|
|
218
|
+
On macOS, libunwind is not a standalone library - it's embedded in libSystem.B.dylib
|
|
219
|
+
which is automatically linked by the compiler. The -lunwind flag is unnecessary
|
|
220
|
+
and causes linking errors with ld64.lld:
|
|
221
|
+
|
|
222
|
+
ld64.lld: error: library not found for -lunwind
|
|
223
|
+
|
|
224
|
+
This transformer removes -lunwind from the arguments on macOS since the
|
|
225
|
+
libunwind symbols are already available via libSystem (automatically linked).
|
|
226
|
+
|
|
227
|
+
Environment Variables:
|
|
228
|
+
CLANG_TOOL_CHAIN_NO_MACOS_UNWIND_FIX: Set to '1' to disable this transformer
|
|
229
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to '1' to disable all automatic features
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
def priority(self) -> int:
|
|
233
|
+
return 125
|
|
234
|
+
|
|
235
|
+
def transform(self, args: list[str], context: ToolContext) -> list[str]:
|
|
236
|
+
"""Remove -lunwind on macOS since libunwind is in libSystem."""
|
|
237
|
+
# Only applies to macOS clang/clang++
|
|
238
|
+
if context.platform_name != "darwin" or context.tool_name not in ("clang", "clang++"):
|
|
239
|
+
return args
|
|
240
|
+
|
|
241
|
+
# Check if disabled (via NO_MACOS_UNWIND_FIX or NO_AUTO)
|
|
242
|
+
if is_feature_disabled("MACOS_UNWIND_FIX"):
|
|
243
|
+
return args
|
|
244
|
+
|
|
245
|
+
# Check if -lunwind is present
|
|
246
|
+
if "-lunwind" not in args:
|
|
247
|
+
return args
|
|
248
|
+
|
|
249
|
+
# Remove -lunwind since macOS provides libunwind via libSystem
|
|
250
|
+
result = [arg for arg in args if arg != "-lunwind"]
|
|
251
|
+
|
|
252
|
+
if len(result) != len(args):
|
|
253
|
+
logger.info("Removed -lunwind on macOS (libunwind is built into libSystem, no standalone library exists)")
|
|
254
|
+
|
|
255
|
+
return result
|
|
256
|
+
|
|
257
|
+
|
|
211
258
|
class LinuxUnwindTransformer(ArgumentTransformer):
|
|
212
259
|
"""
|
|
213
260
|
Transformer for adding bundled libunwind include/library paths on Linux.
|
|
@@ -496,9 +543,9 @@ class RPathTransformer(ArgumentTransformer):
|
|
|
496
543
|
if is_feature_disabled("RPATH"):
|
|
497
544
|
return args
|
|
498
545
|
|
|
499
|
-
# Check if rpath already present
|
|
546
|
+
# Check if $ORIGIN rpath already present (don't skip for other rpaths)
|
|
500
547
|
for arg in args:
|
|
501
|
-
if "
|
|
548
|
+
if "$ORIGIN" in arg:
|
|
502
549
|
return args
|
|
503
550
|
|
|
504
551
|
# Add rpath to look in executable's directory first
|
|
@@ -618,12 +665,13 @@ def create_default_pipeline() -> ArgumentPipeline:
|
|
|
618
665
|
This includes all standard transformers in their default priority order:
|
|
619
666
|
1. DirectivesTransformer (priority=50)
|
|
620
667
|
2. MacOSSDKTransformer (priority=100)
|
|
621
|
-
3.
|
|
622
|
-
4.
|
|
623
|
-
5.
|
|
624
|
-
6.
|
|
625
|
-
7.
|
|
626
|
-
8.
|
|
668
|
+
3. MacOSUnwindTransformer (priority=125)
|
|
669
|
+
4. LinuxUnwindTransformer (priority=150)
|
|
670
|
+
5. LLDLinkerTransformer (priority=200)
|
|
671
|
+
6. ASANRuntimeTransformer (priority=250)
|
|
672
|
+
7. RPathTransformer (priority=275)
|
|
673
|
+
8. GNUABITransformer (priority=300)
|
|
674
|
+
9. MSVCABITransformer (priority=300)
|
|
627
675
|
|
|
628
676
|
Returns:
|
|
629
677
|
Configured ArgumentPipeline ready for use
|
|
@@ -632,6 +680,7 @@ def create_default_pipeline() -> ArgumentPipeline:
|
|
|
632
680
|
[
|
|
633
681
|
DirectivesTransformer(),
|
|
634
682
|
MacOSSDKTransformer(),
|
|
683
|
+
MacOSUnwindTransformer(),
|
|
635
684
|
LinuxUnwindTransformer(),
|
|
636
685
|
LLDLinkerTransformer(),
|
|
637
686
|
ASANRuntimeTransformer(),
|
|
@@ -15,7 +15,9 @@ configuration.
|
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
17
|
import os
|
|
18
|
+
import platform
|
|
18
19
|
import shutil
|
|
20
|
+
from pathlib import Path
|
|
19
21
|
|
|
20
22
|
from clang_tool_chain.env_utils import is_feature_disabled
|
|
21
23
|
|
|
@@ -106,9 +108,37 @@ def detect_sanitizers_from_flags(compiler_flags: list[str]) -> tuple[bool, bool]
|
|
|
106
108
|
return asan_enabled, lsan_enabled
|
|
107
109
|
|
|
108
110
|
|
|
111
|
+
def _get_builtin_suppression_file() -> Path | None:
|
|
112
|
+
"""
|
|
113
|
+
Get path to built-in LSan suppression file for current platform.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Path to built-in suppression file, or None if not applicable.
|
|
117
|
+
|
|
118
|
+
Note:
|
|
119
|
+
- macOS: Returns path to lsan_suppressions_darwin.txt
|
|
120
|
+
- Linux: Returns path to lsan_suppressions_linux.txt
|
|
121
|
+
- Windows: Returns None (LSan not supported on Windows)
|
|
122
|
+
"""
|
|
123
|
+
system = platform.system()
|
|
124
|
+
|
|
125
|
+
# Locate data directory in installed package
|
|
126
|
+
data_dir = Path(__file__).parent.parent / "data"
|
|
127
|
+
|
|
128
|
+
if system == "Darwin":
|
|
129
|
+
suppression_file = data_dir / "lsan_suppressions_darwin.txt"
|
|
130
|
+
elif system == "Linux":
|
|
131
|
+
suppression_file = data_dir / "lsan_suppressions_linux.txt"
|
|
132
|
+
else:
|
|
133
|
+
return None # No suppressions for Windows (no LSan support)
|
|
134
|
+
|
|
135
|
+
return suppression_file if suppression_file.exists() else None
|
|
136
|
+
|
|
137
|
+
|
|
109
138
|
def prepare_sanitizer_environment(
|
|
110
139
|
base_env: dict[str, str] | None = None,
|
|
111
140
|
compiler_flags: list[str] | None = None,
|
|
141
|
+
suppression_file: str | Path | None = None,
|
|
112
142
|
) -> dict[str, str]:
|
|
113
143
|
"""
|
|
114
144
|
Prepare environment with optimal sanitizer options and symbolizer path.
|
|
@@ -122,11 +152,17 @@ def prepare_sanitizer_environment(
|
|
|
122
152
|
installation, enabling proper address-to-symbol resolution (function names,
|
|
123
153
|
file paths, line numbers) without manual configuration.
|
|
124
154
|
|
|
155
|
+
On macOS and Linux, platform-specific LSan suppression files are automatically
|
|
156
|
+
applied to filter out false positive leaks from system libraries.
|
|
157
|
+
|
|
125
158
|
Args:
|
|
126
159
|
base_env: Base environment dictionary to modify. If None, uses os.environ.
|
|
127
160
|
compiler_flags: List of compiler flags used to build the executable.
|
|
128
161
|
Used to detect which sanitizers are enabled. If None, no options
|
|
129
162
|
are injected (safe default).
|
|
163
|
+
suppression_file: Optional path to custom LSan suppression file.
|
|
164
|
+
If None, uses built-in platform-specific suppressions.
|
|
165
|
+
Set to empty string "" to disable built-in suppressions.
|
|
130
166
|
|
|
131
167
|
Returns:
|
|
132
168
|
Environment dictionary with sanitizer options injected as appropriate.
|
|
@@ -141,9 +177,14 @@ def prepare_sanitizer_environment(
|
|
|
141
177
|
|
|
142
178
|
Example:
|
|
143
179
|
>>> env = prepare_sanitizer_environment(compiler_flags=["-fsanitize=address"])
|
|
144
|
-
>>> # env now contains ASAN_OPTIONS, LSAN_OPTIONS, and
|
|
180
|
+
>>> # env now contains ASAN_OPTIONS, LSAN_OPTIONS, ASAN_SYMBOLIZER_PATH, and suppressions
|
|
145
181
|
>>> env = prepare_sanitizer_environment(compiler_flags=["-O2"])
|
|
146
182
|
>>> # env unchanged - no sanitizers enabled
|
|
183
|
+
>>> env = prepare_sanitizer_environment(
|
|
184
|
+
... compiler_flags=["-fsanitize=address"],
|
|
185
|
+
... suppression_file="/path/to/custom.txt"
|
|
186
|
+
... )
|
|
187
|
+
>>> # Uses custom suppression file instead of built-in
|
|
147
188
|
"""
|
|
148
189
|
env = base_env.copy() if base_env is not None else os.environ.copy()
|
|
149
190
|
|
|
@@ -182,4 +223,24 @@ def prepare_sanitizer_environment(
|
|
|
182
223
|
"or ensure clang-tool-chain is properly installed."
|
|
183
224
|
)
|
|
184
225
|
|
|
226
|
+
# Add platform-specific LSan suppressions if LSAN is enabled
|
|
227
|
+
if lsan_enabled:
|
|
228
|
+
# Use built-in suppression file if no custom file specified
|
|
229
|
+
if suppression_file is None:
|
|
230
|
+
suppression_file = _get_builtin_suppression_file()
|
|
231
|
+
|
|
232
|
+
# Apply suppression file if it exists (unless explicitly disabled with "")
|
|
233
|
+
if suppression_file and suppression_file != "" and Path(suppression_file).exists():
|
|
234
|
+
current_lsan = env.get("LSAN_OPTIONS", "")
|
|
235
|
+
suppression_opt = f"suppressions={Path(suppression_file).absolute()}"
|
|
236
|
+
|
|
237
|
+
if current_lsan:
|
|
238
|
+
# Append to existing options
|
|
239
|
+
env["LSAN_OPTIONS"] = f"{current_lsan}:{suppression_opt}"
|
|
240
|
+
else:
|
|
241
|
+
# Set new options
|
|
242
|
+
env["LSAN_OPTIONS"] = suppression_opt
|
|
243
|
+
|
|
244
|
+
logger.info(f"Injecting LSan suppression file: {suppression_file}")
|
|
245
|
+
|
|
185
246
|
return env
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* unwind_windows.c - Windows symbol resolution for libunwind
|
|
3
|
+
*
|
|
4
|
+
* This file implements symbol resolution for libunwind on Windows using
|
|
5
|
+
* COFF symbol table parsing for MinGW executables.
|
|
6
|
+
*
|
|
7
|
+
* The MinGW libunwind library does not implement unw_get_proc_name(),
|
|
8
|
+
* returning UNW_ENOINFO (-6549). This implementation provides working
|
|
9
|
+
* symbol resolution by reading the COFF symbol table embedded in the PE
|
|
10
|
+
* executable.
|
|
11
|
+
*
|
|
12
|
+
* Compile: clang-tool-chain-c unwind_windows.c -c -o unwind_windows.o
|
|
13
|
+
*
|
|
14
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
15
|
+
* Copyright (c) 2026 clang-tool-chain contributors
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#ifdef _WIN32
|
|
19
|
+
|
|
20
|
+
#include "unwind_windows.h"
|
|
21
|
+
|
|
22
|
+
#include <windows.h>
|
|
23
|
+
#include <string.h>
|
|
24
|
+
#include <stdio.h>
|
|
25
|
+
#include <stdlib.h>
|
|
26
|
+
|
|
27
|
+
/* Thread-safe initialization flag */
|
|
28
|
+
static volatile LONG g_initialized = 0;
|
|
29
|
+
static CRITICAL_SECTION g_sym_lock;
|
|
30
|
+
static volatile LONG g_lock_initialized = 0;
|
|
31
|
+
|
|
32
|
+
/* Symbol table structure */
|
|
33
|
+
typedef struct {
|
|
34
|
+
DWORD64 address;
|
|
35
|
+
char name[256];
|
|
36
|
+
} symbol_entry_t;
|
|
37
|
+
|
|
38
|
+
/* Global symbol table (sorted by address for binary search) */
|
|
39
|
+
static symbol_entry_t *g_symbol_table = NULL;
|
|
40
|
+
static size_t g_symbol_count = 0;
|
|
41
|
+
static DWORD64 g_image_base = 0;
|
|
42
|
+
static DWORD64 g_runtime_base = 0;
|
|
43
|
+
|
|
44
|
+
/* Initialize the critical section for thread safety */
|
|
45
|
+
static void init_lock(void) {
|
|
46
|
+
if (InterlockedCompareExchange(&g_lock_initialized, 1, 0) == 0) {
|
|
47
|
+
InitializeCriticalSection(&g_sym_lock);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* COFF symbol table entry (18 bytes) */
|
|
52
|
+
#pragma pack(push, 1)
|
|
53
|
+
typedef struct {
|
|
54
|
+
union {
|
|
55
|
+
char ShortName[8];
|
|
56
|
+
struct {
|
|
57
|
+
DWORD Zeros;
|
|
58
|
+
DWORD Offset;
|
|
59
|
+
} LongName;
|
|
60
|
+
} Name;
|
|
61
|
+
DWORD Value;
|
|
62
|
+
SHORT SectionNumber;
|
|
63
|
+
WORD Type;
|
|
64
|
+
BYTE StorageClass;
|
|
65
|
+
BYTE NumberOfAuxSymbols;
|
|
66
|
+
} COFF_SYMBOL;
|
|
67
|
+
#pragma pack(pop)
|
|
68
|
+
|
|
69
|
+
/* Comparison function for qsort */
|
|
70
|
+
static int compare_symbols(const void *a, const void *b) {
|
|
71
|
+
const symbol_entry_t *sa = (const symbol_entry_t *)a;
|
|
72
|
+
const symbol_entry_t *sb = (const symbol_entry_t *)b;
|
|
73
|
+
if (sa->address < sb->address) return -1;
|
|
74
|
+
if (sa->address > sb->address) return 1;
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Check if section is executable */
|
|
79
|
+
static BOOL is_executable_section(IMAGE_SECTION_HEADER *section) {
|
|
80
|
+
return (section->Characteristics & IMAGE_SCN_CNT_CODE) != 0 ||
|
|
81
|
+
(section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Check if this is a text symbol (function) */
|
|
85
|
+
static BOOL is_text_symbol(COFF_SYMBOL *sym, IMAGE_SECTION_HEADER *sections, WORD num_sections) {
|
|
86
|
+
/* Must have a valid section number */
|
|
87
|
+
if (sym->SectionNumber <= 0 || sym->SectionNumber > num_sections) {
|
|
88
|
+
return FALSE;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Check if symbol is in an executable section */
|
|
92
|
+
IMAGE_SECTION_HEADER *section = §ions[sym->SectionNumber - 1];
|
|
93
|
+
if (!is_executable_section(section)) {
|
|
94
|
+
return FALSE;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Check storage class and type */
|
|
98
|
+
BYTE storage = sym->StorageClass;
|
|
99
|
+
WORD derived_type = (sym->Type >> 4) & 0x3;
|
|
100
|
+
|
|
101
|
+
/* Function type in COFF is 0x20 (function derived type) */
|
|
102
|
+
if (derived_type == 2) { /* IMAGE_SYM_DTYPE_FUNCTION */
|
|
103
|
+
return TRUE;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* External or static symbols in code sections are likely functions */
|
|
107
|
+
if (storage == 2 || storage == 3) { /* EXTERNAL or STATIC */
|
|
108
|
+
return TRUE;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return FALSE;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Parse COFF symbol table from the current executable */
|
|
115
|
+
static int parse_coff_symbols(void) {
|
|
116
|
+
char exe_path[MAX_PATH];
|
|
117
|
+
DWORD path_len = GetModuleFileNameA(NULL, exe_path, MAX_PATH);
|
|
118
|
+
if (path_len == 0 || path_len >= MAX_PATH) {
|
|
119
|
+
return -1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Get runtime base address */
|
|
123
|
+
HMODULE exe_module = GetModuleHandleA(NULL);
|
|
124
|
+
g_runtime_base = (DWORD64)exe_module;
|
|
125
|
+
|
|
126
|
+
/* Open the executable file */
|
|
127
|
+
HANDLE file = CreateFileA(exe_path, GENERIC_READ, FILE_SHARE_READ,
|
|
128
|
+
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
129
|
+
if (file == INVALID_HANDLE_VALUE) {
|
|
130
|
+
return -1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Map the file into memory */
|
|
134
|
+
HANDLE mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
135
|
+
if (mapping == NULL) {
|
|
136
|
+
CloseHandle(file);
|
|
137
|
+
return -1;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
LPVOID base = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
|
|
141
|
+
if (base == NULL) {
|
|
142
|
+
CloseHandle(mapping);
|
|
143
|
+
CloseHandle(file);
|
|
144
|
+
return -1;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Parse PE header */
|
|
148
|
+
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)base;
|
|
149
|
+
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
150
|
+
UnmapViewOfFile(base);
|
|
151
|
+
CloseHandle(mapping);
|
|
152
|
+
CloseHandle(file);
|
|
153
|
+
return -1;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
IMAGE_NT_HEADERS64 *nt = (IMAGE_NT_HEADERS64 *)((BYTE *)base + dos->e_lfanew);
|
|
157
|
+
if (nt->Signature != IMAGE_NT_SIGNATURE) {
|
|
158
|
+
UnmapViewOfFile(base);
|
|
159
|
+
CloseHandle(mapping);
|
|
160
|
+
CloseHandle(file);
|
|
161
|
+
return -1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* Get image base for address calculation */
|
|
165
|
+
g_image_base = nt->OptionalHeader.ImageBase;
|
|
166
|
+
|
|
167
|
+
/* Get COFF symbol table info */
|
|
168
|
+
DWORD symbol_table_offset = nt->FileHeader.PointerToSymbolTable;
|
|
169
|
+
DWORD symbol_count = nt->FileHeader.NumberOfSymbols;
|
|
170
|
+
|
|
171
|
+
if (symbol_table_offset == 0 || symbol_count == 0) {
|
|
172
|
+
/* No COFF symbols - this is normal for release builds */
|
|
173
|
+
UnmapViewOfFile(base);
|
|
174
|
+
CloseHandle(mapping);
|
|
175
|
+
CloseHandle(file);
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Get section headers for address calculation */
|
|
180
|
+
IMAGE_SECTION_HEADER *sections = (IMAGE_SECTION_HEADER *)((BYTE *)&nt->OptionalHeader +
|
|
181
|
+
nt->FileHeader.SizeOfOptionalHeader);
|
|
182
|
+
WORD num_sections = nt->FileHeader.NumberOfSections;
|
|
183
|
+
|
|
184
|
+
/* Get pointer to COFF symbol table */
|
|
185
|
+
COFF_SYMBOL *symbols = (COFF_SYMBOL *)((BYTE *)base + symbol_table_offset);
|
|
186
|
+
|
|
187
|
+
/* String table immediately follows symbol table */
|
|
188
|
+
char *string_table = (char *)((BYTE *)symbols + symbol_count * sizeof(COFF_SYMBOL));
|
|
189
|
+
|
|
190
|
+
/* Count text symbols first */
|
|
191
|
+
size_t func_count = 0;
|
|
192
|
+
for (DWORD i = 0; i < symbol_count; i++) {
|
|
193
|
+
COFF_SYMBOL *sym = &symbols[i];
|
|
194
|
+
|
|
195
|
+
if (is_text_symbol(sym, sections, num_sections)) {
|
|
196
|
+
func_count++;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
i += sym->NumberOfAuxSymbols;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (func_count == 0) {
|
|
203
|
+
UnmapViewOfFile(base);
|
|
204
|
+
CloseHandle(mapping);
|
|
205
|
+
CloseHandle(file);
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* Allocate symbol table */
|
|
210
|
+
g_symbol_table = (symbol_entry_t *)malloc(func_count * sizeof(symbol_entry_t));
|
|
211
|
+
if (!g_symbol_table) {
|
|
212
|
+
UnmapViewOfFile(base);
|
|
213
|
+
CloseHandle(mapping);
|
|
214
|
+
CloseHandle(file);
|
|
215
|
+
return -1;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* Fill symbol table */
|
|
219
|
+
size_t idx = 0;
|
|
220
|
+
for (DWORD i = 0; i < symbol_count && idx < func_count; i++) {
|
|
221
|
+
COFF_SYMBOL *sym = &symbols[i];
|
|
222
|
+
|
|
223
|
+
if (!is_text_symbol(sym, sections, num_sections)) {
|
|
224
|
+
i += sym->NumberOfAuxSymbols;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Get symbol name */
|
|
229
|
+
char name[256];
|
|
230
|
+
if (sym->Name.LongName.Zeros == 0) {
|
|
231
|
+
/* Long name - offset into string table */
|
|
232
|
+
strncpy(name, string_table + sym->Name.LongName.Offset, 255);
|
|
233
|
+
name[255] = '\0';
|
|
234
|
+
} else {
|
|
235
|
+
/* Short name - directly in entry */
|
|
236
|
+
memcpy(name, sym->Name.ShortName, 8);
|
|
237
|
+
name[8] = '\0';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Skip internal symbols */
|
|
241
|
+
if (name[0] == '.') {
|
|
242
|
+
i += sym->NumberOfAuxSymbols;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Calculate virtual address (relative to image base) */
|
|
247
|
+
WORD section_idx = sym->SectionNumber - 1;
|
|
248
|
+
DWORD64 rva = sections[section_idx].VirtualAddress + sym->Value;
|
|
249
|
+
|
|
250
|
+
/* Store runtime address (with ASLR) */
|
|
251
|
+
g_symbol_table[idx].address = g_runtime_base + rva;
|
|
252
|
+
strncpy(g_symbol_table[idx].name, name, 255);
|
|
253
|
+
g_symbol_table[idx].name[255] = '\0';
|
|
254
|
+
idx++;
|
|
255
|
+
|
|
256
|
+
i += sym->NumberOfAuxSymbols;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
g_symbol_count = idx;
|
|
260
|
+
|
|
261
|
+
/* Sort by address for binary search */
|
|
262
|
+
qsort(g_symbol_table, g_symbol_count, sizeof(symbol_entry_t), compare_symbols);
|
|
263
|
+
|
|
264
|
+
UnmapViewOfFile(base);
|
|
265
|
+
CloseHandle(mapping);
|
|
266
|
+
CloseHandle(file);
|
|
267
|
+
|
|
268
|
+
return 0;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* Ensure symbols are loaded */
|
|
272
|
+
static int ensure_initialized(void) {
|
|
273
|
+
init_lock();
|
|
274
|
+
|
|
275
|
+
if (g_initialized) {
|
|
276
|
+
return 0;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
EnterCriticalSection(&g_sym_lock);
|
|
280
|
+
|
|
281
|
+
if (g_initialized) {
|
|
282
|
+
LeaveCriticalSection(&g_sym_lock);
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
int result = parse_coff_symbols();
|
|
287
|
+
g_initialized = 1;
|
|
288
|
+
|
|
289
|
+
LeaveCriticalSection(&g_sym_lock);
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
int unw_windows_sym_init(void) {
|
|
294
|
+
return ensure_initialized();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
void unw_windows_sym_cleanup(void) {
|
|
298
|
+
if (g_initialized) {
|
|
299
|
+
EnterCriticalSection(&g_sym_lock);
|
|
300
|
+
if (g_initialized) {
|
|
301
|
+
if (g_symbol_table) {
|
|
302
|
+
free(g_symbol_table);
|
|
303
|
+
g_symbol_table = NULL;
|
|
304
|
+
}
|
|
305
|
+
g_symbol_count = 0;
|
|
306
|
+
g_initialized = 0;
|
|
307
|
+
}
|
|
308
|
+
LeaveCriticalSection(&g_sym_lock);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* Find symbol by address using binary search */
|
|
313
|
+
static symbol_entry_t *find_symbol(DWORD64 address) {
|
|
314
|
+
if (!g_symbol_table || g_symbol_count == 0) {
|
|
315
|
+
return NULL;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* Binary search for the largest address <= target */
|
|
319
|
+
size_t low = 0;
|
|
320
|
+
size_t high = g_symbol_count;
|
|
321
|
+
symbol_entry_t *best = NULL;
|
|
322
|
+
|
|
323
|
+
while (low < high) {
|
|
324
|
+
size_t mid = low + (high - low) / 2;
|
|
325
|
+
if (g_symbol_table[mid].address <= address) {
|
|
326
|
+
best = &g_symbol_table[mid];
|
|
327
|
+
low = mid + 1;
|
|
328
|
+
} else {
|
|
329
|
+
high = mid;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* Validate that the symbol is reasonable (offset < 1MB) */
|
|
334
|
+
if (best && (address - best->address) < 0x100000) {
|
|
335
|
+
return best;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return NULL;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
int unw_get_proc_name_windows(unw_cursor_t *cursor, char *buf, size_t buf_len,
|
|
342
|
+
unw_word_t *offp) {
|
|
343
|
+
if (!cursor || !buf || buf_len == 0) {
|
|
344
|
+
return UNW_EUNSPEC;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* Initialize symbol table if not already done */
|
|
348
|
+
if (ensure_initialized() != 0) {
|
|
349
|
+
return UNW_EUNSPEC;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* Get instruction pointer from cursor */
|
|
353
|
+
unw_word_t ip = 0;
|
|
354
|
+
if (unw_get_reg(cursor, UNW_REG_IP, &ip) != 0) {
|
|
355
|
+
return UNW_EUNSPEC;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (ip == 0) {
|
|
359
|
+
return UNW_ENOINFO;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* Find symbol containing this address */
|
|
363
|
+
symbol_entry_t *sym = find_symbol((DWORD64)ip);
|
|
364
|
+
if (!sym) {
|
|
365
|
+
return UNW_ENOINFO;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* Copy symbol name to output buffer */
|
|
369
|
+
size_t name_len = strlen(sym->name);
|
|
370
|
+
if (name_len >= buf_len) {
|
|
371
|
+
name_len = buf_len - 1;
|
|
372
|
+
}
|
|
373
|
+
memcpy(buf, sym->name, name_len);
|
|
374
|
+
buf[name_len] = '\0';
|
|
375
|
+
|
|
376
|
+
/* Set offset if requested */
|
|
377
|
+
if (offp) {
|
|
378
|
+
*offp = (unw_word_t)(ip - sym->address);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* _Ux86_64_get_proc_name - Internal libunwind function for symbol resolution
|
|
386
|
+
*
|
|
387
|
+
* This is the internal function that MinGW libunwind calls from unw_get_proc_name().
|
|
388
|
+
* By providing this implementation, we make libunwind's symbol resolution work on Windows.
|
|
389
|
+
*
|
|
390
|
+
* The MinGW libunwind declares this as a weak symbol that returns UNW_ENOINFO.
|
|
391
|
+
* By linking with this library (or compiling this file with your program),
|
|
392
|
+
* our implementation takes precedence and provides actual symbol resolution
|
|
393
|
+
* using the COFF symbol table embedded in the PE executable.
|
|
394
|
+
*
|
|
395
|
+
* Usage:
|
|
396
|
+
* Option 1: Link the pre-built DLL:
|
|
397
|
+
* clang-tool-chain-c your_program.c -lunwind -lunwind_proc_name -o program.exe
|
|
398
|
+
*
|
|
399
|
+
* Option 2: Compile this file with your program:
|
|
400
|
+
* clang-tool-chain-c unwind_windows.c your_program.c -lunwind -o program.exe
|
|
401
|
+
*
|
|
402
|
+
* After linking, unw_get_proc_name() will return function names instead of UNW_ENOINFO.
|
|
403
|
+
*/
|
|
404
|
+
int _Ux86_64_get_proc_name(unw_cursor_t *cursor, char *buf, size_t buf_len,
|
|
405
|
+
unw_word_t *offp) {
|
|
406
|
+
return unw_get_proc_name_windows(cursor, buf, buf_len, offp);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* __wrap__Ux86_64_get_proc_name - For use with -Wl,--wrap linker option
|
|
411
|
+
*
|
|
412
|
+
* This provides an alternative linking method using --wrap:
|
|
413
|
+
* clang-tool-chain-c your_program.c -lunwind -Wl,--wrap=_Ux86_64_get_proc_name -o program.exe
|
|
414
|
+
*
|
|
415
|
+
* When using --wrap, the linker redirects calls to _Ux86_64_get_proc_name
|
|
416
|
+
* to __wrap__Ux86_64_get_proc_name.
|
|
417
|
+
*/
|
|
418
|
+
int __wrap__Ux86_64_get_proc_name(unw_cursor_t *cursor, char *buf, size_t buf_len,
|
|
419
|
+
unw_word_t *offp) {
|
|
420
|
+
return unw_get_proc_name_windows(cursor, buf, buf_len, offp);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Override functions for LLVM libunwind.
|
|
425
|
+
*
|
|
426
|
+
* These are defined when compiling directly (not as shared library).
|
|
427
|
+
* They override the LLVM libunwind functions to use our implementation.
|
|
428
|
+
*
|
|
429
|
+
* When building as shared library (-DBUILDING_DLL), these are not defined
|
|
430
|
+
* to avoid duplicate symbol errors with libunwind.a.
|
|
431
|
+
*/
|
|
432
|
+
#ifndef BUILDING_DLL
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* __unw_get_proc_name - LLVM libunwind internal function
|
|
436
|
+
*
|
|
437
|
+
* This provides the LLVM libunwind internal function that unw_get_proc_name() aliases to.
|
|
438
|
+
* When this file is linked BEFORE libunwind.a, this definition takes precedence.
|
|
439
|
+
*
|
|
440
|
+
* IMPORTANT: Link order matters! This file must be linked BEFORE -lunwind:
|
|
441
|
+
* clang-tool-chain-c unwind_windows.c your_program.c -lunwind -o program.exe
|
|
442
|
+
*/
|
|
443
|
+
int __unw_get_proc_name(unw_cursor_t *cursor, char *buf, size_t buf_len,
|
|
444
|
+
unw_word_t *offp) {
|
|
445
|
+
return unw_get_proc_name_windows(cursor, buf, buf_len, offp);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* unw_get_proc_name - Standard libunwind API function
|
|
450
|
+
*
|
|
451
|
+
* This directly provides the user-facing API function.
|
|
452
|
+
* When this file is linked BEFORE libunwind.a, this definition takes precedence.
|
|
453
|
+
*/
|
|
454
|
+
int unw_get_proc_name(unw_cursor_t *cursor, char *buf, size_t buf_len,
|
|
455
|
+
unw_word_t *offp) {
|
|
456
|
+
return unw_get_proc_name_windows(cursor, buf, buf_len, offp);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
#endif /* BUILDING_DLL */
|
|
460
|
+
|
|
461
|
+
#endif /* _WIN32 */
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* unwind_windows.h - Windows symbol resolution for libunwind
|
|
3
|
+
*
|
|
4
|
+
* This header provides symbol resolution for libunwind on Windows.
|
|
5
|
+
* MinGW libunwind does not implement symbol resolution, returning UNW_ENOINFO.
|
|
6
|
+
* This implementation reads the COFF symbol table embedded in the PE
|
|
7
|
+
* executable for symbol resolution.
|
|
8
|
+
*
|
|
9
|
+
* AUTOMATIC INTEGRATION:
|
|
10
|
+
* When you link with this library, the internal _Ux86_64_get_proc_name()
|
|
11
|
+
* function is provided, making libunwind's unw_get_proc_name() work
|
|
12
|
+
* automatically. No code changes needed!
|
|
13
|
+
*
|
|
14
|
+
* Example:
|
|
15
|
+
* clang-tool-chain-c your_program.c -lunwind -lunwind_proc_name -o program.exe
|
|
16
|
+
*
|
|
17
|
+
* Or compile unwind_windows.c directly:
|
|
18
|
+
* clang-tool-chain-c unwind_windows.c your_program.c -lunwind -o program.exe
|
|
19
|
+
*
|
|
20
|
+
* MANUAL USAGE (alternative):
|
|
21
|
+
* #ifdef _WIN32
|
|
22
|
+
* #include <unwind_windows.h>
|
|
23
|
+
* #define unw_get_proc_name unw_get_proc_name_windows
|
|
24
|
+
* #endif
|
|
25
|
+
*
|
|
26
|
+
* No additional libraries required for linking (uses Windows API only).
|
|
27
|
+
*
|
|
28
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
29
|
+
* Copyright (c) 2026 clang-tool-chain contributors
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
#ifndef UNWIND_WINDOWS_H
|
|
33
|
+
#define UNWIND_WINDOWS_H
|
|
34
|
+
|
|
35
|
+
#ifdef _WIN32
|
|
36
|
+
|
|
37
|
+
#include <libunwind.h>
|
|
38
|
+
#include <stddef.h>
|
|
39
|
+
|
|
40
|
+
#ifdef __cplusplus
|
|
41
|
+
extern "C" {
|
|
42
|
+
#endif
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve function name for the current cursor position.
|
|
46
|
+
*
|
|
47
|
+
* This is a Windows-specific replacement for unw_get_proc_name() that uses
|
|
48
|
+
* DbgHelp for symbol resolution instead of returning UNW_ENOINFO.
|
|
49
|
+
*
|
|
50
|
+
* @param cursor libunwind cursor pointing to a stack frame
|
|
51
|
+
* @param buf Buffer to store the function name
|
|
52
|
+
* @param buf_len Size of the buffer
|
|
53
|
+
* @param offp Pointer to store the offset from function start (can be NULL)
|
|
54
|
+
*
|
|
55
|
+
* @return 0 on success, UNW_ENOINFO if symbol not found, UNW_EUNSPEC on error
|
|
56
|
+
*/
|
|
57
|
+
int unw_get_proc_name_windows(unw_cursor_t *cursor, char *buf, size_t buf_len,
|
|
58
|
+
unw_word_t *offp);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Initialize Windows symbol resolution.
|
|
62
|
+
*
|
|
63
|
+
* This function initializes the DbgHelp library for the current process.
|
|
64
|
+
* It is called automatically by unw_get_proc_name_windows() on first use,
|
|
65
|
+
* but can be called explicitly for earlier initialization.
|
|
66
|
+
*
|
|
67
|
+
* @return 0 on success, non-zero on error
|
|
68
|
+
*/
|
|
69
|
+
int unw_windows_sym_init(void);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Cleanup Windows symbol resolution.
|
|
73
|
+
*
|
|
74
|
+
* Call this before program exit to free DbgHelp resources.
|
|
75
|
+
* Optional but recommended for clean shutdown.
|
|
76
|
+
*/
|
|
77
|
+
void unw_windows_sym_cleanup(void);
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Internal libunwind function for symbol resolution (nongnu libunwind).
|
|
81
|
+
*
|
|
82
|
+
* This function provides the internal implementation for nongnu libunwind.
|
|
83
|
+
* The MinGW libunwind declares this as a weak symbol that returns UNW_ENOINFO.
|
|
84
|
+
* Our implementation overrides the weak symbol and provides actual symbol
|
|
85
|
+
* resolution using the COFF symbol table.
|
|
86
|
+
*
|
|
87
|
+
* @param cursor libunwind cursor pointing to a stack frame
|
|
88
|
+
* @param buf Buffer to store the function name
|
|
89
|
+
* @param buf_len Size of the buffer
|
|
90
|
+
* @param offp Pointer to store the offset from function start (can be NULL)
|
|
91
|
+
*
|
|
92
|
+
* @return 0 on success, UNW_ENOINFO if symbol not found, UNW_EUNSPEC on error
|
|
93
|
+
*/
|
|
94
|
+
int _Ux86_64_get_proc_name(unw_cursor_t *cursor, char *buf, size_t buf_len,
|
|
95
|
+
unw_word_t *offp);
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* The following functions override LLVM libunwind functions.
|
|
99
|
+
* They are only defined when compiling unwind_windows.c directly with your
|
|
100
|
+
* program (not when building as a DLL).
|
|
101
|
+
*
|
|
102
|
+
* Compile order: unwind_windows.c must come BEFORE -lunwind in the command:
|
|
103
|
+
* clang-tool-chain-c unwind_windows.c your_program.c -lunwind -o program.exe
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
#ifdef __cplusplus
|
|
107
|
+
}
|
|
108
|
+
#endif
|
|
109
|
+
|
|
110
|
+
#endif /* _WIN32 */
|
|
111
|
+
|
|
112
|
+
#endif /* UNWIND_WINDOWS_H */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clang-tool-chain
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: Clang Tool Chain - C/C++ compilation toolchain utilities
|
|
5
5
|
Project-URL: Homepage, https://github.com/zackees/clang-tool-chain
|
|
6
6
|
Project-URL: Repository, https://github.com/zackees/clang-tool-chain
|
|
@@ -117,7 +117,7 @@ Comprehensive test coverage across all platforms and tool categories ensures rel
|
|
|
117
117
|
| **lldb** | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lldb-win.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lldb-linux-x86.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lldb-linux-arm.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lldb-macos-x86.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lldb-macos-arm.yml) |
|
|
118
118
|
| **cosmocc** | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-cosmocc-win.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-cosmocc-linux-x86.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-cosmocc-linux-arm.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-cosmocc-macos-x86.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-cosmocc-macos-arm.yml) |
|
|
119
119
|
| **lib-deploy** | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lib-deploy-win.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lib-deploy-linux-x86.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lib-deploy-linux-arm.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lib-deploy-macos-x86.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-lib-deploy-macos-arm.yml) |
|
|
120
|
-
| **libunwind** |
|
|
120
|
+
| **libunwind** | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-libunwind-win.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-libunwind-linux-x86.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-libunwind-linux-arm.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-libunwind-macos-x86.yml) | [](https://github.com/zackees/clang-tool-chain/actions/workflows/test-libunwind-macos-arm.yml) |
|
|
121
121
|
|
|
122
122
|
**📖 [Complete Test Matrix Documentation](docs/TEST_MATRIX.md)** - Tool category descriptions, test organization, running tests locally.
|
|
123
123
|
|
|
@@ -1009,19 +1009,52 @@ See: https://github.com/google/sanitizers/issues/899
|
|
|
1009
1009
|
|
|
1010
1010
|
---
|
|
1011
1011
|
|
|
1012
|
-
## 📚
|
|
1012
|
+
## 📚 libunwind Support (All Platforms)
|
|
1013
1013
|
|
|
1014
|
-
**
|
|
1014
|
+
**Cross-platform stack unwinding for debugging and profiling**
|
|
1015
1015
|
|
|
1016
|
-
|
|
1016
|
+
clang-tool-chain provides libunwind support on all platforms with platform-specific implementations:
|
|
1017
|
+
- **Linux**: Bundled libunwind (headers + shared libraries) - no system packages required
|
|
1018
|
+
- **Windows**: MinGW sysroot libunwind - included in toolchain
|
|
1019
|
+
- **macOS**: System libunwind from macOS SDK - no additional installation required
|
|
1017
1020
|
|
|
1018
|
-
|
|
1021
|
+
All platforms support `#include <libunwind.h>` and `-lunwind` out of the box.
|
|
1022
|
+
|
|
1023
|
+
### What's Bundled (Linux)
|
|
1019
1024
|
|
|
1020
1025
|
| Component | Files | Size |
|
|
1021
1026
|
|-----------|-------|------|
|
|
1022
1027
|
| Headers | `libunwind.h`, `libunwind-common.h`, `libunwind-x86_64.h`/`libunwind-aarch64.h`, `unwind.h` | ~20 KB |
|
|
1023
1028
|
| Libraries | `libunwind.so.*`, `libunwind-x86_64.so.*` (or `aarch64`) | ~300 KB |
|
|
1024
1029
|
|
|
1030
|
+
### Platform-Specific Details
|
|
1031
|
+
|
|
1032
|
+
| Platform | Source | Installation | Symbol Resolution | Notes |
|
|
1033
|
+
|----------|--------|--------------|-------------------|-------|
|
|
1034
|
+
| **Linux** | Bundled (from Debian packages) | Automatic with toolchain | ✅ Full support via `unw_get_proc_name()` | Includes headers + shared libraries |
|
|
1035
|
+
| **Windows** | MinGW sysroot | Automatic with toolchain | ⚠️ Limited - use `llvm-symbolizer` | Stack walking works; symbol resolution requires external tool |
|
|
1036
|
+
| **macOS** | System SDK | Pre-installed with macOS | ✅ Full support via `unw_get_proc_name()` | Uses system libunwind |
|
|
1037
|
+
|
|
1038
|
+
**Windows Symbol Resolution:**
|
|
1039
|
+
|
|
1040
|
+
MinGW libunwind on Windows doesn't support `unw_get_proc_name()`. While `llvm-symbolizer` is bundled, Windows ASLR makes runtime address resolution complex.
|
|
1041
|
+
|
|
1042
|
+
**Recommended**: Use `clang-tool-chain-lldb` for debugging with symbols, or use Linux/macOS for built-in symbol resolution.
|
|
1043
|
+
|
|
1044
|
+
**Verification** (llvm-symbolizer works with static addresses):
|
|
1045
|
+
```bash
|
|
1046
|
+
# Compile with debug symbols
|
|
1047
|
+
clang-tool-chain-c program.c -o program.exe -g
|
|
1048
|
+
|
|
1049
|
+
# Get function addresses and resolve
|
|
1050
|
+
llvm-nm program.exe | grep main
|
|
1051
|
+
# Shows: 1400014d0 T main
|
|
1052
|
+
|
|
1053
|
+
llvm-symbolizer -e program.exe -f -C 0x1400014d0
|
|
1054
|
+
# Shows: main
|
|
1055
|
+
# C:\path\to\program.c:10:0
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1025
1058
|
### How It Works
|
|
1026
1059
|
|
|
1027
1060
|
When compiling on Linux, clang-tool-chain **automatically**:
|
|
@@ -1068,11 +1101,13 @@ clang-tool-chain-c backtrace.c -lunwind -o backtrace
|
|
|
1068
1101
|
|
|
1069
1102
|
### Configuration
|
|
1070
1103
|
|
|
1071
|
-
**Disable bundled libunwind** (use system version instead):
|
|
1104
|
+
**Disable bundled libunwind on Linux** (use system version instead):
|
|
1072
1105
|
```bash
|
|
1073
|
-
export CLANG_TOOL_CHAIN_NO_BUNDLED_UNWIND=1
|
|
1106
|
+
export CLANG_TOOL_CHAIN_NO_BUNDLED_UNWIND=1 # Linux only
|
|
1074
1107
|
```
|
|
1075
1108
|
|
|
1109
|
+
**Note**: This environment variable only applies to Linux. Windows and macOS always use their respective platform sources (MinGW sysroot and system SDK).
|
|
1110
|
+
|
|
1076
1111
|
### Platform Support
|
|
1077
1112
|
|
|
1078
1113
|
| Platform | Headers | Libraries | Status |
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
clang_tool_chain/__init__.py,sha256=u-qgOuQxh5W2g7n8BgUtBnUWsK66rM4N6LJXY9PiEx0,880
|
|
2
|
-
clang_tool_chain/__version__.py,sha256=
|
|
2
|
+
clang_tool_chain/__version__.py,sha256=QdDf2Y_Q6wfrydeJ6-C_vCqCore3Rs1Q1qnCfaTFRNQ,71
|
|
3
3
|
clang_tool_chain/archive.py,sha256=t3reh7cm5XP2rhTqfRIDAQZv5XQq7SsstyiROYg8wFA,27697
|
|
4
4
|
clang_tool_chain/archive_cache.py,sha256=5ZmlwXIJZDcrDFkTbdgBQYN9sulGn0WyI6qwWqC4HEU,6806
|
|
5
5
|
clang_tool_chain/checksums.py,sha256=KFXeAeDz5ZlcZVOxsHDpNDCrm9UDoJ8bMA4PeNhuzdA,9868
|
|
6
|
-
clang_tool_chain/cli.py,sha256=
|
|
6
|
+
clang_tool_chain/cli.py,sha256=VLw7rulCBpiO9Ch2qXIyXnzagYg85-6DxFcKJVAInyo,44535
|
|
7
7
|
clang_tool_chain/cli_args.py,sha256=oYAzjhpS9m_Vxr5Z1I97pv07BlxV22cnGaPmV6aON_g,949
|
|
8
8
|
clang_tool_chain/cli_parsers.py,sha256=cHVDyW75N_Kao1VYeAgw_L-1K65DjyvWUigQScTFivY,6814
|
|
9
9
|
clang_tool_chain/component_db.py,sha256=ohtlycsrEokui83iZyPsTyxmvXgkD7_OpVwAn1Cq8gI,9096
|
|
10
10
|
clang_tool_chain/downloader.py,sha256=fqErxTaI3oEYbloLZnrWXJ26j2iGOngPnl4i8kqOWNs,6041
|
|
11
11
|
clang_tool_chain/env_breadcrumbs.py,sha256=bvPTz8xABILhzrXTEBzdGrSbpEXLf2YVgcYEe-IdhNY,2335
|
|
12
|
-
clang_tool_chain/env_utils.py,sha256=
|
|
12
|
+
clang_tool_chain/env_utils.py,sha256=DVyvoD2xqJwA2LYt7MqnvFSBJiTLOaicCDribFoHTg0,4380
|
|
13
13
|
clang_tool_chain/fetch.py,sha256=DwsNl5DZkNqEYXL-FbCTnp6IA2iCAa9pMl5oPjyuOS4,4696
|
|
14
14
|
clang_tool_chain/installer.py,sha256=GuGeUvVcAw4HMj9jrub-I11ixmksw-vgtSOFrlupmPA,3323
|
|
15
15
|
clang_tool_chain/interrupt_utils.py,sha256=7YvazvGzyItRVDZ_pzUSK6at8PCw-Dgih69HLSz0tT4,1153
|
|
@@ -35,11 +35,11 @@ clang_tool_chain/deployment/dll_detector.py,sha256=Zf2NAjAg6bEg6uvb7dFkj0qvk8Mvz
|
|
|
35
35
|
clang_tool_chain/deployment/dylib_deployer.py,sha256=6GLn_CMLYUAdQZgwcTMx0XXo_AxGvUKUFYt04v2Kip0,19315
|
|
36
36
|
clang_tool_chain/deployment/factory.py,sha256=Mq6cMMUctoYBZ81I16_hXUmDLQGSAngCmBzPlu_0iaU,6205
|
|
37
37
|
clang_tool_chain/deployment/libdeploy.py,sha256=cfyDFmbuUmimss5fv7exO7NOulDnw7agrKGFgbSK7BM,9050
|
|
38
|
-
clang_tool_chain/deployment/so_deployer.py,sha256=
|
|
38
|
+
clang_tool_chain/deployment/so_deployer.py,sha256=eAygmGojioi1iz5cHeqmKjJ5fI-5XooG9Ro-5TIA0gw,12867
|
|
39
39
|
clang_tool_chain/directives/__init__.py,sha256=MJDNYL_MD2MF0HFsrTsSTX645bYo6vtjq2pOTtfykaU,198
|
|
40
40
|
clang_tool_chain/directives/parser.py,sha256=6J7mO1JtvuHkkKS0Xges5b_jT9b3uTF6ULI0ZiwGAdw,11179
|
|
41
41
|
clang_tool_chain/execution/__init__.py,sha256=fFQEW9VejCdbBvt4quNJ2X8udh_PKgewTfzUXHmXzyc,940
|
|
42
|
-
clang_tool_chain/execution/arg_transformers.py,sha256=
|
|
42
|
+
clang_tool_chain/execution/arg_transformers.py,sha256=NOuU3WheufJA2MVS79kWtStiL_db-LmdHXPm7GFKEqA,25773
|
|
43
43
|
clang_tool_chain/execution/build.py,sha256=YHS1BJTZg5pBS9czVko41mBdfswSPad5hxfitMoLvsI,13275
|
|
44
44
|
clang_tool_chain/execution/build_pipeline.py,sha256=ORJEJ8WYp8c7bhWAa-e3w_ySXwenpUczlmXgoGByToY,17823
|
|
45
45
|
clang_tool_chain/execution/core.py,sha256=7CJ0azznC5lq5bw8amk2kwCIN2I_OnDiKytpapkvrdY,25273
|
|
@@ -49,7 +49,7 @@ clang_tool_chain/execution/iwyu.py,sha256=bmP0d_PZObA1JfmFYp3qIOKCb7y32AWPm2_ReF
|
|
|
49
49
|
clang_tool_chain/execution/lldb.py,sha256=VpxkWTPS6PsyskaKTALeziR5Z5NLwarW174Fm1SMX9k,20718
|
|
50
50
|
clang_tool_chain/execution/nodejs_resolver.py,sha256=8QsJWvIfmt5mBDV7n0ypSjsPyXS-eZTizhBli937I-g,11172
|
|
51
51
|
clang_tool_chain/execution/platform_executor.py,sha256=sF4GyW0ujy2EewG8y2Eo1gUWGzss5G5iIkv02w7-2_o,14362
|
|
52
|
-
clang_tool_chain/execution/sanitizer_env.py,sha256
|
|
52
|
+
clang_tool_chain/execution/sanitizer_env.py,sha256=-Gg852-n3YzPhq0D70YCds4BCCqoCjilWnAYRZzrx9k,10084
|
|
53
53
|
clang_tool_chain/installers/__init__.py,sha256=NAV5woPCEDKSbFr1UmfQsrg4Ua5UdghN4q7H3ymvRsg,279
|
|
54
54
|
clang_tool_chain/installers/base.py,sha256=OS78bau9zoYPitmhla7pKsfCPEj-zLY0DkvVzjE31Tw,15437
|
|
55
55
|
clang_tool_chain/installers/clang.py,sha256=rUtheVRF7mq_1YdmQ3XzIybrJqsHbm2Xf0cbhRbH7RQ,16994
|
|
@@ -66,11 +66,13 @@ clang_tool_chain/platform/paths.py,sha256=K0IjeVwbmgPlAWQO8mS3r1WS4C2dN6IYrSqPpc
|
|
|
66
66
|
clang_tool_chain/sdk/__init__.py,sha256=G8Zt5N5bkOviX76lTX38YMVX_wESJUTk1a0GKEowWb4,521
|
|
67
67
|
clang_tool_chain/sdk/macos.py,sha256=b7eshUH5h2TIfYeiS1ROReGfeOVTGf6I3HswEF9Z5r8,6600
|
|
68
68
|
clang_tool_chain/sdk/windows.py,sha256=8zMLAoFz2OKMz-w6Kqxr3A-6Cofto2VWZvCvRi7knd8,6286
|
|
69
|
+
clang_tool_chain/symbolizer/unwind_windows.c,sha256=kycjNPB9yU-by50OQAd5oVmxg3WjTUYyAsYdTnlKTZU,13802
|
|
70
|
+
clang_tool_chain/symbolizer/unwind_windows.h,sha256=XTWhJDv13AAMUPAzWCfRN7tO6EbNhv1-0ys0VbXET58,3684
|
|
69
71
|
clang_tool_chain/testing/__init__.py,sha256=-sYqOOCuTV_u-MkmExrD4uKdTHG4RmMwR3D1kIG281Q,208
|
|
70
72
|
clang_tool_chain/testing/diagnostic_runner.py,sha256=mnmFUEOQulY3-Ggu6hKVGZwjrKQNmV6kY80PRTUu2qU,5293
|
|
71
73
|
clang_tool_chain/testing/diagnostic_tests.py,sha256=GmtKWrDcddZTpx9_yIKfhRAy6YOde8dj7SksCWVEME4,6019
|
|
72
|
-
clang_tool_chain-1.
|
|
73
|
-
clang_tool_chain-1.
|
|
74
|
-
clang_tool_chain-1.
|
|
75
|
-
clang_tool_chain-1.
|
|
76
|
-
clang_tool_chain-1.
|
|
74
|
+
clang_tool_chain-1.1.2.dist-info/METADATA,sha256=XvG34S1grw40mal9rG5dGnwlfnrALxatwQMzFwQkIio,60267
|
|
75
|
+
clang_tool_chain-1.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
76
|
+
clang_tool_chain-1.1.2.dist-info/entry_points.txt,sha256=N0a0OVPkCFbf6BisRkHj-m2TcZ-f1mqxfXxAHQxfrQg,2800
|
|
77
|
+
clang_tool_chain-1.1.2.dist-info/licenses/LICENSE,sha256=51FO1oc2pZbQNI0v0_THnznnZIF4iFgawG1xnQ58kKo,10997
|
|
78
|
+
clang_tool_chain-1.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|