Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ validated protocol record or explicit failure state

| Stage | Invariants | Enforcement and failure behavior |
|---|---|---|
| Target selection | `IDENT-1` | `-p`/`-t` mode semantics, PID filters, and namespace-aware discovery define the allowed runtime scope; unavailable scope fails setup. |
| Target selection | `SCOPE-1`, `IDENT-1` | Linux x86_64 build guards and target ELF validation establish the supported platform; `-p`/`-t` mode semantics, PID filters, and namespace-aware discovery define the allowed runtime scope. |
| Module and debug information | `IDENT-1`, `SEM-1` | Runtime mappings, module cookies, load offsets, and available debug-file identity checks bind semantics to a module; unverifiable or loose matches are surfaced as weakened evidence. |
| Semantic planning | `SEM-1`, `FAIL-1` | The DWARF engine resolves scope, type, location, and relocation at the probe PC; unsupported plans produce diagnostics instead of guessed values. |
| eBPF execution | `SAFE-1`, `COST-1` | The compiler emits bounded observational programs, and the kernel verifier rejects unsafe programs. |
Expand Down
24 changes: 14 additions & 10 deletions docs/design-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ does not permit known unsupported paths to return plausible but unproven data.

## Supported Operating Envelope

- Official release artifacts and correctness testing target Linux x86_64.
Source code compiling on another architecture does not make that architecture
supported.
- Official release artifacts and correctness testing target Linux x86_64. The
runtime build rejects other build targets, and trace setup rejects target
objects that are not 64-bit little-endian x86_64 ELF files.
- Source-aware values require DWARF that corresponds to the module being
traced. Embedded DWARF is read from that module. For separate debug files,
available `.gnu_debuglink` CRC and Build ID evidence is checked strictly by
Expand Down Expand Up @@ -60,13 +60,17 @@ but it cannot guarantee unchanged timing.
**Guarantee.** Official builds and correctness claims are scoped to Linux
x86_64.

**Enforcement.** Release artifacts are built and named for x86_64, and
architecture-specific register, ABI, TLS, and unwind behavior is tested in that
environment.
**Enforcement.** The platform crate has a compile-time Linux x86_64 guard that
makes the runtime build fail on other targets. The installer rejects unsupported
hosts or requested architectures, and trace setup validates `-t` objects, `-p`
process executables, and loaded DWARF modules before semantic interpretation.
Release artifacts are built and named for x86_64, and architecture-specific
register, ABI, TLS, and unwind behavior is tested in that environment.

**Failure boundary.** Other architectures must not be presented as supported
until their platform mappings and end-to-end behavior are implemented and
tested. Accidental compilation is not evidence of support.
**Failure boundary.** An unsupported host fails to build or install. An
unsupported target object fails setup before DWARF interpretation or uprobe
attachment. Other architectures must not be presented as supported until their
platform mappings and end-to-end behavior are implemented and tested.

### SAFE-1: Observation does not control the target

Expand Down Expand Up @@ -189,7 +193,7 @@ main evidence currently lives in these areas:

| Invariant | Primary evidence |
|---|---|
| `SCOPE-1` | x86_64 release workflow and platform-specific unit tests |
| `SCOPE-1` | x86_64 release workflow, platform-specific unit tests, installer rejection, and unsupported-target ELF tests |
| `SAFE-1` | Script/compiler operation surface, eBPF helper usage, verifier-backed load tests |
| `IDENT-1` | PID-specific execution tests and container-topology tests |
| `SEM-1` | PC-context, scalar, global, optimized-code, and cross-module fixtures with exact-value oracles |
Expand Down
2 changes: 1 addition & 1 deletion docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ only that a command exits successfully.

| Invariant | Minimum evidence when affected |
|---|---|
| `SCOPE-1` | Build/release target remains Linux x86_64, plus platform-specific unit coverage |
| `SCOPE-1` | Build/release target remains Linux x86_64, plus installer and unsupported-target ELF rejection coverage |
| `SAFE-1` | Generated helper/operation review and a verifier-backed load test for new eBPF behavior |
| `IDENT-1` | Positive target attribution and a wrong-PID/module/namespace negative case |
| `SEM-1` | A fixture with an exact source-level value oracle at a known PC, including relevant optimization/module variants |
Expand Down
4 changes: 4 additions & 0 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
- Stable eBPF for tracing - recommended Linux 4.4+
- **Architecture**: x86_64 (AMD64) only currently

