Форум программистов, компьютерный форум, киберфорум
Evg
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  

Что такое метка на низком уровне

Запись от Evg размещена 20.03.2014 в 14:40
Показов 14297 Комментарии 18
Метки c, label, метка, си

За основу взят данный пост: https://www.cyberforum.ru/asm-... ost5938013. Изначально предполагал более развёрнутое объяснение поместить там, но в очередной раз получилось много букв и решил выделить в блог, т.к. скорее всего что-то придётся исправлять, к тому же информация может оказаться полезной и для других. В этом смысле данную статью нельзя рассматривать как законченную статью. Скорее это просто рабочая страница из записной книжки с мыслями "чтобы не потерялось"

Чтобы не вводить в заблуждение, сразу же оговариваю две вещи. Я НЕ считаю ассемблер языком программирования (прошу не путать понятия "язык" и "язык программирования" - это НЕ одно и то же). Под словом "метка" подразумевается НЕ метка языка программирования, а метка в понимании ассемблерного программирования. Все попытки начать спор на тему того, является ассемблер языком программирования или нет, буду удалять. Хотелось бы переносить все такие посты куда-нибудь в другое место, но технически такой возможности нет (во всяком случае у меня). Если кому-то очень хочется поспорить - на форуме есть специальный раздел для холиваров




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

Следующий пример я компилировал на i386-linux, а потому синтаксис ассемблерного файла соответствующий. При перекладывании кода под другие системы ассемблерный файл нужно будет переписать под свой синтаксис. Возможно, что ещё и имя метки надо будет поменять, т.к., например, gcc от cygwin'а ко всем глобальным именам пририсовывает символ подчерка

C
/* Файл t.c */
#include <stdio.h>
 
/* Конкретный тип нам не важен, т.к. мы здесь работаем только с адресом
 * внешней метки с именем "label" */
extern int label;
 
int main (void)
{
  printf ("&label = %p\n", &label);
  return 0;
}
Assembler
# Файл t1.s
  .globl label
label:
  .byte 0
Что у нас написано в исходниках? В программе на Си мы описали внешнюю переменную с именем label и хотим напечатать её адрес. С точки зрения языка программирования мы работаем с переменными, в то время как с точки зрения ассемблера или бинарного файла у нас нет никаких переменных, а есть только данные в памяти и метки. В файле t1.s мы создаём 1 байт данных, куда помещаем байт с нулевым значением и создаём метку label, которая настроена на этот байт. По умолчанию ассемблер считает, что всё создаётся в исполняемых секциях, а потому этот нулевой байт создан именно в секции с кодом, а не в секции с данными. Это не играет никакой принципиальной роли, т.к. мы просто хотим напечтать адрес метки (а более правильно - "значение метки")

Code
$ gcc t.c t1.s
$ ./a.out 
&label = 0x804840c
Таким образом, после линковки у нас в коде программы оказался нулевой байт. Адрес этого нулевого байта записан в метку label. Значение метки label равно 0x804840c. Другими словами, в память по адресу 0x804840c и попал наш нулевой байт

Пока тут было всё более менее стандартно

Теперь возьмём другой ассемблерный файл

Assembler
# Файл t2.s
  .globl label
  .set label, 0x11223344
Что тут делается? Заводится метка с именем label и значением 0x11223344. При этом не заводятся никакие данные. Т.е. метка висит абстрактно, не указывает ни на какие конкретно данные, но при этом имеет конкретное значение 0x11223344

Code
$ gcc t.c t2.s
$ ./a.out 
&label = 0x11223344
Да, для многих это будет непривычно, но именно так устроен мир. Метка - это некая сущность, которая имеет некоторое целочисленное значение. В обычной жизни метка ассоциируется с блоком данных или фрагментом кода, а потому значение метки трактуется как адрес переменной, адрес функции или адрес некоторой точки внутри функции. Это бытовое понятие метки. Но на самом деле метка, как уже говорилось - это абстрактная конструкция, описывающая ЗНАЧЕНИЕ, которое может в том числе совпадать с адресом другой сущности

Давайте посмотрим, как в объектном файле в реальности представлена метка:

C
/* Файл t.c */
int a = 1;
int b = 2;
int c = 3;
Code
 $ gcc t.c -c
 
$ readelf --sections t.o
...
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
...
  [ 2] .data             PROGBITS        00000000 000034 00000c 00  WA  0   0  4
...
 
$ readelf --symbols t.o
...
   Num:    Value  Size Type    Bind   Vis      Ndx Name
...
     7: 00000000     4 OBJECT  GLOBAL DEFAULT    2 a
     8: 00000004     4 OBJECT  GLOBAL DEFAULT    2 b
     9: 00000008     4 OBJECT  GLOBAL DEFAULT    2 c
...
 
