Thread: understanding c preprocessor directives...

  1. #1
    Registered User
    Join Date
    Oct 2007
    Posts
    100

    understanding c preprocessor directives...

    hello everybody!
    I am trying to understand some code of linux but i am not 100% able to understand preprocessor's directives and programming conventions...

    here is the code i want to understand:
    Code:
    #define __define_initcall(level,fn,id) \
    	static initcall_t __initcall_##fn##id __attribute_used__ \
    	__attribute__((__section__(".initcall" level ".init"))) = fn
    
    #define device_initcall(fn)		__define_initcall("6",fn,6)
    
    #define __initcall(fn) device_initcall(fn)
    
    #define module_init(x)	__initcall(x);
    I can understand almost everything besides the 1st #define (all the rest are substitutions of symbols, isn't it?):
    the symbol
    Code:
     __define_initcall(level,fn,id)
    will be replaced with
    Code:
    static initcall_t __initcall_##fn##id __attribute_used__  __attribute__((__section__(".initcall" level ".init"))) = fn
    the ##fn## has to be replaced with fn and the symbols at the left and right have to be concatenated, eg:
    Code:
    __define_initcall("6",fn,6)
    will become
    Code:
    static initcall_t __initcall_fnid __attribute_used__  __attribute__((__section__(".initcall" level ".init"))) = fn
    is this a correct interpretation? what is the convention behing the double underscore (__) before the name of the macro?

    thanks for your help! Bye

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Well, but level will get replaced with "6" and id with 6 as well.

  3. #3
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    Quote Originally Posted by tabstop View Post
    Well, but level will get replaced with "6" and id with 6 as well.
    so we can say the answer is:
    Code:
    static initcall_t __initcall_fn6 __attribute_used__  __attribute__((__section__(".initcall6.init"))) = fn
    ?
    in general, when is the use of "__" advised?
    thanks again! bye

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Actually, double underscore symbols are reserved for the compiler implementation, so techncially, that is "undefined behaviour" and can cause breakages - but I doubt that gcc will ever use those symbol names, and and since gcc is the only compiler used to compile the Linux kernel (which my guess is that these macros come from, more specifically the kernel loadable module section). Of course, the likelyhood of that actually happening is probably low.

    And to explain a bit further:
    __atrribute_used__ tells the compiler that it should not warn about unused variables or functions, even if the compiler doesn't think it is use.

    The __attribute__((__section__(".initcall6.init"))) part tells the compiler to put this symbol in a special section. In particular ".initcallX.init" is a special set of sections that are processed by the system loader. The entire content of initcallX.init is assumed to be function pointers, and the system loader will call all those functions in the order they appear, starting with the lowest X.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    Quote Originally Posted by matsp View Post
    Actually, double underscore symbols are reserved for the compiler implementation, so techncially, that is "undefined behaviour" and can cause breakages - but I doubt that gcc will ever use those symbol names, and and since gcc is the only compiler used to compile the Linux kernel (which my guess is that these macros come from, more specifically the kernel loadable module section). Of course, the likelyhood of that actually happening is probably low.

    And to explain a bit further:
    __atrribute_used__ tells the compiler that it should not warn about unused variables or functions, even if the compiler doesn't think it is use.

    The __attribute__((__section__(".initcall6.init"))) part tells the compiler to put this symbol in a special section. In particular ".initcallX.init" is a special set of sections that are processed by the system loader. The entire content of initcallX.init is assumed to be function pointers, and the system loader will call all those functions in the order they appear, starting with the lowest X.

    --
    Mats
    thanks a lot Mats!
    of course your guess is correct: I am studying modules right now!

  6. #6
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by smoking81 View Post
    thanks a lot Mats!
    of course your guess is correct: I am studying modules right now!
    I did think so. Now you can impress your teacher by explaining exactly how a module is initialized .

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    We should add that these are gcc extensions. Avoid like the plague if possible.
    The entire linux kernel is unportable and can't even be compiled with other compilers, plus it's full of macro hacks, from what I can tell.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Elysia View Post
    We should add that these are gcc extensions. Avoid like the plague if possible.
    The entire linux kernel is unportable and can't even be compiled with other compilers, plus it's full of macro hacks, from what I can tell.
    And the Windows kernel, of course, is compilable by a handful of compilers, right? No, not so.

    Unless the OS is SPECIFICALLY written to support many compilers, it usually doesn't (which is HARD - the OS I work with supports about 4 different compilers, and quite frequently, something goes wrong with one of the less commonly used compilers, because some developer added/changed some code in a way that causes the build on one of the compilers that developer DIDN'T test to fail).

    And there are plenty of macro "hacks" in our OS too, since we have to do things like:
    Code:
    #if COMPILER1
    #define A abc
    #elsif COMPILER2
    #define A bca
    #else 
    #define A foo
    #endif
    It is not easy to write an OS. It is a lot harder to write an OS that works on multiple compilers.

    Those macros are perfectly valid for the purpose they have: To support automatic calling of initialization functions for kernel modules. That is not something you would want to compiler with any other compiler anyways, because it integrates directly to the kernel.

    There is NO standard way to define what the section name a particular variable should go in, so how can we do this in a portable way? You'd have to use #if/#elsif... coding to sort that out - and often the syntax is such that you can't just replace one thing with another, because one compiler uses a #pragma, the other an attribute or other compiler extension.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by matsp View Post
    And the Windows kernel, of course, is compilable by a handful of compilers, right? No, not so.
    I thought it wouldn't.
    However, that doesn't mean they should use extensions, especially not without specific #ifdef/#ifndef for specific compilers.
    What if they need to switch compiler at some point?

    And there are plenty of macro "hacks" in our OS too, since we have to do things like:
    Code:
    #if COMPILER1
    #define A abc
    #elsif COMPILER2
    #define A bca
    #else 
    #define A foo
    #endif
    I'd much rather want to do away with hacks. Of course, if I can't see them, I really can't say if there's a solution to those hacks using another language...

    It is not easy to write an OS. It is a lot harder to write an OS that works on multiple compilers.
    I just want to add that I'm not defending any OS, or that they should make it work with many compilers.
    But I don't like hacks or extensions.

    There is NO standard way to define what the section name a particular variable should go in, so how can we do this in a portable way? You'd have to use #if/#elsif... coding to sort that out - and often the syntax is such that you can't just replace one thing with another, because one compiler uses a #pragma, the other an attribute or other compiler extension.
    I do understand that there is need for unportable code, but I'm just adding that it is unportable and that you should avoid them like the plague. I'm not implying you should always stay clear - clearly if the need is greater than the added cost of not doing so, it should/can be done.

    What I'd like to see is a kernel / OS that's written or works in C++ and not C.
    I don't think it's impossible, but Windows kernel and Linux kernel are just too old (too much written in and for C in mind) for that to work reliably...
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Elysia
    However, that doesn't mean they should use extensions, especially not without specific #ifdef/#ifndef for specific compilers.
    What if they need to switch compiler at some point?
    Why would they need to switch compiler? They can always fork GCC if necessary, after all.

    Quote Originally Posted by Elysia
    What I'd like to see is a kernel / OS that's written or works in C++ and not C.
    I recall matsp saying that the OS he works on is written in C++. matsp, is this true?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    The OS I work on is almost entirely written in C++, with the exception of:
    1. A few bits of assembler, because it's not possible / suitable to write those funcitons in C++.
    2. Some libraries that come from third parties, that are written in C rather than C++ - such as OpenGL libraries that by defintion is a C library, not a C++ library [although one could of course implement it as C++ - but that severely limits the number of suppliers of OpenGL that one could license the code from]. I'm pretty sure there are some other libraries that come from third parties that aren't available as C++ code, and we do not want to or have the resources to rewrite those as C++.

    I'm not going to discuss in public what company I work for.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by laserlight View Post
    Why would they need to switch compiler? They can always fork GCC if necessary, after all.
    Well, I don't know, I'm just listing the worst case if scenario.
    The kernel is slow and they realize there's a much faster compiler out there that has recently been released?

    Quote Originally Posted by matsp View Post
    The OS I work on is almost entirely written in C++, with the exception of:
    That is awesome, but I do wonder how advanced functionality, such as templates, it uses, and if it's heavily OOP or uses typical C++ paradigms and such or if it's just pretty much C written as C++?
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Elysia View Post
    I do understand that there is need for unportable code, but I'm just adding that it is unportable and that you should avoid them like the plague. I'm not implying you should always stay clear - clearly if the need is greater than the added cost of not doing so, it should/can be done.
    I do agree that you should not frivolously (sp?) use these constructs. And they aren't used frequently in kernel modules in Linux - kernel modules or kernel drivers are unportable to a large extent in and of themselves, so I don't really see the problem with that.

    I'm pretty sure no-one has suggested that we should add __attribute__(__segment("...")) type things to code arbitrarily without further understanding of the consequences.

    I also fail to see what C++ has to do with this - we want to stuff the address of a function into a specific segment [like text/code, data, bss, etc] so that the function can be executed as part of the loading of a kernel module. How would you do that in standard C++? You wouldn't because there is no standard solution for it. And without changing how the kernel works [and how else would you suggest that we store the initialization functions in for a kernel module?], we can not do anything else.

    Linux in itself, despite relying on gcc, is very portable, shown by the number of processor architectures that it works on - far more than Windows or any other OS that I know of aside from maybe one or two embedded OS's or such.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by matsp View Post
    I do agree that you should not frivolously (sp?) use these constructs. And they aren't used frequently in kernel modules in Linux - kernel modules or kernel drivers are unportable to a large extent in and of themselves, so I don't really see the problem with that.
    Well, at least it's good to hear, but when I see a snippet from a kernel with worst case scenario code and haven't actually looked at the entire source, I draw conclusions that it may be littered with bad practices kind of code.

    I'm pretty sure no-one has suggested that we should add __attribute__(__segment("...")) type things to code arbitrarily without further understanding of the consequences.
    You never know with some people, especially when they read kernel code

    I also fail to see what C++ has to do with this - we want to stuff the address of a function into a specific segment [like text/code, data, bss, etc] so that the function can be executed as part of the loading of a kernel module. How would you do that in standard C++? You wouldn't because there is no standard solution for it. And without changing how the kernel works [and how else would you suggest that we store the initialization functions in for a kernel module?], we can not do anything else.
    Oh, I was just wishing to see a kernel written in C++ instead of that horrible, gibberish C code that makes me tick faster (and that's bad, of course).

    Linux in itself, despite relying on gcc, is very portable, shown by the number of processor architectures that it works on - far more than Windows or any other OS that I know of aside from maybe one or two embedded OS's or such.
    Windows is bad™
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  15. #15
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Elysia View Post
    Well, I don't know, I'm just listing the worst case if scenario.
    The kernel is slow and they realize there's a much faster compiler out there that has recently been released?
    Usually, only a small portion of the kernel is actually being exercised frequently, so it is probably easier to rewrite those particular functions in assembler (inline or out-of-line). Yes, that's even less portable, but it's still easier than replacing the compiler, because that affects more of the code. We have recently had one of our software engineers spend about 5 weeks trying to compile one component with a new compiler. That is not the whole OS, but ONE graphics component [admittedly a fairly large one].

    That is awesome, but I do wonder how advanced functionality, such as templates, it uses, and if it's heavily OOP or uses typical C++ paradigms and such or if it's just pretty much C written as C++?
    Not a lot of templating, but some. It is entirely OOP, not C written as C++. For example, you create a thread object to create a thread, and then call a begin method to make the thread go active. Likewise to create a process, or a semaphore, etc, etc.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need help understanding a problem
    By dnguyen1022 in forum C++ Programming
    Replies: 2
    Last Post: 04-29-2009, 04:21 PM
  2. Understanding Headers
    By AeonMoth in forum C++ Programming
    Replies: 2
    Last Post: 06-27-2007, 05:53 AM
  3. trouble understanding the source file structure
    By Mario F. in forum C++ Programming
    Replies: 5
    Last Post: 05-26-2006, 06:46 PM
  4. Understanding Enumerations
    By Diablo84 in forum C++ Programming
    Replies: 5
    Last Post: 04-18-2005, 04:05 PM
  5. understanding recursive functions
    By houler in forum C Programming
    Replies: 7
    Last Post: 12-09-2004, 12:56 PM