Readel находит абсолютный адрес

У меня есть программа C, которая имеет одну глобальную и одну локальную переменную. Мой вопрос по риделфу. Ниже приведены мои вопросы; 1. Когда я беру дамп адреса, используя «readelf --symbols», я получаю адрес для своей глобальной переменной, который совпадает с адресом, который я печатаю при запуске программы. Как readelf может узнать абсолютный адрес до запуска или загрузки моей программы? 2. Почему нет информации о символах локальных переменных?. Я вижу только символы глобальных переменных.


person Franc M    schedule 13.03.2016    source источник


Ответы (1)


Как readelf может узнать абсолютный адрес до запуска или загрузки моей программы?

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

Почему нет информации о символах локальных переменных

В вашей программе может быть три вида "локальных" переменных.

main.c

static int static_filescope_i = 1;

int f()
{
    static int static_local_i = 2;
    return static_local_i;
}

int g()
{
    int automatic_i = 3;
    return automatic_i;
}

int global_i = 4;

int main()
{
    return global_i + f() + g() + static_filescope_i;
}

Автоматическая переменная, такая как automatic_local_i, создается в стеке во время выполнения каждый раз, когда программа входит в блок, в котором она определена, и прекращает свое существование, когда покидает этот блок. Такая переменная не занимает памяти в исполняемом файле, поэтому она не представлена ​​в таблице символов.

Переменную типа static_filescope_i часто называют статической глобальной, чтобы отличить ее от такой, как static_local_i. static_local_i нельзя увидеть за пределами блока, в котором он определен. static_filescope_i можно увидеть в любой функции, определенной в том же объектном файле (main.o), но не в любом другом объектном файле: он глобальный внутри main.o, но локальный для этого объектного файла внутри программа в целом.

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

global_i, конечно, является глобальным для всей программы: его можно увидеть в main.o и любых других файлах, которые мы можем связать с main.o.

Если мы скомпилируем main.c с параметрами по умолчанию (без оптимизации):

$ gcc -c main.c

то находим:

$ readelf -s main.o | grep automatic_i
$

...нет символа automatic_i.

$ readelf -s main.o | grep global_i
    12: 0000000000000004     4 OBJECT  GLOBAL DEFAULT    3 global_i

...глобальный символ для global_i.

$ readelf -s main.o | grep static_filescope_i
     5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    3 static_filescope_i

...местный символ static_filescope_i

$ readelf -s main.o | grep static_local_i
     6: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    3 static_local_i.1833

... а также местный символ для static_local_i, но с добавленным суффиксом, различающим область действия.

Здесь GLOBAL означает, что может быть виден компоновщику, а LOCAL означает, что не может быть виден компоновщику.

Таким образом, для связывания main.o с любыми другими объектными файлами или библиотеками для создания исполняемого файла static_filescope_i и static_local_i может и не существовать.

Это не означает, что они совершенно бесполезны в объектном файле. Они полезны для отладки. Они полезны для исследования того, из чего состоит статическое хранилище исполняемого файла, как мы делаем сейчас.

Но они бесполезны для компоновщика, поэтому, если вы скомпилируете main.c с любым уровнем оптимизации > 0, компилятор предположит, что вам нужен объектный код, не предназначенный для целей отладки или исследования, и он не будет испускать любые локальные символы:

$ gcc -O1 -c main.c
$ readelf -s main.o | grep static_local_i
$ readelf -s main.o | grep static_filescope_i
$ readelf -s main.o | grep global_i
    11: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_i

...остается только global_i.

Это должно объяснить, почему вы не видите ни одного из «местных» символов. Ваши автоматические переменные никогда не находятся в таблице символов. Ваши статические переменные находятся в таблице символов только в том случае, если вы отключили всю оптимизацию.

person Mike Kinghan    schedule 13.03.2016
comment
Спасибо, Майк. Это прояснило мой второй вопрос о локальных переменных. Но мой первый вопрос остается с небольшими сомнениями. Если я создаю свой код для другой машины, как мой локальный компоновщик может узнать о структуре памяти удаленных машин. Адрес глобальных переменных печатается одинаково на обоих компьютерах, тогда как адреса локальных переменных различаются. Я заметил, что адрес глобальных переменных печатается как 6-значный, а локальные переменные - 12-значный. Разве адрес глобальной переменной не является адресом физической памяти ОЗУ? - person Franc M; 14.03.2016