$ objdump -s t.o
...
Contents of section .data:
 <0000> 01000000 02000000 03000000
...
В печати "readelf --sections" нам нужно только то, что секция .data (секция с данными) имеет номер 2 (колонки Name и [Nr]). Интерес для нас представляет печать "readelf --symbols". Метки представлены в колонке "Name". У меток есть секция (колонка Ndx), для которой 2 означает секция .data. У меток есть значение (колонка Value). Видим, что, например, метка "c" описана как метка, лежащая в секции .data и имеющая значение 8. Что означает, что метка адресует блок памяти в секции .data со смещением 8 байт от начала секции. Последняя печать "objdump -s" просто показывает побайтное содержимое секции .data. Т.е. у всех потребители метки "c" точка использования "c" будет заменена на адрес секции .data, соответствующий модулю t.o + 8 байт. Итоговый адрес этой конструкции будет знать только линкер

Теперь возьмём другой исходник

Assembler
# Файл t.s
  .globl a
  .set a, 0x11223344
  .globl b
  .set b, 0x55667788
  .globl c
  .set c, 0x99aabbcc
Code
$ gcc t.s -c
 
$ readelf --symbols t.o
...
   Num:    Value  Size Type    Bind   Vis      Ndx Name
...
     4: 11223344     0 NOTYPE  GLOBAL DEFAULT  ABS a
     5: 55667788     0 NOTYPE  GLOBAL DEFAULT  ABS b
     6: 99aabbcc     0 NOTYPE  GLOBAL DEFAULT  ABS c
...
Здесь в качестве секции для меток указано ABS. Это псевдосекция, говорящая о том, что метка ассоциирована не с какими-то конкретными данными, а имеет значение, не привязанное к данным (абсолютное значение). Т.е. у всех потребители метки "c" точка использования "c" будет заменена на значение 0x99aabbcc. Подстановкой значения так же будет заниматься линкер, но делаться будет это немного по другим правилам.

