由于编译器仅将用户级swapcontext视为函数调用,下面的示例演示了简单函数调用的问题.
#include <stdio.h>struct Foo { int x; int y;};__thread Foo* volatile foo;voID bar() { asm("nop");}voID f() { foo->x = 5; bar(); asm volatile("":::"memory"); // We desire a second computation of the address of foo here as an offset // from the FS register. foo->y = 7;}int main(){ foo = new Foo; f(); delete foo;}
接下来,我们运行以下命令进行编译和反汇编.请注意,-fPIC标志似乎是重现此问题所必需的,并且对于我的用例也是必需的,因为我正在构建库.假设上面的代码在一个名为TL.cc的文件中
g++ -std=c++11 -O3 -fPIC -Wall -g TL.cc -o TL objdump -d TL
这是函数f()的汇编转储.
400760: 53 push %rbx # Notice this computation happens only once. 400761: 64 48 8b 04 25 00 00 mov %fs:0x0,%rax 400768: 00 00 40076a: 48 8d 80 f8 ff ff ff lea -0x8(%rax),%rax 400771: 48 89 c3 mov %rax,%rbx 400774: 48 8b 00 mov (%rax),%rax 400777: c7 00 05 00 00 00 movlg++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609copyright (C) 2015 Free Software Foundation,Inc.This is free software; see the source for copying conditions. There is NOwarranty; not even for MERCHANTABIliTY or fitness FOR A PARTIculaR PURPOSE.x5,(%rax) 40077d: e8 ce ff ff ff callq 400750 <_Z3barv> # Observe that the value of rbx came from before the function call,# so if the function bar() actually returned on a different kernel # thread,we would be referencing the original kernel thread's # version of foo,instead of the new kernel thread's version. 400782: 48 8b 03 mov (%rbx),%rax 400785: c7 40 04 07 00 00 00 movl#define SafeTLS(Typename,name) \struct name##_SafeClass { \ name##_SafeClass& \ __attribute__ ((noinline)) \ operator=(const Typename& other) { \ asm (""); \ name = const_cast<Typename&>(other); \ return *this; \ } \ Typename& \ __attribute__ ((noinline)) \ operator->() { \ asm (""); \ return get(); \ } \ operator Typename() { return get(); } \ Typename& \ __attribute__ ((noinline)) \ get() { \ asm (""); \ return name; \ } \ \ Typename* \ operator&() { \ asm (""); \ return &name; \ } \} name##_Safex7,0x4(%rax) 40078c: 5b pop %rbx 40078d: c3 retq 40078e: 66 90 xchg %ax,%ax
我们观察到寄存器rax正在从内存重新加载,但是在调用bar()之前确定了内存位置.
有没有办法强制重新加载变量的地址作为fs寄存器当前值的偏移量?
如果存在这些,我可以使用gcc特定的黑客攻击.
这是输出g –version
#include <stdio.h>#include "TLS.h"struct Foo { int x; int y;};__thread Foo* volatile foo;__thread int bar;SafeTLS(Foo* volatile,foo);SafeTLS(int,bar);voID f2() { asm("nop");}voID f() { foo_Safe->x = 5; f2(); asm volatile("":::"memory"); // We desire a second computation of the address of foo here as an offset // from the FS register. (*foo_Safe).y = 7; bar = 7; printf("%d\n",bar); printf("%d %d\n",foo->x,foo->y); bar = 8; printf("%d\n",bar_Safe.get());}int main(){ foo = new Foo; f(); delete foo;}解决方法 我编写了以下Hack,它强制在我测试的所有情况下重新加载TLS,但是会欣赏有关它可能被破坏的所有方式的反馈.
这是一个使用它的更复杂的测试用例.
总结以上是内存溢出为你收集整理的c – 是否可以在跨内核线程迁移后强制重新加载thread_local变量?全部内容,希望文章能够帮你解决c – 是否可以在跨内核线程迁移后强制重新加载thread_local变量?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)