Что такое объекты в git? [Дубликат]

Что такое «неопределенный ссылочный / неразрешенный внешний символ»

Я попытаюсь объяснить, что такое «неопределенный ссылочный / неразрешенный внешний символ».

note : я использую g ++ и Linux, и все примеры для него

Например, у нас есть некоторый код

// src1.cpp
void print();

static int local_var_name; // 'static' makes variable not visible for other modules
int global_var_name = 123;

int main()
{
    print();
    return 0;
}

и

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
//extern int local_var_name;

void print ()
{
    // printf("%d%d\n", global_var_name, local_var_name);
    printf("%d\n", global_var_name);
}

Создание объектных файлов

$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o

После фазы ассемблера у нас есть объектный файл, который содержит любые экспортируемые символы. Посмотрите на символы

$ readelf --symbols src1.o
  Num:    Value          Size Type    Bind   Vis      Ndx Name
     5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 _ZL14local_var_name # [1]
     9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_var_name     # [2]

Я отклонил некоторые строки из вывода, потому что они не имеют значения

Итак, мы видим следующие символы для экспорта.

[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable

src2.cpp ничего не экспортирует, и мы не видели его символов

Свяжите наши объектные файлы

$ g++ src1.o src2.o -o prog

и запустите его

$ ./prog
123

Linker видит экспортированные символы и связывает их. Теперь мы пытаемся раскомментировать строки в src2.cpp, как здесь

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
extern int local_var_name;

void print ()
{
    printf("%d%d\n", global_var_name, local_var_name);
}

, и перестроить объектный файл

$ g++ -c src2.cpp -o src2.o

OK (нет ошибок), потому что мы только строим объектный файл, связь еще не завершена. Попробуйте установить ссылку

$ g++ src1.o src2.o -o prog
src2.o: In function `print()':
src2.cpp:(.text+0x6): undefined reference to `local_var_name'
collect2: error: ld returned 1 exit status

Это произошло потому, что наше local_var_name статично, то есть оно не отображается для других модулей. Теперь глубже. Получить выход фазы перевода

$ g++ -S src1.cpp -o src1.s

// src1.s
look src1.s

    .file   "src1.cpp"
    .local  _ZL14local_var_name
    .comm   _ZL14local_var_name,4,4
    .globl  global_var_name
    .data
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; assembler code, not interesting for us
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

Итак, мы видели, что для local_var_name нет метки, поэтому линкер не нашел его. Но мы хакеры :), и мы можем это исправить. Откройте src1.s в текстовом редакторе и измените

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

на

    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789

i.e. вам должно быть как ниже

    .file   "src1.cpp"
    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789
    .globl  global_var_name
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; ...

мы изменили видимость local_var_name и установили его значение в 456789. Попробуйте построить из него объектный файл

$ g++ -c src1.s -o src2.o

ok, см.

$ readelf --symbols src1.o
8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name

В настоящее время local_var_name имеет привязку GLOBAL (LOCAL)

link

$ g++ src1.o src2.o -o prog

и запускает ее

$ ./prog 
123456789

ok, мы взломаем его:)

Итак, в результате - «неопределенная ссылка / неразрешенная внешняя ошибка символа» происходит, когда компоновщик не может найти глобальные символы в объектных файлах.

45
задан SerMetAla 31 January 2014 в 11:14
поделиться

2 ответа

Короткий ответ

Это просто вывод команды git git count-objects -v для нажатия (та же команда вызывается для вывода, когда gc, pull и clone).

$ git count-objects -v
...
size: 14 # The "Compressing objects: 100% (14/14)" part (the size in KiB)
in-pack: 22 # The "Counting objects: 22" part (the number of objects)
...

Длинный ответ

Counting objects: 22, done.

Это git 22 internal. git-count-objects (1) .

объекты подсчитываются для этого конкретного коммита. Практически все в git - это объект, и в основном это капли, сохраненные в вашей папке .git / objects под их соответствующим хэшем. Дополнительная информация на страницах руководства: 9.2 Git Internals - Git Objects .

Compressing objects: 100% (14/14), done.

Это git сжимает объекты перед отправкой. 14/14 - это прогрессия в KiB сжатия (14 KiB для сжатия).

Writing objects: 100% (14/14), 1.89 KiB | 0 bytes/s, done.

Это отправка git (если удаленная) и запись объектов. 1.89 KiB | 0 байтов / с - это прогрессия в KiB и скорость (0 байт / с при завершении).

Total 14 (delta 10), reused 0 (delta 0)

Это вывод алгоритма packfile в git (см. 9.4 Git Internals - Packfiles ) и довольно неясен. Он в основном упаковывает неиспользуемые объекты, как правило, более старую историю, в .git / objects / pack . После упаковки git проверяет, может ли он повторно использовать пакеты (следовательно, повторно используется 0 часть). Дельта 0 - это коэффициент усиления в KiB из упаковки или повторного использования.

53
ответ дан Quentin Pradet 26 August 2018 в 21:20
поделиться

Git - это адресная файловая система. то есть он принимает объект (файл, дерево, фиксацию) и сохраняет его в файлах, адресуемых хешами.

Предположим, вы вносите очень небольшое изменение в файл. Должен ли git хранить полный файл как другой объект? Хорошо. Но иногда (во время push, pull) git вычисляет изменения файлов как дельта и сохраняет их вместо полных файлов.

Это означает, что самая последняя версия файла хранится в полном объеме (поскольку она должна быть доступна наиболее легко), более старая версия того же файла - это просто объект, содержащий разницу между двумя и так далее.

Таким образом, git экономит пространство, сохраняя возможность восстановить файл для любой ревизии, которую вы бросаете на нее.

Теперь, придя к вашему вопросу:

Counting objects: 22, done. : Git подсчитывает объект, связанный с вашими коммитами, которые вы нажимаете.

Total 14 (delta 10): Git удалось уменьшить количество объектов, набрав 10 дельт.

reused 0 (delta 0) : Git может повторно использовать объекты дельты, если они уже существуют. Например, если подобные изменения могли быть введены в другом файле, дельта может быть схожей и многоразовой. Здесь не было ничего, что можно было бы повторно использовать.

Writing objects: 100% (14/14), 1.89 KiB | 0 bytes/s, done. Здесь Git отправляет (или записывает) объекты по сети, и вы можете видеть статистику прогресса и скорости, как это делается.

Надеюсь, это поможет.

13
ответ дан Flimm 26 August 2018 в 21:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: