About IFunc

[
** Updated 02/03/2015 **
The patch to implement IFUNC for arm is submitted here - https://sourceware.org/ml/binutils/2015-01/msg00258.html
]

Scenario

A nasty bug happens in the IFUNC implementation, so write down what I understand for IFUNC for future reference.

IFunc is nothing advanced, it is merely a trick to choose, usually depending on cpu features, a certain function implementation version, the decision is not made before every function invocation, but just once right before binary execution.

A typical usage would be to select one of the following memcpy implementation for a certain hardware.

  • memcpy_neon() …
  • memcpy_vfp() …
  • memcpy_generic_arm() …

The naive way

<pre><code>
void* memcpy(source, dest, size)
{
cpu_features = get_cpu_feture();
if (cpu_has_neon(cpu_features))
return memcpy_neon(source, dest, size);
else if(cpu_has_vfp(cpu_features))
return memcpy_vfp(source, dest, size);
return memcpy_generic_arm(source, dest, size);
}
</code></pre>

Which apparently incurs big performance penalty, the same logic executes for every memcpy invocation.

The ifunc way

IFunc comes in rescue for this scenario - defines a memcpy resolve function, instead of doing actual work, returning a function pointer, depending on a certain logic, in which the actual work will be done. Mark memcpy as a ifunc with resolver set to the aforementioned “memcpy resolver” like below.

<pre><code>
void *memcpy (void *, const void *, size_t)
attribute ((ifunc ("resolve_memcpy")));

// Returns a function pointer
static void (resolve_memcpy (void)) (void)
{
cpu_features = xx; /
for arm, r0 is preset to the the cpu feature value. */

if (cpu_has_neon(cpu_features))
return &memcpy_neon;
else if(cpu_has_vfp(cpu_features))
return &memcpy_vfp;
return &memcpy_generic_arm;
}
</code></pre>

The big difference from “the naive way” is that resolve_memcpy is guaranteed to be called only and exactly once, and that is before main execution (usually in __start).

Implementation

Compiler side

