2016-12-07 10 views
0

Я играю с аргументами командной строки. В частности, я пытаюсь провести некоторое тестирование в строке argv [1]. Если я использую двухэтапный метод получения адреса в argv [1], мой код работает нормально.Адресация памяти NASM

mov ebx, [ebp+12] 
mov eax, [ebx+4] ; address of argv[1] 

Если я использую один шаг, моя программа печатает смеху.

mov eax, [ebp+16] ; address of argv[1] 

Я неверно, полагая, что любой из методов теперь будет ссылаться на адрес [ebp + 16]? Я пропустил что-то тривиальное?

+5

Это не то же самое. Первый делает '* (* (ebp + 12) +4)' второй просто '* (ebp + 16)'. Вы не можете отказаться от разыменования. – Jester

+2

Спасибо @ DanMašek за то, что он бесполезен. – Eric

+0

@Jester Много оценено. Я подумаю об этом. – Eric

ответ

4

Легко запутаться при работе с указателями на указатели в сборке.

argv является «массивом строк» ​​или лучше массив указателей на символ, так как в C-распаде массивов в указатели на их тип элемента при передаче в качестве аргументов, в действительности argv это указатель на указатель на символ или char** argv ,

Это говорит нам, что нам нужно два разыменования для доступа к символам любой из строк и один для доступа к любому указателю на любую из таких строк.

Предполагая Cdecl конвенции, где параметры передаются через стек в обратном порядке, и предполагая, стандартный пролог, который устанавливает стандартный фрейм-указатель мы имеем, что значение argc находится на ebp+0ch.
Отметьте, что ebp имеет семантику указателя, и поэтому ebp+0ch - это просто указательная арифметика, чтобы получить другой указатель, на этот раз до значения argc.

Если бы мы были готовы дать ebp+0ch тип C, это было бы char***, поэтому для доступа к указателю 0-требуется два разговора.

код, чтобы получить argv[1] в ESI является:

;typeof(ebp+0ch) = char*** 

mov esi, DWORD [ebp+0ch]  ;1st defer, esi = argv, typeof(esi) = char** 
mov esi, DWORD [esi+04h]  ;2nd defer, esi = argv[1], typeof(esi) = char* 

;Optional, Get a char 
mov al, BYTE [esi]   ;3rd defer, al = argv[1][0], typeof(al) = char 

Типы проверки.


Звучит непонятно?
Давайте нарисуем эти указатели!

 The stack          The memory 

100ch | 2000h | argv       2000h | 2008h | argv[0] 
1008h | 2  | argc       2004h | 2010h | argv[1] 
1004h | yyyyyy | return address    2008h | file | argv[0][0..3] 
1000h | xxxxxx | old frame pointer   200ch | .a\0\0 | argv[0][4..7] 
               2010h | -arg | argv[1][0..3] 
EBP = 1000h         2014h | 1\0\0\0 | argv[1][4..7] 

ebp+0ch является 1000h + 0ch = 100CH и это адрес значения argv.
mov esi, DWORD [ebp+0ch] это как mov esi, DWORD [100ch], и он устанавливает ESI в 2000h.
2000h - это значение argv, которое представляет собой массив, так что это адрес argv[0].

Адрес argv[1] имеет длину 4 байта, таким образом 2000h + 04h = 2004h.
mov esi, DWORD [esi+04h] как mov esi, DWORD [2004h] и он устанавливает ESI в 2010h.
2010h - адрес строки "-arg1".


Обратите внимание, что на картинке выше не C, ни C++ совместим со стандартом, как argv[argc] должен быть равен 0.
я оставил, что из картины.

+0

Спасибо за продуманный, продуманный ответ. Я понимаю с точки зрения указателей в C. Можете ли вы быстро рассказать о своих обозначениях для адресов? Я смущен h и ch. – Eric

+4

@ Эрик: суффикс 'h' означает шестнадцатеричный код и является общим для сборки DOS/Windows в качестве альтернативы' 0x100c'. 'c' - шестнадцатеричная цифра. (Многие ассемблеры DOS/Windows не поддерживают '0xDEADBEEF', только менее читаемый-IMO' 0DEADBEEFh', поэтому, к сожалению, этот стиль trailing-h распространен в asm x86.) NASM поддерживает оба способа и другие суффиксы/префиксы для другие базы чисел. (база 2 часто бывает полезной). –

-2

Это ответ на ваш вопрос.

mov eax, [ebp+16] 
lea ebx, [ebp+12] 
mov eax, [ebx+4] 

или

mov eax, [ebp+16] 
mov ebx, ebp 
add ebx, 12 
mov eax, [ebx+4] 

Бывший экономит несколько байт кода, но они функционально эквивалентны.

+2

mmm ... Неясно, почему вы используете 'mov eax, [ebp + 16]', когда вы перезаписываете 'eax' две/три инструкции позже, не используя его. Кроме того, ваш код - это просто глупый способ написать «mov eax, [ebp + 16]», который будет обращаться к третьему (нестандартному, но часто предоставленному) параметру * main *, а не 'argv [1]'. Это не ответ на вопрос ОП. –