我可以指定代码,并传递给下一条指令吗?

时间:2010-06-11 20:27:53

标签: c gcc

喜欢此链接http://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Labels-as-Values.html
我可以获取标签的内存地址,所以如果我声明一个标签,获取你的地址,并添加你的地址,我会转到下一条指令吗?一些ilustration>


int main () {
   void *ptr;
   label:

    instruction 1;
    instruction 2;

   ptr = &&label;

   // So if I do it...

   ptr = ptr + 1;
  // I will get the instruction 2 correct??

感谢所有答案。

6 个答案:

答案 0 :(得分:4)

不,我不这么认为。

首先,您似乎采用了标签的地址,但这不起作用。标签由编译器解释,但它不代表代码中的实际地址。

其次,C / C ++中的每个语句(实际上是任何语言)都可以转换为许多机器语言指令,因此指令1可以转换为3,5,10或甚至更多的机器指令。

第三,你的指针指向无效。 C编译器不知道如何递增void指针。通常,当您递增指针时,它会将您指向的数据类型的大小添加到该地址。因此,增加一个长指针将增加4个字节;递增一个char指针将增加1个字节。在这种情况下,你有一个void-pointer,它指向什么都没有,因此无法递增。

第四,我不认为x86机器语言中的所有指令都用相同的字节数表示。因此,您不能指望向指针添加内容以使其到达下一条指令。您可能也会在下一条指令的中间结束。

答案 1 :(得分:1)

请注意,GCC文档中的 C Extensions 部分描述了&&label语法。它不是C,它是 GC C。

另外,void*不允许指针算术 - 它是C中用于指向任何事物的 catch-all 类型。假设编译器不知道它指向的对象的大小(但程序员应该:)。

甚至更多,指令大小在不同的体系结构上有很大的不同 - 例如SPARC上的四个字节,但x86上的长度可变。例如。

即。它在C中不起作用。你必须使用inline assembler来做这类事情。

答案 2 :(得分:1)

不,因为你不能增加void *。

void fcn() { printf("hello, world\n"); }

int main()
{
    void (*pt2Function)() = fcn; 

    pt2Function(); // calls fcn();

    // error C2171: '++' : illegal on operands of type 'void (__cdecl *)(void)'
    // ++pt2Function; 

    return 0;
}

这是VC ++,但我怀疑gcc是相似的。

已编辑添加

只是为了好玩,我试过这个 - 它崩溃了:

int nGlobal = 0;
__declspec(naked) void fcn()
{ 
    // nop is 1-byte instruction that does nothing
    _asm { nop }

    ++nGlobal;

    _asm { ret }
}

int main()
{
    void (*pt2Function)() = fcn; 

    // this works, incrementing nGlobal:
    pt2Function();
    printf("nGlobal: %d", nGlobal);

    char *p = (char *) pt2Function;
    ++p; // point past the NOP?
    pt2Function = (void (*)()) p;

    // but this crashes...
    pt2Function();
    printf("nGlobal: %d", nGlobal);

    return 0;
}

它崩溃了,因为这条线没有按照我的想法做到:

void (*pt2Function)() = fcn;

思考它将采用fcn()的第一条指令的地址,并将其放入pt2Function。这样我的++p会指向第二条指令(nop长度为一个字节)。

没有。它将 jmp指令的地址(在大跳转表中找到)放入pt2Function。当你将它递增一个字节时,它指向跳转表中无意义的位置。

我认为这是特定于实现的。

答案 3 :(得分:0)

我会说“可能不是”。指针的值是正确的,因为编译器知道,但我怀疑+ 1会知道指令的长度。

答案 4 :(得分:0)

你不能对void *执行算术运算,并且编译器不会知道要添加到指针的内容,无论如何都指向下一个'指令' - C语句和C语句之间没有1对1的对应关系编译器发出的机器代码。即使对于具有“常规”指令集的CPU,其中指令的大小相同(与x86中的指令具有可变字节数的情况相反),单个C语句可能会产生多个CPU指令(或者可能只有一个) - 谁知道?)。

扩展GCC文档中的示例,您可能可以使用以下内容,但它需要为您要定位的每个语句添加标签:

   void *statements[] = { &&statement1, &&statement2 };
   void** ptr;

   statement1:
    instruction 1;

   statement2:
    instruction 2;

   ptr = statements;

   // goto **ptr;      // <== this will jump to 'instruction 1'
   // goto **(ptr+1);  // <== this will jump to 'instruction 2' 

答案 5 :(得分:0)

让我们假设有一种获取标签地址的方法(不是特定编译器的扩展)。那么问题就是“下一个指令”的想法:很难知道下一条指令是哪一个。这取决于处理器,并且在像x86这样的处理器上知道你必须解码它的指令的长度,当然不是完全的,但无论如何它是一些复杂的工作...在着名的RISC架构上,指令的长度要容易得多获取下一条指令可能就像将地址递增4一样简单。但是在运行时没有通用的方法,而在编译时它可能更容易,但是以C连贯的方式允许它,C应该具有类型“指令”,因此“指令*”可以是指向指令的指针,并且递增这样的指针将正确指向下一条指令,前提是代码在编译时已知(因此,这样的指针可以' t指向真正指向一般指针的一切)。在编译时,编译器可以轻松地在“第一个”“标签”指向的生成指令之外添加另一个“标签”来实现此功能。但这会是作弊......

此外,让我们假设您获得C标签或C函数的地址,或者其他什么。如果你跳过第一条指令,可能你将无法“使用”该地址来执行代码(少于第一条指令),因为如果没有那条指令,代码可能会变成错误......除非你确定你知道可以跳过那条单指令并获得你想要的东西,但你不能确定...除非你看一下代码(可能与编译器不同,编译器),然后做这样的事情的所有要点从C消失。

所以,简单来说,答案是否定的,你无法计算指向下一条指令的指针;如果你做某事,你指向代码的事实变得毫无意义,因为你不能跳到那个地址并确保最终的行为。