Is & errno legal C?

Per 7.5,

[errno] expands to modifiable lvalue175), which is of type int, whose value is set to the number of positive errors by several library functions. It is not indicated whether errno is a macro or identifier declared with external communication. If the macro definition is suppressed to access the actual object or the program defines an identifier named errno, the behavior is undefined.

175) The errno macro does not have to be an object identifier. It can expand to a mutable lvalue called by a function call (e.g. * errno ()).

It’s not clear to me whether this is enough to prevent &errno from violating the restriction. The C language has lvalues ​​(for example, variables of the register-storage-class type, however, they can only be automatic, therefore errno cannot be defined as such) for which the & operator violates the restriction.

If &errno is legal C, is constant required?

+48
c language-lawyer errno
Oct 18
source share
5 answers

Thus, section 6.5.3.2p1 states

The operand of the unary and operator must be either the designation of the function, the result of the operator [], or the unary *, or the value l, which indicates an object that is not a bit field and is not declared in the register storage class.

Which I think can be considered meaning that &lvalue great for any lvalue that is not in these two categories. And, as you already mentioned, errno cannot be declared using the register storage class specifier, and I think (although I am not following the validation reference right now) that you cannot have a bit field of type plain int .

Therefore, I believe the specification requires &(errno) be legal C.

If & errno is legal C, is persistence required?

As I understand it, the part of the point that allows errno be a macro (and the reason why it is located, for example, glibc), should allow it to be a link to the local storage of threads, in which case it will certainly not be constant on threads. And I see no reason to believe that it should be permanent. As long as the errno value retains the specified semantics, I see no reason that the perverted C library could not change &errno to refer to different memory addresses during the program - for example, freeing up and redistributing backup storage every time you install errno .

You might imagine that you maintain a ring buffer of the last N errno values ​​set by the library, and &errno always points to the latter. I do not think this would be particularly useful, but I see no way to violate the specification.

+17
Oct 18
source share

I am surprised that no one has quoted the C11 spec yet. Apologies for the long quote, but I think this is relevant.

7.5 Errors

The header defines several macros ...

... and

errno

which expands to modifiable lvalue (201), which has the int type and local stream storage time, the value of which is set to the number of positive errors for several library functions. If the macro definition is suppressed to access a real object or the program defines an identifier named errno , the behavior is undefined.

The errno value in the original thread is zero when the program starts (the initial errno value in other threads is an undefined value), but it is never set to zero by any function library. (202) The value of errno can be set to a non-zero value by the library of a function call, regardless of whether or not there is an error, provided that errno used, it is not described in the function description in this International Standard.

(201) The errno macro does not have to be an object identifier. It can change the value of lvalue caused by a function call (e.g. *errno() ).

(202) Thus, a program that uses errno to check for errors should set it to zero before calling the library function, then check it before calling the library again. Of course, the library function can save the errno value when writing, and then set it to zero until the original value is restored if errno s is still zero before returning.

"Local stream" means register missing. The int type means that no bitfields are available (IMO). So &errno looks legit for me.

The constant use of words like "this" and "meaning" suggests that the authors of the standard did not consider &errno as inconsistent. I suppose you can imagine an implementation in which &errno not constant in a certain thread, but in order to use what the footnotes say (set to zero and then check after calling the library function), this should be intentionally adversarial, and perhaps will require specialized compiler support just to be competitive.

In short, if the specification permits erratic &errno , I don't think it was intentional.

[update]

R. asks a wonderful question in the comments. Thinking about it, I believe that now I know the correct answer to his question and the original question. Let me see if I can convince you, dear reader.

R. indicates that GCC allows something similar at the top level:

 register int errno asm ("r37"); // line R 

This will declare errno as the global value stored in register r37 . Obviously, this would be a locally modifiable lvalue value. So, can the corresponding C implementation declare errno as follows?

The answer is no . When you or I use the word "declaration", we usually mean a colloquial and intuitive concept. But the standard does not speak conversationally or intuitively; he speaks for sure, and he aims only at the use of terms that are clearly defined. In the case of an “announcement,” the standard itself defines the term; and when he uses the term, he uses his own definition.

By reading the specification, you can find out exactly what an “announcement” is and what exactly. In other words, the standard describes the language "C". It does not describe "some language that is not C". Regarding the standard, “C with extensions” is simply “some language that is not C”.