Если взять два последних примера и прилинковать их к другим модулям, которые потребляют метки "a", "b" и "c", то для всех прочих потребителей оба варианта получения объектного файла t.o принципиально ничем не будут отличаться. Есть точка потребления метки, есть модуль, в котором описано определение метки. Просто в одном случае определение метки ассоциировано с некоторыми данными (т.е. value метки есть адрес точки внутри данных), а во втором случае определение метки есть просто число
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 18
Комментарии
  1. Старый комментарий
    Аватар для Mikl___
    Evg,
    по поводу того, что в ассемблере "нет таких понятий как переменная и массив"
    Цитата Сообщение от Mikl___
    По этой директиве описывается переменная X
    Цитата Сообщение от Evg
    На самом деле заводится 1 байт данных, в метку X присваивается адрес этого байта
    Цитата Сообщение от Mikl___
    Для описания переменной-массива с некоторыми начальными значениями, применяется директива DB с несколькими операндами
    Цитата Сообщение от Evg
    На самом деле просто заводится блок памяти в 4 байта, эти байты инициализируются значениями 2, -2, <undef>, '*', а в метку M присваивается адрес первого байта. Нету никаких переменных или массивов.
    Студентам обучающимся применению языка ассемблер в качестве практики предлагается писать программы, которые выполняют арифметические, логические, статистические и т.д. вычисления, в которые входят переменные и массивы (одно/двух/многомерные), и хотя с точки зрения компилятора данные (переменные и массивы) можно рассматривать как метки, но на этом основании, говорить о том, что "Нету никаких переменных или массивов" так же не стоит
    Запись от Mikl___ размещена 22.03.2014 в 09:22 Mikl___ вне форума
  2. Старый комментарий
    Аватар для Evg
    Переменная языка является меткой в ассемблере. Обратное неверно. В ассемблере нету понятия переменной или массива. Такие понятие есть только в голове программиста, который пишет программу. Т.е. он ТРАКТУЕТ кусок памяти, как переменную типа int16, массив из 10 элементов типа float32 и т.д.

    Я не знаю, чего там преподают студентам. Скорее всего, расписывать через переменные понятнее и проще. А в качестве результата имеем тех, кто "выучил ассемблер", но по прежнему нифига не разбираются во внутренностях машины или хотя бы в бинарных файлах
    Запись от Evg размещена 22.03.2014 в 11:54 Evg вне форума
  3. Старый комментарий
    Аватар для taras atavin
    Метка языка высокого уровня - это просто адрес некоторого места в коде.
    Запись от taras atavin размещена 22.03.2014 в 12:59 taras atavin вне форума
  4. Старый комментарий
    Аватар для Evg
    Цитата Сообщение от taras atavin
    Метка языка высокого уровня - это просто адрес некоторого места в коде.
    В начале добавил абзац с уточнением, что есть "метка" в контексте данной статьи
    Запись от Evg размещена 22.03.2014 в 13:21 Evg вне форума
  5. Старый комментарий
    Аватар для programina
    Под меткой всегда подразумевала ту самую метку, на которую переходят инструкцией goto. Но здесь, я полагаю, имеются в виду совсем другие метки. Программа readelf выводит таблицу из объектного файла с этими метками в поле name, то есть эти метки живут в объектных файлах?
    Запись от programina размещена 22.03.2014 в 23:17 programina вне форума
  6. Старый комментарий
    Аватар для Evg
    Да, именно эти. В терминах объектных файлов эти метки называют symbol (в русскоязычной терминологии часто называют "символьная ссылка"). Symbol - это та сущность, через которую работает линковка. Если ты будешь смотреть не объектный файл, а ассемблерный (выдача по -S), то основная масса символов на ассемблере выглядит как имя и двоеточие, что обычно называется "метка"
    Запись от Evg размещена 22.03.2014 в 23:26 Evg вне форума
  7. Старый комментарий
    Аватар для programina
    q.cpp
    C++
    int main()
    {
        int a = 7, b = 9;
        
        if(a < b) a += b;
    }
    g++ -S q.cpp
    Perl
        .file   "q.cpp"
        .def    ___main;    .scl    2;  .type   32; .endef
        .text
        .globl  _main
        .def    _main;  .scl    2;  .type   32; .endef
    _main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $16, %esp
        call    ___main
        movl    $7, 12(%esp)
        movl    $9, 8(%esp)
        movl    12(%esp), %eax
        cmpl    8(%esp), %eax
        jge L2
        movl    8(%esp), %eax
        addl    %eax, 12(%esp)
    L2:
        movl    $0, %eax
        leave
        ret

    То есть L2: - это метка, _main: получается тоже метка? Интересно, что будет если в строчке jge L2 написать jge _main, а затем каким-нибудь образом скомпилировать этот файл с асемблером и запустить полученную программу.
    Запись от programina размещена 23.03.2014 в 01:17 programina вне форума
  8. Старый комментарий
    Аватар для Evg
    Цитата Сообщение от programina
    То есть L2: - это метка, _main: получается тоже метка?
    Именно так

    Цитата Сообщение от programina
    Интересно, что будет если в строчке jge L2 написать jge _main, а затем каким-нибудь образом скомпилировать этот файл с асемблером и запустить полученную программу.
    "Каким-нибудь образом скомпилировать" можно, подав команду "g++ q.s", где q.s - это файл, полученный приказом "g++ -S q.cpp" и нужным образом отредактированный. Если поменять "jge L2" на "jge _main", то конкретно в данном случае ничего не будет, т.к. этот переход всё равно не исполнится (потому что 7 меньше 9), но если сделать a = 10, то при исполнении управление честно передастся на метку _main. А дальше скорее всего зациклится (я не знаю, что делается внутри библиотечной ___main)

    Внутри процессора нету ни переменных, ни процедур. Процессор тупо исполняет последовательность операций. Какую последовательность операций подсунули, такую и исполнит. До тех пор, пока не нарвётся на недопустимую операцию (в случае приложения это обращение по некорректному адресу, переход на несуществующий адрес и т.п.)
    Запись от Evg размещена 23.03.2014 в 01:44 Evg вне форума
  9. Старый комментарий
    Аватар для programina
    Цитата Сообщение от Evg
    g++ q.s
    Cкомпилированная программа вылетает с кодом ошибки c00000fd
    Запись от programina размещена 23.03.2014 в 02:59 programina вне форума
  10. Старый комментарий
    Аватар для Mikl___
    Цитата Сообщение от Evg
    Я не знаю, чего там преподают студентам. Скорее всего, расписывать через переменные понятнее и проще. А в качестве результата имеем тех, кто "выучил ассемблер", но по прежнему нифига не разбираются во внутренностях машины или хотя бы в бинарных файлах
    Мужчин и женщин также можно рассматривать как протоплазму или ДНК-цепочки. Но общаться с протоплазмой будет сложнее . Иногда нужно остановится на "верхнем уровне абстракции"...
    Запись от Mikl___ размещена 23.03.2014 в 04:05 Mikl___ вне форума
  11. Старый комментарий
    Аватар для taras atavin
    [QUOTE=Evg;bt9021]В начале добавил абзац с уточнением, что есть "метка" в контексте данной статьи[/QUOTE]Ассемблерная метка - это тоже адресе, но уже не места именно в коде, а вообще любого места в программе, включая место, в котором с точки зрения программиста находится инициируемая с бинарника переменная.
    Запись от taras atavin размещена 23.03.2014 в 05:34 taras atavin вне форума
  12. Старый комментарий
    Аватар для Evg
    Цитата Сообщение от Mikl___
    Иногда нужно остановится на "верхнем уровне абстракции"...
    На верхнем уровне абстракции имеет смысл остановиться, если пишешь учебник. А у тебя ведь не учебник, а справочник. Или ты предполагал переписать его. Что-то уже из головы начало всё вылетать.

    Ну а даже если и учебник. В учебниках всегда есть разделы для продвинутого изучения. Для тех, у кого голова работает, никогда не будет лишним описать истинную природу вещей. Просто мне казалось, что если и есть смысл писать свой учебник, то побудительным мотивом для этого должно быть желание написать не так, как у всех, а правильно и удобно
    Запись от Evg размещена 23.03.2014 в 10:24 Evg вне форума
  13. Старый комментарий
    Аватар для taras atavin
    Справочник по языку должен ограничиваться самим языком, учебник должен учить, что хоть и возможно без разбора низкого уровня, но только если учишь совсем уж новичков и требования к знаниям пока не велики.
    Запись от taras atavin размещена 23.03.2014 в 10:49 taras atavin вне форума
  14. Старый комментарий
    Аватар для Evg
    Цитата Сообщение от taras atavin
    Ассемблерная метка - это тоже адресе, но уже не места именно в коде
    Бытовое понятие метки именно такое. Но в реальности, метка - это по сути дела целочисленное значение, которое совпадает с адресом. А может и не совпадать ни с чьим с адресом. А может. Метка - это то, что в конечном исполняемом коде будет заменено на число.

    Assembler
    # максимальное количество очков
    A equ 0x100
     
    # текущее количество очков
    B db 0x0
     
    # записываем в %eax максимальное количество очков
    mov %eax, A
     
    # записываем в %ebx адрес ячейки с количеством текущих очков
    mov %ebx, B
    На бытовом уровне в %ebx мы записываем целочисленную константу, а в регистр %ebx - адрес переменной. Но с точки зрения машины эти два mov'а ничем не отличаются, т.к. записывают в регистр числа. Для связки ассемблер-линкер "A" и "B" являются просто числами. Просто значение числа A можно по простому получить ещё на этапе ассемблирования, а значения числа B можно получить (уже не по простому) на этапе линковки. Но итог будет одинаковый. То, что в ассемблерной программе было меткой, в итоговом коде будет заменено на число. Ибо метка - это всего лишь удобная форма записи, чтобы не оперировать конкретными числами
    Запись от Evg размещена 23.03.2014 в 11:08 Evg вне форума
  15. Старый комментарий
    Аватар для Evg
    Цитата Сообщение от programina
    Cкомпилированная программа вылетает с кодом ошибки c00000fd
    Если есть желание - запусти из-под отладчика gdb и пошагово протрассируй, найдёшь, где упало. "Пошагово" - в смысле машинного кода, а не языка программирования. "nexti" - команда отладчика, которая выполняет одну инструкцию машины

    Ты можешь передать управление вообще на какую-нибудь глобальную переменную. Программа нормально скомпилируется. Но упадёт на исполнении, т.к. современные операционные системы имеют исполняемые страницы памяти, и неисполняемые. Всё это можно пролечить mprotect'ом
    Запись от Evg размещена 23.03.2014 в 11:13 Evg вне форума
  16. Старый комментарий
    Аватар для Evg
    Цитата Сообщение от taras atavin
    Справочник по языку должен ограничиваться самим языком, учебник должен учить, что хоть и возможно без разбора низкого уровня, но только если учишь совсем уж новичков и требования к знаниям пока не велики.
    Я ни в коем случае ничего не навязываю. Просто нарисовался момент, который в "обычных" учебниках редко описывается, а потому есть возможность сделать лучше, чем у других. Наверное, это сильно выходит за рамки учебника для начинающих, но тем не менее...
    Запись от Evg размещена 23.03.2014 в 11:16 Evg вне форума
  17. Старый комментарий
    Аватар для Mikl___
    Цитата Сообщение от Evg
    На бытовом уровне в %ebx мы записываем целочисленную константу, а в регистр %ebx - адрес переменной. Но с точки зрения машины эти два mov'а ничем не отличаются
    Да ну? Очень даже отличаются
    код команда комментарий
    0xbbd0004000mov ebx,0x0040000d0 в ebx число 0x40000d0
    0x8b1dd0004000mov ebx,[0x004000d0]в ebx содержимое памяти по адресу 0x4000d0
    Запись от Mikl___ размещена 24.03.2014 в 11:26 Mikl___ вне форума
  18. Старый комментарий
    Аватар для Evg
    В моём примере квадратных скобок не было. Там даже было написано в комментарии "записываем в %ebx адрес ячейки с количеством текущих очков". Адрес ячейки, а не содержимое. Квадратные скобки - это содержимое
    Запись от Evg размещена 24.03.2014 в 21:44 Evg вне форума
 
Новые блоги и статьи
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru