Path: ...!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.lang.c Subject: Re: relearning C: why does an in-place change to a char* segfault? Date: Sat, 03 Aug 2024 17:11:55 -0700 Organization: None to speak of Lines: 85 Message-ID: <877ccxdu44.fsf@nosuchdomain.example.com> References: <20240801114615.906@kylheku.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Injection-Date: Sun, 04 Aug 2024 02:11:56 +0200 (CEST) Injection-Info: dont-email.me; posting-host="d07bdb3c66a7f25ead61bbbbc164c6d5"; logging-data="3865857"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+Hcd/CBbuMMOoZEDR2SHYl" User-Agent: Gnus/5.13 (Gnus v5.13) Cancel-Lock: sha1:TcLfZJR8p3vVCKET7Vl1IE4rS74= sha1:VnTdc3FQbUC0Vn8sADG8VIwZPxI= Bytes: 4602 scott@slp53.sl.home (Scott Lurndal) writes: > David Brown writes: >>On 01/08/2024 22:42, Bart wrote: >>>   char text[]="this is a test"; >>> >>> But this can be changed without making the program self-modifying. >> >>"this is a test" is a string literal, and is typically part of the >>program's image. (There are some C implementations that do things >>differently, like storing such initialisation data in a compressed format.) >> >>The array "char text[]", however, is a normal variable of type array of >>char. It is most definitely not part of the program image - it is in >>ram (statically allocated or on the stack, depending on the context) and >>is initialised by copying the characters from the string literal (prior >>to main(), or at each entry to its scope if it is a local variable). > > Linux (ELF): > > A file-scope static declaration of char text[] will emit the string > literal into the .data section and that data section will be loaded > into memory by the ELF loader. There is no copy made at runtime > before main(). > > #include > #include > #include > > char text1[] = "This is a test of a static-scope string"; In the abstract machine, there's an anonymous array object corresponding to the string literal, and `text` is a distinct object, also with static storage duration. The compiler optimizes it away and only stores the data in `text`. > int > main(int argc, const char **argv) > { > char text2[] = "This is a test of a function-scope string"; Since the second string literal is identical, the compiler is permitted to store them in the same place (it's unspecified, so the implementation doesn't have to document this). Presumably there's code to copy from the static array into `text2`, executed within `main`. > > fprintf(stdout, "%p %s\n", &text1, text1); > fprintf(stdout, "%s\n", text2); > > return 0; > } > > $ /tmp/a > 0x601060 This is a test of a static-scope string > This is a test of a function-scope string > > $ objdump -p /tmp/a > > /tmp/a: file format elf64-x86-64 > > Program Header: > PHDR off 0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3 > filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x > INTERP off 0x0000000000000238 vaddr 0x0000000000400238 paddr 0x0000000000400238 align 2**0 > filesz 0x000000000000001c memsz 0x000000000000001c flags r-- > LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21 > filesz 0x00000000000007dc memsz 0x00000000000007dc flags r-x > LOAD off 0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**21 > filesz 0x0000000000000278 memsz 0x0000000000000290 flags rw- > > .data section: > > 0000e00: 0000 0000 0000 0000 0000 0000 0000 0000 ................ [36 lines deleted] > 0001050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ > 0001060: 5468 6973 2069 7320 6120 7465 7374 206f This is a test o > 0001070: 6620 6120 7374 6174 6963 2d73 636f 7065 f a static-scope > 0001080: 2073 7472 696e 6700 4743 433a 2028 474e string.GCC: (GN > > $ printf "0x%x\n" $(( 0x601060 - 0x0000000000600e10 )) > 0x250 -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com void Void(void) { Void(); } /* The recursive call of the void */