-5

我有一个特定的类,它仅作为全局数组的元素分配(想象一下文件句柄的示例)。

class MyClass;
MyClass A[1000];

我需要破解一个 C++ 编译器(例如 g++ 或 clang),以强制该类发出和解释指针,而不是作为该数组内的索引的地址。

所以这意味着我需要类的方法来接受索引作为“this”指针和构造函数来返回数组内的索引而不是指针。

因为我使用的是 x86 架构,所以当类的大小为 1 2 4 或 8 字节时,将这种索引转换为“有效地址”只是寻址模式的问题。

为了管理某种“外部指针”或类似的东西,这种可能性可能已经存在。

我发现知道的是 CLANG 内部的功能如下所述:

使用地址空间#256 对指针进行注释会导致它是相对于 X86 GS 段寄存器生成的代码,而地址空间#257 会导致它相对于 X86 FS 段。请注意,这是一个非常非常低级的功能,只有在您知道自己在做什么时才应该使用(例如在 OS 内核中)。

这是一个例子:

#define GS_RELATIVE __attribute__((address_space(256)))
int foo(int GS_RELATIVE *P) {
   return *P; 
}

https://clang.llvm.org/docs/LanguageExtensions.html#memory-references-to-specified-segments

这可以解决相对性问题,但不能解决索引乘以到达地址。但是无论如何都不清楚如何使用它来从对象构造函数中获取相对地址。

这个问题由于不够清楚而被搁置,但无论如何我已经使用最终简单的方法解决了它,这里是解决方案的草稿(我最后到达),以帮助理解同样的问题也是一个可能的解决方案。

一个如此复杂的问题可以用足够“简单”的方法来解决,这似乎很奇怪。这正是我需要的解决方案,因为我可以将索引用作对象。

// compilation using g++ on x86 with flags -O3 -m32 

// vect is the class we are acting upon
struct vect{
    int dummy_for_clarity_of_offset;
    int x;
// the "mull" method just multiplies member x with i and j
// according to the regparm(3) convention parameters up to 3 are passed in 
// registers, so the pointer to the vect structure is passed in EAX the i parameter 
// is passed in EDX and the j parameter in ECX (So the order is EAX EDX ECX )
    __attribute__ ((noinline)) __attribute__ ((regparm(3))) void mull(int i,int j){
        asm ("leal (%esi,%eax,8),%eax"); // the index in eax is converted to address
        x*=i*j;
    }
};

register vect* A asm ("%esi"); // the vect* A holds the start address of our array and                     
                              // is a register variable as it is the base of our calcs

// the same operations as above but as a function instead of method
__attribute__ ((noinline)) __attribute__ ((regparm(3))) void mull(vect& a,int i,int j)           

{
    asm ("leal (%esi,%eax,8),%eax");  
    a.x*=i*j;
}


int main(int argc, char **argv)
{
    vect* A1=new(vect[0x10]);
    A=A1;
    A[1].x=9;
    vect* p=(vect*)1; // according to "our convention" should point to A[1]
    int i0=11;int i1=21;
    mull(*p,10,20); // multiplies the member x of A[1] with 10 and 20
    p->mull(i0,i1); // multiplies the member x of A[1] with 11 and 21
}
4

2 回答 2

2

我不明白你为什么需要这样做,因为 C++ 提供了很大的灵活性而无需采取如此激烈的步骤:

  • 即使对于作为全局数组元素分配的类,我也不明白使用普通的旧类指针有什么问题。
  • 您可以(例如)重载 operator new以确保您的类仅在此全局数组中分配,然后使用标准指针。
  • 您还可以编写自己的智能指针类。它可以充当数组索引的包装器,并为其用户提供类似指针的语义(通过重载*and )。->
  • 如果你知道一个对象的所有实例都存在于一个特定的数组中,那么指针算法可以很容易地找到对象的索引:

MyObject GlobalArray[];
int GetIndex(const MyObject *obj) {
    return obj - GlobalArray;
}

但是破解 C++ 编译器本身以更改所有指针(甚至this)似乎需要做很多工作,并且会造成很多混乱。

于 2014-08-21T17:07:58.347 回答
1

您不需要修改编译器。您可能可以使用自己的类似指针的类型:

ArrayElement array[42];
class MyPointer{
    std::size_t index;
public:
    MyPointer(std::size_t i=0):index(i){}
    ArrayElement &operator*() const{
        return array[index];
    }
    ArrayElement *operator->() const{
        return array+index;
    }
    MyPointer &operator++(){index++;return *this}
    MyPointer operator+(std::size_t i) const{return MyPointer(index+i);}
    MyPointer &operator--(){index--;return *this}
    MyPointer operator-(std::size_t i) const{return MyPointer(index-i);}
    std::ptrdiff_t operator-(MyPointer i) const{return index-i.index;}
    // After a few years when they accept defaulted operators into c++:
    default: ==, !=, <, >, <=, >=;
    // Otherwise write your own boilerplate
};

...
MyPointer p{11};
p->asdf(); // array[11].asdf();
p-=5;
p->asdf(); // array[6].asdf();
于 2014-08-21T17:11:46.120 回答