Merge pull request #10304 from ziglang/musl-stubgrade

Improve musl libc.so stubs
This commit is contained in:
Andrew Kelley 2021-12-09 14:12:51 -08:00 committed by GitHub
commit 01cb0bdb83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 5215 additions and 4267 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1688,3 +1688,10 @@ pub const R_X86_64_GOTPCRELX = 41;
/// Load from 32 bit signed PC relative offset to GOT entry with REX prefix, relaxable
pub const R_X86_64_REX_GOTPCRELX = 42;
pub const R_X86_64_NUM = 43;
pub const STV = enum(u2) {
DEFAULT = 0,
INTERNAL = 1,
HIDDEN = 2,
PROTECTED = 3,
};

View File

@ -191,11 +191,20 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
return comp.build_crt_file("c", .Lib, c_source_files.items);
},
.libc_so => {
const target = comp.getTarget();
const arch_define = try std.fmt.allocPrint(arena, "-DARCH_{s}", .{
@tagName(target.cpu.arch),
});
const clang_argv: []const []const u8 = if (target.cpu.arch.ptrBitWidth() == 64)
&[_][]const u8{ "-DPTR64", arch_define }
else
&[_][]const u8{arch_define};
const sub_compilation = try Compilation.create(comp.gpa, .{
.local_cache_directory = comp.global_cache_directory,
.global_cache_directory = comp.global_cache_directory,
.zig_lib_directory = comp.zig_lib_directory,
.target = comp.getTarget(),
.target = target,
.root_name = "c",
.main_pkg = null,
.output_mode = .Lib,
@ -223,8 +232,9 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
.clang_passthrough_mode = comp.clang_passthrough_mode,
.c_source_files = &[_]Compilation.CSourceFile{
.{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "libc.s" }) },
.{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "libc.S" }) },
},
.clang_argv = clang_argv,
.skip_linker_dependencies = true,
.soname = "libc.so",
});

View File

