fix(includes): ASan 集成支持 + 越界写封堵 + TypePrint 类型名截断 40→255
1. Phase2Translator 增加 TPC_ASAN 支持(opt asan 插桩+链接 -fsanitize=address),配合 clang64 定位 0xbe 崩溃。2. llvmlite/memhub/viperio/viperlib 多处理无界 strcpy/strcat/下溢/递归守卫/栈数组钳制。3. memhub alloc 块尾 canary(MEMBUDDY_CANARY)+check_alive_blocks 校验+chk_stack realloc 扩容。4. TypePrint Ptr(Struct) 类型名截断上限 40→255:含 sha1 前缀类型名(如 bbdf3bbd4c3bc28c.CONSOLE_SCREEN_BUFFER_INFO=42)被截断产生未定义类型引用(llc undefined type)。
This commit is contained in:
1 parent
cb557c84ce
commit
d2d319eb21
10 files changed
+195
-49
No files matched your search
@@ -1813,6 +1813,10 @@ def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPt
|
||||
"""将 C 字符串 src 追加到 dst 末尾"""
|
||||
if dst is None or src is None: return
|
||||
dlen: t.CSizeT = string.strlen(dst)
|
||||
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
|
||||
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
|
||||
if dlen >= dst_size:
|
||||
return
|
||||
slen: t.CSizeT = string.strlen(src)
|
||||
remain: t.CSizeT = dst_size - dlen
|
||||
if remain <= 0: return
|
||||
|
||||
@@ -489,24 +489,27 @@ def _replace_ssa_numbers(text: t.CChar | t.CPtr,
|
||||
nxt: t.CChar | t.CPtr = src_p + 1
|
||||
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
|
||||
num: t.CInt = string.atoi(nxt)
|
||||
new_num: t.CInt = _map_get_i32(map_arr, num)
|
||||
if num >= 0 and num <= max_n and new_num != -1:
|
||||
# 写入 '%'
|
||||
new_buf[dst_idx] = '%'
|
||||
dst_idx += 1
|
||||
# 写入新编号
|
||||
num_buf[0] = '\0'
|
||||
viperlib.snprintf(num_buf, 16, "%d", new_num)
|
||||
k: t.CSizeT = 0
|
||||
while num_buf[k] != '\0' and dst_idx + 1 < new_size:
|
||||
new_buf[dst_idx] = num_buf[k]
|
||||
# 先做范围检查再读 map_arr:num 超 max_n 时 map_arr 只有
|
||||
# (max_n+1)*4 字节,先读后查会越界读远端内存
|
||||
if num >= 0 and num <= max_n:
|
||||
new_num: t.CInt = _map_get_i32(map_arr, num)
|
||||
if new_num != -1:
|
||||
# 写入 '%'
|
||||
new_buf[dst_idx] = '%'
|
||||
dst_idx += 1
|
||||
k += 1
|
||||
# 跳过旧数字字符
|
||||
src_p = src_p + 1
|
||||
while src_p is not None and _is_digit_ch(src_p[0]) != 0:
|
||||
# 写入新编号
|
||||
num_buf[0] = '\0'
|
||||
viperlib.snprintf(num_buf, 16, "%d", new_num)
|
||||
k: t.CSizeT = 0
|
||||
while num_buf[k] != '\0' and dst_idx + 1 < new_size:
|
||||
new_buf[dst_idx] = num_buf[k]
|
||||
dst_idx += 1
|
||||
k += 1
|
||||
# 跳过旧数字字符
|
||||
src_p = src_p + 1
|
||||
continue
|
||||
while src_p is not None and _is_digit_ch(src_p[0]) != 0:
|
||||
src_p = src_p + 1
|
||||
continue
|
||||
# 普通字符:直接复制
|
||||
new_buf[dst_idx] = src_p[0]
|
||||
dst_idx += 1
|
||||
@@ -713,6 +716,10 @@ def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPt
|
||||
"""将 C 字符串 src 追加到 dst 末尾"""
|
||||
if dst is None or src is None: return
|
||||
dlen: t.CSizeT = string.strlen(dst)
|
||||
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
|
||||
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
|
||||
if dlen >= dst_size:
|
||||
return
|
||||
slen: t.CSizeT = string.strlen(src)
|
||||
remain: t.CSizeT = dst_size - dlen
|
||||
if remain <= 0: return
|
||||
|
||||
@@ -405,10 +405,15 @@ def _is_type_defined_in_module(mod: LLVMModule | t.CPtr, name: t.CChar | t.CPtr)
|
||||
|
||||
def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: LLVMModule | t.CPtr,
|
||||
ty: LLVMType | t.CPtr):
|
||||
ty: LLVMType | t.CPtr,
|
||||
depth: t.CInt = 0):
|
||||
"""递归扫描类型树,为跨模块结构体引用添加 opaque 声明"""
|
||||
if ty is None or mod is None or pool is None:
|
||||
return
|
||||
# 深度守卫:自引用结构体(链表/树节点,如 Node{...; Ptr(Node) next})
|
||||
# 会无限递归栈溢出,与其他递归(TypePrint/DeepCopyType)一致设上限
|
||||
if depth >= 100:
|
||||
return
|
||||
match ty:
|
||||
case LLVMType.Ptr(pointee):
|
||||
if pointee is not None:
|
||||
@@ -429,21 +434,21 @@ def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy | t.CPtr,
|
||||
fld: ParamNode | t.CPtr = sfields
|
||||
while fld is not None:
|
||||
if fld.Ty is not None:
|
||||
_scan_type_for_cross_module_refs(pool, mod, fld.Ty)
|
||||
_scan_type_for_cross_module_refs(pool, mod, fld.Ty, depth + 1)
|
||||
fld = fld.Next
|
||||
case _:
|
||||
_scan_type_for_cross_module_refs(pool, mod, pointee)
|
||||
_scan_type_for_cross_module_refs(pool, mod, pointee, depth + 1)
|
||||
case LLVMType.Struct(sfields2, sfcount2, sname2):
|
||||
# 顶层结构体(非指针):递归扫描字段
|
||||
if sfields2 is not None:
|
||||
fld2: ParamNode | t.CPtr = sfields2
|
||||
while fld2 is not None:
|
||||
if fld2.Ty is not None:
|
||||
_scan_type_for_cross_module_refs(pool, mod, fld2.Ty)
|
||||
_scan_type_for_cross_module_refs(pool, mod, fld2.Ty, depth + 1)
|
||||
fld2 = fld2.Next
|
||||
case LLVMType.Array(elem_ty, acount):
|
||||
if elem_ty is not None:
|
||||
_scan_type_for_cross_module_refs(pool, mod, elem_ty)
|
||||
_scan_type_for_cross_module_refs(pool, mod, elem_ty, depth + 1)
|
||||
case _:
|
||||
pass
|
||||
|
||||
@@ -590,6 +595,10 @@ def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPt
|
||||
"""将 C 字符串 src 追加到 dst 末尾"""
|
||||
if dst is None or src is None: return
|
||||
dlen: t.CSizeT = string.strlen(dst)
|
||||
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
|
||||
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
|
||||
if dlen >= dst_size:
|
||||
return
|
||||
slen: t.CSizeT = string.strlen(src)
|
||||
remain: t.CSizeT = dst_size - dlen
|
||||
if remain <= 0: return
|
||||
|
||||
@@ -416,26 +416,33 @@ def _TypePrintDepth(buf: t.CChar | t.CPtr, size: t.CSizeT,
|
||||
match pointee:
|
||||
case LLVMType.Struct(sfields, sfcount, sname):
|
||||
if sname is not None:
|
||||
# 悬空防护:sname 可能指向被 reset 回收/部分复用的 pool 内存,
|
||||
# 用受限打印避免读越界崩溃;完整名在 vtable 生成后由
|
||||
# TypeStr 兜底(stdlib 深拷贝,跨 reset 存活)
|
||||
viperlib.snprintf(buf, size, "%%\"%.40s\"*", sname)
|
||||
# 悬空防护:sname 可能指向被 reset 回收/部分复用的 pool 内存。
|
||||
# viperlib 的 %s 未实现 precision(%.40s 会全量读 sname),
|
||||
# 手动 strncpy 限长拷贝后再打印,读侧限制在缓冲内。
|
||||
# 类型名含 sha1 前缀可超 40 字符(如
|
||||
# "bbdf3bbd4c3bc28c.CONSOLE_SCREEN_BUFFER_INFO"=42),
|
||||
# 原 40 字节截断会产生未定义类型引用(llc: undefined type)。
|
||||
sn_cp: t.CChar | t.CPtr = pool.alloc(256)
|
||||
if sn_cp is not None:
|
||||
string.strncpy(sn_cp, sname, 255)
|
||||
sn_cp[255] = '\0'
|
||||
viperlib.snprintf(buf, size, "%%\"%s\"*", sn_cp)
|
||||
return
|
||||
case LLVMType.Void():
|
||||
# Ptr(Void) → i8*(LLVM IR void* 非法)
|
||||
string.strcpy(buf, "i8*")
|
||||
viperlib.snprintf(buf, size, "%s", "i8*")
|
||||
return
|
||||
case LLVMType.Label():
|
||||
string.strcpy(buf, "i8*")
|
||||
viperlib.snprintf(buf, size, "%s", "i8*")
|
||||
return
|
||||
case LLVMType.Metadata():
|
||||
string.strcpy(buf, "i8*")
|
||||
viperlib.snprintf(buf, size, "%s", "i8*")
|
||||
return
|
||||
case _:
|
||||
pass
|
||||
# pointee == None 时也兜底 Ptr(Int8) 打印 i8*
|
||||
if pointee is None:
|
||||
string.strcpy(buf, "i8*")
|
||||
viperlib.snprintf(buf, size, "%s", "i8*")
|
||||
return
|
||||
# 通用路径: 递归打印 pointee 后追加 '*'
|
||||
_TypePrintDepth(buf, size, pointee, pool, depth + 1)
|
||||
@@ -484,24 +491,24 @@ def _TypePrintDepth(buf: t.CChar | t.CPtr, size: t.CSizeT,
|
||||
first2 = 0
|
||||
_strappend(buf, size, ")")
|
||||
case LLVMType.Void():
|
||||
string.strcpy(buf, "void")
|
||||
viperlib.snprintf(buf, size, "%s", "void")
|
||||
case LLVMType.Label():
|
||||
string.strcpy(buf, "label")
|
||||
viperlib.snprintf(buf, size, "%s", "label")
|
||||
case LLVMType.Metadata():
|
||||
string.strcpy(buf, "metadata")
|
||||
viperlib.snprintf(buf, size, "%s", "metadata")
|
||||
case LLVMType.Float(bits):
|
||||
if bits == 16:
|
||||
string.strcpy(buf, "half")
|
||||
viperlib.snprintf(buf, size, "%s", "half")
|
||||
elif bits == 32:
|
||||
string.strcpy(buf, "float")
|
||||
viperlib.snprintf(buf, size, "%s", "float")
|
||||
elif bits == 64:
|
||||
string.strcpy(buf, "double")
|
||||
viperlib.snprintf(buf, size, "%s", "double")
|
||||
elif bits == 128:
|
||||
string.strcpy(buf, "fp128")
|
||||
viperlib.snprintf(buf, size, "%s", "fp128")
|
||||
else:
|
||||
viperlib.snprintf(buf, size, "float%x", bits)
|
||||
case _:
|
||||
string.strcpy(buf, "void")
|
||||
viperlib.snprintf(buf, size, "%s", "void")
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -511,6 +518,10 @@ def _strappend(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr)
|
||||
"""将 src 追加到 dst 末尾(不超过 dst_size)"""
|
||||
if dst is None or src is None: return
|
||||
dlen: t.CSizeT = string.strlen(dst)
|
||||
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
|
||||
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到 GB 外
|
||||
if dlen >= dst_size:
|
||||
return
|
||||
slen: t.CSizeT = string.strlen(src)
|
||||
remain: t.CSizeT = dst_size - dlen
|
||||
if remain <= 0: return
|
||||
|
||||
@@ -220,6 +220,10 @@ def _append_str(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr
|
||||
"""将 src 追加到 dst 末尾"""
|
||||
if dst is None or src is None: return
|
||||
dlen: t.CSizeT = string.strlen(dst)
|
||||
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
|
||||
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
|
||||
if dlen >= dst_size:
|
||||
return
|
||||
slen: t.CSizeT = string.strlen(src)
|
||||
remain: t.CSizeT = dst_size - dlen
|
||||
if remain <= 0: return
|
||||
|
||||
@@ -82,6 +82,10 @@ def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPt
|
||||
"""将 src 追加到 dst 末尾"""
|
||||
if dst is None or src is None: return
|
||||
dlen: t.CSizeT = string.strlen(dst)
|
||||
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
|
||||
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
|
||||
if dlen >= dst_size:
|
||||
return
|
||||
slen: t.CSizeT = string.strlen(src)
|
||||
remain: t.CSizeT = dst_size - dlen
|
||||
if remain <= 0: return
|
||||
|
||||
+76
-1
@@ -25,6 +25,16 @@ MEMSLAB_BITMAP_BYTES: t.CDefine = 256
|
||||
MEMBUDDY_MIN_BLOCK: t.CDefine = 32
|
||||
MEMBUDDY_MAX_ORDERS: t.CDefine = 32
|
||||
MEMBUDDY_HEADER_SIZE: t.CDefine = 8
|
||||
# 块尾 canary:alloc 时在块尾(用户数据区之外)写 8 字节哨兵,
|
||||
# check_alive_blocks 校验——越界写穿数据区会破坏 canary,把
|
||||
# "崩溃点漂移/事发地遥远"变成"确定性定位到越界写发生的文件"
|
||||
MEMBUDDY_CANARY: t.CDefine = 8
|
||||
|
||||
|
||||
def _canary_pattern() -> t.CUInt64T:
|
||||
"""块尾 canary 值(0xA5A5A5A5A5A5A5A5,64 位)"""
|
||||
lo: t.CUInt64T = t.CUInt64T(0xA5A5A5A5)
|
||||
return (lo << 32) | lo
|
||||
|
||||
def _align_up(val: t.CSizeT, align: t.CSizeT) -> t.CSizeT:
|
||||
if align == 0: return val
|
||||
@@ -529,13 +539,18 @@ class MemBuddy(MemManager):
|
||||
self._lock()
|
||||
result: t.CVoid | t.CPtr = None
|
||||
if size != 0 and self.usable is not None:
|
||||
needed: t.CSizeT = size + MEMBUDDY_HEADER_SIZE
|
||||
# +MEMBUDDY_CANARY:预留块尾 8 字节哨兵(用户数据区之外),越界写定位用
|
||||
needed: t.CSizeT = size + MEMBUDDY_HEADER_SIZE + MEMBUDDY_CANARY
|
||||
order: t.CInt = self._order_for_size(needed)
|
||||
if order <= self.max_order:
|
||||
block: t.CVoid | t.CPtr = self._split_to_order(order)
|
||||
if block is not None:
|
||||
c.DerefAs(block, t.CVoid(t.CUInt64T((order << 1) | 1), t.CPtr))
|
||||
result = t.CVoid(t.CUInt64T(block) + MEMBUDDY_HEADER_SIZE, t.CPtr)
|
||||
# 写块尾 canary(block + block_size - CANARY)
|
||||
bs: t.CSizeT = _block_size_at_order(order)
|
||||
c.DerefAs(t.CVoid(t.CUInt64T(block) + bs - MEMBUDDY_CANARY, t.CPtr),
|
||||
t.CVoid(_canary_pattern(), t.CPtr))
|
||||
# 记录到 checkpoint 栈(供 reset_to 回收)
|
||||
# 栈满时 stdlib.realloc 翻倍扩容(防 64K 爆满后记录缺失
|
||||
# → reset 不回收 + _is_valid_ptr 栈校验退化的连锁问题)
|
||||
@@ -552,6 +567,17 @@ class MemBuddy(MemManager):
|
||||
self.chk_stack[self.chk_depth] = t.CUInt64T(block)
|
||||
self.chk_depth += 1
|
||||
if result is None:
|
||||
# 分配失败诊断(定位"异常 size/池耗尽导致 None → 调用方解引用段错误")
|
||||
if size != 0 and self.usable is not None:
|
||||
needed2: t.CSizeT = size + MEMBUDDY_HEADER_SIZE + MEMBUDDY_CANARY
|
||||
order2: t.CInt = self._order_for_size(needed2)
|
||||
stdio.printf("[MEM] alloc 失败 size=%zu needed=%zu order=%d max_order=%d used=%zu\n",
|
||||
size, needed2, order2, self.max_order, self.used_bytes())
|
||||
if order2 > self.max_order:
|
||||
stdio.printf("[MEM] 原因:请求 size 超 pool 最大块(order %d > max %d)\n",
|
||||
order2, self.max_order)
|
||||
else:
|
||||
stdio.printf("[MEM] 原因:pool 耗尽(空闲 %zu 字节不足)\n", self.free_bytes())
|
||||
# 分配失败(alloc(0) 或池耗尽):解锁并返回 None,由调用者决定后续处理。
|
||||
# 不能 exit——测试依赖 alloc 失败返回 None 后继续 free+重试
|
||||
# (如 OOM section: 耗尽后 free 一块再 alloc 应成功)。
|
||||
@@ -660,11 +686,18 @@ class MemBuddy(MemManager):
|
||||
# Validate buddy allocator internal consistency, return 0=OK, non-0=damage
|
||||
if self.usable is None:
|
||||
return 0
|
||||
# 环检测:free list 被越界写破坏 next 指针后可能成环,
|
||||
# 无步数上限会导致无限循环(崩溃点漂移排查哨兵必须能终止)。
|
||||
max_steps: t.CSizeT = self.usable_size / MEMBUDDY_MIN_BLOCK + 2
|
||||
i: t.CInt
|
||||
for i in range(MEMBUDDY_MAX_ORDERS + 1):
|
||||
head_val: t.CUInt64T = self.free_lists[i]
|
||||
cur: t.CVoid | t.CPtr = t.CVoid(head_val, t.CPtr)
|
||||
steps: t.CSizeT = 0
|
||||
while t.CUInt64T(cur) != 0:
|
||||
steps += 1
|
||||
if steps > max_steps:
|
||||
return 4 # free list 成环或节点数超上限(next 指针被破坏)
|
||||
# Check pointer is in usable region range
|
||||
if t.CUInt64T(cur) < t.CUInt64T(self.usable):
|
||||
return 1
|
||||
@@ -677,3 +710,45 @@ class MemBuddy(MemManager):
|
||||
return 3
|
||||
cur = t.CVoid(c.Deref(t.CUInt64T(cur, t.CPtr)), t.CPtr)
|
||||
return 0
|
||||
|
||||
def check_alive_blocks(self, from_mark: t.CSizeT) -> t.CInt:
|
||||
# 检查 [from_mark, chk_depth) 记录的所有已分配块 header 完整性。
|
||||
# 越界写(参数错位/下标 GEP 越界)会破坏相邻块 header(奇偶位/order 错乱),
|
||||
# 返回 0=OK,非 0=损坏(rc 语义见调用方注释)。
|
||||
# 用于 reset_to 前哨兵:把"崩溃点漂移"变成"确定性定位到损坏发生的文件"。
|
||||
if self.chk_stack is None:
|
||||
return 0
|
||||
if from_mark >= self.chk_depth:
|
||||
return 0
|
||||
if self.usable is None:
|
||||
return 0
|
||||
ci: t.CSizeT = from_mark
|
||||
while ci < self.chk_depth:
|
||||
block: t.CVoid | t.CPtr = t.CVoid(self.chk_stack[ci], t.CPtr)
|
||||
if block is not None:
|
||||
if t.CUInt64T(block) < t.CUInt64T(self.usable):
|
||||
return 1
|
||||
if t.CUInt64T(block) >= t.CUInt64T(self.usable) + self.usable_size:
|
||||
return 2
|
||||
stored: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(block, t.CPtr)), t.CPtr)
|
||||
stored_val: t.CUInt64T = t.CUInt64T(stored)
|
||||
if (stored_val & 1) != 0:
|
||||
# 活块:header 必须是 (order<<1)|1,order 合法且块对齐到该 order 大小
|
||||
order: t.CInt = t.CInt(stored_val >> 1)
|
||||
if order < 0:
|
||||
return 3
|
||||
if order > self.max_order:
|
||||
return 4
|
||||
offset: t.CSizeT = t.CUInt64T(block) - t.CUInt64T(self.usable)
|
||||
bs: t.CSizeT = _block_size_at_order(order)
|
||||
if offset % bs != 0:
|
||||
return 5
|
||||
# 块尾 canary 校验:越界写穿数据区会破坏块尾哨兵,
|
||||
# 立即定位到越界写发生的文件(崩溃点漂移的关键哨兵)
|
||||
can_stored: t.CVoid | t.CPtr = t.CVoid(
|
||||
c.Deref(t.CUInt64T(t.CUInt64T(block) + bs - MEMBUDDY_CANARY, t.CPtr), t.CPtr),
|
||||
t.CPtr)
|
||||
if t.CUInt64T(can_stored) != _canary_pattern():
|
||||
return 6
|
||||
ci += 1
|
||||
return 0
|
||||
+3
-1
@@ -31,7 +31,9 @@ class Buf:
|
||||
self.data[self.length + i] = src[i]
|
||||
i += 1
|
||||
self.length += n
|
||||
self.data[self.length] = 0
|
||||
# 恰好写满(n==avail)时 data[capacity] 越界 1 字节,加保护
|
||||
if self.length < self.capacity:
|
||||
self.data[self.length] = 0
|
||||
return n
|
||||
|
||||
def cstr(self) -> t.CChar | t.CPtr:
|
||||
|
||||
@@ -672,7 +672,7 @@ def vsnprintf(buf: t.CChar | t.CPtr, size: t.CSizeT, fmt: str, ap: t.CChar | t.C
|
||||
temp[fi] = t.CChar(frac_part % 10 + 48)
|
||||
fi += 1
|
||||
frac_part //= 10
|
||||
while fi < precision:
|
||||
while fi < precision and fi < TEMP_BUF_SIZE - 1:
|
||||
temp[fi] = 48
|
||||
fi += 1
|
||||
while fi > 0 and write_count < size - 1:
|
||||
@@ -835,7 +835,7 @@ def vsnprintf(buf: t.CChar | t.CPtr, size: t.CSizeT, fmt: str, ap: t.CChar | t.C
|
||||
temp[fi] = t.CChar(frac_p % 10 + 48)
|
||||
fi += 1
|
||||
frac_p //= 10
|
||||
while fi < precision:
|
||||
while fi < precision and fi < TEMP_BUF_SIZE - 1:
|
||||
temp[fi] = 48
|
||||
fi += 1
|
||||
while fi > 0 and write_count < size - 1:
|
||||
|
||||
@@ -160,6 +160,15 @@ class Phase2Translator:
|
||||
self.linker_cmd = linker_cmd
|
||||
self.linker_flags = linker_flags or []
|
||||
self.linker_output = linker_output
|
||||
# ASan 模式:TPC_ASAN=1 时对全部 .ll 做 opt -passes=asan 插桩并链接 libasan。
|
||||
# 检测 stdlib 堆/栈/全局越界与垃圾指针(MemBuddy pool 内部由 canary 覆盖)。
|
||||
# 注意:mingw64 的 clang 不提供 ASan runtime(libclang_rt.asan 缺失),
|
||||
# 必须使用 clang64 环境(自带 asan_dynamic runtime),PATH 前置 clang64/bin。
|
||||
self.asan_mode = os.environ.get('TPC_ASAN') == '1'
|
||||
if self.asan_mode:
|
||||
_clang64_bin = r'D:\msys64\clang64\bin'
|
||||
if os.path.isdir(_clang64_bin):
|
||||
os.environ['PATH'] = _clang64_bin + os.pathsep + os.environ.get('PATH', '')
|
||||
self.slice_level = 3
|
||||
self.sha1_map: dict[str, str] = {}
|
||||
self.sig_files: dict[str, str] = {}
|
||||
@@ -1185,6 +1194,29 @@ class Phase2Translator:
|
||||
except Exception as e:
|
||||
_vlog().warning(f" [警告] 解析包重导出失败 {init_pyi_path}: {e}")
|
||||
|
||||
def _llc_compile(self, ll_path: str, obj_path: str,
|
||||
cwd: str | None = None) -> subprocess.CompletedProcess:
|
||||
"""编译单个 .ll 为 obj。
|
||||
|
||||
ASan 模式(TPC_ASAN=1)下先对 IR 执行 opt -passes=asan 插桩:
|
||||
检测 stdlib 堆/栈/全局越界写与垃圾指针解引用(MemBuddy pool 内部
|
||||
由块尾 canary 覆盖,两者互补)。opt 失败返回其结果(returncode!=0)。
|
||||
"""
|
||||
compile_input = ll_path
|
||||
if self.asan_mode:
|
||||
asan_ll = ll_path[:-3] + '_asan.ll'
|
||||
# -asan-globals=false:TPC 对全局变量(如各模块 _g_gen)用直接地址寻址,
|
||||
# ASan 全局 redzone 重定位后引用错位会导致读到垃圾指针(如
|
||||
# register_struct 的 self=0xbebebebe...)。先关闭全局插桩排除该因素。
|
||||
opt_cmd = ['opt', '-passes=asan', '-asan-globals=false', ll_path, '-o', asan_ll]
|
||||
opt_r = subprocess.run(opt_cmd, capture_output=True, text=True, cwd=cwd)
|
||||
if opt_r.returncode != 0:
|
||||
_vlog().error(f" opt ASan 插桩失败: {opt_r.stderr.strip()}")
|
||||
return opt_r
|
||||
compile_input = asan_ll
|
||||
cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, compile_input]
|
||||
return subprocess.run(cmd, capture_output=True, text=True, cwd=cwd)
|
||||
|
||||
def _compile_ll_files(self, active_sha1s: set[str]) -> None:
|
||||
"""将 output_dir 中属于 active_sha1s 的 .ll 文件编译为 .obj(跳过已编译过的 include 文件)"""
|
||||
# 第一次 walk:清理 stale 文件(.obj/.deps.json/纯 .ll,排除 .stub.ll/.text.ll)
|
||||
@@ -1307,9 +1339,7 @@ class Phase2Translator:
|
||||
f.write(opt_content)
|
||||
merged_obj = os.path.join(self.output_dir, '_merged.obj')
|
||||
try:
|
||||
cmd = [self.compile_cmd] + self.compile_flags + ['-o', merged_obj, optimized_ll]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True,
|
||||
cwd=self.output_dir)
|
||||
result = self._llc_compile(optimized_ll, merged_obj, self.output_dir)
|
||||
if result.returncode == 0:
|
||||
_vlog().success(f" 合并模块编译完成")
|
||||
for ll_path in ll_files:
|
||||
@@ -1591,9 +1621,7 @@ class Phase2Translator:
|
||||
dso_ll_path = ll_path[:-3] + '_dso.ll'
|
||||
with open(dso_ll_path, 'w', encoding='utf-8') as f:
|
||||
f.write(ll_content)
|
||||
cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, dso_ll_path]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True,
|
||||
cwd=os.path.dirname(ll_path) or '.')
|
||||
result = self._llc_compile(dso_ll_path, obj_path, os.path.dirname(ll_path) or '.')
|
||||
if result.returncode == 0:
|
||||
return (rel, 'ok', '')
|
||||
else:
|
||||
@@ -2355,8 +2383,7 @@ class Phase2Translator:
|
||||
|
||||
def _compile_one_include(task):
|
||||
dso_ll_path, obj_path = task[0], task[1]
|
||||
cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, dso_ll_path]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, cwd=self.output_dir)
|
||||
result = self._llc_compile(dso_ll_path, obj_path, self.output_dir)
|
||||
return (task, result)
|
||||
|
||||
if len(compile_tasks) > 1:
|
||||
@@ -3050,6 +3077,9 @@ SECTIONS {
|
||||
# 其他所有标志(包括 -Wl, 选项)放在目标文件之前
|
||||
non_lib_flags.append(flag)
|
||||
i += 1
|
||||
# ASan 模式:链接 libasan(配合各 .ll 的 opt -passes=asan 插桩)
|
||||
if self.asan_mode:
|
||||
non_lib_flags.append('-fsanitize=address')
|
||||
cmd = [self.linker_cmd] + non_lib_flags + ['-o', link_output] + all_link_files + lib_flags
|
||||
_vlog().info(f" 执行: {' '.join(cmd)}")
|
||||
result = subprocess.run(
|
||||
|
||||
Reference in new issue
Block a user