Доступ к указателю *next на структуру с помощью GNU Assembly

Я использую сборку GNU, пытаюсь перебрать структуру C, связанную в списке, и найти определенное значение из одного из значений структур. Мне интересно, как мне добраться до указателя *next структуры, чтобы перейти к следующему узлу и проверить значение. Ниже приведен пример кода C, который я написал, чтобы попробовать и изучить.

struct node{
   struct node *next;
   int id;
  };

struct node *root;

void functionToBuildLinkList(){
   //Code to malloc and link 4 nodes
 }

int main(int argc, char *argv[]){

   functionToBuildLinkList();

   int valueOne;

   rootPtr = rootPtr->next;
   valueOne = rootPtr->id;
   printf("The valueOne is: %i\n", valueOne);

   return 0;
}

Чтобы попытаться помочь себе разобраться, я взглянул на objdump файла main.

mov    0x804a024,%eax   //Moving the rootPtr->next address into eax
mov    0x4(%eax),%eax   //Offset 4 bytes for id
mov    %eax,0x804a024   //Compiler nonsense?
mov    0x804a024,%eax   //Compiler nonsense?
mov    (%eax),%eax      //Moving the contents of EAX into EAX or more nonsense?
mov    %eax,0x1c(%esp)  //Moving id into the stack
mov    $0x804861c,%eax  //What address is being moved into EAX?
mov    0x1c(%esp),%edx  //Moving id into edx
mov    %edx,0x4(%esp)   //Moving edx (id) into the stack
mov    %eax,(%esp)      //What's the address in EAX? The top of stack - ESP?
call   8048340 <printf@plt>
mov    $0x0,%eax        //Returning O

Я немного сбит с толку, потому что похоже, что компилятор предоставляет жестко закодированные адреса памяти. Как мне узнать, какой адрес памяти у следующего или где находятся определенные узлы? Когда я создал список, выделив память в куче, он находится в последовательном порядке, чтобы я мог попытаться вычислить его таким образом? Как бы я пробирался по списку? Кроме того, если указатель *next является первой переменной-членом, не будет ли для него смещения (для доступа к нему), когда я получу адрес памяти в начало структуры? Любая помощь приветствуется.

Спасибо всем за помощь! Таким образом, для итерации по списку с использованием корня глобального указателя из примера кода будет:

movl root, %eax   //Move root into EAX
movl (%eax), %eax //eax = eax->next

person RoberJimmer    schedule 07.10.2012    source источник
comment
root и rootPtr кажутся одной и той же переменной с двумя разными именами.   -  person Vaughn Cato    schedule 07.10.2012
comment
root — это глобальная переменная. Компоновщик устанавливает определенные области памяти для всех глобальных переменных.   -  person Vaughn Cato    schedule 07.10.2012
comment
Если вы попросите GCC скомпилировать ваш исходный код с -S, вы получите аннотированный вывод сборки с дополнительной информацией, такой как имена символов, которые объяснят, что такое $0x804861c, и что это за бессмысленные строки компилятора (подсказка: это не чепуха).   -  person DCoder    schedule 07.10.2012


Ответы (2)


Используются два разных режима адресации:

mov    0x804a024,%eax   //Moving the rootPtr (content) into eax
mov    [%eax], %ebx     //access tmp=rootPtr->next

add    0x4, %ebx        // increment tmp by 4 to get the address of &tmp->id

mov    $0x804861c,%eax  // Moving an address to eax

Первая строка часто записывается как

mov.l  [0x804a024], %eax

Первая "ерунда" соответствует левой части задания

rootPtr = rootPtr->next;

В то время как следующая строка может быть оптимизирована.

person Aki Suihkonen    schedule 07.10.2012
comment
Спасибо, у меня есть возможность запустить свой код, и вы правы, чтобы перейти к следующему узлу, это movl (%eax), %ebx. - person RoberJimmer; 08.10.2012

Давайте аннотируем это правильно...

mov    0x804a024,%eax   // eax = rootPtr (global variable)
mov    0x4(%eax),%eax   // eax = eax->next (offset 4)
mov    %eax,0x804a024   // rootPtr = eax

mov    0x804a024,%eax   // eax = rootPtr
mov    (%eax),%eax      // eax = eax->id (offset 0)
mov    %eax,0x1c(%esp)  // valueOne = eax (local variable, on stack)
mov    $0x804861c,%eax  // eax = "The valueOne is: %i\n" (static string)
mov    0x1c(%esp),%edx  // edx = valueOne
mov    %edx,0x4(%esp)   // put edx on argument stack (position 1, offset 4)
mov    %eax,(%esp)      // put eax on argument stack (position 0, offset 0)
call   8048340 <printf@plt> // call printf(eax, edx) (= printf(string, valueOne))
mov    $0x0,%eax        // return 0

В этом примере много бесполезных ходов. Если вы компилируете в легком режиме оптимизации (например, -O), вы обычно можете получить более простой код. На высоких уровнях, таких как -O3, код может стать очень трудным для понимания из-за сложной оптимизации.

Обратите внимание, что написанный вами ассемблерный код не будет использовать «жестко закодированные» адреса; если вам нужно обратиться к глобалу, поставьте на него метку и обращайтесь к нему через метку. Обратите внимание, что доступ к ->next, например, это просто вопрос доступа к 0x4(%eax) (при условии, что указатель узла находится в eax); это потому, что указатель next находится в 4 байтах от начала структуры.

person nneonneo    schedule 07.10.2012
comment
Спасибо за прояснение всего этого. Я обновил свои вопросы, чтобы помочь прояснить пару вопросов, которые у меня есть. - person RoberJimmer; 07.10.2012