///|
fn bitcode_table_range(
data : Bytes,
start : Int32,
size : Int32,
field : Int32,
stride : Int32,
) -> (Int32, Int32) raise InvalidObject {
let offset = object_number(data, start + field, 4, true, size)
let count = object_number(
data,
start + field + 4,
4,
true,
(size - offset) / stride,
)
if count > 0 && (offset < 76 || offset % 4 != 0) {
object_reject("OB001", start + field, "invalid bitcode symbol-table range")
}
(start + offset, count)
}
///|
fn bitcode_string(
data : Bytes,
field : Int32,
strings : Int32,
size : Int32,
) -> (Int32, Int32) raise InvalidObject {
let offset = object_number(data, field, 4, true, size)
let length = object_number(data, field + 4, 4, true, size - offset)
(strings + offset, length)
}
///|
fn bitcode_symbols(
data : Bytes,
table : (Int32, Int32),
strings : (Int32, Int32),
module_count : Int32,
budget : ObjectBudget,
) -> Array[Bytes] raise InvalidObject {
let (start, size) = table
let (str_start, str_size) = strings
if size < 4 {
object_reject("OB001", start, "truncated bitcode symbol-table version")
}
if object_number(data, start, 4, true, 2147483647) != 3 {
object_reject("OB002", start, "unsupported bitcode symbol-table version")
}
if size < 76 {
object_reject("OB001", start, "truncated bitcode symbol-table header")
}
let ranges = [
bitcode_table_range(data, start, size, 12, 12),
bitcode_table_range(data, start, size, 20, 12),
bitcode_table_range(data, start, size, 28, 24),
bitcode_table_range(data, start, size, 36, 24),
bitcode_table_range(data, start, size, 68, 8),
]
let strides = [12, 12, 24, 24, 8]
for i in 0.. 0 &&
other_count > 0 &&
first < other + other_count * strides[j] &&
other < first + count * strides[i] {
object_reject("OB001", first, "overlapping bitcode symbol-table ranges")
}
}
}
let (modules, modules_count) = ranges[0]
let (comdats, comdat_count) = ranges[1]
let (symbols, count) = ranges[2]
let (uncommon, uncommon_count) = ranges[3]
let (libraries, library_count) = ranges[4]
if modules_count != module_count {
object_reject(
"OB002",
start + 12,
"bitcode symbol table does not cover its module group",
)
}
object_charge_symbols(budget, count, start + 32)
for field in [4, 44, 52, 60] {
ignore(bitcode_string(data, start + field, str_start, str_size))
}
let mut next_symbol = 0
for i in 0..= comdat_count.reinterpret_as_uint() {
object_reject(
"OB001",
field + 16,
"bitcode symbol refers outside its comdat table",
)
}
let flags = object_tag(data, field + 20, true)
if flags >> 14 != 0U {
object_reject("OB002", field + 20, "unsupported bitcode symbol flags")
}
if (flags & 3U) == 3U {
object_reject("OB001", field + 20, "invalid bitcode symbol visibility")
}
if (flags & 4U) != 0U {
used_uncommon += 1
}
if used_uncommon > uncommon_count {
object_reject(
"OB001",
field + 20,
"bitcode symbol lacks an uncommon record",
)
}
if (flags & 8U) != 0U || (flags & 2048U) != 0U || (flags & 1040U) == 0U {
continue
}
if length == 0 {
object_reject("OB001", field, "empty defined bitcode symbol name")
}
if length > budget.names_left {
object_reject("OB003", field, "expanded symbol-name byte limit exceeded")
}
budget.names_left -= length
for j in name..<(name + length) {
if data[j] == b'\x00' {
object_reject("OB001", j, "NUL inside bitcode symbol name")
}
}
let value = data[name:name + length].to_owned()
if seen.get(value) is None {
seen[value] = ()
result.push(value)
}
}
if used_uncommon != uncommon_count {
object_reject("OB001", start + 36, "unreferenced bitcode uncommon records")
}
result
}
///|
fn object_bitcode(
data : Bytes,
budget : ObjectBudget,
) -> Array[Bytes] raise InvalidObject {
let wrapped = data.has_prefix(b"\xde\xc0\x17\x0b"[:])
let (start, end) = if wrapped {
object_range(data, 0, 20)
if object_number(data, 4, 4, true, 2147483647) != 0 {
object_reject("OB002", 4, "unsupported bitcode wrapper version")
}
let start = object_number(data, 8, 4, true, data.length())
let length = object_number(data, 12, 4, true, data.length() - start)
if start < 20 {
object_reject("OB001", 8, "bitcode wrapper payload overlaps its header")
}
(start, start + length)
} else {
(0, data.length())
}
if end - start < 4 || data[start:start + 4] != b"BC\xc0\xde"[:] {
object_reject("OB001", start, "missing bitcode magic")
}
let cursor : BitcodeCursor = {
data,
origin: start,
byte: start + 4,
bit: 0,
end,
}
let mut modules = 0
let mut groups = 0
let mut pending : (Int32, Int32)? = None
let result : Array[Bytes] = []
let seen : Map[Bytes, Unit] = Map([])
while cursor.byte < end {
if cursor.bit == 0 && end - cursor.byte <= 7 {
let mut padding = true
for i in cursor.byte.. 0 else {
object_reject(
"OB002",
cursor.byte,
"bitcode lacks a symbol table for its module group",
)
}
for name in bitcode_symbols(data, table, strings, modules, budget) {
if seen.get(name) is None {
seen[name] = ()
result.push(name)
}
}
groups += 1
modules = 0
pending = None
}
cursor.byte = block_end
cursor.bit = 0
}
if groups == 0 || modules != 0 || pending is Some(_) {
object_reject(
"OB002", start, "bitcode requires an embedded version-3 symbol table and string table",
)
}
result
}