Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Comments on C Language Standard Linking Specifications

Post

C Language Standard Linking Specifications

+5
−0

What (if anything) does the C standard have to say about linking objects? My guess is that, because C only defines language->behavior rules, it completely ignores any potential intermediate form the code may take. Obviously, C doesn't guarantee anything about the executable itself because it may well be "compiled" down to something that isn't an executable for a computer platform or even something similar. That said, does C make any guarantees about interoperability between compiled output targets on a given platform? For example, if I add this to a source file:

extern int foo(void);

the resulting C program will expect to be able to jump to such a symbol but doesn't require any knowledge of the definition to successfully compile and output valid code. Does C require that the above eventually resolve to an executable entity (i.e. during linking), or is C itself completely indifferent to any linking mechanics that take place after compilation? If the former, what guarantees does C make?

EDIT: Following some commentary, I think the question can be further clarified.

In a freestanding C program, there is no I/O capability, simply due to the fact that the language imposes no restrictions whatsoever on the capabilities of a compliant C implementation without a hosted environment like an operating system. In practice, to perform a processor I/O call on an x86 machine in a kernel program, a call must be made out of the C code to assembly (or something similar). An assembly module is by no means, of course, required to comply in any way to the C standard. Can such a module be considered a separate translation unit? According to the standard, I'd assume it cannot. The way the C module and the assembly module are linked together is completely dependent on the environment and implementation.

Having said all this, consider the above function foo once more. If this function is defined in assembly, it is not part of any C translation unit, and therefore can only be linked by relying on implementation details. My question is this: is this environment-specific linking behavior implementation-defined? If so, there should be reference to it within the C standard somewhere, and I'd like to know exactly what can be said about linking C translation units with other non-C libraries. However, intuitively I'd assume that, since the definition of foo is not contained within any C translation unit, the C program should not be valid. This would mean that every kernel ever written that has any I/O of any kind is not standards-compliant C. That doesn't necessarily matter in practice because a non-compliant program that does its job is just as useful as a compliant one. My question is purely theoretical: is my analysis correct? Is a compliant freestanding C program unable to produce observable results? Or am I missing something in the standard that relates to linking against something that is not a C translation unit?

History
Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

1 comment thread

"My question is this: is this environment-specific linking behavior implementation-defined?" (6 comments)
"My question is this: is this environment-specific linking behavior implementation-defined?"
elgonzo‭ wrote over 2 years ago

If so, there should be reference to it within the C standard somewhere, and I'd like to know exactly what can be said about linking C translation units with other non-C libraries.

It is implementation-defined, and therefore not described nor referenced in the C standard, precisely because different implementation can do things differently.

The only rules the C standard established are with respect to identifiers (as part of the C source code) and whether each usage of the same identifier is required to identify the same thing or not (function/object/storage location) within or across translation units. How this linkage is then actually achieved is of no concern to the C standard, and a linker (or linking step, if you will) is free to do it however it wants as long as it obeys the rules about identifier set in place by the C standard.

elgonzo‭ wrote over 2 years ago · edited over 2 years ago

This would mean that every kernel ever written that has any I/O of any kind is not standards-compliant C.

Incorrect. You cannot become non-compliant by "violating" non-existing parts of the standard.

With regard to linking object/machine code that is written in other languages then C (like for example assembler). The C standard is simply unconcerned about the process of linking and how it is realized or what the actual end result will look like precisely, as long as the resulting program adheres to the behavior accurately described by the C source code.

elgonzo‭ wrote over 2 years ago · edited over 2 years ago

Any code that is not part of the C source code (such as a function provided by some object/machine code written in assembler) has its behavior obviously not described by the C source code nor the C standard, and from the perspective of C is thusly undefined behavior. (In other words, neither the C standard nor the C source code itself describe nor care about what will happen when invoking a function that is defined outside the C language domain.)

elgonzo‭ wrote over 2 years ago · edited over 2 years ago

This would mean that every kernel ever written that has any I/O of any kind is not standards-compliant C.

Again incorrect. Memory-mapped I/O can be done entirely in C without needing language extensions nor code modules written in other languages. Depending on the actual architecture or specific hardware implementation of some architecture, a compiler might offer additional keyword extensions or some such to provide a convenient way to define configuration parameters with respect to port configurations, memory layout, and other things right there together with the C source code, so you don't have to rely on other tools to set HW configuration parameters. (You can see things like these when developing firmware for microcontrollers and such...)

Josh Hyatt‭ wrote over 2 years ago

elgonzo‭ I did forget about memory-mapped I/O. In that case, am I correct in assuming that C must be informed that these memory locations should be considered volatile to avoid implementations potentially optimizing away reads or writes from them? And is memory-mapped I/O the only way to perform I/O without language extensions in a freestanding environment?

elgonzo‭ wrote over 2 years ago · edited over 2 years ago

Yes, memory-mapped I/O is the only way to do it with standard-conform C in a freestanding implementation, and typically involving volatile -qualified types. (I erroneously mentioned in my previous comment that port-based I/O could be done in a C standard-conforming way as well, but that is obviously hog-wash. I confused that with POSIX -- there are a POSIX-conforming ways to do port-based I/O. Sorry about that. I edited this piece of misinformation out of my comment...)