使用llvmlite调用sprintf将i32转换为i8*时遭遇链接错误,snprintf/sprintf_s运行时崩溃
llvmlite调用sprintf将i32转换为i8*时遭遇链接错误,snprintf/sprintf_s运行时崩溃
哎呀,你这问题我一看就发现了两个核心问题——先别急,咱们一个个解决:
第一个要命的错误:sprintf/snprintf的参数顺序完全搞反了!
你看C里sprintf的原型是int sprintf(char *buffer, const char *format, ...),第一个参数是要写入的缓冲区,第二个才是格式字符串。但你的代码里写的是:
builder.call(sprintf, [fmt_ptr, buf_ptr, value])
把格式串指针(fmt_ptr)放到了第一个参数的位置,缓冲区指针(buf_ptr)放到了第二个位置!
这就导致程序运行时,sprintf会试图往格式串的内存里写数据——而你的格式串是全局常量(@".2" = global [3 x i8] c"%d\00"),属于只读内存区域,写入直接触发崩溃,这就是snprintf/sprintf_s编译过了但运行崩的原因!
第二个问题:sprintf的链接错误(Windows MSVC环境)
你的目标triple是x86_64-pc-windows-msvc,用的是MSVC的CRT,但你尝试加的-lmsvcrt是MinGW的链接参数,对MSVC目标不适用。
解决办法:
- 确保clang编译时链接MSVC的CRT库,比如在编译命令末尾加上
/link msvcrt.lib(这是MSVC风格的链接参数)。 - 另外,MSVC里
sprintf被标记为“不安全”,如果遇到编译警告,可以在编译时加-D_CRT_SECURE_NO_WARNINGS宏定义。
修正后的代码示例
我给你调整了参数顺序,同时换成更安全的snprintf(推荐用这个,避免缓冲区溢出):
module = ctx.scope.module builder: ir.IRBuilder = ctx.scope.builder # 正确定义snprintf的函数类型:参数是缓冲区、大小、格式串、可变参数 snprintf = ctx.compiler.get_func('snprintf', ir.FunctionType( int.type, [string.type, ir.IntType(64), string.type], var_arg=True )) INT_BUF_SIZE = 16 fmt_type = ir.ArrayType(ir.IntType(8), 3) fmt_str = ir.GlobalVariable(module, fmt_type, module.get_unique_name()) fmt_str.initializer = ir.Constant(fmt_type, bytearray(b'%d\0')) buf_type = ir.ArrayType(ir.IntType(8), INT_BUF_SIZE) buf = builder.alloca(buf_type) zero = ir.Constant(ir.IntType(32), 0) fmt_ptr = builder.gep(fmt_str, [zero, zero]) buf_ptr = builder.gep(buf, [zero, zero]) # 修正参数顺序:缓冲区、大小、格式串、要转换的数值 buf_size = ir.Constant(ir.IntType(64), INT_BUF_SIZE) builder.call(snprintf, [buf_ptr, buf_size, fmt_ptr, value])
再看你的LLVM IR问题
你贴的IR里,sprintf调用是:
%.7 = call i32 (i8*, i8*, ...) @sprintf(i8* %.5, i8* %.6, i32 25)
这里%.5是格式串指针,%.6是缓冲区指针,顺序完全反了,修正后应该把%.6放第一个位置,%.5放第二个,这样就不会往只读内存写了。
备注:内容来源于stack exchange,提问作者user29006542
相关产品推荐
相关产品推荐