Thus, from a standard point of view, the string R is not a declaration at all. He doesn't even make out! He can also read:

 long long long __Foo_e!r!r!n!o()blurfl??/** 

Regarding the specification, it is just as “declaration” as the string R; those. not at all.

So, when the C11 specification says, in section 6.5.3.2:

The operand of the unary operator & must be either a function notation, the result of the operator [] or unary * , or lvalue, which indicates an object that is not a bit field and is not declared with the register storage specification.

... that means something very precise that doesn't apply to anything like Line R.

Now consider the declaration of the int object that errno refers to. (Note: I do not mean the declaration of the name errno , since, of course, there can be no such declaration if errno is, say, a macro. I mean the declaration of the base int object.)

The above language says that you can take the lvalue address if it does not indicate a bit field or does not indicate an “declared” register object. And the specification for the base errno object says that it is a modifiable value of int l with local stream duration.

Now it is true that the specification does not mean that the errno base object should be declared at all. Maybe it just appears with the help of a certain compiler magic, defined for implementation. But then again, when the specification says "declared using the register storage class specifier", it uses its own terminology.

Thus, the errno base object is "declared" in the standard sense, in which case it cannot be both register and thread-local; or it is not declared at all, in which case it is not declared register . In any case, since this is an l value, you can accept its address.

(If it is not a bit field, but I think we agree that the bit field is not an object of type int .)

+14
Oct 31 '12 at 3:12
source share

The original errno implementation was an int global variable, according to which the various components of the Standard C Library used to indicate the value of an error if they encountered an error. However, even in those days, one had to be careful about reentrant code or with calls to library functions that could set errno to a different value, since you were handling the error. Typically, a value would be stored in a temporary variable if an error code was needed for some time due to the possibility of some other function or piece of code setting errno either explicitly or by calling a library function.

Thus, with this initial implementation of the global int, using the address of the operator and depending on the fact that the address remains constant, is largely embedded in the library structure.

However, with multithreading, there was no longer one global one, since one global value was not thread safe. Thus, the idea is to have a local storage stream , possibly using a function that returns a pointer to the selected area. So you can see something like the following fully imaginary example:

 #define errno (*myErrno()) typedef struct { // various memory areas for thread local stuff int myErrNo; // more memory areas for thread local stuff } ThreadLocalData; ThreadLocalData *getMyThreadData () { ThreadLocalData *pThreadData = 0; // placeholder for the real thing // locate the thread local data for the current thread through some means // then return a pointer to this thread local data for the C run time return pThreadData; } int *myErrno () { return &(getMyThreadData()->myErrNo); } 

Then errno will be used as if it were the only global, not stream safe int variable with errno = 0; or checking it as if (errno == 22) { // handle the error and even something like int *pErrno = &errno; . This all works because at the end the local data area of ​​the stream is allocated and remains placed and does not move, and the macro definition, which makes errno look like an extern int , hides the plumbing of its actual implementation.

The only thing we don’t want is that the errno address suddenly shifts between time slices of the stream with some kind of dynamic allocation, cloning, deletion of the sequence when we access the value. When your time slice is up, it gets up and, if you don’t have any synchronization or somehow hold the processor after the time of your slice expires, then moving the local area of ​​the stream seems to be a very risky proposition for me.

This, in turn, implies that you can depend on the address of the operator giving you a constant value for a particular thread, although the constant value will differ between threads. I can clearly see the library using the errno address in order to reduce the overhead of doing some kind of local stream search every time the library function is called.

Having the errno address as a constant in the stream also provides backward compatibility with the old source code that uses the errno.h include file, as they should have done (see this linux man page for errno , which explicitly warns you not to use extern int errno; as was common in the old days).

The way I read the standard is to allow such local thread storage, preserving semantics and syntax similar to the old extern int errno; when errno used and allows you to use the old use for some kind of cross compiler for an embedded device that does not support multithreading. However, the syntax may be similar due to the use of macro definitions, so the old style abbreviation declaration should not be used because this declaration is not true errno .

+3
Nov 01 '12 at 3:27
source share

We can find a counterexample: since a bit field can be of type int , errno can be a bit field. In this case, &errno will be invalid. The behavior of the standard here does not explicitly mean that you can write &errno , which is why the definition of undefined behavior is used here.

C11 (n1570), § 4. Compliance
undefined behavior is specified in this International Standard by the words "undefined behavior or due to lack of an explicit definition of behavior.

0
Oct 27 '12 at 15:00
source share

This seems like a valid implementation, where &errno will be a violation of the constraint:

 struct __errno_struct { signed int __val:12; } *__errno_location(void); #define errno (__errno_location()->__val) 

So, I think the answer is probably not ...

0
Aug 22 '13 at 4:28
source share



All Articles