The installer rejects non-Linux hosts, non-x86_64 hosts, and unsupported
`--arch` values. The runtime also rejects target executables and shared objects
that are not 64-bit little-endian x86_64 ELF files.

## Installation

### Quick Install Script (Recommended)
Expand Down
2 changes: 1 addition & 1 deletion docs/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Both uprobes and GDB install breakpoint-based instrumentation in the target proc
GhostScope's script and compiler surface is observational: it does not provide operations that intentionally modify application-visible state, variable values, or control flow. This does not mean the target is untouched. Uprobes install trap points, and each hit synchronously adds uprobe and eBPF execution to the target thread.

### 4. Platform and Architecture Support
Official builds and correctness testing currently support **Linux x86_64 (AMD64)** only because GhostScope depends on Linux **eBPF** and **uprobes** and currently implements x86_64 register, ABI, TLS, and unwind behavior. Other architectures are outside the support contract even if the source happens to compile on them.
Official builds and correctness testing currently support **Linux x86_64 (AMD64)** only because GhostScope depends on Linux **eBPF** and **uprobes** and currently implements x86_64 register, ABI, TLS, and unwind behavior. The runtime build fails on other build targets, the installer rejects non-Linux systems and other architectures, and trace setup rejects target objects that are not 64-bit little-endian x86_64 ELF files.

## Soft Limitations

Expand Down
2 changes: 1 addition & 1 deletion docs/zh/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ GhostScope 的源码语义并不来自某一个独立组件。最终展示的值

| 阶段 | 不变量 | 保证机制与失败行为 |
|---|---|---|
| 目标选择 | `IDENT-1` | `-p`/`-t` 模式语义、PID 过滤和感知 namespace 的进程发现共同定义允许运行的范围;范围不可用时 setup 失败。 |
| 目标选择 | `SCOPE-1`、`IDENT-1` | Linux x86_64 构建守卫和目标 ELF 验证建立平台边界;`-p`/`-t` 模式语义、PID 过滤和感知 namespace 的进程发现共同定义允许运行的范围。 |
| 模块与调试信息 | `IDENT-1`、`SEM-1` | 运行时映射、模块 Cookie、加载偏移和可用的调试文件身份检查把语义绑定到模块;无法验证或 loose match 的情况会被标记为证据减弱。 |
| 语义规划 | `SEM-1`、`FAIL-1` | DWARF 引擎在 probe PC 上解析作用域、类型、位置和重定位;不支持的计划产生诊断,而不是猜测值。 |
| eBPF 执行 | `SAFE-1`、`COST-1` | 编译器生成有界的观测程序,内核 verifier 拒绝不安全程序。 |
Expand Down
8 changes: 4 additions & 4 deletions docs/zh/design-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ GhostScope 把运行中程序的机器状态转换为源码感知的观测结果

## 支持范围

- 官方发布产物和正确性测试面向 Linux x86_64。源码可以在其他架构上编译,不代表该架构受到支持
- 官方发布产物和正确性测试面向 Linux x86_64。运行时构建会拒绝其他构建目标,trace setup 也会拒绝不是 64 位小端 x86_64 ELF 的目标文件
- 源码级值依赖与目标模块对应的 DWARF。内嵌 DWARF 直接来自目标模块;独立调试文件默认严格检查可用的 `.gnu_debuglink` CRC 和 Build ID 证据。
- C 语言的端到端覆盖最完整。C++ 和 Rust 目前主要采用能力更有限的“按 DWARF 布局访问”模型。具体语言覆盖见[使用限制](limitations.md)。
- 内核必须提供所需的 eBPF、uprobe 和事件传输能力,GhostScope 也必须具备使用这些设施的权限。
Expand All @@ -29,9 +29,9 @@ GhostScope 依赖 Linux 内核、eBPF verifier 和 helper、目标模块及编

