Ifunc 解释


#1

直接执行 /lib/libc.so.6 我得到下面的输出

GNU C Library (Gentoo 2.18 p1) stable release version 2.18, by Roland McGrath et al.
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.8.1.
Compiled on a Linux 3.10.0 system on 2013-08-26.
Available extensions:
        C stubs add-on version 2.1.2
        crypt add-on version 2.1 by Michael Glad and others
        GNU Libidn by Simon Josefsson
        Native POSIX Threads Library by Ulrich Drepper et al
        BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.

于是很好奇, libc ABIs: UNIQUE IFUNC 里那个 IFUNC 是啥意思。

现在终于知道了。 IFUNC 是一种实现动态 dispatch 的技术。比如说 memcpy , 它有多个版本: AVX2 优化的版本, SSE 优化的版本, 和普通的 x86 指令的版本。

调用 memcpy 将导致 最适合当前 CPU 架构的版本被调用。这个就是利用 IFUNC 实现的。

IFUNC 技术下,只有第一次调用 memcpy 会引起 CPU 类型查询。之后的调用都是直接执行。不会再浪费时间进行派发。如果不是这样, IFUNC 技术就没有意义了,不是么!

那么IFUNC是如何实现的呢?

是这样的,普通的导入表, 如你有个函数 abc() , 导入表里有 abc 这个符号,然后还有 abc 这个符号的地址。在调用 abc 的地方动态连接器会将 abc 的调用修改为对 abc 这个符号地址的调用。

但是,如果是 IFUNC , 则有一个特殊的 STT_GNU_IFUNC 符号表。 abc 这个符号对应的地址并不是 abc 这个函数的地址,而是另外一个选择器 的地址。选择器返回的才是 abc 符号的真正地址。

假设 abc() 是 IFUNC 的符号, abc 第一次被调用的时候 , 将导致动态连接器执行 abc 对于的选择器,然后根据选择器返回的地址写回 调用处。那么第二次调用就不会执行选择器了, 而是直接执行选择好的函数。

这就是 IFUNC 的目的和实现原理。

要在自己的程序里使用 IFUNC 技术?

简单,看下面的例子

__attribute__ ((target ("default")))
int foo ()
{
  // The default version of foo.
  return 0;
}

__attribute__ ((target ("sse4.2")))
int foo ()
{
  // foo version for SSE4.2
  return 1;
}

__attribute__ ((target ("arch=atom")))
int foo ()
{
  // foo version for the Intel ATOM processor
  return 2;
}

__attribute__ ((target ("arch=amdfam10")))
int foo ()
{
  // foo version for the AMD Family 0x10 processors.
  return 3;
}
int main ()
{
  int (*p)() = &foo;
  assert ((*p) () == foo ());
  return 0;
}


#2

编译器越來越牛了啊,可以 attribute ((target xx 了 .

希望啥时候编译器可以自动判断当前的cpu类型, 自动加入 attribute ((target xx … 再编译. 或者像交叉编译那样, 可以指定 --target = cpu类型


#3

好东西,学习了。(占个位子还要打多少字?)


#4

不错啊, 知识贴, 学习了