你可以在内核模式之外inputx64的32位“长兼容性子模式”吗?

这可能是一个确切的重复是否有可能通过执行模式切换在64位进程中执行32位代码? ,但这个问题是从一年前,只有一个答案,不给任何源代码。 我希望得到更详细的答案。

我正在运行64位Linux(Ubuntu 12.04,如果重要的话)。 这里有一些代码分配一个页面,写入一些64位的代码,并执行该代码。

#include <assert.h> #include <malloc.h> #include <stdio.h> #include <sys/mman.h> // mprotect #include <unistd.h> // sysconf unsigned char test_function[] = { 0xC3 }; // RET int main() { int pagesize = sysconf(_SC_PAGE_SIZE); unsigned char *buffer = memalign(pagesize, pagesize); void (*func)() = (void (*)())buffer; memcpy(buffer, test_function, sizeof test_function); // func(); // will segfault mprotect(buffer, pagesize, PROT_EXEC); func(); // works fine } 

现在,纯粹为了娱乐价值,我想做同样的事情,但含有任意32位(ia32)代码,而不是64位代码的buffer 。 本页意味着您可以通过将CS段描述符的位设置为LMA=1, L=0, D=1来进入“长兼容性子模式”,从而在64位处理器上执行32位代码。 我愿意将我的32位代码包装在执行此设置的序言/尾声中。

但是可以做这个设置,在Linux中,在用户模式? (BSD /达尔文的答案也将被接受。)这是我开始对这些概念感到朦胧的地方。 我认为解决scheme涉及到添加一个新的段描述符到GDT(或者是LDT?),然后通过lcall指令切换到该段。 但是,所有这一切都可以在用户模式下完成?

这里有一个示例函数,当在兼容性子模式下成功运行时应返回4,在长模式下运行时为8。 我的目标是获取指令指针来获取这个代码path,并从%rax=4 ,而不必进入内核模式(或者只通过文档化的系统调用来实现)。

 unsigned char behave_differently_depending_on_processor_mode[] = { 0x89, 0xE0, // movl %esp, %eax 0x56, // push %{e,r}si 0x29, 0xE0, // subl %esp, %eax 0x5E, // pop %{e,r}si 0xC3 // ret }; 

是的你可以。 甚至使用完全支持的接口也是可行的。 使用modify_ldt将32位代码段安装到LDT中,然后设置一个远指针指向32位代码,然后使用AT&T表示法中的“ljumpl *(%eax)”间接跳转到它。

虽然你会面对各种混乱。 你的堆栈指针的高位可能会被破坏。 如果你真的想运行真正的代码,你可能需要一个数据段。 而且你还需要做一个远程跳转才能回到64位模式。

一个完整的例子是在我的linux时钟测试 test_vsyscall.cc。 (在任何发布的内核上都会有点问题:int cc会崩溃,你应该把它改成更聪明的东西,比如“nop”,看看intcc32。