**保证。** 官方构建和正确性声明的范围是 Linux x86_64。

**保证机制。** 发布产物按 x86_64 构建和命名,架构相关的寄存器、ABI、TLS 与 unwind 行为也在该环境中验证。
**保证机制。** platform crate 使用 Linux x86_64 编译期守卫,使运行时构建在其他目标上失败;安装器拒绝不支持的主机或请求架构;trace setup 在语义解释前验证 `-t` 目标、`-p` 进程可执行文件和实际加载的 DWARF 模块。发布产物按 x86_64 构建和命名,架构相关的寄存器、ABI、TLS 与 unwind 行为也在该环境中验证。

**失败边界。** 在完成平台映射和端到端测试之前,其他架构不能被描述为已支持。源码偶然能够编译不构成支持证据
**失败边界。** 不支持的主机无法构建或安装;不支持的目标文件会在 DWARF 解释或 uprobe 附加之前让 setup 失败。在完成平台映射和端到端测试之前,其他架构不能被描述为已支持。

### SAFE-1:观测不能控制目标程序

Expand Down Expand Up @@ -113,7 +113,7 @@ GhostScope 依赖 Linux 内核、eBPF verifier 和 helper、目标模块及编

| 不变量 | 主要证据 |
|---|---|
| `SCOPE-1` | x86_64 release workflow 和平台相关单元测试 |
| `SCOPE-1` | x86_64 release workflow、平台相关单元测试、安装器拒绝和不支持目标 ELF 测试 |
| `SAFE-1` | 脚本/编译器操作面、eBPF helper 使用范围、经过 verifier 的加载测试 |
| `IDENT-1` | PID 专属执行测试和容器拓扑测试 |
| `SEM-1` | PC 上下文、标量、全局变量、优化代码和跨模块 fixture 的精确值 oracle |
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ docker build -t ghostscope-builder:ubuntu20.04 -f docker/base-build/Dockerfile .

| 不变量 | 受影响时的最低证据要求 |
|---|---|
| `SCOPE-1` | 构建/发布目标仍是 Linux x86_64,并具有平台相关单元测试 |
| `SCOPE-1` | 构建/发布目标仍是 Linux x86_64,并覆盖安装器和不支持目标 ELF 的拒绝路径 |
| `SAFE-1` | 审查新增的 helper/操作,并为新的 eBPF 行为提供经过 verifier 的加载测试 |
| `IDENT-1` | 正向目标归因,以及错误 PID/模块/namespace 的负向场景 |
| `SEM-1` | 在已知 PC 上使用具有精确源码级值 oracle 的 fixture,并覆盖相关优化或模块变体 |
Expand Down
2 changes: 2 additions & 0 deletions docs/zh/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
- 稳定的 eBPF 追踪支持 - 建议 Linux 4.4+
- **架构**: 目前仅支持 x86_64 (AMD64)

安装器会拒绝非 Linux 主机、非 x86_64 主机和不支持的 `--arch` 参数。运行时也会拒绝不是 64 位小端 x86_64 ELF 的目标可执行文件或共享库。

## 安装

### 快速安装脚本(推荐)
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ uprobe 和 GDB 都会在目标进程中安装基于断点的探测机制,同
GhostScope 的脚本和编译器能力面只用于观测,不提供主动修改应用可见状态、变量值或控制流的操作。但这不代表目标完全不受影响:uprobe 会安装 trap 点,每次命中都会让目标线程同步承担 uprobe 和 eBPF 执行。

### 4. 平台与架构支持
官方构建和正确性测试目前只支持 **Linux x86_64 (AMD64)**。GhostScope 依赖 Linux **eBPF** 和 **uprobe**,并且当前实现的是 x86_64 寄存器、ABI、TLS 和 unwind 行为。即使源码偶然能在其他架构上编译,这些架构仍不在支持契约内
官方构建和正确性测试目前只支持 **Linux x86_64 (AMD64)**。GhostScope 依赖 Linux **eBPF** 和 **uprobe**,并且当前实现的是 x86_64 寄存器、ABI、TLS 和 unwind 行为。运行时构建会在其他构建目标上失败,安装器会拒绝非 Linux 系统和其他架构,trace setup 也会拒绝不是 64 位小端 x86_64 ELF 的目标文件

