世界性Libc:可在任何地方运行一次构建C库

2020-12-28 12:14:44

换句话说,编译器假设在调用函数时, 15个单独的寄存器,所有存储器将被覆盖。看到 系统V 有关更多详细信息,请参见ABI。这可能是有问题的 常用功能,例如memcpy,因为它禁止许多 优化,它在编译器寄存器中丢了一把扳手 分配算法,从而导致堆栈溢出,从而进一步 在降低输出二进制大小的同时降低性能。 那么Cosmopolitan为memcpy()和其他许多项目做了什么 经常被称为核心库叶子函数,正在定义一个简单的 宏包装程序,它告诉编译器abi的正确子集 实际需要的,例如 #定义memcpy(DEST,SRC,N)({\ 无效*目标=(目标); \ 无效* Src =(SRC); \ size_t大小=(N); \ asm(& call memcpy" \ :" = m"(*(char(*)[Size])(Dest))\ :" D"(Dest)," S"(Src)," d"(n),\ " m"(*(char(*)[Size])(Src))\ :" rcx&#34 ;、" xmm3&#34 ;、" xmm4&#34 ;、" cc"); \ 目的地\ })

这意味着世界性memcpy()不仅速度很快,而且 也使不相关的代码在调用它的函数中也更快 副作用。首次为memcpy()实现此技术时 仅世界性代码库中的许多功能都有 生成的代码大小减少了三分之一。 有关此类功能的示例,请考虑使用strlcpy, 这是BSD说strcpy的方式: / ** *复制字符串,BSD方式。 * * @parad d是不需要初始化的缓冲区 * @param s是NUL终止的字符串 * @param n是d的字节容量 * @return strlen(s) * @note d和s不能重叠 * @note我们更喜欢memccpy() * / size_t strlcpy(char * d,const char * s,size_t n){ size_t slen,实际; slen = strlen(s); 如果(n){ 实际= MIN(n,slen); memcpy(d,s,实际的); d [actual] =' \ 0&#39 ;; } 返回slen; }

如果我们编译strlcpy函数,则这里是 编译器输出的汇编代码: /用传统的libc编译 strlcpy: 推送%rbp mov%rsp,%rbp 推%r14 mov%rsi,%r14 推%r13 mov%rdi,%r13 mov%rsi,%rdi 推%r12 推送%rbx mov%rdx,%rbx 召唤 mov%rax,%r12 测试%rbx,%rbx 1月1日 弹出%rbx mov%r12,%rax 弹出%r12 弹出%r13 弹出%r14 弹出%rbp 退回 1:cmp%rbx,%rax mov%r14,%rsi mov%r13,%rdi cmovbe%rax,%rbx mov%rbx,%rdx 打电话给memcpy movb $ 0,0(%r13,%rbx) mov%r12,%rax 弹出%rbx 弹出%r12 弹出%r13 弹出%r14 弹出%rbp 退回 .endfn strlcpy,globl

/用国际化libc编译 strlcpy: mov%rdx,%r8 mov%rdi,%r9 mov%rsi,%rdi 召唤 测试%r8,%r8 je 1f cmp%r8,%rax mov%r8,%rdx mov%r9,%rdi cmovbe%rax,%rdx 致电MemCpy movb $ 0,(%r9,%rdx) 1:退 .endfn strlcpy,globl

这是生成代码大小的巨大改进。以上两个 编译使用相同的gcc标志,并且无需更改代码即可 被制造。更改的只是我们使用了cosmopolitan.h(而不是 平台c库string.h),其中包含ABI专业化宏 对于memcpy和strlen。这是一个很好的例子 仅选择更好的C库可以如何系统地消除 在整个代码库中膨胀。