Whenever seeing a “__attribute((ifunc(...))”, mark the function symbol as “IFUNC” in the symbol table, that’s it, simple enough.

Static linker side

[
** Updated 02/03/2015 ** – notice, arm and aarch64 has some slightly different implementation here. For aarch64, the resolve function address is encoded in addend field of a relocation, while for arm, the address is written into the got entry.

<pre><code>
// This is aarch64 implementation - aarch64/dl-irel.h
if (__glibc_likely (r_type == R_AARCH64_IRELATIVE))
{
// the resolve function address is encoded in addend field.
ElfW(Addr) value = elf_ifunc_invoke (reloc->r_addend);
*reloc_addr = value;
}

// This is arm implementation – arm/dl-irel.h
if (__builtin_expect (r_type == R_ARM_IRELATIVE, 1))
{
// the resolve function address in written into the relocation address (the got entry)
Elf32_Addr value = elf_ifunc_invoke (*reloc_addr);
*reloc_addr = value;
}
</code></pre>

This example is based on arm implementation.
]

Whenever seeing a call to an ifunc, the linker does these 3 things -

  • make this call via plt
  • set the corresponding plt.got entry to the address of the resolver function.
  • attach a IRELATIVE to the plt entry.

For example -
<pre><code>
memcpy_pltentry:
0 add r12, pc, #4
4 add r12, r12, #0
8 ldr pc, [r12, #0] // transfer pc to 2000, the content of [12]

memcpy_gotentry:
12 2000 // Attach an IRELATIVE relocation here.

a_routine:
1000 b 0 // call memcpy via plt,
// 0 is the address of memcpy_pltentry
...

memcpy_resolver:
2000 mov r0, 3000
bx lr

memcpy_neon:
3000 ...

memcpy_vfp:
4000 ...

memcpy_generic_arm:
5000 ...
</code></pre>

Right before executing main

glibc will iterative all IRELATIVE relocations, for each such relocation it

  • loads content from IRELATIVE address
  • sets this content to PC (basically this runs the function, whose address is stored in the address denoted by the IRELATIVE relocation) For the example above, IRELATIVE address is 12, its content is 2000, so set pc to 2000, which is memcpy_resolver
  • re-writes the IRELATIVE memory address with the return value from above function invocation. For the example above, the IRELATIVE address is 12 and the return value is 3000, so write 3000 to 12

All later invocation to memcpy goes to memcpy_neon, and memcpy_resolver will ** never be called again**.

After step 1,2, the above memory layout becomes -

<pre><code>
memcpy_pltentry:
0 add r12, pc, #4
4 add r12, r12, #0
8 ldr pc, [r12, #0] // transfer pc to 3000 now,
// the content of [12]

memcpy_gotentry:
12 3000 // 3000 is the value returned by memcpy_resolver.

a_routine:
1000 b 0 // call memcpy via plt,
// 0 is the address of memcpy_pltentry
...

memcpy_resolver:
2000 mov r0, 3000
bx lr

memcpy_neon:
3000 ...

memcpy_vfp:
4000 ...

memcpy_generic_arm:
5000 ...
</code></pre>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末组力,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子师幕,更是在濱河造成了極大的恐慌骆撇,老刑警劉巖汹买,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吝羞,居然都是意外死亡秦忿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門沛鸵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來括勺,“玉大人,你說我怎么就攤上這事曲掰〖埠矗” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵栏妖,是天一觀的道長(zhǎng)乱豆。 經(jīng)常有香客問我,道長(zhǎng)吊趾,這世上最難降的妖魔是什么宛裕? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任瑟啃,我火速辦了婚禮,結(jié)果婚禮上揩尸,老公的妹妹穿的比我還像新娘蛹屿。我一直安慰自己,他們只是感情好岩榆,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布错负。 她就那樣靜靜地躺著,像睡著了一般勇边。 火紅的嫁衣襯著肌膚如雪犹撒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天粒褒,我揣著相機(jī)與錄音识颊,去河邊找鬼。 笑死怀浆,一個(gè)胖子當(dāng)著我的面吹牛谊囚,可吹牛的內(nèi)容都是我干的怕享。 我是一名探鬼主播执赡,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼函筋!你這毒婦竟也來了沙合?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤跌帐,失蹤者是張志新(化名)和其女友劉穎首懈,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谨敛,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡究履,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脸狸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片最仑。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖炊甲,靈堂內(nèi)的尸體忽然破棺而出泥彤,到底是詐尸還是另有隱情,我是刑警寧澤卿啡,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布吟吝,位于F島的核電站,受9級(jí)特大地震影響颈娜,放射性物質(zhì)發(fā)生泄漏剑逃。R本人自食惡果不足惜浙宜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蛹磺。 院中可真熱鬧梆奈,春花似錦、人聲如沸称开。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鳖轰。三九已至清酥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蕴侣,已是汗流浹背焰轻。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昆雀,地道東北人辱志。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像狞膘,于是被迫代替她去往敵國(guó)和親揩懒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • **2014真題Directions:Read the following text. Choose the be...
    又是夜半驚坐起閱讀 9,572評(píng)論 0 23
  • (一) 我想了很多個(gè)我們重逢的情景智亮,卻發(fā)現(xiàn)我們沒有重逢的機(jī)會(huì)〉愦——題記 我們總在得知結(jié)果以后埋怨不夠勇敢的自己阔蛉,就...
    一只行走的板栗閱讀 258評(píng)論 0 0
  • 看完了一部英劇,還是想很有格調(diào)的來一個(gè)標(biāo)題癞埠,這個(gè)題目似乎也還不錯(cuò)的樣子状原。其實(shí),每次連續(xù)地看美劇看英劇燕差,我都忍不住用...
    whayou閱讀 264評(píng)論 0 1
  • 吉英·班納特 班府上全家人這一個(gè)晚上大致都過得很高興遭笋。大小姐蒙彬格萊先生邀她 跳了兩次舞,而且這位貴人的姐妹們都對(duì)...
    初瀾閱讀 294評(píng)論 0 0
  • 如果沒有遇見您 “如果當(dāng)時(shí)沒有遇見您徒探,現(xiàn)在我們會(huì)在哪里瓦呼?’ 這一句話我經(jīng)常問我的先生,如果4年前我們沒有遇見,現(xiàn)在...
    DISC胡曉敏閱讀 306評(píng)論 1 0