Что означает * адрес (найден в printf) в сборке? - PullRequest
3 голосов
/ 01 апреля 2010

Разборка printf не дает много информации:

(gdb) disas printf
Dump of assembler code for function printf:
0x00401b38 <printf+0>:  jmp    *0x405130
0x00401b3e <printf+6>:  nop
0x00401b3f <printf+7>:  nop
End of assembler dump.


(gdb) disas 0x405130
Dump of assembler code for function _imp__printf:
0x00405130 <_imp__printf+0>:    je     0x405184 <_imp__vfprintf+76>
0x00405132 <_imp__printf+2>:    add    %al,(%eax)

Как это реализовано под капотом?

Почему не помогает разборка?

Что означает * до 0x405130?

Ответы [ 6 ]

8 голосов
/ 01 апреля 2010
3 голосов
/ 04 апреля 2010

* - это синтаксис ассемблера AT & T для косвенного обращения к памяти. То есть

jmp *<addr>

означает «перейти к адресу, хранящемуся в <addr>».

Это эквивалентно следующему синтаксису Intel:

jmp [addr]

Адресация ветвей с использованием регистров или операндов памяти должна начинаться с префикса '*'

Источник

2 голосов
/ 01 апреля 2010

Практически все компиляторы C предоставляют источнику свои библиотеки времени выполнения, а не только компиляторы с открытым исходным кодом. К сожалению, они часто пишутся в довольно трудной для понимания форме и обычно не сопровождаются документами с обоснованием дизайна.

Итак, очень хороший ресурс для решения этой проблемы - P.J. Plauger's «Стандартная библиотека C» , которая предоставляет не только источник для реализации библиотеки, но также содержит подробные сведения о том, как она спроектирована, и особые ситуации, которые может понадобиться такой библиотеке.

По ценам, которые предлагаются для некоторых «использованных» версий книги, это воровство и должно быть на книжной полке любого серьезного программиста на Си.

У Plauger есть похожие книги, ориентированные на библиотеку C ++, которые, как мне кажется, имеют аналогичное значение:

1 голос
/ 02 апреля 2010

Что касается

Что * означает до 0x405130?

Я не знаком с дизассемблером GDB, но похоже, что jmp *0x405130 - это косвенный переход через указатель. Вместо того, чтобы разбирать, что находится в 0x405130, вы должны сбросить 4 байта памяти. Я готов поспорить, что вы найдете там другой адрес, и если вы разберете это местоположение, вы найдете код printf() (насколько читабельно может быть эта разборка, это другая история).

Другими словами, _imp__printf - это указатель на printf(), а не printf().


Изменить после получения дополнительной информации в комментариях ниже:

Небольшое возмущение указывает, что jmp *0x405130 - это синтаксис сборки GAS / AT & T для инструкции jmp [0x405130] при использовании синтаксиса Intel.

