You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust调用Synctex C库出现段错误,如何排查修复?

Rust调用Synctex C库出现段错误的排查与修复

问题背景

在Rust项目中调用C函数解析LaTeX的synctex文件,添加FFI调用后出现段错误。已在Alpine系统生成核心转储并使用rust-gdb调试,栈跟踪显示错误来自get_pdf_pos函数,但无法定位具体问题点。

调试操作

apk add rust-gdb
# 打开核心转储
rust-gdb hub-server core.22

栈跟踪结果

/app # rust-gdb hub-server core.22
GNU gdb (GDB) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-alpine-linux-musl".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from texhub-server...
[New LWP 24]
[New LWP 22]
[New LWP 25]
[New LWP 23]
Core was generated by `./hub-server'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000000000 in ?? ()
[Current thread is 1 (LWP 24)]
(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x00007f0fb60d5043 in texhub_server::service::project::project_service::get_pdf_pos::hb310e6fc665715f6 ()
#2  0x00007f0fb61215c3 in actix_web::handler::handler_service::{{closure}}::{{closure}}::h39d982929516ac8a ()
#3  0x00007f0fb611a5d9 in <actix_web::resource::Resource<T> as actix_web::service::HttpServiceFactory>::register::{{closure}}::{{closure}}::h3bd02824b0edd4f6
    ()
#4  0x00007f0fb611a289 in <actix_web::scope::Scope<T> as actix_web::service::HttpServiceFactory>::register::{{closure}}::{{closure}}::h783ac7acd866effe ()
#5  0x00007f0fb613eca7 in actix_http::h1::dispatcher::InnerDispatcher<T,S,B,X,U>::poll_request::ha4f84a5821e588a5 ()
#6  0x00007f0fb60ef810 in <actix_http::h1::dispatcher::Dispatcher<T,S,B,X,U> as core::future::future::Future>::poll::h0990c119c53c81bb ()
#7  0x00007f0fb60f32ed in <actix_http::service::HttpServiceHandlerResponse<T,S,B,X,U> as core::future::future::Future>::poll::hb8b9ff149f3fc186 ()
#8  0x00007f0fb61b6e55 in <actix_service::and_then::AndThenServiceResponse<A,B,Req> as core::future::future::Future>::poll::h653ef60f84678589 ()
#9  0x00007f0fb60beda7 in tokio::runtime::task::core::Core<T,S>::poll::h5887eb0791d95431 ()
#10 0x00007f0fb61e0560 in tokio::runtime::task::harness::Harness<T,S>::poll::h17564a689f284eaa ()
#11 0x00007f0fb6669d3b in tokio::task::local::LocalSet::tick::h7bb0abdadba8b00f ()
#12 0x00007f0fb647fb26 in <tokio::task::local::RunUntil<T> as core::future::future::Future>::poll::h1f674f053295753b ()
#13 0x00007f0fb645edc4 in <core::pin::Pin<P> as core::future::future::Future>::poll::hb6c0a60ccc0b4122 ()
#14 0x00007f0fb647e223 in tokio::runtime::scheduler::current_thread::Context::enter::hd61e38f74364c077 ()
#15 0x00007f0fb64735f0 in tokio::runtime::context::scoped::Scoped<T>::set::h3d287d08efa14f62 ()
#16 0x00007f0fb646cc15 in tokio::runtime::context::set_scheduler::hb93173d8d88fa59b ()
#17 0x00007f0fb647e7ea in tokio::runtime::scheduler::current_thread::CoreGuard::block_on::hd292f5e65288f4ad ()
#18 0x00007f0fb64693f7 in tokio::runtime::context::runtime::enter_runtime::h716ffe00d79797d0 ()
#19 0x00007f0fb646f3dc in std::sys_common::backtrace::__rust_begin_short_backtrace::h12093c8b1ce41ac4 ()
#20 0x00007f0fb645ce0e in core::ops::function::FnOnce::call_once{{vtable.shim}}::hcf492da42f2b3495 ()
#21 0x00007f0fb68de565 in alloc::boxed::{impl#47}::call_once<(), dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global> ()
    at library/alloc/src/boxed.rs:1985
#22 alloc::boxed::{impl#47}::call_once<(), alloc::boxed::Box<dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global>, alloc::alloc::Global> ()
    at library/alloc/src/boxed.rs:1985
#23 std::sys::unix::thread::{impl#2}::new::thread_start () at library/std/src/sys/unix/thread.rs:108
#24 0x00007f0fb6910405 in start () at ../src_musl/src/thread/pthread_create.c:203
#25 0x00007f0fb6911923 in __clone () at ../src_musl/src/thread/x86_64/clone.s:22
Backtrace stopped: frame did not save the PC
(gdb)

相关Rust代码

pub fn get_pdf_pos(params: &GetPdfPosParams) {
    let proj_dir = get_proj_base_dir(&params.project_id);
    let pdf_file_name = format!(
        "{}{}",
        get_filename_without_ext(&params.file),
        ".pdf".to_owned()
    );
    let file_path = join_paths(&[&proj_dir, &pdf_file_name.to_string()]);
    warn!("pdf file path: {}", file_path);
    unsafe {
        let c_file_path = CString::new(file_path.clone());
        if let Err(e) = c_file_path {
            error!("parse out path error,{},{}", e, file_path.clone());
            return;
        }
        let c_build_path = CString::new(proj_dir.clone());
        if let Err(e) = c_build_path {
            error!("parse build path error,{},{}", e, proj_dir.clone());
            return;
        }
        warn!("start to get canner, file path: {}", file_path.clone());
        let scanner = synctex_scanner_new_with_output_file(
            c_file_path.unwrap().as_ptr(),
            c_build_path.unwrap().as_ptr(),
            1,
        );
        warn!("c scanner: {:?}", scanner);
        let tex_file_path = join_paths(&[proj_dir, params.file.clone()]);
        let c_tex_file_path = CString::new(tex_file_path);
        let node_number =
            synctex_display_query(scanner, c_tex_file_path.unwrap().as_ptr(), 1, 1, 0);
        if node_number > 0 {
            for i in 0..node_number {
                let node: synctex_node_p = synctex_scanner_next_result(scanner);
                println!("node...{:?}", node);
                let page = synctex_node_page(node);
                println!("page: {:?}", page);
                let h = synctex_node_visible_h(node);
                println!("h: {:?}", h);
                let v = synctex_node_visible_v(node);
                println!("v: {:?}", v);
                let height = synctex_node_visible_height(node);
                println!("height: {:?}", height);
                let width = synctex_node_visible_width(node);
                println!("width: {:?}", width);
            }
        }
        warn!("node_number result: {:?}", node_number);
    }
}

已将共享库libsynctex_parser.so放置在应用根目录,寻求段错误修复方案。


排查与修复步骤

1. 修复CString生命周期问题

原代码中c_file_path、c_build_path在调用unwrap().as_ptr()后,原CString值被立即丢弃,导致指针指向的内存被释放,后续C函数访问时触发段错误。需将CString绑定到变量,确保其生命周期覆盖整个C函数调用过程:

pub fn get_pdf_pos(params: &GetPdfPosParams) {
    let proj_dir = get_proj_base_dir(&params.project_id);
    let pdf_file_name = format!(
        "{}{}",
        get_filename_without_ext(&params.file),
        ".pdf"
    );
    let file_path = join_paths(&[&proj_dir, &pdf_file_name]);
    warn!("pdf file path: {}", file_path);
    
    // 提前处理CString,绑定变量延长生命周期
    let c_file_path = match CString::new(file_path.clone()) {
        Ok(s) => s,
        Err(e) => {
            error!("parse out path error: {}, path: {}", e, file_path);
            return;
        }
    };
    let c_build_path = match CString::new(proj_dir.clone()) {
        Ok(s) => s,
        Err(e) => {
            error!("parse build path error: {}, path: {}", e, proj_dir);
            return;
        }
    };
    
    unsafe {
        warn!("start to get scanner, file path: {}", file_path);
        let scanner = synctex_scanner_new_with_output_file(
            c_file_path.as_ptr(),
            c_build_path.as_ptr(),
            1,
        );
        // 检查scanner是否为空指针
        if scanner.is_null() {
            error!("Failed to create synctex scanner");
            return;
        }
        warn!("c scanner: {:?}", scanner);
        
        let tex_file_path = join_paths(&[proj_dir, params.file.clone()]);
        let c_tex_file_path = match CString::new(tex_file_path) {
            Ok(s) => s,
            Err(e) => {
                error!("parse tex path error: {}", e);
                synctex_scanner_free(scanner); // 释放已分配资源
                return;
            }
        };
        
        let node_number = synctex_display_query(
            scanner, 
            c_tex_file_path.as_ptr(), 
            1, 
            1, 
            0
        );
        warn!("node_number result: {:?}", node_number);
        
        if node_number > 0 {
            for _ in 0..node_number {
                let node = synctex_scanner_next_result(scanner);
                // 检查node是否为空指针
                if node.is_null() {
                    warn!("Got null synctex node, breaking loop");
                    break;
                }
                println!("node...{:?}", node);
                let page = synctex_node_page(node);
                println!("page: {:?}", page);
                let h = synctex_node_visible_h(node);
                println!("h: {:?}", h);
                let v = synctex_node_visible_v(node);
                println!("v: {:?}", v);
                let height = synctex_node_visible_height(node);
                println!("height: {:?}", height);
                let width = synctex_node_visible_width(node);
                println!("width: {:?}", width);
            }
        }
        
        // 必须释放Synctex scanner资源,避免内存泄漏
        synctex_scanner_free(scanner);
    }
}

2. 空指针检查

  • 调用synctex_scanner_new_with_output_file后,立即检查返回的scanner是否为空,空指针直接访问会触发段错误。
  • 调用synctex_scanner_next_result后,检查返回的node是否为空,避免后续访问空指针成员。

3. 资源释放

Synctex的scanner需要手动调用synctex_scanner_free释放,否则会造成内存泄漏,长期运行可能引发其他问题。

4. 共享库加载验证

在Alpine系统中确保共享库被正确加载:

  • 将libsynctex_parser.so移动到/usr/lib目录,或者运行时指定库路径:
LD_LIBRARY_PATH=. ./hub-server
  • 编译时确保链接器能找到该库,在Cargo.toml中添加:
[build-dependencies]
cc = "1.0"

[dependencies]
libc = "0.2"

并创建build.rs文件指定链接参数:

fn main() {
    println!("cargo:rustc-link-search=.");
    println!("cargo:rustc-link-lib=synctex_parser");
}

5. 增强调试定位

如果问题仍存在,使用rust-gdb切换到错误栈帧查看变量值:

(gdb) frame 1
(gdb) print file_path
(gdb) print scanner

内容的提问来源于stack exchange,提问作者spark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 20:42:03