Was the “forced change” the abandonment of Fortran-COMMON-block
semantics altogether for C globals?
On Sat, 26 Jul 2025 05:43:10 -0000 (UTC), Lawrence D'Oliveiro wrote:
Was the “forced change” the abandonment of Fortran-COMMON-block
semantics altogether for C globals?
It was never a good idea but a joy of legacy code is variables were
sometimes defined in header files. I think it was gcc 10, or whatever
shipped with Debian Bullseye when gcc put its foot down and threw errors about multiply defined variables.
-fno-common was made the default flag. Luckily -fcommon restored the lax behavior. That was easier than going down the rabbit hole of putting a variable definition in multiple applications.
On Sat, 26 Jul 2025 05:43:10 -0000 (UTC), Lawrence D'Oliveiro wrote:
Was the “forced change” the abandonment of Fortran-COMMON-block
semantics altogether for C globals?
It was never a good idea but a joy of legacy code is variables were
sometimes defined in header files. I think it was gcc 10, or whatever
shipped with Debian Bullseye when gcc put its foot down and threw errors about multiply defined variables.
-fno-common was made the default flag. Luckily -fcommon restored the lax behavior. That was easier than going down the rabbit hole of putting a variable definition in multiple applications.
On 7/26/25 00:03, rbowman wrote:
On Sat, 26 Jul 2025 05:43:10 -0000 (UTC), Lawrence D'Oliveiro wrote:
Was the “forced change” the abandonment of Fortran-COMMON-block
semantics altogether for C globals?
It was never a good idea but a joy of legacy code is variables were
sometimes defined in header files. I think it was gcc 10, or whatever
shipped with Debian Bullseye when gcc put its foot down and threw errors
about multiply defined variables.
-fno-common was made the default flag. Luckily -fcommon restored the lax
behavior. That was easier than going down the rabbit hole of putting a
variable definition in multiple applications.
Sort of. For PL/I I have to specify the ld option "-z muldefs" to get
this to work. No a common block, each global (PL/I EXTERNAL) variable is
its own section.
On 2025-07-26, Peter Flass <Peter@Iron-Spring.com> wrote:
No a common block, each global (PL/I EXTERNAL) variable is its own
section.
Isn't that exactly the same: A named CSECT ? (C for COMMON) It's
been decades, but tht is what I thinbk I remember.
On 2025-07-26, Peter Flass <Peter@Iron-Spring.com> wrote:
On 7/26/25 00:03, rbowman wrote:
On Sat, 26 Jul 2025 05:43:10 -0000 (UTC), Lawrence D'Oliveiro wrote:
Was the “forced change” the abandonment of Fortran-COMMON-block
semantics altogether for C globals?
It was never a good idea but a joy of legacy code is variables were
sometimes defined in header files. I think it was gcc 10, or whatever
shipped with Debian Bullseye when gcc put its foot down and threw errors >>> about multiply defined variables.
-fno-common was made the default flag. Luckily -fcommon restored the lax >>> behavior. That was easier than going down the rabbit hole of putting a
variable definition in multiple applications.
Sort of. For PL/I I have to specify the ld option "-z muldefs" to get
this to work. No a common block, each global (PL/I EXTERNAL) variable is
its own section.
Isn't that exactly the same: A named CSECT ? (C for COMMON)
It's been decades, but tht is what I thinbk I remember.
FORTRAN Blank Common is one big glob, and it's interpretation
depends on how the programs declare it. Named COMMON should be the
same, with one linker section per common section. I thought the
first was what everyone was talking about in the context of C, and
the second is what I'm doing.
The articles on the development of C include some interesting
historical detail. One point that stood out for me was the handling of
global variables.
When I first came across C (back in K&R days), the semantics of
duplicated global variable declarations -- overlay the allocated
storage for each allocation of a variable with the same name, so the
variable ends up being the largest size of all the declarations -- immediately reminded me of Fortran COMMON blocks. And one article
makes it clear that was a conscious decision, to try to ease
implementation of the language on non-Unix systems.
On Sat, 26 Jul 2025 15:46:10 -0700, Peter Flass wrote:
FORTRAN Blank Common is one big glob, and it's interpretation
depends on how the programs declare it. Named COMMON should be the
same, with one linker section per common section. I thought the
first was what everyone was talking about in the context of C, and
the second is what I'm doing.
“Blank COMMON” is just a COMMON block with an implementation-defined name that is distinct from every possible name that the programmer may give to
a named COMMON block.
(Code that can be built and does what was intended when built with "-fcommon", but not with "-fno-common", breaks the requirement that
there must be "exactly one external definition for the identifier" that
has been in all C standard versions.)
Moving from AIX to Linux was fun. AIX apparently had a special little bit bucket to handle attempts to do something with a null address, shrugged,
and moved on. Linux had no sense of humor.
On 2025-07-27, rbowman <bowman@montana.com> wrote:
Moving from AIX to Linux was fun. AIX apparently had a special little bit
bucket to handle attempts to do something with a null address, shrugged,
and moved on. Linux had no sense of humor.
I believe you can use mmap to instruct Linux to provide a mapped,
writable page at address zero. Or multiple pages, in proportion
to your need.
Of course, you still have GCC to contend with, in situations when
behavior is undefined when a pointer is null, and things are optimized accordingly.
On 2025-07-27, rbowman <bowman@montana.com> wrote:
Moving from AIX to Linux was fun. AIX apparently had a special little bit >> bucket to handle attempts to do something with a null address, shrugged,
and moved on. Linux had no sense of humor.
I believe you can use mmap to instruct Linux to provide a mapped,
writable page at address zero. Or multiple pages, in proportion
to your need.
Of course, you still have GCC to contend with, in situations when--
behavior is undefined when a pointer is null, and things are optimized accordingly.
On 2025-07-27, rbowman <bowman@montana.com> wrote:
Moving from AIX to Linux was fun. AIX apparently had a special little bit >> bucket to handle attempts to do something with a null address, shrugged,
and moved on. Linux had no sense of humor.
I believe you can use mmap to instruct Linux to provide a mapped,
writable page at address zero. Or multiple pages, in proportion
to your need.
Kaz Kylheku <643-408-1753@kylheku.com> writes:
I believe you can use mmap to instruct Linux to provide a mapped,
writable page at address zero. Or multiple pages, in proportion
to your need.
I don't think you can.
In alt.folklore.computers Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
I believe you can use mmap to instruct Linux to provide a mapped,
writable page at address zero. Or multiple pages, in proportion
to your need.
I don't think you can.
You can, on some Linux systems. I was able to to do it on a Linux x86-32 bit system to run an old MS-DOS executable via the vm86() system call (and I had to implement enough MS-DOS calls to run just this one executable). Why? I wanted to pipe stdin/stdout to some other Unix program and hacking DosBOX seemed like the harder option.
-spc
The change history indicates that this behavior has not been changed. Interestingly, what the standard now says about MAP_FAILED, it used to
say about (void*)-1. The fact that it didn't use a null value to
indicate failure may imply that a null value was intended to be allowed
as a successful return.
On 2025-07-27 22:33, sean@conman.org wrote:
In alt.folklore.computers Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Kaz Kylheku <643-408-1753@kylheku.com> writes:
I believe you can use mmap to instruct Linux to provide a mapped,
writable page at address zero. Or multiple pages, in proportion
to your need.
As Keith pointed out, passing it a null pointer leaves the
implementation free to choose whatever location it wants, even if
MAP_FIXED is chosen. An implementation could choose to return a null
pointer value, but there's nothing you can do to instruct it to do so.
With regards to
pa=mmap(addr, len, prot, flags, fildes, off);
The Single Unix standard says:
"When the implementation selects a value for pa, it never places a
mapping at address 0, nor does it replace any extant mapping."
However, that occurs in a paragraph which starts with "When MAP_FIXED is
not set ...", which implies that restriction does not apply when
MAP_FIXED is set.
The change history indicates that this behavior has not been changed. >Interestingly, what the standard now says about MAP_FAILED, it used to
say about (void*)-1. The fact that it didn't use a null value to
indicate failure may imply that a null value was intended to be allowed
as a successful return.
As Keith pointed out, passing it a null pointer leaves the
implementation free to choose whatever location it wants, even if
MAP_FIXED is chosen. An implementation could choose to return a null
pointer value, but there's nothing you can do to instruct it to do so.
On 2025-07-28, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
The change history indicates that this behavior has not been changed.
Interestingly, what the standard now says about MAP_FAILED, it used to
say about (void*)-1. The fact that it didn't use a null value to
indicate failure may imply that a null value was intended to be allowed
as a successful return.
On a Linux 4.15 system (older Ubuntu) it succeeds if the caller is superuser. Perhaps there is some CAP_* capability for finer-grained access to this:
$ cat mmap-null.c
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
int res = munmap(0, 4096);
void *ptr = mmap(0, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
printf("res = %d, ptr = %p, errno = %d\n", res, ptr, errno);
return 0;
}
$ make CFLAGS='-W -Wall -O2' mmap-null
cc -W -Wall -O2 mmap-null.c -o mmap-null
$ ./mmap-null
res = 0, ptr = 0xffffffff, errno = 1
$ sudo ./mmap-null
res = 0, ptr = (nil), errno = 0
If you have some legacy app which needs writable memory at tne null pointer, it's doable but not without some inconveniences, like not being able to run as
a regular user on a system without sudo access.
On Mon, 28 Jul 2025 08:02:37 -0400, James Kuyper wrote:
As Keith pointed out, passing it a null pointer leaves the
implementation free to choose whatever location it wants, even if
MAP_FIXED is chosen. An implementation could choose to return a null
pointer value, but there's nothing you can do to instruct it to do so.
But C does not specify that the NULL address is actually address 0 (even
if it is denotable by an integer literal equal to 0).
This leaves the door open to MAP_FIXED (or better still, MAP_FIXED_NOREPLACE) to creating a mapping at address 0 if you specify it
-- if you can find some way to specify address 0 from C ...
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On a Linux 4.15 system (older Ubuntu) it succeeds if the caller is superuser.
Perhaps there is some CAP_* capability for finer-grained access to this:
$ cat mmap-null.c
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
int res = munmap(0, 4096);
void *ptr = mmap(0, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
printf("res = %d, ptr = %p, errno = %d\n", res, ptr, errno);
return 0;
}
$ make CFLAGS='-W -Wall -O2' mmap-null
cc -W -Wall -O2 mmap-null.c -o mmap-null
$ ./mmap-null
res = 0, ptr = 0xffffffff, errno = 1
$ sudo ./mmap-null
res = 0, ptr = (nil), errno = 0
If you have some legacy app which needs writable memory at tne null pointer, >> it's doable but not without some inconveniences, like not being able to run as
a regular user on a system without sudo access.
I get similar results on Ubuntu 24.04.2, Linux 6.14.0-24-generic.
I'm able to read from address 0 without a segfault.
Kaz Kylheku <643-408-1753@kylheku.com> writes:
On 2025-07-28, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
The change history indicates that this behavior has not been changed.
Interestingly, what the standard now says about MAP_FAILED, it used to
say about (void*)-1. The fact that it didn't use a null value to
indicate failure may imply that a null value was intended to be allowed
as a successful return.
On a Linux 4.15 system (older Ubuntu) it succeeds if the caller is superuser.
Perhaps there is some CAP_* capability for finer-grained access to this:
$ cat mmap-null.c
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
int res = munmap(0, 4096);
void *ptr = mmap(0, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
printf("res = %d, ptr = %p, errno = %d\n", res, ptr, errno);
return 0;
}
$ make CFLAGS='-W -Wall -O2' mmap-null
cc -W -Wall -O2 mmap-null.c -o mmap-null
$ ./mmap-null
res = 0, ptr = 0xffffffff, errno = 1
$ sudo ./mmap-null
res = 0, ptr = (nil), errno = 0
If you have some legacy app which needs writable memory at tne null pointer, >> it's doable but not without some inconveniences, like not being able to run as
a regular user on a system without sudo access.
I get similar results on Ubuntu 24.04.2, Linux 6.14.0-24-generic.
I'm able to read from address 0 without a segfault.
On Fri 7/25/2025 10:43 PM, Lawrence D'Oliveiro wrote:
The articles on the development of C include some interesting
historical detail. One point that stood out for me was the handling of
global variables.
When I first came across C (back in K&R days), the semantics of
duplicated global variable declarations -- overlay the allocated
storage for each allocation of a variable with the same name, so the
variable ends up being the largest size of all the declarations --
immediately reminded me of Fortran COMMON blocks. And one article
makes it clear that was a conscious decision, to try to ease
implementation of the language on non-Unix systems.
Both C89/90 rationale and C99 rationale have entire sections on the
ref/def models, which were taken into consideration. They provides the
same reasoning for the decision made by the committee: not burdening the weaker platforms with the task of merging/cleaning-out repetitive definitions. The responsibility to ensure that there is at most one definition for entities with external linkage lies on the user.
On a related note, it is interesting to point out that the language continues to staunchly stick to the same approach in its later
iterations: C99 introduced inline functions, and the definition model
for inline functions with external linkage is also strikingly different from, say, C++. The user is required to manually choose the definition
site and provide only one `extern inline` definition for the function
(i.e. regular non-inlined body, in case the compiler decides to use one).
Sysop: | DaiTengu |
---|---|
Location: | Appleton, WI |
Users: | 1,064 |
Nodes: | 10 (0 / 10) |
Uptime: | 159:15:24 |
Calls: | 13,691 |
Calls today: | 1 |
Files: | 186,936 |
D/L today: |
7,070 files (2,120M bytes) |
Messages: | 2,411,313 |