Короткий ответ: вместо этого используйте objdump
или readelf
.
Длинный ответ: Давайте посмотрим на конкретный пример случая, example.c
:
#include <stdio.h>
static const char global1[] = "static const char []";
static const char *global2 = "static const char *";
static const char *const global3 = "static const char *const";
const char global4[] = "const char []";
const char *global5 = "const char *";
const char *const global6 = "const char *const";
char global7[] = "char []";
char *global8 = "char *";
char *const global9 = "char *const";
int main(void)
{
static const char local1[] = "static const char []";
static const char *local2 = "static const char *";
static const char *const local3 = "static const char *const";
const char local4[] = "const char []";
const char *local5 = "const char *";
const char *const local6 = "const char *const";
char local7[] = "char []";
char *local8 = "char *";
char *const local9 = "char *const";
printf("Global:\n");
printf("\t%s\n", global1);
printf("\t%s\n", global2);
printf("\t%s\n", global3);
printf("\t%s\n", global4);
printf("\t%s\n", global5);
printf("\t%s\n", global6);
printf("\t%s\n", global7);
printf("\t%s\n", global8);
printf("\t%s\n", global9);
printf("\n");
printf("Local:\n");
printf("\t%s\n", local1);
printf("\t%s\n", local2);
printf("\t%s\n", local3);
printf("\t%s\n", local4);
printf("\t%s\n", local5);
printf("\t%s\n", local6);
printf("\t%s\n", local7);
printf("\t%s\n", local8);
printf("\t%s\n", local9);
return 0;
}
Вы можете скомпилировать его в объектный файл, используя, например,
gcc -W -Wall -c example.c
и исполняемый файл, используя
gcc -W -Wall example.c -o example
Вы можете использовать objdump -tr example.o
сваливать символьную информацию и переселения для (нединамического) объектного файла, или objdump -TtRr example
сбросить то же самое для исполняемого файла (и файлы динамических объектов). Использование
objdump -t example.o
на x86-64 я
example.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 example.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 l O .rodata 0000000000000015 global1
0000000000000000 l O .data 0000000000000008 global2
0000000000000048 l O .rodata 0000000000000008 global3
00000000000000c0 l O .rodata 0000000000000015 local1.2053
0000000000000020 l O .data 0000000000000008 local2.2054
00000000000000d8 l O .rodata 0000000000000008 local3.2055
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000050 g O .rodata 000000000000000e global4
0000000000000008 g O .data 0000000000000008 global5
0000000000000080 g O .rodata 0000000000000008 global6
0000000000000010 g O .data 0000000000000008 global7
0000000000000018 g O .data 0000000000000008 global8
00000000000000a0 g O .rodata 0000000000000008 global9
0000000000000000 g F .text 000000000000027a main
0000000000000000 *UND* 0000000000000000 puts
0000000000000000 *UND* 0000000000000000 printf
0000000000000000 *UND* 0000000000000000 putchar
0000000000000000 *UND* 0000000000000000 __stack_chk_fail
Выход описан в man 1 objdump
под -t
заголовком. Обратите внимание, что второй «столбец» фактически является фиксированной шириной: семь символов в ширину, описывая тип объекта. Третий столбец - это имя раздела, *UND*
для undefined, .text
для кода, .rodata
для данных только для чтения (неизменяемых) данных, .data
для инициализированных измененных данных и .bss
для неинициализированных изменяемых данных и т. Д.
Мы можем видеть из приведенных выше таблиц символов, которая local4
, local5
, local6
, local7
, local8
и local9
переменных фактически не получить записи в таблице символов на всех. Это связано с тем, что они локальны для main()
. Содержимое строк, на которые они ссылаются, хранится в .data
или .rodata
(или построено на лету), в зависимости от того, что лучше видит компилятор.
Давайте посмотрим на последующие записи о перемещении. Использование
objdump -r example.o
Я получаю
example.o: file format elf64-x86-64
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000000000000037 R_X86_64_32S .rodata+0x000000000000005e
0000000000000040 R_X86_64_32S .rodata+0x000000000000006b
0000000000000059 R_X86_64_32S .rodata+0x0000000000000088
0000000000000062 R_X86_64_32S .rodata+0x000000000000008f
0000000000000067 R_X86_64_32 .rodata+0x00000000000000a8
000000000000006c R_X86_64_PC32 puts-0x0000000000000004
0000000000000071 R_X86_64_32 .rodata+0x00000000000000b0
0000000000000076 R_X86_64_32 .rodata
0000000000000083 R_X86_64_PC32 printf-0x0000000000000004
000000000000008a R_X86_64_PC32 .data-0x0000000000000004
000000000000008f R_X86_64_32 .rodata+0x00000000000000b0
000000000000009f R_X86_64_PC32 printf-0x0000000000000004
00000000000000a6 R_X86_64_PC32 .rodata+0x0000000000000044
00000000000000ab R_X86_64_32 .rodata+0x00000000000000b0
00000000000000bb R_X86_64_PC32 printf-0x0000000000000004
00000000000000c0 R_X86_64_32 .rodata+0x00000000000000b0
00000000000000c5 R_X86_64_32 global4
00000000000000d2 R_X86_64_PC32 printf-0x0000000000000004
00000000000000d9 R_X86_64_PC32 global5-0x0000000000000004
00000000000000de R_X86_64_32 .rodata+0x00000000000000b0
00000000000000ee R_X86_64_PC32 printf-0x0000000000000004
00000000000000f5 R_X86_64_PC32 global6-0x0000000000000004
00000000000000fa R_X86_64_32 .rodata+0x00000000000000b0
000000000000010a R_X86_64_PC32 printf-0x0000000000000004
000000000000010f R_X86_64_32 .rodata+0x00000000000000b0
0000000000000114 R_X86_64_32 global7
0000000000000121 R_X86_64_PC32 printf-0x0000000000000004
0000000000000128 R_X86_64_PC32 global8-0x0000000000000004
000000000000012d R_X86_64_32 .rodata+0x00000000000000b0
000000000000013d R_X86_64_PC32 printf-0x0000000000000004
0000000000000144 R_X86_64_PC32 global9-0x0000000000000004
0000000000000149 R_X86_64_32 .rodata+0x00000000000000b0
0000000000000159 R_X86_64_PC32 printf-0x0000000000000004
0000000000000163 R_X86_64_PC32 putchar-0x0000000000000004
0000000000000168 R_X86_64_32 .rodata+0x00000000000000b5
000000000000016d R_X86_64_PC32 puts-0x0000000000000004
0000000000000172 R_X86_64_32 .rodata+0x00000000000000b0
0000000000000177 R_X86_64_32 .rodata+0x00000000000000c0
0000000000000184 R_X86_64_PC32 printf-0x0000000000000004
000000000000018b R_X86_64_PC32 .data+0x000000000000001c
0000000000000190 R_X86_64_32 .rodata+0x00000000000000b0
00000000000001a0 R_X86_64_PC32 printf-0x0000000000000004
00000000000001a7 R_X86_64_PC32 .rodata+0x00000000000000d4
00000000000001ac R_X86_64_32 .rodata+0x00000000000000b0
00000000000001bc R_X86_64_PC32 printf-0x0000000000000004
00000000000001c1 R_X86_64_32 .rodata+0x00000000000000b0
00000000000001d6 R_X86_64_PC32 printf-0x0000000000000004
00000000000001db R_X86_64_32 .rodata+0x00000000000000b0
00000000000001ef R_X86_64_PC32 printf-0x0000000000000004
00000000000001f4 R_X86_64_32 .rodata+0x00000000000000b0
0000000000000209 R_X86_64_PC32 printf-0x0000000000000004
000000000000020e R_X86_64_32 .rodata+0x00000000000000b0
0000000000000223 R_X86_64_PC32 printf-0x0000000000000004
0000000000000228 R_X86_64_32 .rodata+0x00000000000000b0
000000000000023d R_X86_64_PC32 printf-0x0000000000000004
0000000000000242 R_X86_64_32 .rodata+0x00000000000000b0
0000000000000257 R_X86_64_PC32 printf-0x0000000000000004
0000000000000271 R_X86_64_PC32 __stack_chk_fail-0x0000000000000004
RELOCATION RECORDS FOR [.data]:
OFFSET TYPE VALUE
0000000000000000 R_X86_64_64 .rodata+0x0000000000000015
0000000000000008 R_X86_64_64 .rodata+0x000000000000005e
0000000000000018 R_X86_64_64 .rodata+0x0000000000000088
0000000000000020 R_X86_64_64 .rodata+0x0000000000000015
RELOCATION RECORDS FOR [.rodata]:
OFFSET TYPE VALUE
0000000000000048 R_X86_64_64 .rodata+0x0000000000000029
0000000000000080 R_X86_64_64 .rodata+0x000000000000006b
00000000000000a0 R_X86_64_64 .rodata+0x000000000000008f
00000000000000d8 R_X86_64_64 .rodata+0x0000000000000029
RELOCATION RECORDS FOR [.eh_frame]:
OFFSET TYPE VALUE
0000000000000020 R_X86_64_PC32 .text
Переселения запись сгруппирована в секции они перемещение проживает. Поскольку содержимое строки в секциях .data
или .rodata
, мы можем ограничиться смотреть на переездах где VALUE
начинается с .data
или .rodata
. (Переменные строки, такие как char global7[] = "char []";
, хранятся в .data
и неизменяемых строках и строковых литералах в .rodata
.)
Если мы должны были скомпилировать код с включенными символами отладки, было бы легче определить, какая переменная использовалась для ссылки на какую строку, но я могу просто посмотреть фактическое содержимое при каждом значении перемещения (цели) чтобы посмотреть, какие ссылки на неизменяемые строки нуждаются в фиксации.
Команда Сочетание
objdump -r example.o | awk '($3 ~ /^\..*\+/) { t = $3; sub(/\+/, " ", t); n[t]++ } END { for (r in n) printf "%d %s\n", n[r], r }' | sort -g
будет выводить количество переездов в мишень, а затем целевой секции, а затем целевого смещения в разделе, отсортированный с мишенью, которая происходит наиболее переездов последние , То есть, последние строки, выведенные выше, - это те, на которые вам нужно сосредоточиться. Для меня, я получаю
1 .rodata
1 .rodata 0x0000000000000044
1 .rodata 0x00000000000000a8
1 .rodata 0x00000000000000b5
1 .rodata 0x00000000000000c0
1 .rodata 0x00000000000000d4
2 .rodata 0x0000000000000015
2 .rodata 0x0000000000000029
2 .rodata 0x000000000000005e
2 .rodata 0x000000000000006b
2 .rodata 0x0000000000000088
2 .rodata 0x000000000000008f
18 .rodata 0x00000000000000b0
Если добавить оптимизацию (gcc -W -Wall -O3 -fomit-frame-pointer -c example.c
), результат
1 .rodata 0x0000000000000020
1 .rodata 0x0000000000000040
1 .rodata.str1.1
1 .rodata.str1.1 0x0000000000000058
2 .rodata.str1.1 0x000000000000000d
2 .rodata.str1.1 0x0000000000000021
2 .rodata.str1.1 0x000000000000005f
2 .rodata.str1.1 0x000000000000006c
3 .rodata.str1.1 0x000000000000003a
3 .rodata.str1.1 0x000000000000004c
18 .rodata.str1.1 0x0000000000000008
, который показывает, что опции компилятора есть большой эффект, но есть то, что одна цель, которая является в любом случае 18 раз: раздел .rodata
смещение 0xb0
(.rodata.str1.1
смещение 0x8
, если оптимизация включена во время компиляции).
Это строковый литерал `` \ t% s \ n ".
Изменение исходной программы в
char *local8 = "char *";
char *const local9 = "char *const";
const char *const fmt = "\t%s\n";
printf("Global:\n");
printf(fmt, global1);
printf(fmt, global2);
и так далее, заменив строку формата с неизменяемой указателем на строку fmt
, устраняет эти 18 переездов в целом. (Можно также использовать эквивалентное const char fmt[] = "\t%s\n";
, конечно.)
Приведенный выше анализ указывает на то, что по крайней мере с GCC-4.6.3, большинство из которых можно избежать переездов вызваны (повторное использование) строковых литералов. Заменяя их массивом const chars (const char fmt[] = "\t%s\n";
) или указателем константы на const chars (const char *const fmt = "\t%s\n";
) - оба случая, помещающие содержимое в раздел .rodata
, только для чтения и ссылка на указатель/массив тоже неизменяемы - кажется, эффективной и безопасной стратегии для меня.
Кроме того, преобразование строковых литералов в неизменяемые указатели строк или массивы символов полностью задает исходный уровень. То есть, если вы преобразовываете все строковые литералы с использованием вышеуказанного метода, вы можете исключить по крайней мере одно перемещение в строковый литерал.
На самом деле, я не вижу, как анализ уровня объекта поможет вам многое здесь. Это скажет вам, если ваши изменения уменьшают количество необходимых переездов.
выше awk
строфа может быть расширена на функцию, которая выводит строковые константы для динамических ссылок с положительными сдвигами:
#!/bin/bash
if [ $# -ne 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
exec >&2
echo ""
echo "Usage: %s [ -h | --help ]"
echo " %s object.o"
echo ""
exit 1
fi
export LANG=C LC_ALL=C
objdump -wr "$1" | awk '
BEGIN {
RS = "[\t\v\f ]*[\r\n][\t\n\v\f\r ]*"
FS = "[\t\v\f ]+"
}
$1 ~ /^[0-9A-Fa-f]+/ {
n[$3]++
}
END {
for (s in n)
printf "%d %s\n", n[s], s
}
' | sort -g | gawk -v filename="$1" '
BEGIN {
RS = "[\t\v\f ]*[\r\n][\t\n\v\f\r ]*"
FS = "[\t\v\f ]+"
cmd = "objdump --file-offsets -ws " filename
while ((cmd | getline) > 0)
if ($3 == "section") {
s = $4
sub(/:$/, "", s)
o = $NF
sub(/\)$/, "", o)
start[s] = strtonum(o)
}
close(cmd)
}
{
if ($2 ~ /\..*\+/) {
s = $2
o = $2
sub(/\+.*$/, "", s)
sub(/^[^\+]*\+/, "", o)
o = strtonum(o) + start[s]
cmd = "dd if=\"" filename "\" of=/dev/stdout bs=1 skip=" o " count=256"
OLDRS = RS
RS = "\0"
cmd | getline hex
close(cmd)
RS = OLDRS
gsub(/\\/, "\\\\", hex)
gsub(/\t/, "\\t", hex)
gsub(/\n/, "\\n", hex)
gsub(/\r/, "\\r", hex)
gsub(/\"/, "\\\"", hex)
if (hex ~ /[\x00-\x1F\x7F-\x9F\xFE\xFF]/ || length(hex) < 1)
printf "%s\n", $0
else
printf "%s = \"%s\"\n", $0, hex
} else
print $0
}
'
Это немного грубо, просто ударил вместе, так что я не знаю, как портативный есть. На моей машине он, кажется, находит строковые литералы для нескольких тестовых примеров, которые я пробовал; вы должны, вероятно, переписать его в соответствии с вашими потребностями. Или даже используйте фактический язык программирования с поддержкой ELF для непосредственного изучения объектных файлов.
Для примера программы, показанной выше (до изменений, я предлагаю, чтобы уменьшить количество переездов), составленное без оптимизации, выше сценарий дает выходной сигнал
1 .data+0x000000000000001c = ""
1 .data-0x0000000000000004
1 .rodata
1 .rodata+0x0000000000000044 = ""
1 .rodata+0x00000000000000a8 = "Global:"
1 .rodata+0x00000000000000b5 = "Local:"
1 .rodata+0x00000000000000c0 = "static const char []"
1 .rodata+0x00000000000000d4 = ""
1 .text
1 __stack_chk_fail-0x0000000000000004
1 format
1 global4
1 global5-0x0000000000000004
1 global6-0x0000000000000004
1 global7
1 global8-0x0000000000000004
1 global9-0x0000000000000004
1 putchar-0x0000000000000004
2 .rodata+0x0000000000000015 = "static const char *"
2 .rodata+0x0000000000000029 = "static const char *const"
2 .rodata+0x000000000000005e = "const char *"
2 .rodata+0x000000000000006b = "const char *const"
2 .rodata+0x0000000000000088 = "char *"
2 .rodata+0x000000000000008f = "char *const"
2 puts-0x0000000000000004
18 .rodata+0x00000000000000b0 = "\t%s\n"
18 printf-0x0000000000000004
Наконец, можно заметить, что использование указатель функции на printf()
вместо вызова printf()
напрямую уменьшит еще 18 перемещений из кода примера, но я считаю это ошибкой.
Для кода, вы хотите Перемещение, так как непрямые вызовы функций (вызовы с помощью указателей функций) намного медленнее прямых вызовов. Проще говоря, эти перестановки делают вызовы функций и подпрограмм намного быстрее, поэтому вы определенно хотите их сохранить.
Извинения за длинный ответ; Надеюсь, вы найдете это полезным. Вопросов?
Что вы хотите узнать: объектный файл, который требует символ? Или объектный файл или разделяемая библиотека, содержащая символ? У вас много объектных файлов или у вас есть одна общая библиотека? –
@MartinRosenau: Объектный файл, содержащий символ. И у меня есть разделяемая библиотека и файлы .o', с которой она была связана (у меня также есть источник, но grepping для 'static const char \ *. * []' Только доводит меня до сих пор ...). –