1 คะแนน โดย GN⁺ 3 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Cpp2Rust เป็นตัวแปลแบบอาศัยไวยากรณ์ที่ทำงานบนพื้นฐานของ clang AST โดยแปลงโค้ด C++ อินพุตให้เป็นโค้ด Rust ที่ปลอดภัยทั้งหมดโดยอัตโนมัติ
  • กระบวนการแปลจะพาร์สไฟล์ C++ ด้วย clang เพื่อสร้าง AST จากนั้นเดิน AST และสร้างโค้ด Rust ออกมาในรูปสตริง ก่อนจัดรูปแบบและส่งออกเป็นไฟล์ .rs เดียวด้วย rustfmt
  • ค่าเริ่มต้นคือ โมเดล reference counting และสามารถสร้าง unsafe Rust ได้ด้วย --model=unsafe เพื่อใช้ในการดีบักและเปรียบเทียบประสิทธิภาพ
  • โค้ดที่สร้างขึ้นพึ่งพาไลบรารีรันไทม์ libcc2rs และพอยน์เตอร์แบบ C จะถูกแปลงเป็น Ptr<T> ที่จำลอง null, arithmetic และ aliasing
  • การแปลทั้งโปรแกรมต้องใช้ compile_commands.json และสำหรับโปรเจกต์ CMake สามารถสร้างได้ด้วยแฟลก CMAKE_EXPORT_COMPILE_COMMANDS=ON

แปลงจาก C++ เป็น Rust ที่ปลอดภัยโดยอัตโนมัติ

  • Cpp2Rust เป็นเครื่องมือที่แปล C++ ให้เป็น Rust ที่ปลอดภัยทั้งหมดโดยอัตโนมัติ
  • เป็น ตัวแปลแบบอาศัยไวยากรณ์ ที่ทำงานบนพื้นฐานของ AST ของ clang
  • อัลกอริทึมการแปลอธิบายไว้ในงานวิจัยที่ตีพิมพ์ใน PLDI 2026 ชื่อ Cpp2Rust: Automatic Translation of C++ to Safe Rust

ไปป์ไลน์การแปล

  • เริ่มจากพาร์สไฟล์ C++ อินพุตด้วย clang เพื่อสร้าง AST
  • จากนั้นเดิน AST และส่งออกโค้ด Rust ในรูปสตริง
  • หากจำเป็นจะมีการแทรกการเรียกใช้ไลบรารีรันไทม์ libcc2rs
    • ตัวอย่างคือการจัดการ raw pointer semantics
  • ขั้นสุดท้ายใช้ rustfmt เพื่อจัดระเบียบโค้ด Rust เป็นไฟล์ .rs เดียว

โมเดล Rust ที่ปลอดภัยและ unsafe Rust

  • การทำงานปริยายใช้ โมเดล reference counting และสร้างเอาต์พุตเป็น Rust ที่ปลอดภัยทั้งหมด
  • มีตัวสร้าง unsafe Rust ให้ด้วย
    • อาร์กิวเมนต์บรรทัดคำสั่งคือ --model=unsafe
    • ใช้สำหรับ การดีบัก และการเปรียบเทียบประสิทธิภาพ

ไลบรารีรันไทม์ libcc2rs

  • โค้ดที่สร้างขึ้นพึ่งพา ไลบรารีรันไทม์ ที่ออกแบบมาเพื่อทำให้กระบวนการแปลงง่ายขึ้น
  • พอยน์เตอร์แบบ C จะถูกแปลงเป็นชนิด Ptr<T> ที่ libcc2rs จัดเตรียมไว้ให้
  • Ptr<T> ใช้จำลองความหมายของพอยน์เตอร์แบบ C
    • null
    • pointer arithmetic
    • aliasing
  • borrow checker ของ Rust จะถูกทำให้ผ่านได้ด้วย การทำงานรันไทม์ที่มีการตรวจสอบ

ขั้นตอนการบิลด์และรัน

  • ตัวอย่างการติดตั้ง dependency บน Ubuntu มี libclang-22-dev, clang++-22, ninja-build, cmake, ruff
  • การบิลด์ทำตามลำดับ cmake -GNinja .., ninja, ninja check
  • คำสั่งสำหรับแปลไฟล์เดี่ยวมีรูปแบบดังนี้
./build/cpp2rust/cpp2rust --file=<file>.cpp -o=<file>.rs
  • หากต้องการสร้าง unsafe Rust ให้รันดังนี้
./build/cpp2rust/cpp2rust --file=<file>.cpp -o=<file>.rs --model=unsafe

ตัวอย่างขั้นต่ำและการแปลทั้งโปรแกรม

  • ตัวอย่าง printf("hello world\n") ใน hello.cpp จะถูกแปลงเป็นโค้ดที่มี println!("hello world") ของ Rust
  • hello.rs ที่แปลงแล้วสามารถคอมไพล์และรันได้ดังนี้
rustc hello.rs -L ../libcc2rs/target/debug
./hello
  • การแปลทั้งโปรแกรมต้องใช้ compile_commands.json
  • ใน CMake สามารถสร้าง compile_commands.json ได้ด้วยแฟลกต่อไปนี้
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
  • คำสั่งแปลทั้งโปรแกรมมีรูปแบบดังนี้
./build/cpp2rust/cpp2rust --dir=<dir> -o <output>.rs
  • <dir> ต้องเป็นไดเรกทอรีที่มี compile_commands.json

คำสั่งทดสอบ

  • รันการทดสอบทั้งหมดด้วย ninja check
  • รัน unit test ด้วย ninja check-unit
  • รัน unit test ของ libcc2rs ด้วย ninja check-libcc2rs
  • รัน unit test ของ libcc2rs-macros ด้วย ninja check-libcc2rs-macros
  • หากต้องการสร้างเอาต์พุตที่คาดหวังใหม่หลังจากแก้ไขโดยตั้งใจ ให้ใช้ REPLACE_EXPECTED=1 ninja check-unit

1 ความคิดเห็น

 
GN⁺ 3 시간 전
ความคิดเห็นจาก Lobste.rs
  • ผมได้ฟังการนำเสนอของ Dan Wallach เมื่อเดือนกุมภาพันธ์ปีนี้ ซึ่งเขาแนะนำโครงการ TRACTOR(Translating All C to Rust) ของ DARPA [1] [2] [3]
    เห็นว่าการทดสอบและประเมินผลของโปรแกรมวิจัยนี้ดำเนินการโดยทีม MIT Lincoln Laboratory
    เท่าที่ผมรู้ยังอยู่ในขั้นวิจัย แต่ผมสนใจหัวข้อนี้มาก และค่อนข้างอยากรู้ว่าเครื่องมือกับไอเดียแบบนี้จะออกมาอย่างไร
    ดูเหมือนว่าจะช่วยกำจัดบั๊ก “เล็กๆ น้อยๆ” จำนวนมากที่มาจากการจัดการหน่วยความจำได้จริง
    [1] https://www.darpa.mil/research/programs/translating-all-c-to-rust
    [2] https://github.com/DARPA-TRACTOR-Program
    [3] https://ll.mit.edu/r-d/projects/…

  • น่าสนใจ ถ้าได้เห็น เดโมของไลบรารีหรือแอป สักหนึ่งหรือสองตัวที่ผู้เขียนแปลงด้วยเครื่องมือนี้ และผู้เขียนเองก็มองว่าน่าสนใจ ก็คงดีมาก

    • ที่ https://github.com/Cpp2Rust/cpp2rust-testsuite มีตัวอย่างจากงานวิจัยที่เกี่ยวข้อง และมีหลายเวอร์ชันรวมถึง WOFF2, brotli ฯลฯ
      เช่น ถ้าดู https://github.com/Cpp2Rust/cpp2rust-testsuite/… ก็อาจเป็น Rust ที่ปลอดภัย แต่แน่นอนว่ายากจะเรียกว่าเป็น Rust แบบ idiomatic
  • ผมไม่ค่อยเข้าใจว่าทำไมต้องมี ขั้นตอนการแปลง ถ้ามีข้อมูลเพียงพอที่จะเปลี่ยนเป็น Rust ที่ปลอดภัยด้านหน่วยความจำได้ ก็แปลว่าน่าจะมีข้อมูลพอให้รับประกันด้วย static analysis ได้ว่า C++ ก็ปลอดภัยไม่ใช่หรือ?

    • เป็นวิธีที่ใส่ การตรวจสอบใหม่ ระหว่างกระบวนการแปลง จึงใกล้เคียงกับการประนีประนอมว่า “โค้ดใหม่จะล้มเหลวและหยุดทำงานอย่างชัดเจน แทนที่จะทำให้หน่วยความจำเสียหาย” มากกว่าจะบอกว่า “โค้ดเดิมปลอดภัยอยู่แล้ว”
    • มีเรื่องนี้อยู่ตอนต้นของเปเปอร์ที่ลิงก์ไว้ใน README
      เขาระบุว่าช่องโหว่ด้านความปลอดภัยในซอฟต์แวร์ที่กระจายใช้อย่างแพร่หลายราว 70% มาจาก บั๊กด้าน memory safety ในภาษาอย่าง C และ C++ และแม้จะลงทุนกับมาตรการลดความเสี่ยงอย่าง static analysis, sanitizer และการแยกโดเมนด้วยฮาร์ดแวร์มาหลายทศวรรษ ผู้โจมตีก็ยังคงใช้ประโยชน์จากการดำเนินการกับหน่วยความจำที่ไม่ปลอดภัยได้
      เขาอธิบายว่าทางออกที่มีแนวโน้มดีในระยะยาวคือการย้ายโค้ดเบส C++ เดิมไปยังภาษาที่ปลอดภัยด้านหน่วยความจำอย่าง Rust แต่การทำด้วยมือมีต้นทุนสูงเกินไปและเกิดข้อผิดพลาดได้ง่าย
      Cpp2Rust อ้างว่าเป็นระบบแรกที่สามารถแปลงโปรแกรม C++ โดยอัตโนมัติให้เป็นโค้ด Rust ที่ทำงานเทียบเท่ากันและปลอดภัยด้านหน่วยความจำ
      โดยยอมแลกประสิทธิภาพบางส่วนกับความปลอดภัย และแก้ความไม่สอดคล้องพื้นฐานระหว่างการอ้างอิงแบบ alias ที่ไม่จำกัดของ C++ กับโมเดล ownership ของ Rust ด้วยการแทรกการตรวจสอบ ownership และ mutability ตอนรันไทม์ เพื่อรักษาความหมายเดิมพร้อมรับประกันความปลอดภัย
      นอกจากนี้ยังสร้างการปรับแต่งแบบข้ามซอร์สสำหรับโค้ด Rust เพื่อลด overhead ของการตรวจสอบแบบไดนามิก และลบการดำเนินการด้าน ownership ที่ซ้ำซ้อน เพื่อกู้ประสิทธิภาพที่เสียไปกลับมาได้มากพอสมควร
      ดังนั้นแรงจูงใจไม่ใช่แค่การพิสูจน์ความปลอดภัย แต่เป็นการ พอร์ตไปยัง Rust และทำให้การพัฒนาต่อจากนี้ย้ายไป Rust ได้ง่ายขึ้น
    • ไม่มีข้อมูลเพียงพอ และตาม ทฤษฎีบทของ Rice ต่อไปก็จะไม่มี
      ไอเดียของโปรเจกต์แบบนี้คือการสร้างจุดเริ่มต้นสำหรับรีแฟกเตอร์ต่อให้เป็น Rust แบบ idiomatic มากขึ้น และเมื่อทำเช่นนั้นก็จะทำให้โค้ดวิเคราะห์ได้
      หวังว่าจะสามารถเอาการตรวจสอบตอนรันไทม์หรือส่วนที่ไม่ปลอดภัยออกจากโค้ดที่แปลงแล้วได้ด้วย
      อาจบอกได้ว่า “ก็รีแฟกเตอร์ C++ ให้เป็นทรง Rust แล้วทำให้วิเคราะห์ได้เหมือนกันก็พอไม่ใช่หรือ” แต่ C++ มี semantics ที่ต่างออกไปและอนุญาตความยืดหยุ่นมากกว่า ทำให้เส้นทางที่ต้องวิเคราะห์เพิ่มขึ้นแบบเอ็กซ์โปเนนเชียล หรือทำให้ static analysis ไปติดทางตันที่คลุมเครือได้
      คุณต้องสัญญาว่าจะไม่ทำสิ่งที่สร้างงานยากให้ static analyzer และไม่ใช้ประโยชน์จากกรณีขอบต่างๆ ซึ่งถ้าจะทำเช่นนั้นก็อาจต้องสร้าง dialect ของ C++ ที่มี semantics จำกัดมากขึ้นและมี annotation เพิ่มเติม
      Circle/Safe C++ แสดงให้เห็นว่ามีความเป็นไปได้ แต่ C++ WG ปฏิเสธแนวทางนั้นอย่างหนัก ดังนั้นจึงเหลือทางเลือกแค่เขียน dialect ของ C++ ที่คล้าย Rust และไม่ได้รับการสนับสนุนเอง หรือไม่ก็ใช้ Rust ไปเลย
  • ถ้าโค้ดอินพุตเอง ไม่ปลอดภัย จริงๆ แล้วจะทำงานอย่างไร?

    • จัดการด้วย การตรวจสอบตอนรันไทม์ ดังนั้นทันทีที่เกิดการละเมิด ก็สามารถแครชอย่างปลอดภัยและกำหนดผลได้ แทนที่จะทำให้หน่วยความจำเสียหาย