LINUX.ORG.RU

как работает в ассемблере функция memset?


0

0

Здравствуйте,

читаю в вики-учебнике «Ассемблер в Linux для программистов C»

не могу написать на ассемблере пограмму-считалочку (задание из середины учебника). На СИ написала без проблем...

сейчас загвоздка в том, что я не могу заставить работать функцию memset. хочу забить символами 'z' всю строку

вот код


.data

format_item_massiv:    /* формат для ф-и printf, для элемента миссива */ 
.string "p[n] = %c \n"

m: 
.byte 6

a: 
.int 'z'

p: /* указатель на блок памяти из m элементов */ 
.string "abcdea"

.text

.globl main main:

/* вызываем memset(p, a, m) */ 
pushl m 
pushl a 
pushl p 
call memset 
addl $12, %esp

/* PROVERKA SODERGIMOGO alementa massiva nomer 3 */ 

movl $3, %edx       /* что в 3-м элементе массива? */ 
pushl p(,%edx,1) 
pushl $format_item_massiv  
call printf  
addl $8, %esp 

если закомментаровать 5 строчек посвященных работе функции memset - все работает, элемент массива правильно печатается. если их раскомментировать «Ошибка сегментирования».

вообще-то автор предлагает работать с нулями и единицами, а не с символами-буквами, но мне проще с символами, потому, что количество байт в стеке должно быть кратно 4 байтам, и у меня только с символьной строкой получается с помощью printf посмотреть что в строке делается. или можно как-нибудь без стека функции вызывать?

и еще вопрос: почему printf работает без подключения библиотеки <stdio.h>? и для memeset я ничего не подключаю

в общем темный лес, ясности нет в голове совсем :(

Перемещено boombick из Web-development

> почему printf работает без подключения библиотеки <stdio.h>? и для memeset я ничего не подключаю

в С по историческим причинам <stdio.h> и <stdlib.h> это не библиотеки. Это наборы деклараций символов. Библиотека это libc, с которой по умолчанию все линкуется.

dilmah ★★★★★
()

Простой совет - используй gcc -S на сишном коде - увидишь, во что это ранслируется на асме.

ratatosk
()

О! большое спасибо всем, пойду разбираться

Natasha
() автор топика

> /* PROVERKA SODERGIMOGO alementa massiva nomer 3 */ 

очепятка по фрейду? :)

....
call memset
addl $12, %esp
....
call printf 
addl $8, %esp 

addl? зачем? это вам не pascal calling convention.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от imhotep

> А ты нничего не путаешь ? В паскале как раз вызываемый код заботится
> о стеке, а в си - вызывающий.

--- test.c ---
#include <string.h>

char buf[123];

void
bee(int c)
{
    memset(buf, c, sizeof(buf));
}

void
foo(void)
{
    char ch;
    for (ch = 'A'; ch < 'Z'; ch++) {
        bee(ch);
    }
}
--- test.c ---

$ gcc -c -S test.c

--- test.s ---
    .file   "test.c"
    .text
.globl bee
    .type   bee, @function
bee:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $123, 8(%esp)
    movl    8(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    $buf, (%esp)
    call    memset
    leave
    ret
    .size   bee, .-bee
.globl foo
    .type   foo, @function
foo:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movb    $65, -1(%ebp)
    jmp .L4
.L5:
    movsbl  -1(%ebp),%eax
    movl    %eax, (%esp)
    call    bee
    addb    $1, -1(%ebp)
.L4:
    cmpb    $89, -1(%ebp)
    jle .L5
    leave
    ret
    .size   foo, .-foo
    .comm   buf,123,32
    .ident  "GCC: (GNU) 4.1.2 20071124 (Red Hat 4.1.2-42)"
    .section    .note.GNU-stack,"",@progbits
--- test.s ---

впрочем, с другой стороны:

--- cut ---
Dump of assembler code for function memset:
0x00a6a2b0 <memset+0>:  cld
0x00a6a2b1 <memset+1>:  push   %edi
0x00a6a2b2 <memset+2>:  mov    0x8(%esp),%edx
0x00a6a2b6 <memset+6>:  mov    0x10(%esp),%ecx
0x00a6a2ba <memset+10>: movzbl 0xc(%esp),%eax
0x00a6a2bf <memset+15>: jecxz  0xa6a2ed <memset+61>
0x00a6a2c1 <memset+17>: mov    %edx,%edi
0x00a6a2c3 <memset+19>: and    $0x3,%edx
0x00a6a2c6 <memset+22>: je     0xa6a2d9 <memset+41>
0x00a6a2c8 <memset+24>: jp     0xa6a2ce <memset+30>
0x00a6a2ca <memset+26>: stos   %al,%es:(%edi)
0x00a6a2cb <memset+27>: dec    %ecx
0x00a6a2cc <memset+28>: je     0xa6a2ed <memset+61>
0x00a6a2ce <memset+30>: stos   %al,%es:(%edi)
0x00a6a2cf <memset+31>: dec    %ecx
0x00a6a2d0 <memset+32>: je     0xa6a2ed <memset+61>
0x00a6a2d2 <memset+34>: xor    $0x1,%edx
0x00a6a2d5 <memset+37>: jne    0xa6a2d9 <memset+41>
0x00a6a2d7 <memset+39>: stos   %al,%es:(%edi)
0x00a6a2d8 <memset+40>: dec    %ecx
0x00a6a2d9 <memset+41>: mov    %ecx,%edx
0x00a6a2db <memset+43>: shr    $0x2,%ecx
0x00a6a2de <memset+46>: and    $0x3,%edx
0x00a6a2e1 <memset+49>: imul   $0x1010101,%eax,%eax
0x00a6a2e7 <memset+55>: rep stos %eax,%es:(%edi)
0x00a6a2e9 <memset+57>: mov    %edx,%ecx
0x00a6a2eb <memset+59>: rep stos %al,%es:(%edi)
0x00a6a2ed <memset+61>: mov    0x8(%esp),%eax
0x00a6a2f1 <memset+65>: pop    %edi
0x00a6a2f2 <memset+66>: ret
--- cut ---

т.е. чистит все-таки вызывающий. но штатным образом это делается
несколько по другому :)

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