Что делает это любопытным, так это то, что вы говорите, что команда gdb x/xw 0x405130 показывает, что этот адрес содержит 0x00005274 (что, похоже, совпадает с тем, что вы получили, когда разбирали 0x405130). Однако это будет означать, что jmp [0x405130] попытается перейти к адресу 0x00005274, что кажется неправильным (и GDB сказал то же самое, когда вы попытались разобрать этот адрес.

Возможно, что запись _imp_printf использует какую-то ленивую технику привязки, когда первое время выполнения переходит через 0x405130, оно достигает адреса 0x00005274, что заставляет ОС выставить ловушку и исправить динамическое соединение. После исправления ОС возобновит выполнение с правильным адресом ссылки в 0x405130. Но это просто догадки с моей стороны. Я понятия не имею, если система, которую вы используете, делает что-то подобное (на самом деле, я даже не знаю, на какой системе вы работаете), но это технически возможно. Если что-то подобное происходит, вы не увидите правильный адрес в 0x405130 до тех пор, пока не будет сделан первый вызов printf().

Я думаю, вам нужно будет выполнить пошаговый вызов printf() на уровне сборки, чтобы увидеть, что на самом деле происходит.


Обновленная информация в сеансе GDB:

Вот проблема, с которой вы столкнулись - вы смотрите на процесс до того, как система загрузит DLL и исправит связи с DLL. Вот сеанс отладки простой программы "hello world", скомпилированной с MinGW, отлаженной с помощью GDB:

C:\temp>\mingw\bin\gdb test.exe
GNU gdb (GDB) 7.1
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\temp/test.exe...done.

(gdb) disas main
Dump of assembler code for function main:
   0x004012f0 <+0>:     push   %ebp
   0x004012f1 <+1>:     mov    %esp,%ebp
   0x004012f3 <+3>:     sub    $0x8,%esp
   0x004012f6 <+6>:     and    $0xfffffff0,%esp
   0x004012f9 <+9>:     mov    $0x0,%eax
   0x004012fe <+14>:    add    $0xf,%eax
   0x00401301 <+17>:    add    $0xf,%eax
   0x00401304 <+20>:    shr    $0x4,%eax
   0x00401307 <+23>:    shl    $0x4,%eax
   0x0040130a <+26>:    mov    %eax,-0x4(%ebp)
   0x0040130d <+29>:    mov    -0x4(%ebp),%eax
   0x00401310 <+32>:    call   0x401850 <_alloca>
   0x00401315 <+37>:    call   0x4013d0 <__main>
   0x0040131a <+42>:    movl   $0x403000,(%esp)
   0x00401321 <+49>:    call   0x4018b0 <printf>
   0x00401326 <+54>:    mov    $0x0,%eax
   0x0040132b <+59>:    leave
   0x0040132c <+60>:    ret
End of assembler dump.

Обратите внимание, что разборка printf() приводит к аналогичному косвенному скачку:

(gdb) disas printf
Dump of assembler code for function printf:
   0x004018b0 <+0>:     jmp    *0x4050f8     ; <<-- indirect jump
   0x004018b6 <+6>:     nop
   0x004018b7 <+7>:     nop
End of assembler dump.

И что символ _imp__printf не имеет смысла как код ...

(gdb) disas 0x4050f8
Dump of assembler code for function _imp__printf:
   0x004050f8 <+0>:     clc                 ; <<-- how can this be printf()?
   0x004050f9 <+1>:     push   %ecx
   0x004050fa <+2>:     add    %al,(%eax)
End of assembler dump.

или как указатель ...

(gdb) x/xw 0x4050f8
0x4050f8 <_imp__printf>:        0x000051f8  ; <<-- 0x000051f8 is an invalid pointer

Теперь давайте установим точку останова на main() и перейдем к ней:

(gdb) break main
Breakpoint 1 at 0x40131a: file c:/temp/test.c, line 5.

(gdb) run
Starting program: C:\temp/test.exe
[New Thread 11204.0x2bc8]
Error while mapping shared library sections:
C:\WINDOWS\SysWOW64\ntdll32.dll: No such file or directory.

Breakpoint 1, main () at c:/temp/test.c:5
5           printf( "hello world\n");

printf() выглядит так же:

(gdb) disas printf
Dump of assembler code for function printf:
   0x004018b0 <+0>:     jmp    *0x4050f8
   0x004018b6 <+6>:     nop
   0x004018b7 <+7>:     nop
End of assembler dump.

, но _imp__printf выглядит иначе - динамическое соединение теперь исправлено:

(gdb) x/xw 0x4050f8
0x4050f8 <_imp__printf>:        0x77bd27c2

И если мы разберем то, на что сейчас указывает _imp__printf, это может быть не очень читабельным, но теперь это явно код. Это printf() как реализовано в MSVCRT.DLL:

(gdb) disas _imp__printf
Dump of assembler code for function printf:
   0x77bd27c2 <+0>:     push   $0x10
   0x77bd27c4 <+2>:     push   $0x77ba4770
   0x77bd27c9 <+7>:     call   0x77bc84c4 <strerror+554>
   0x77bd27ce <+12>:    mov    $0x77bf1cc8,%esi
   0x77bd27d3 <+17>:    push   %esi
   0x77bd27d4 <+18>:    push   $0x1
   0x77bd27d6 <+20>:    call   0x77bcca49 <msvcrt!_lock+4816>
   0x77bd27db <+25>:    pop    %ecx
   0x77bd27dc <+26>:    pop    %ecx
   0x77bd27dd <+27>:    andl   $0x0,-0x4(%ebp)
   0x77bd27e1 <+31>:    push   %esi
   0x77bd27e2 <+32>:    call   0x77bd400d <wscanf+3544>
   0x77bd27e7 <+37>:    mov    %eax,-0x1c(%ebp)
   0x77bd27ea <+40>:    lea    0xc(%ebp),%eax
   0x77bd27ed <+43>:    push   %eax
   0x77bd27ee <+44>:    pushl  0x8(%ebp)
   0x77bd27f1 <+47>:    push   %esi
   0x77bd27f2 <+48>:    call   0x77bd3330 <wscanf+251>
   0x77bd27f7 <+53>:    mov    %eax,-0x20(%ebp)
   0x77bd27fa <+56>:    push   %esi
   0x77bd27fb <+57>:    pushl  -0x1c(%ebp)
   0x77bd27fe <+60>:    call   0x77bd4099 <wscanf+3684>
   0x77bd2803 <+65>:    add    $0x18,%esp
   0x77bd2806 <+68>:    orl    $0xffffffff,-0x4(%ebp)
   0x77bd280a <+72>:    call   0x77bd281d <printf+91>
   0x77bd280f <+77>:    mov    -0x20(%ebp),%eax
   0x77bd2812 <+80>:    call   0x77bc84ff <strerror+613>
   0x77bd2817 <+85>:    ret
   0x77bd2818 <+86>:    mov    $0x77bf1cc8,%esi
   0x77bd281d <+91>:    push   %esi
   0x77bd281e <+92>:    push   $0x1
   0x77bd2820 <+94>:    call   0x77bccab0 <msvcrt!_lock+4919>
   0x77bd2825 <+99>:    pop    %ecx
   0x77bd2826 <+100>:   pop    %ecx
   0x77bd2827 <+101>:   ret
   0x77bd2828 <+102>:   int3
   0x77bd2829 <+103>:   int3
   0x77bd282a <+104>:   int3
   0x77bd282b <+105>:   int3
   0x77bd282c <+106>:   int3
End of assembler dump.

Наверное, читать сложнее, чем можно было бы надеяться, потому что я не уверен, что для него доступны надлежащие символы (или может ли GDB правильно читать эти символы).

Однако, , как я уже упоминал в другом ответе , обычно вы можете получить исходный код для подпрограмм времени выполнения C с вашим компилятором, будь то с открытым исходным кодом или нет. MinGW не поставляется с исходным кодом для MSVDRT.DLL, так как это для Windows, но вы можете получить его (или что-то очень похожее) в дистрибутиве Visual Studio - я думаю, что даже бесплатный VC ++ Express поставляется с источником времени выполнения (но я могу ошибаться).

1 голос
/ 01 апреля 2010

Я бы сказал, что дизассемблирование здесь прекрасно работает, и что printf реализован здесь «под капотом» с использованием vfprintf, что вполне соответствует ожиданиям. Обратите внимание, что ассемблер, как правило, гораздо более многословен, чем C, и отнимает много времени, чтобы понять, где у вас нет аннотированного источника. Вывод компилятора также не является хорошим способом научить себя ассемблеру.

0 голосов
/ 01 апреля 2010

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

Я действительно не помню, как это работает;вероятно, что оптимизации усложняют процесс.Но вы поняли.

...