From 18183e7fb5bf4204465c04ab41ec8682caf47631 Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Sun, 6 Sep 2026 18:45:04 +0800 Subject: [PATCH] llvm: bind LLVM 22 pointer address extraction --- PtrToAddrBindings.cpp | 18 +++++++ PtrToAddrBindings.h | 18 +++++++ ptrtoaddr_llvm22.go | 32 ++++++++++++ ptrtoaddr_llvm22_test.go | 108 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 PtrToAddrBindings.cpp create mode 100644 PtrToAddrBindings.h create mode 100644 ptrtoaddr_llvm22.go create mode 100644 ptrtoaddr_llvm22_test.go diff --git a/PtrToAddrBindings.cpp b/PtrToAddrBindings.cpp new file mode 100644 index 0000000..cae9667 --- /dev/null +++ b/PtrToAddrBindings.cpp @@ -0,0 +1,18 @@ +#include "PtrToAddrBindings.h" +#include "llvm/Config/llvm-config.h" + +#if LLVM_VERSION_MAJOR >= 22 +#include "llvm/IR/Constants.h" +#include "llvm/IR/IRBuilder.h" + +using namespace llvm; + +LLVMValueRef LLVMGoBuildPtrToAddr(LLVMBuilderRef B, LLVMValueRef V, + const char *Name) { + return wrap(unwrap(B)->CreatePtrToAddr(unwrap(V), Name)); +} + +LLVMValueRef LLVMGoConstPtrToAddr(LLVMValueRef V, LLVMTypeRef T) { + return wrap(ConstantExpr::getPtrToAddr(unwrap(V), unwrap(T))); +} +#endif diff --git a/PtrToAddrBindings.h b/PtrToAddrBindings.h new file mode 100644 index 0000000..53a9a6d --- /dev/null +++ b/PtrToAddrBindings.h @@ -0,0 +1,18 @@ +#ifndef LLVM_BINDINGS_GO_PTRTOADDR_H +#define LLVM_BINDINGS_GO_PTRTOADDR_H + +#include "llvm-c/Core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LLVMValueRef LLVMGoBuildPtrToAddr(LLVMBuilderRef B, LLVMValueRef V, + const char *Name); +LLVMValueRef LLVMGoConstPtrToAddr(LLVMValueRef V, LLVMTypeRef T); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ptrtoaddr_llvm22.go b/ptrtoaddr_llvm22.go new file mode 100644 index 0000000..b461979 --- /dev/null +++ b/ptrtoaddr_llvm22.go @@ -0,0 +1,32 @@ +//go:build llvm22 || (!llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21) + +package llvm + +/* +#include "PtrToAddrBindings.h" +#include +*/ +import "C" +import "unsafe" + +const PtrToAddr Opcode = C.LLVMPtrToAddr + +// CreatePtrToAddr extracts the address of val without capturing its provenance. +// The result uses the pointer address space's index width from the containing +// module's DataLayout (and preserves the shape of pointer vectors). The builder +// must have an insertion point in a module with the intended DataLayout. +// Available with LLVM 22 and later. +func (b Builder) CreatePtrToAddr(val Value, name string) (v Value) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + v.C = C.LLVMGoBuildPtrToAddr(b.C, val.C, cname) + return +} + +// ConstPtrToAddr extracts a constant pointer's address without capturing its +// provenance. t must have the pointer address space's DataLayout index width +// and the same scalar or vector shape as val. Available with LLVM 22 and later. +func ConstPtrToAddr(val Value, t Type) (v Value) { + v.C = C.LLVMGoConstPtrToAddr(val.C, t.C) + return +} diff --git a/ptrtoaddr_llvm22_test.go b/ptrtoaddr_llvm22_test.go new file mode 100644 index 0000000..e6ad8c8 --- /dev/null +++ b/ptrtoaddr_llvm22_test.go @@ -0,0 +1,108 @@ +//go:build llvm22 || (!llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21) + +package llvm + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestPtrToAddrDataLayoutAndRoundTrip(t *testing.T) { + ctx := NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("ptrtoaddr") + defer mod.Dispose() + mod.SetDataLayout("e-p:64:64-p1:64:64:64:32") + builder := ctx.NewBuilder() + defer builder.Dispose() + + for _, tc := range []struct { + name string + ptr Type + addr Type + }{ + {"scalar", PointerType(ctx.Int8Type(), 0), ctx.Int64Type()}, + {"narrow_index", PointerType(ctx.Int8Type(), 1), ctx.Int32Type()}, + {"vector", VectorType(PointerType(ctx.Int8Type(), 1), 2), VectorType(ctx.Int32Type(), 2)}, + } { + fn := AddFunction(mod, tc.name, FunctionType(tc.addr, []Type{tc.ptr}, false)) + builder.SetInsertPointAtEnd(ctx.AddBasicBlock(fn, "entry")) + addr := builder.CreatePtrToAddr(fn.Param(0), "addr") + if addr.Type() != tc.addr || addr.InstructionOpcode() != PtrToAddr { + t.Fatalf("%s: unexpected ptrtoaddr: %s", tc.name, addr.String()) + } + builder.CreateRet(addr) + } + + global := AddGlobal(mod, ctx.Int8Type(), "data") + addr := ConstPtrToAddr(global, ctx.Int64Type()) + if addr.Opcode() != PtrToAddr { + t.Fatalf("unexpected constant opcode: %s", addr.String()) + } + alias := AddGlobal(mod, ctx.Int64Type(), "address") + alias.SetInitializer(addr) + if err := VerifyModule(mod, ReturnStatusAction); err != nil { + t.Fatal(err) + } + path := filepath.Join(t.TempDir(), "ptrtoaddr.ll") + if err := os.WriteFile(path, []byte(mod.String()), 0600); err != nil { + t.Fatal(err) + } + buf, err := NewMemoryBufferFromFile(path) + if err != nil { + t.Fatal(err) + } + roundTrip, err := ctx.ParseIR(buf) // ParseIR consumes buf. + if err != nil { + t.Fatal(err) + } + defer roundTrip.Dispose() + if err := VerifyModule(roundTrip, ReturnStatusAction); err != nil { + t.Fatal(err) + } + if !strings.Contains(roundTrip.String(), "ptrtoaddr (ptr @data to i64)") { + t.Fatalf("constant expression lost during round trip:\n%s", roundTrip.String()) + } +} + +func TestPtrToAddrDoesNotExposeProvenance(t *testing.T) { + ctx := NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("provenance") + defer mod.Dispose() + mod.SetDataLayout("e-p:64:64") + b := ctx.NewBuilder() + defer b.Dispose() + exposeTy := FunctionType(ctx.VoidType(), []Type{ctx.Int64Type()}, false) + expose := AddFunction(mod, "observe_address", exposeTy) + for _, name := range []string{"address", "pointer"} { + fn := AddFunction(mod, name, FunctionType(ctx.Int32Type(), nil, false)) + b.SetInsertPointAtEnd(ctx.AddBasicBlock(fn, "entry")) + ptr := b.CreateAlloca(ctx.Int32Type(), "p") + b.CreateStore(ConstInt(ctx.Int32Type(), 7, false), ptr) + var addr Value + if name == "address" { + addr = b.CreatePtrToAddr(ptr, "addr") + } else { + addr = b.CreatePtrToInt(ptr, ctx.Int64Type(), "addr") + } + b.CreateCall(exposeTy, expose, []Value{addr}, "") + b.CreateRet(b.CreateLoad(ctx.Int32Type(), ptr, "value")) + } + options := NewPassBuilderOptions() + defer options.Dispose() + if err := mod.RunPasses("default", TargetMachine{}, options); err != nil { + t.Fatal(err) + } + if err := VerifyModule(mod, ReturnStatusAction); err != nil { + t.Fatal(err) + } + if ir := mod.NamedFunction("address").String(); !strings.Contains(ir, "ret i32 7") || strings.Contains(ir, "load i32") { + t.Fatalf("ptrtoaddr unexpectedly exposed provenance:\n%s", ir) + } + if ir := mod.NamedFunction("pointer").String(); !strings.Contains(ir, "load i32") { + t.Fatalf("ptrtoint control did not expose provenance:\n%s", ir) + } +}