## 软性限制

Expand Down
42 changes: 42 additions & 0 deletions e2e-tests/tests/startup_load_report_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,48 @@ async fn test_startup_report_shows_explicit_debug_file_failure() -> Result<()> {
Ok(())
}

#[tokio::test]
#[serial_test::serial]
async fn test_startup_rejects_aarch64_target() -> Result<()> {
init();

if !is_host_topology() {
println!("skipping target architecture e2e outside host->host topology");
return Ok(());
}

let fixture = ensure_startup_report_fixture()?;
let temp_dir = TempDir::new().context("failed to create unsupported target temp dir")?;
let target = temp_dir.path().join("aarch64-target");
let mut target_bytes = fs::read(&fixture.embedded_binary).with_context(|| {
format!(
"failed to read fixture binary {}",
fixture.embedded_binary.display()
)
})?;
target_bytes[18..20].copy_from_slice(&183u16.to_le_bytes());
fs::write(&target, target_bytes)
.with_context(|| format!("failed to write unsupported target {}", target.display()))?;

let run = run_startup_report_command_for_binary(&fixture, &target, &[])?;

assert!(
!run.status.success(),
"AArch64 target should fail before DWARF loading\n{}",
run.output
);
assert_output_contains(&run.output, "unsupported target object");
assert_output_contains(&run.output, "expected 64-bit little-endian x86_64 ELF");
assert_output_contains(&run.output, "architecture=Aarch64");
assert!(
!run.output.contains("DWARF ready:"),
"unsupported target must not reach DWARF-ready state\n{}",
run.output
);

Ok(())
}

