C – продължение

by Vasil Kolev

Като продължение на предишния постинг, едно обяснение на цялата ситуация, понеже изглежда има нужда :)

Та, пак трите фрагмента:

Фрагмент 1:

char *pesho="pesho";

Фрагмент 2:

char *pesho=(char *) malloc (6);
strcpy(pesho,"pesho");

Фрагмент 3:

char pesho[6]="pesho";

Първият фрагмент записва низа в секцията .rodata (преди изпълнимия код на програмата) и насочва указателя натам. По принцип там не може да се пише (освен ако не работите под DOS или нещо подобно, което няма защита на паметта), но може да се предава напред-назад като указател.

Вторият фрагмент заделя памет в heap-а и записва в нея string-а. Указателя може да се предава навсякъде, там може да се пише и т.н..

Третия фрагмент е има два под-случая: ако pesho е глобална или локална променлива. В първия случай паметта се заделя в .globl секцията, където се пишат глобалните символи с имената им, за да може динамичния linker да се ориентира кое къде е – този може да се предава навсякъде като аргумент на функция, може да се пише отгоре му и т.н.. Втория случай е локален за функцията, заделя памет в stack-а и записва в него стойността. Указател към това може да се предава към функциите, които текущата вика, но не и на функцията, която е извикала настоящата (понеже при излизането от функцията тая памет вече се de-alloc-ва).

Който иска да види как точно стават нещата, може да си напише нещата в една програмка и да я компилира до асемблер с

gcc -Wall -o pesho.s -S pesho.c

и да разгледа pesho.s, там си личи най-добре.

Нещата са тествани на няколко unix-а (32 и 64битови) и на 32битово Visual C++.

Tags:

13 Responses to “C – продължение”

  1. пропаднал физик Says:

    Дискусията ми напомня за начините за промяна на стойността на числените константи (!!) във FORTRAN от времената преди защитата на паметта, напр.

    SUBROUTINE FOO(A,B,C)
    INTEGER A,B,C
    C=A*B
    END

    SUBROUTINE BAR(BAZ)
    INTEGER BAZ
    PRINT *,BAZ
    END

    CALL FOO(2,3,5)
    CALL BAR(5)

    При липса на защита на паметта, последната програма печата щастливо:
    6
    Причината е, че всички аргументи във FORTRAN се подават по адрес.

  2. admin Says:

    Физика, можеш ли това да го докараш до асемблер, страшно ми е интересно как точно изглежда, па не съм сигурен, че мога да сглобя както трябва цялата fortran програма…

  3. пропаднал физик Says:

    Константите:
    .section .rodata
    .LC3:
    .align 4
    .LC0:
    .long 5
    .align 4
    .LC1:
    .long 3
    .align 4
    .LC2:
    .long 2

    Първото извикване:
    movl $.LC0, %edx
    movl $.LC1, %esi
    movl $.LC2, %edi
    movl $0, %eax
    call foo_
    Второто извикване:
    movl $.LC0, %edi
    movl $0, %eax
    call bar_

    Естествено, програмата дава Segmentation fault, но сега ще си поиграя с ELF-а и ще направя .rodata секцията RW ;)

  4. пропаднал физик Says:

    След консултация с readelf и /usr/include/elf.h, с помощта на шестнадесетичния редактор на Midnight Commander вдигам W флаговете за секция .rodata и сегмента, който я съдържа (DOS compatibility mode ;)):
    [hristo@node001 ~]$ ./change5
    6
    Преди това:
    [hristo@node001 ~]$ ./change5
    Segmentation fault

  5. admin Says:

    Това е по-скоро бъг на компилатора, трябва да пази ‘5’ на няколко места (или изобщо да не дава да се правят подобни изпълнения), ужасно странно изглежда и може да доведе до наистина невероятни изпълнения (ако няма защита на паметта :) ).

    Би било адски смешно навсякъде вместо 5 да е 6 :)

  6. Димитър Says:

    Пропаднал физик, ти си ненормален :)

  7. Сви Says:

    Добре казано. Определено има нужда – човек така се пристрастява към езици с “боклукчия”…

  8. Делян Делчев Says:

    Поведението на фортран-а е нормално, той създава 5 като константа, и понеже всички предавания са по reference е възможно да запишеш на адреса на константата друга стойност. Не създава много 5-ци поради това , че не ги предава по стойност, трябва да ги преалокира, и така пести адреси в паметта

  9. Сви Says:

    Леко лирично отклонение

  10. BORIME4KA Says:

    Абе щом е “pesho”, много ясно, че ще работи ;-)

  11. chervo Says:

    eto kak izglezhdat neshtata na druga arhitektura:

    primordium 106% cat t.c
    #include <stdio.h>

    char *p = “asdf”;

    int main() {
    p[0] = ‘t’;
    return 0;
    }
    primordium 107% cc t.c
    primordium 108% dbx a.out
    dbx version 7.3 MR 55458_Apr30_MR Apr 30 1999 13:44:41
    dbx Warning: Unknown processor type 0xe, assuming this is not an R8000
    Executable /usr/people/nasko/a.out
    (dbx) run
    Process 13592 (a.out) started
    Process 13592: region 3 identical to prev ignored
    Process 13592 (a.out) stopped on signal SIGSEGV: Segmentation violation (default) at [main:6 +0xc,0x10000ce8]
    6 p[0] = ‘t’;
    (dbx) quit
    primordium 109% cc -version
    MIPSpro Compilers: Version 7.3.1.3m
    primordium 110% uname -aR
    IRIX64 primordium 6.5 6.5.29f 01090133 IP30
    primordium 111%

    asemblera e na http://SIGBUS.PVTRIDVS.NET/pool/tmp/t.s
    кирливица

  12. пропаднал физик Says:

    В светлината на всичко по-горе, изненада от страна на DEC:

    # cat t.c
    #include

    char *p = “asdf”;

    int main() {
    p[0] = ‘t’;
    printf(“%s\n”, p);
    return 0;
    }
    # cc t.c
    # ./a.out
    tsdf
    # cc -V
    DEC C V5.2-030 on Digital UNIX V4.0 (Rev. 464)
    … (200 реда с версиите на всички компоненти) …
    # uname -a
    OSF1 flamingo V4.0 464 alpha

    По подразбиране cc на DEC компилира с опция -writable_strings

  13. alex242 Says:

    мазохисти ! маскари ! мискини ! михлюзи ! мърди !
    хората се опитват да ви лекуват с дотНЕТ, Жаби и други разни …
    май наистина Керниган/Ричи са написали Цъ-то за да му е гадно на човекя …

Leave a Reply