то есть я правильно делаю, что чищу...

Natasha
() автор топика
Ответ на: комментарий от imhotep

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

ведь memset ждет указатель

Natasha
() автор топика
Ответ на: комментарий от klalafuda

>т.е. чистит все-таки вызывающий. но штатным образом это делается
несколько по другому :)

Гражданин - вы назначение leave знаете ?

/* Вначале подготавливается место под передаваемые параметры вывзываемой ф-ции */
pushl %ebp
movl %esp, %ebp
subl $24, %esp
.........
/* восстанавливается значение указателя стека - leave эквивалентен
паре movl %ebp, %esp; pop %ebp
leave
ret

В итоге просто так удобней в ЯВУ при генерации кода.

imhotep
()
Ответ на: комментарий от Natasha

Наташенька - а ты попробуй :) Потому что метки являются при генерации кода как раз значением смщения. Посмотри хотя бы что ты тут пишешь

>pushl $format_item_massiv


Тут у тебя есть счастье ? :)

imhotep
()
Ответ на: комментарий от Natasha

> тока счастья все-равно пока нет, ошибка сегментирования, но счастье придет

а то!

что называется, найдите 10 отличий..

--- cut ---
.data

fmt:
.string "p[n] = %c \n"

size:
.int 6

c:
.int 'z'

buf:
.string "abcdea"

.text

.globl main
main:

pushl   size
pushl   c
pushl   $buf
call    memset
addl    $12, %esp

movl    $3, %edx
pushl   buf(,%edx,1)
pushl   $fmt
call    printf
addl    $8, %esp

ret
--- cut ---

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

ошибка сегментирования, кажется, потому, что я память не выделяю... нужно сначала c помощью malloc память выделить


pushl m /* необходимое количество символов в блоке памяти */ 
call malloc 
addl $4, %esp 

функция malloc возвращает результат (указатель на начало выделенного блока памяти) в регистр %eax

теперь нужно этот указатель поместить в мой p

movl %eax, p

и в секции 
data: 

p будет не  

p: .string "abcdea"  

а просто

p: .int 

только я не пойму, функция malloc в регистр %eax возвращает указатель или указатель на указатель? если указатель на указатель, как же его достать из регистра в p?

Natasha
() автор топика
Ответ на: комментарий от Natasha

> ошибка сегментирования, кажется, потому, что я память не выделяю... нужно сначала c помощью malloc память выделить

зачем так сложно? массив, который вы заполняете, уже выделен в сегменте .data с правами read/write. его можно спокойно модифицировать.

> только я не пойму, функция malloc в регистр %eax возвращает указатель или указатель на указатель? если указатель на указатель, как же его достать из регистра в p?

malloc() в eax возвращает указатель на начало выделенной области памяти.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от Natasha

> ЗАРАБОТАЛО!!!
> СПАСИБО

теперь осталось сделать работу над ошибками и понять, почему же не работала оригинальная версия.. ;)

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от Natasha

>только я не пойму, функция malloc в регистр %eax возвращает указатель или указатель на указатель?

В асме нет никаких указателей - тут вообще ничего нет :) массивов нет - это все роскошь из ЯВУ :) Есть только ячейки памяти и адресация в соответствии с возможностями архитектуры. В eax тебе возвратился адрес первого байта выделенной памяти. Если ты до вызова memset ничего больше делать не будешь то вместо передачи своего ненужного теперь р можешь подставить pushl %eax. Но лучше разберись почему не работал твой предыдущий код - там банальная опечатка, klalafuda же нарисовал как правильно.

imhotep
()
Ответ на: комментарий от klalafuda

вратце:
>m:

>.byte 6

...
>pushl m

а-та-та!!

>p: /* указатель на блок памяти из m элементов */

>.string "abcdea"

...
>pushl p

а-та-та!!

xydo ★★
()
Ответ на: комментарий от klalafuda

Завтра с самого утра сяду за работу над ошибками :)

Вы все расписали очень подробно, спасибо.

Natasha
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.