#[derive(Debug, Clone)]
struct StartupReportFixture {
binary: PathBuf,
Expand Down
11 changes: 11 additions & 0 deletions ghostscope-dwarf/src/objfile/loading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ impl LoadedObjfile {
);

let binary_mapped = Arc::new(MappedFile::open(&module_mapping.path)?);
let binary_object = binary_mapped.parse_object().map_err(|error| {
anyhow::anyhow!(
"Failed to parse target object {}: {}",
module_mapping.path.display(),
error
)
})?;
ghostscope_process::ensure_supported_target_object(&binary_object, &module_mapping.path)?;
drop(binary_object);

let (dwarf, mapped_file_for_dwarf, debug_info_source) = if let Some(debug_file_path) =
explicit_debug_file
{
Expand Down Expand Up @@ -564,6 +574,7 @@ impl LoadedObjfile {

fn load_dwarf_sections(file_data: &Arc<MappedFile>) -> Result<DwarfData> {
let object = file_data.parse_object()?;
ghostscope_process::ensure_supported_target_object(&object, &file_data.path)?;
let endian = dwarf_endian_from_object(&object);

let load_section = |id: gimli::SectionId| -> Result<_> {
Expand Down
3 changes: 3 additions & 0 deletions ghostscope-platform/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
compile_error!("GhostScope runtime currently supports only Linux x86_64");

/// Platform-specific utilities for the GhostScope eBPF debugging tool
///
/// This crate provides platform-specific abstractions for:
Expand Down
5 changes: 5 additions & 0 deletions ghostscope-process/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod offsets;
pub mod pid;
pub mod pinned_bpf_maps;
pub mod proc_maps;
pub mod target_arch;
pub use offsets::{PidOffsetsEntry, ProcessManager, SectionOffsets};
pub use pid::{
build_runtime_pid_plan, detect_runtime_environment, host_pid_for_proc_pid,
Expand All @@ -15,4 +16,8 @@ pub use pid::{
pub mod sysmon;
pub use sysmon::{ProcessSysmon, SysEvent, SysEventKind, SysmonConfig, SysmonEventMask};
pub mod util;
pub use target_arch::{
ensure_supported_pid_executable, ensure_supported_target_object, ensure_supported_target_path,
SUPPORTED_TARGET_DESCRIPTION,
};
pub use util::is_shared_object;
112 changes: 112 additions & 0 deletions ghostscope-process/src/target_arch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use anyhow::{Context, Result};
use memmap2::MmapOptions;
use object::{Architecture, BinaryFormat, Endianness, Object};
use std::fs::File;
use std::path::{Path, PathBuf};

pub const SUPPORTED_TARGET_DESCRIPTION: &str = "64-bit little-endian x86_64 ELF";

pub fn ensure_supported_target_path(path: &Path) -> Result<()> {
let file = File::open(path)
.with_context(|| format!("failed to open target object {}", path.display()))?;
// SAFETY: The file is opened read-only and the mapping is used immutably
// only for object-header validation during this call.
let mapped = unsafe { MmapOptions::new().map(&file) }
.with_context(|| format!("failed to map target object {}", path.display()))?;
let object = object::File::parse(&mapped[..])
.with_context(|| format!("failed to parse target object {}", path.display()))?;

ensure_supported_target_object(&object, path)
}

pub fn ensure_supported_pid_executable(pid: u32) -> Result<()> {
let executable = PathBuf::from(format!("/proc/{pid}/exe"));
ensure_supported_target_path(&executable)
.with_context(|| format!("target process {pid} is not supported"))
}

pub fn ensure_supported_target_object(object: &object::File<'_>, path: &Path) -> Result<()> {
let supported = object.format() == BinaryFormat::Elf
&& object.architecture() == Architecture::X86_64
&& object.is_64()
&& object.endianness() == Endianness::Little;

if !supported {
anyhow::bail!(
"unsupported target object {}: expected {}, found format={:?}, architecture={:?}, \
class={}, endianness={:?}",
path.display(),
SUPPORTED_TARGET_DESCRIPTION,
object.format(),
object.architecture(),
if object.is_64() { "64-bit" } else { "32-bit" },
object.endianness(),
);
}

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;

const EM_386: u16 = 3;
const EM_X86_64: u16 = 62;
const EM_AARCH64: u16 = 183;

fn elf64(machine: u16) -> [u8; 64] {
let mut elf = [0u8; 64];
elf[0..4].copy_from_slice(b"\x7fELF");
elf[4] = 2;
elf[5] = 1;
elf[6] = 1;
elf[16..18].copy_from_slice(&2u16.to_le_bytes());
elf[18..20].copy_from_slice(&machine.to_le_bytes());
elf[20..24].copy_from_slice(&1u32.to_le_bytes());
elf[52..54].copy_from_slice(&64u16.to_le_bytes());
elf
}

fn elf32(machine: u16) -> [u8; 52] {
let mut elf = [0u8; 52];
elf[0..4].copy_from_slice(b"\x7fELF");
elf[4] = 1;
elf[5] = 1;
elf[6] = 1;
elf[16..18].copy_from_slice(&2u16.to_le_bytes());
elf[18..20].copy_from_slice(&machine.to_le_bytes());
elf[20..24].copy_from_slice(&1u32.to_le_bytes());
elf[40..42].copy_from_slice(&52u16.to_le_bytes());
elf
}

fn validate_bytes(bytes: &[u8]) -> Result<()> {
let mut file = tempfile::NamedTempFile::new()?;
file.write_all(bytes)?;
ensure_supported_target_path(file.path())
}

#[test]
fn accepts_x86_64_elf() {
validate_bytes(&elf64(EM_X86_64)).expect("x86_64 ELF should be supported");
}

#[test]
fn rejects_aarch64_elf() {
let error = validate_bytes(&elf64(EM_AARCH64)).expect_err("AArch64 must be rejected");
let message = error.to_string();
assert!(message.contains(SUPPORTED_TARGET_DESCRIPTION));
assert!(message.contains("architecture=Aarch64"));
}

#[test]
fn rejects_32_bit_x86_elf() {
let error = validate_bytes(&elf32(EM_386)).expect_err("32-bit x86 must be rejected");
let message = error.to_string();
assert!(message.contains(SUPPORTED_TARGET_DESCRIPTION));
assert!(message.contains("architecture=I386"));
assert!(message.contains("class=32-bit"));
}
}
Loading
Loading