Re: Discussion about why GNU/Linux system upgrades cause old programs to break

Arsen Arsenović <[email protected]>
Newsgroups gmane.emacs.devel
Message-ID <[email protected]>
Richard Stallman <[email protected]> writes:

> [[[ To any NSA and FBI agents reading my email: please consider    ]]]
> [[[ whether defending the US Constitution against all enemies,     ]]]
> [[[ foreign or domestic, requires you to follow Snowden's example. ]]]
>
>   > There are two different classes of users of compilers: program
>   > authors and program users building the source form.
>
>   > The former is harmed, substantially, by such default laxness, because
>   > they're enabled to write programs that are slightly wrong.  The latter
>   > receives some slight relief from default backwards compatibility.
>
> You're arbitrarily defining "wrong" based on official Standard C and
> taking that moral stance for granted.  But that's not the standard
> that I chose for the GNU Project.  Our rule is that there is a certain
> range of machine behaviors that we try to handle, and we don't need to
> go to any lengths to make code work on weird machines outside of that.
>
> This GNU Project practice is prior to ANSI C (which wasn't finished yet).
> I decided we didn't need to try to support all possible machines,
> only the ones it was reasonably useful to support.

Yes, I'm aware that GNU predates ANSI C.

I'm not taking a moral stance here.

Like any other program, the compiler exposes some guarantees to its
users.  These are, usually, called "the interface" of whatever is
exposing those guarantees (say, the interface of the C compiler is the
language it implements and the semantics it provides).

Those guarantees have, for a long time now, been what the C standard
(and C++ standard and so on) says.

(As a side note, even if it was the case that the toolchain projects
adhere 100% strictly to the standards, it would be incorrect to frame
this in opposition to what GNU considers correct, because GNU developers
play a significant part in the development of these standard)

Of course, we also expose some extensions, and support other standard,
and delegate some tasks out of the compiler to other components, etc.

But, that aside..

Something that I'm considering wrong in the above text is something that
fails to adhere to the rules of the interfaces in question, or which
rely on assumptions not supported by guarantees of the interfaces in
question.

For instance, the following code is wrong, because there's no guarantee
by any of those, and thus not by the compiler either, that the
assumption the developer made (below represented as an assertion) holds:

  /* Write the 4-byte B to P.  */
  void
  write_4b (uint32_t *p, uint32_t b)
  {
    *p = b;
  }

  ...
    uint32_t b = /* Given.  */;
    uint64_t q = 0;
    write_4b ((uint32_t*)&q, b);
    assert (q == b);

We're able to reason what will happen assuming a given system: we may,
possibly even correctly, reason that 0) '*p = b' in isolation would
compile to something like 'movl %esi, (%rdi)', thus we may reason that
the above write_4b 1) has no alignment requirements, 2) is atomic, 3)
sets the value of q to b, with the top 32 bits being left as zero.

But none of the steps in that reasoning are supported by the interface
the compiler defines (in this instance, all of the rules pertaining to
the above are part of ISO C). If they were, most compiler
transformations would be invalid (how can one transform a program based
on the "as-if" rule if even the instruction selection reasoned about in
clause 0 is part of what is considered observable behaviour?).

Obviously, the above program is rather useless.  But such helper
functions have been seen before.

A correct "write_4b", that writes four bytes to a memory location, would
be something like:

  void
  write_4b (void *p, char b[4])
  {
    memcpy (p, b, 4);
  }

The above compiles to:

  write_4b:
        movl    (%rsi), %eax
        movl    %eax, (%rdi)
        ret

This has an extra memory dereference due to the extra pointer argument,
but, if put in a header as an inline function, GCC would easily do away
with that dereference.  The above also, actually, has no alignment
requirement, unlike the example of the wrong implementation (but it's
still not guaranteed to be atomic), even though the compiler won't use
the knowledge of alignment in either example (you can see the difference
on, e.g. SPARC, though).

Though, I'd note that it is not quite as simple as I've made it out in
this illustration.

The former write_4b, the one I called wrong, is wrong because of the
way it's used.  If it was only ever used to assign uint32_t values
through a uint32_t pointer, it'd be fine (but quite useless, I'm sure
you'd agree).

The latter write_4b, which I called correct, can nonetheless be used
incorrectly, of course.  For instance, by over-running the storage it's
being used to write into:  char x; write_4b (&x, "foo")

In my experience, most breakages that occur on a toolchain update fall
into this category.  Not all, of course.  But most.

Even though a given platform doesn't actually manifest a bug, since
there's no guarantee of the behavior of the above, the bug may manifest
in the future, after a toolchain update, because an unfounded assumption
was made.
-- 
Arsen Arsenović
signature.asc (application/pgp-signature, 418 B)
-----BEGIN PGP SIGNATURE-----

iQECBAEWCgCqFiEE/uKz0RP8AKMWLWBhUsKUMB6ixJMFAmp+QMQbFIAAAAAABAAO
bWFudTIsMi41KzEuMTIsMiwyXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25z
Lm9wZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXRGRUUyQjNEMTEzRkMwMEEzMTYyRDYw
NjE1MkMyOTQzMDFFQTJDNDkzEBxhcnNlbkBhYXJzZW4ubWUACgkQUsKUMB6ixJOa
XAEAhWgCLZChhCKporHM4theGGN5QXuHldSXN0jZDmktk4UBAN7S6QCrwDBeD/KK
04f1ZQp9hb5P4ZYMcMK8DPEzFQYM
=FOdg
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.