@ -1,120 +1,758 @@
//! Example usage:
//! ./gen_stubs /path/to/musl/build-all >libc.S
//!
//! The directory 'build-all' is expected to contain these subdirectories:
//! arm i386 mips mips64 powerpc powerpc64 riscv64 x86_64
//!
//! ...each with 'lib/libc.so' inside of them.
//!
//! When building the resulting libc.S file, these defines are required:
//! * `-DPTR64`: when the architecture is 64-bit
//! * One of the following, corresponding to the CPU architecture:
//! - `-DARCH_riscv64`
//! - `-DARCH_mips`
//! - `-DARCH_i386`
//! - `-DARCH_x86_64`
//! - `-DARCH_powerpc`
//! - `-DARCH_powerpc64`
//! - `-DARCH_aarch64`
// TODO: pick the best index to put them into instead of at the end
// - e.g. find a common previous symbol and put it after that one
// - they definitely need to go into the correct section
const std = @import("std");
const builtin = std.builtin;
const mem = std.mem;
const log = std.log;
const elf = std.elf;
const native_endian = @import("builtin").target.cpu.arch.endian();
const Symbol = struct {
name: []const u8,
section: []const u8,
kind: enum {
global,
weak,
},
type: enum {
none,
function,
object,
},
protected: bool,
const arches: [7]std.Target.Cpu.Arch = blk: {
var result: [7]std.Target.Cpu.Arch = undefined;
for (.{ .riscv64, .mips, .i386, .x86_64, .powerpc, .powerpc64, .aarch64 }) |arch| {
result[archIndex(arch)] = arch;
}
break :blk result;
};
const MultiSym = struct {
size: [arches.len]u64,
present: [arches.len]bool,
binding: [arches.len]u4,
section: u16,
ty: u4,
visib: elf.STV,
fn allPresent(ms: MultiSym) bool {
for (arches) |_, i| {
if (!ms.present[i]) {
return false;
}
}
return true;
}
fn is32Only(ms: MultiSym) bool {
return ms.present[archIndex(.riscv64)] == false and
ms.present[archIndex(.mips)] == true and
ms.present[archIndex(.i386)] == true and
ms.present[archIndex(.x86_64)] == false and
ms.present[archIndex(.powerpc)] == true and
ms.present[archIndex(.powerpc64)] == false and
ms.present[archIndex(.aarch64)] == false;
}
fn commonSize(ms: MultiSym) ?u64 {
var size: ?u64 = null;
for (arches) |_, i| {
if (!ms.present[i]) continue;
if (size) |s| {
if (ms.size[i] != s) {
return null;
}
} else {
size = ms.size[i];
}
}
return size.?;
}
fn commonBinding(ms: MultiSym) ?u4 {
var binding: ?u4 = null;
for (arches) |_, i| {
if (!ms.present[i]) continue;
if (binding) |b| {
if (ms.binding[i] != b) {
return null;
}
} else {
binding = ms.binding[i];
}
}
return binding.?;
}
fn isPtrSize(ms: MultiSym) bool {
const map = .{
.{ .riscv64, 8 },
.{ .mips, 4 },
.{ .i386, 4 },
.{ .x86_64, 8 },
.{ .powerpc, 4 },
.{ .powerpc64, 8 },
.{ .aarch64, 8 },
};
inline for (map) |item| {
const arch = item[0];
const size = item[1];
const arch_index = archIndex(arch);
if (ms.present[arch_index] and ms.size[arch_index] != size) {
return false;
}
}
return true;
}
fn isPtr2Size(ms: MultiSym) bool {
const map = .{
.{ .riscv64, 16 },
.{ .mips, 8 },
.{ .i386, 8 },
.{ .x86_64, 16 },
.{ .powerpc, 8 },
.{ .powerpc64, 16 },
.{ .aarch64, 16 },
};
inline for (map) |item| {
const arch = item[0];
const size = item[1];
const arch_index = archIndex(arch);
if (ms.present[arch_index] and ms.size[arch_index] != size) {
return false;
}
}
return true;
}
fn isWeak64(ms: MultiSym) bool {
const map = .{
.{ .riscv64, 2 },
.{ .mips, 1 },
.{ .i386, 1 },
.{ .x86_64, 2 },
.{ .powerpc, 1 },
.{ .powerpc64, 2 },
.{ .aarch64, 2 },
};
inline for (map) |item| {
const arch = item[0];
const binding = item[1];
const arch_index = archIndex(arch);
if (ms.present[arch_index] and ms.binding[arch_index] != binding) {
return false;
}
}
return true;
}
};
const Parse = struct {
arena: mem.Allocator,
sym_table: *std.StringArrayHashMap(MultiSym),
sections: *std.StringArrayHashMap(void),
blacklist: std.StringArrayHashMap(void),
elf_bytes: []align(@alignOf(elf.Elf64_Ehdr)) u8,
header: elf.Header,
arch: std.Target.Cpu.Arch,
};
// Example usage:
// objdump --dynamic-syms /path/to/libc.so | ./gen_stubs > lib/libc/musl/libc.s
pub fn main() !void {
const stdin = std.io.getStdIn().reader();
const stdout = std.io.getStdOut().writer();
var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena_instance.deinit();
const arena = arena_instance.allocator();
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const ally = arena.allocator();
const args = try std.process.argsAlloc(arena);
const build_all_path = args[1];
var symbols = std.ArrayList(Symbol).init(ally);
var sections = std.ArrayList([]const u8).init(ally);
var build_all_dir = try std.fs.cwd().openDir(build_all_path, .{});
// This is many times larger than any line objdump produces should ever be
var buf: [4096]u8 = undefined;
var line_number: usize = 0;
var sym_table = std.StringArrayHashMap(MultiSym).init(arena);
var sections = std.StringArrayHashMap(void).init(arena);
var blacklist = std.StringArrayHashMap(void).init(arena);
// Sample input line:
// 00000000000241b0 g DF .text 000000000000001b copy_file_range
while (try stdin.readUntilDelimiterOrEof(&buf, '\n')) |line| {
line_number += 1;
try blacklist.ensureUnusedCapacity(blacklisted_symbols.len);
for (blacklisted_symbols) |name| {
blacklist.putAssumeCapacityNoClobber(name, {});
}
// the lines we want all start with a 16 digit hex value
if (line.len < 16) continue;
_ = std.fmt.parseInt(u64, line[0..16], 16) catch continue;
for (arches) |arch| {
const libc_so_path = try std.fmt.allocPrint(arena, "{s}/lib/libc.so", .{@tagName(arch)});
// Ignore non-dynamic symbols
if (line[22] != 'D') continue;
// Read the ELF header.
const elf_bytes = try build_all_dir.readFileAllocOptions(
arena,
libc_so_path,
100 * 1024 * 1024,
1 * 1024 * 1024,
@alignOf(elf.Elf64_Ehdr),
null,
);
const header = try elf.Header.parse(elf_bytes[0..@sizeOf(elf.Elf64_Ehdr)]);
const section = line[25 .. 25 + mem.indexOfAny(u8, line[25..], &std.ascii.spaces).?];
// the last whitespace-separated column is the symbol name
const name = line[1 + mem.lastIndexOfAny(u8, line, &std.ascii.spaces).? ..];
const symbol = Symbol{
.name = try ally.dupe(u8, name),
.section = try ally.dupe(u8, section),
.kind = if (line[17] == 'g' and line[18] == ' ')
.global
else if (line[17] == ' ' and line[18] == 'w')
.weak
else
std.debug.panic("unexpected kind on line {d}:\n{s}", .{ line_number, line }),
.type = switch (line[23]) {
'F' => .function,
'O' => .object,
' ' => .none,
else => unreachable,
},
.protected = mem.indexOf(u8, line, ".protected") != null,
const parse: Parse = .{
.arena = arena,
.sym_table = &sym_table,
.sections = &sections,
.blacklist = blacklist,
.elf_bytes = elf_bytes,
.header = header,
.arch = arch,
};
for (sections.items) |s| {
if (mem.eql(u8, s, symbol.section)) break;
} else {
try sections.append(symbol.section);
switch (header.is_64) {
true => switch (header.endian) {
.Big => try parseElf(parse, true, .Big),
.Little => try parseElf(parse, true, .Little),
},
false => switch (header.endian) {
.Big => try parseElf(parse, false, .Big),
.Little => try parseElf(parse, false, .Little),
},
}
try symbols.append(symbol);
}
std.sort.sort(Symbol, symbols.items, {}, cmpSymbols);
std.sort.sort([]const u8, sections.items, {}, alphabetical);
const stdout = std.io.getStdOut().writer();
try stdout.writeAll(
\\#ifdef PTR64
\\#define WEAK64 .weak
\\#define PTR_SIZE_BYTES 8
\\#define PTR2_SIZE_BYTES 16
\\#else
\\#define WEAK64 .globl
\\#define PTR_SIZE_BYTES 4
\\#define PTR2_SIZE_BYTES 8
\\#endif
\\
);
for (sections.items) |section| {
try stdout.print("{s}\n", .{section});
var prev_section: u16 = std.math.maxInt(u16);
var prev_pp_state: enum { none, ptr32, special } = .none;
for (sym_table.values()) |multi_sym, sym_index| {
const name = sym_table.keys()[sym_index];
for (symbols.items) |symbol| {
if (!mem.eql(u8, symbol.section, section)) continue;
if (multi_sym.section != prev_section) {
prev_section = multi_sym.section;
const sh_name = sections.keys()[multi_sym.section];
try stdout.print("{s}\n", .{sh_name});
}
switch (symbol.kind) {
.global => try stdout.print(".globl {s}\n", .{symbol.name}),
.weak => try stdout.print(".weak {s}\n", .{symbol.name}),
}
switch (symbol.type) {
.function => try stdout.print(".type {s}, %function;\n", .{symbol.name}),
.object => try stdout.print(".type {s}, %object;\n", .{symbol.name}),
if (multi_sym.allPresent()) {
switch (prev_pp_state) {
.none => {},
.ptr32, .special => {
try stdout.writeAll("#endif\n");
prev_pp_state = .none;
},
}
if (symbol.protected)
try stdout.print(".protected {s}\n", .{symbol.name});
try stdout.print("{s}:\n", .{symbol.name});
} else if (multi_sym.is32Only()) {
switch (prev_pp_state) {
.none => {
try stdout.writeAll("#ifdef PTR32\n");
prev_pp_state = .ptr32;
},
.special => {
try stdout.writeAll("#endif\n#ifdef PTR32\n");
prev_pp_state = .ptr32;
},
.ptr32 => {},
}
} else {
switch (prev_pp_state) {
.none => {},
.special, .ptr32 => {
try stdout.writeAll("#endif\n");
},
}
prev_pp_state = .special;
var first = true;
try stdout.writeAll("#if ");
for (arches) |arch, i| {
if (multi_sym.present[i]) continue;
if (!first) try stdout.writeAll(" && ");
first = false;
try stdout.print("!defined(ARCH_{s})", .{@tagName(arch)});
}
try stdout.writeAll("\n");
}
if (multi_sym.commonBinding()) |binding| {
switch (binding) {
elf.STB_GLOBAL => {
try stdout.print(".globl {s}\n", .{name});
},
elf.STB_WEAK => {
try stdout.print(".weak {s}\n", .{name});
},
else => unreachable,
}
} else if (multi_sym.isWeak64()) {
try stdout.print("WEAK64 {s}\n", .{name});
} else {
for (arches) |arch, i| {
log.info("symbol '{s}' binding on {s}: {d}", .{
name, @tagName(arch), multi_sym.binding[i],
});
}
}
switch (multi_sym.ty) {
elf.STT_NOTYPE => {},
elf.STT_FUNC => {
try stdout.print(".type {s}, %function;\n", .{name});
// omitting the size is OK for functions
},
elf.STT_OBJECT => {
try stdout.print(".type {s}, %object;\n", .{name});
if (multi_sym.commonSize()) |size| {
try stdout.print(".size {s}, {d}\n", .{ name, size });
} else if (multi_sym.isPtrSize()) {
try stdout.print(".size {s}, PTR_SIZE_BYTES\n", .{name});
} else if (multi_sym.isPtr2Size()) {
try stdout.print(".size {s}, PTR2_SIZE_BYTES\n", .{name});
} else {
for (arches) |arch, i| {
log.info("symbol '{s}' size on {s}: {d}", .{
name, @tagName(arch), multi_sym.size[i],
});
}
//try stdout.print(".size {s}, {d}\n", .{ name, size });
}
},
else => unreachable,
}
switch (multi_sym.visib) {
.DEFAULT => {},
.PROTECTED => try stdout.print(".protected {s}\n", .{name}),
.INTERNAL, .HIDDEN => unreachable,
}
try stdout.print("{s}:\n", .{name});
}
switch (prev_pp_state) {
.none => {},
.ptr32, .special => try stdout.writeAll("#endif\n"),
}
}
fn parseElf(parse: Parse, comptime is_64: bool, comptime endian: builtin.Endian) !void {
const arena = parse.arena;
const elf_bytes = parse.elf_bytes;
const header = parse.header;
const Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym;
const S = struct {
fn endianSwap(x: anytype) @TypeOf(x) {
if (endian != native_endian) {
return @byteSwap(@TypeOf(x), x);
} else {
return x;
}
}
fn symbolAddrLessThan(_: void, lhs: Sym, rhs: Sym) bool {
return endianSwap(lhs.st_value) < endianSwap(rhs.st_value);
}
};
// A little helper to do endian swapping.
const s = S.endianSwap;
// Obtain list of sections.
const Shdr = if (is_64) elf.Elf64_Shdr else elf.Elf32_Shdr;
const shdrs = mem.bytesAsSlice(Shdr, elf_bytes[header.shoff..])[0..header.shnum];
// Obtain the section header string table.
const shstrtab_offset = s(shdrs[header.shstrndx].sh_offset);
log.debug("shstrtab is at offset {d}", .{shstrtab_offset});
const shstrtab = elf_bytes[shstrtab_offset..];
// Maps this ELF file's section header index to the multi arch section ArrayHashMap index.
const section_index_map = try arena.alloc(u16, shdrs.len);
// Find the offset of the dynamic symbol table.
var dynsym_index: u16 = 0;
for (shdrs) |shdr, i| {
const sh_name = try arena.dupe(u8, mem.sliceTo(shstrtab[s(shdr.sh_name)..], 0));
log.debug("found section: {s}", .{sh_name});
if (mem.eql(u8, sh_name, ".dynsym")) {
dynsym_index = @intCast(u16, i);
}
const gop = try parse.sections.getOrPut(sh_name);
section_index_map[i] = @intCast(u16, gop.index);
}
if (dynsym_index == 0) @panic("did not find the .dynsym section");
log.debug("found .dynsym section at index {d}", .{dynsym_index});
// Read the dynamic symbols into a list.
const dyn_syms_off = s(shdrs[dynsym_index].sh_offset);
const dyn_syms_size = s(shdrs[dynsym_index].sh_size);
const dyn_syms = mem.bytesAsSlice(Sym, elf_bytes[dyn_syms_off..][0..dyn_syms_size]);
const dynstr_offset = s(shdrs[s(shdrs[dynsym_index].sh_link)].sh_offset);
const dynstr = elf_bytes[dynstr_offset..];
// Sort the list by address, ascending.
std.sort.sort(Sym, dyn_syms, {}, S.symbolAddrLessThan);
for (dyn_syms) |sym| {
const this_section = s(sym.st_shndx);
const name = try arena.dupe(u8, mem.sliceTo(dynstr[s(sym.st_name)..], 0));
const ty = @truncate(u4, sym.st_info);
const binding = @truncate(u4, sym.st_info >> 4);
const visib = @intToEnum(elf.STV, @truncate(u2, sym.st_other));
const size = s(sym.st_size);
if (parse.blacklist.contains(name)) continue;
if (size == 0) {
log.warn("{s}: symbol '{s}' has size 0", .{ @tagName(parse.arch), name });
}
switch (binding) {
elf.STB_GLOBAL, elf.STB_WEAK => {},
else => {
log.debug("{s}: skipping '{s}' due to it having binding '{d}'", .{
@tagName(parse.arch), name, binding,
});
continue;
},
}
switch (ty) {
elf.STT_NOTYPE, elf.STT_FUNC, elf.STT_OBJECT => {},
else => {
log.debug("{s}: skipping '{s}' due to it having type '{d}'", .{
@tagName(parse.arch), name, ty,
});
continue;
},
}
switch (visib) {
.DEFAULT, .PROTECTED => {},
.INTERNAL, .HIDDEN => {
log.debug("{s}: skipping '{s}' due to it having visibility '{s}'", .{
@tagName(parse.arch), name, @tagName(visib),
});
continue;
},
}
const gop = try parse.sym_table.getOrPut(name);
if (gop.found_existing) {
if (gop.value_ptr.section != section_index_map[this_section]) {
const sh_name = mem.sliceTo(shstrtab[s(shdrs[this_section].sh_name)..], 0);
fatal("symbol '{s}' in arch {s} is in section {s} but in arch {s} is in section {s}", .{
name,
@tagName(parse.arch),
sh_name,
archSetName(gop.value_ptr.present),
parse.sections.keys()[gop.value_ptr.section],
});
}
if (gop.value_ptr.ty != ty) blk: {
if (ty == elf.STT_NOTYPE) {
log.warn("symbol '{s}' in arch {s} has type {d} but in arch {s} has type {d}. going with the one that is not STT_NOTYPE", .{
name,
@tagName(parse.arch),
ty,
archSetName(gop.value_ptr.present),
gop.value_ptr.ty,
});
break :blk;
}
if (gop.value_ptr.ty == elf.STT_NOTYPE) {
log.warn("symbol '{s}' in arch {s} has type {d} but in arch {s} has type {d}. going with the one that is not STT_NOTYPE", .{
name,
@tagName(parse.arch),
ty,
archSetName(gop.value_ptr.present),
gop.value_ptr.ty,
});
gop.value_ptr.ty = ty;
break :blk;
}
fatal("symbol '{s}' in arch {s} has type {d} but in arch {s} has type {d}", .{
name,
@tagName(parse.arch),
ty,
archSetName(gop.value_ptr.present),
gop.value_ptr.ty,
});
}
if (gop.value_ptr.visib != visib) {
fatal("symbol '{s}' in arch {s} has visib {s} but in arch {s} has visib {s}", .{
name,
@tagName(parse.arch),
@tagName(visib),
archSetName(gop.value_ptr.present),
@tagName(gop.value_ptr.visib),
});
}
} else {
gop.value_ptr.* = .{
.present = [1]bool{false} ** arches.len,
.section = section_index_map[this_section],
.ty = ty,
.binding = [1]u4{0} ** arches.len,
.visib = visib,
.size = [1]u64{0} ** arches.len,
};
}
gop.value_ptr.present[archIndex(parse.arch)] = true;
gop.value_ptr.size[archIndex(parse.arch)] = size;
gop.value_ptr.binding[archIndex(parse.arch)] = binding;
}
}
fn archIndex(arch: std.Target.Cpu.Arch) u8 {
return switch (arch) {
// zig fmt: off
.riscv64 => 0,
.mips => 1,
.i386 => 2,
.x86_64 => 3,
.powerpc => 4,
.powerpc64 => 5,
.aarch64 => 6,
else => unreachable,
// zig fmt: on
};
}
fn archSetName(arch_set: [arches.len]bool) []const u8 {
for (arches) |arch, i| {
if (arch_set[i]) {
return @tagName(arch);
}
}
return "(none)";
}
fn cmpSymbols(_: void, lhs: Symbol, rhs: Symbol) bool {
return alphabetical({}, lhs.name, rhs.name);
fn fatal(comptime format: []const u8, args: anytype) noreturn {
log.err(format, args);
std.process.exit(1);
}
fn alphabetical(_: void, lhs: []const u8, rhs: []const u8) bool {
var i: usize = 0;
while (i < lhs.len and i < rhs.len) : (i += 1) {
if (lhs[i] == rhs[i]) continue;
return lhs[i] < rhs[i];
}
return lhs.len < rhs.len;
}
const blacklisted_symbols = [_][]const u8{
"__adddf3",
"__addkf3",
"__addsf3",
"__addtf3",
"__ashldi3",
"__ashlti3",
"__ashrdi3",
"__ashrti3",
"__atomic_compare_exchange",
"__atomic_compare_exchange_1",
"__atomic_compare_exchange_2",
"__atomic_compare_exchange_4",
"__atomic_compare_exchange_8",
"__atomic_exchange",
"__atomic_exchange_1",
"__atomic_exchange_2",
"__atomic_exchange_4",
"__atomic_exchange_8",
"__atomic_fetch_add_1",
"__atomic_fetch_add_2",
"__atomic_fetch_add_4",
"__atomic_fetch_add_8",
"__atomic_fetch_and_1",
"__atomic_fetch_and_2",
"__atomic_fetch_and_4",
"__atomic_fetch_and_8",
"__atomic_fetch_nand_1",
"__atomic_fetch_nand_2",
"__atomic_fetch_nand_4",
"__atomic_fetch_nand_8",
"__atomic_fetch_or_1",
"__atomic_fetch_or_2",
"__atomic_fetch_or_4",
"__atomic_fetch_or_8",
"__atomic_fetch_sub_1",
"__atomic_fetch_sub_2",
"__atomic_fetch_sub_4",
"__atomic_fetch_sub_8",
"__atomic_fetch_xor_1",
"__atomic_fetch_xor_2",
"__atomic_fetch_xor_4",
"__atomic_fetch_xor_8",
"__atomic_load",
"__atomic_load_1",
"__atomic_load_2",
"__atomic_load_4",
"__atomic_load_8",
"__atomic_store",
"__atomic_store_1",
"__atomic_store_2",
"__atomic_store_4",
"__atomic_store_8",
"__clear_cache",
"__clzdi2",
"__clzsi2",
"__clzti2",
"__cmpdf2",
"__cmpsf2",
"__cmptf2",
"__ctzdi2",
"__ctzsi2",
"__ctzti2",
"__divdf3",
"__divdi3",
"__divkf3",
"__divmoddi4",
"__divmodsi4",
"__divsf3",
"__divsi3",
"__divtf3",
"__divti3",
"__dlstart",
"__eqdf2",
"__eqkf2",
"__eqsf2",
"__eqtf2",
"__extenddfkf2",
"__extenddftf2",
"__extendhfsf2",
"__extendhftf2",
"__extendsfdf2",
"__extendsfkf2",
"__extendsftf2",
"__ffsdi2",
"__ffssi2",
"__ffsti2",
"__fixdfdi",
"__fixdfsi",
"__fixdfti",
"__fixkfdi",
"__fixkfsi",
"__fixsfdi",
"__fixsfsi",
"__fixsfti",
"__fixtfdi",
"__fixtfsi",
"__fixtfti",
"__fixunsdfdi",
"__fixunsdfsi",
"__fixunsdfti",
"__fixunskfdi",
"__fixunskfsi",
"__fixunssfdi",
"__fixunssfsi",
"__fixunssfti",
"__fixunstfdi",
"__fixunstfsi",
"__fixunstfti",
"__floatdidf",
"__floatdikf",
"__floatdisf",
"__floatditf",
"__floatsidf",
"__floatsikf",
"__floatsisf",
"__floatsitf",
"__floattidf",
"__floattisf",
"__floattitf",
"__floatundidf",
"__floatundikf",
"__floatundisf",
"__floatunditf",
"__floatunsidf",
"__floatunsikf",
"__floatunsisf",
"__floatunsitf",
"__floatuntidf",
"__floatuntisf",
"__floatuntitf",
"__gedf2",
"__gekf2",
"__gesf2",
"__getf2",
"__gnu_f2h_ieee",
"__gnu_h2f_ieee",
"__gtdf2",
"__gtkf2",
"__gtsf2",
"__gttf2",
"__ledf2",
"__lekf2",
"__lesf2",
"__letf2",
"__lshrdi3",
"__lshrti3",
"__ltdf2",
"__ltkf2",
"__ltsf2",
"__lttf2",
"__moddi3",
"__modsi3",
"__modti3",
"__muldc3",
"__muldf3",
"__muldi3",
"__mulkf3",
"__mulodi4",
"__muloti4",
"__mulsc3",
"__mulsf3",
"__mulsi3",
"__multc3",
"__multf3",
"__multi3",
"__mulxc3",
"__nedf2",
"__negdf2",
"__negsf2",
"__nekf2",
"__nesf2",
"__netf2",
"__paritydi2",
"__paritysi2",
"__parityti2",
"__popcountdi2",
"__popcountsi2",
"__popcountti2",
"__subdf3",
"__subkf3",
"__subsf3",
"__subtf3",
"__truncdfhf2",
"__truncdfsf2",
"__trunckfdf2",
"__trunckfsf2",
"__truncsfhf2",
"__trunctfdf2",
"__trunctfhf2",
"__trunctfsf2",
"__udivdi3",
"__udivmoddi4",
"__udivmodsi4",
"__udivmodti4",
"__udivsi3",
"__udivti3",
"__umoddi3",
"__umodsi3",
"__umodti3",
"__unorddf2",
"__unordkf2",
"__unordsf2",
"__unordtf2",
"__zig_probe_stack",
"fmaq",
};