Можно ли сделать указатель на код и перейти к следующей инструкции? - PullRequest
1 голос
/ 12 июня 2010

Мне нравится эта ссылка http://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Labels-as-Values.html
Я могу получить адрес памяти метки, поэтому, если я объявлю метку, получу ваш адрес и добавлю ваш адрес, я перейду к следующей инструкции? немного иллюстрации>


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 ]

4 голосов
/ 12 июня 2010

Нет, я так не думаю.

Прежде всего, вы, кажется, взяли адрес метки, которая не работает. Метка интерпретируется компилятором, но она не представляет фактический адрес в вашем коде.

Во-вторых, каждое утверждение в C / C ++ (фактически, любой язык) может быть переведено во многие инструкции машинного языка, поэтому инструкция 1 может быть переведена в 3, 5, 10 или даже больше машинных команд.

В-третьих, ваш указатель указывает на пустоту. Компилятор C не знает, как увеличить пустой указатель. Обычно, когда вы увеличиваете указатель, он добавляет размер типа данных, на который вы указываете адрес. Таким образом, увеличение длинного указателя добавит 4 байта; увеличение char-указателя добавит 1 байт. В этом случае у вас есть указатель void, который указывает на ничто и, следовательно, не может быть увеличен.

В-четвертых, я не думаю, что все инструкции на машинном языке x86 представлены одинаковым количеством байтов. Таким образом, вы не можете ожидать от добавления чего-либо к указателю, что оно попадет в следующую инструкцию. Вы также можете оказаться в середине следующей инструкции.

1 голос
/ 12 июня 2010

Нет, потому что вы не можете увеличить 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;

I думал , что для этого нужно взять адрес первой инструкции fcn() и поместить его в pt2Function.Таким образом, мой ++p будет указывать на вторую инструкцию (nop длиной в один байт).

Это не так. помещает адрес jmp инструкции (найденной в большой таблице переходов) в pt2Function.Когда вы увеличиваете его на один байт, он указывает на бессмысленное место в таблице переходов.

Я предполагаю, что это зависит от реализации.

1 голос
/ 12 июня 2010

Обратите внимание, что синтаксис &&label описан в разделе C Extensions в документации GCC. Это не C, это GC C.

Кроме того, void* не допускает арифметику указателей - это тип универсальный типа C для указания на что-либо. Предполагается, что компилятор не знает размер объекта, на который он указывает (но программист должен:).

Более того, размеры команд в разных архитектурах сильно различаются - четыре байта в SPARC, но, например, переменной длины в x86.

т.е. это не работает в C. Вам придется использовать встроенный ассемблер для такого рода вещей.

0 голосов
/ 12 июня 2010

Предположим, есть способ получить адрес метки (это не расширение конкретного компилятора).Тогда проблема на самом деле заключается в идее «следующей инструкции»: может быть очень трудно понять, какая следующая инструкция.Это зависит от процессора и от процессоров, таких как x86, чтобы знать длину инструкции, которую нужно декодировать, конечно, не полностью, но это в любом случае сложная работа ... на известных архитектурах RISC длина инструкций намного прощеи получить следующую инструкцию может быть так же просто, как увеличить адрес на 4. Но общего способа сделать это во время выполнения не существует, хотя во время компиляции это может быть проще, но, чтобы разрешить это C-связным способом, C должениметь тип «инструкция», так что «инструкция *» может быть указателем на инструкцию, и приращение такого указателя будет правильно указывать на следующую инструкцию, при условии, что код известен во время компиляции (таким образом, такой указатель можетНа самом деле указатель может указывать на все, на что может указывать указатель).Во время компиляции компилятор может реализовать эту функцию, легко добавив еще одну «метку», которая находится за пределами сгенерированной инструкции, указанной «первой» «меткой».Но это было бы обманом ...

Более того, давайте предположим, что вы получили адрес метки C, или функции C, или чего-то еще.Если вы пропустите первую инструкцию, скорее всего, вы не сможете «использовать» этот адрес для выполнения кода (за исключением первой инструкции), поскольку без этой единственной инструкции код может стать ошибочным ... если вы точно не знаете, чтоможет пропустить эту единственную инструкцию и получить то, что вы хотите, но вы не можете быть уверены ... если вы не посмотрите на код (который может отличаться от компилятора к компилятору), а затем весь смысл делать такие вещииз C исчезает.

Итак, вкратце, ответ - нет, вы не можете вычислить указатель на следующую инструкцию;и если вы так или иначе сделаете это, то тот факт, что вы указываете на код, теряет смысл, поскольку вы не можете перейти по этому адресу и быть уверенным в окончательном поведении.

0 голосов
/ 12 июня 2010

Вы не можете выполнить арифметику с пустым пространством *, и компилятор не будет знать, что добавить к указателю, чтобы он все равно указывал на следующую «инструкцию» - между оператором C и 1 нет соответствия 1: 1машинный код, испускаемый компилятором.Даже для процессоров, у которых есть «обычный» набор команд, где инструкции имеют одинаковый размер (в отличие от чего-то вроде 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' 
0 голосов
/ 12 июня 2010

Я бы сказал "вероятно, нет". Значение указателя будет правильным, потому что компилятор знает, но я сомневаюсь, что + 1 будет знать длину инструкций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...