Breaking Eggs And Making Omelettes

Topics On Multimedia Technology and Reverse Engineering


Archives:

Of ctors and dtors

February 17th, 2011 by Multimedia Mike

I haven’t given up on the Sega Dreamcast programming. I was able to compile a bunch of homebrew code for the DC many years ago and I can’t make it work anymore. Again, I was working with a purpose-built, open source RTOS named KallistiOS (or KOS). I can make the programs compile but not run. I had ELF files left over from years ago which still executed. But when I tried to build new ELF files, no luck– the programs crashed before even reaching my main() function.

I found the problem: ELF files are comprised of a number of sections and 2 of these sections are named ‘.ctors’ and ‘.dtors’ which stand for constructors and destructors. The KOS RTOS performs a manual traversal of .ctors section during program initialization and this is where things go bad. The traversal code doesn’t seem to account for a .ctors section that only contains a single entry. I commented out the function that does the traversal and programs started to work, at least until it was time to exit the program and return control to the program loader. That’s when the counterpart .dtors section traversal code ran and demonstrated the same problem. I’ll exhibit the problematic code at the end of this post.

So I’m finally tinkering with Sega Dreamcast programming once again and with a slightly better grasp of software engineering than the first time I did this.

Portable and Compatible C?
If nothing else, this low-level embedded stuff exposes you to some serious toolchain arcana, the likes of which you will likely never see working strictly in the desktop arena.

Still, this exercise makes me wonder why C code from a decade ago doesn’t compile reliably now. Part of it is because gcc has gotten stricter about the syntax it will accept. In the case of this specific crashing problem, I suspect it comes down to a difference in the way the linker generates the final ELF file. I’ve written a list of items I have had to modify in the KOS codebase in order to get it to compile on more recent gcc versions. I wonder if it would be worth publishing the specifics, or if anyone would ever find the information useful? Oh, who am I kidding? Of course I’ll write it up, perhaps publish a new version of the code, if only because that’s the best chance I have of finding my own work again some years down the road.

Problematic C Code
See if this code makes any sense to you. It somehow traverse a list of 32-bit function pointers (in different directions, depending on constructors or destructors), executing each in turn. However, it appears to fall over if the list of pointers consists of a single entry.

  1. typedef void (*fptr)(void);
  2.  
  3. static fptr ctor_list[1] __attribute__((section(".ctors"))) = { (fptr) -1 };
  4. static fptr dtor_list[1] __attribute__((section(".dtors"))) = { (fptr) -1 };
  5.  
  6. /* Call this to execute all ctors */
  7. void arch_ctors() {
  8.         fptr *fpp;
  9.  
  10.         /* Run up to the end of the list (defined by crtend) */
  11.         for (fpp=ctor_list + 1; *fpp != 0; ++fpp)
  12.                 ;
  13.  
  14.         /* Now run the ctors backwards */
  15.         while (--fpp > ctor_list)
  16.                 (**fpp)();
  17. }
  18.  
  19. /* Call this to execute all dtors */
  20. void arch_dtors() {
  21.         fptr *fpp;
  22.  
  23.         /* Do the dtors forwards */
  24.         for (fpp=dtor_list + 1; *fpp != 0; ++fpp )
  25.                 (**fpp)();
  26. }

Posted in Programming, Sega Dreamcast | 6 Comments »

6 Responses

  1. Reimar Says:

    Actually, it fails on an empty ctor/dtor list. The lists are NULL-terminated by the looks of it, and due to starting with ctor_list+1 it will jump over the terminating NULL for an empty list.
    No idea why they considered it reasonable to assume there’s always one entry though.
    Actually neither does it seem to ever run the first entries either.
    I’d guess an off-by-one error, but I can’t imagine someone would make one that obvious/stupid?!?
    Maybe it helps if you actually have initialized, non-constant data like
    int a = 54;
    ? I admit I have no real clue, but I’d assume a ctor would be used to set those up.
    I still can’t see how the code would work though, that variable would still stay uninitialized.
    If my assumptions above are right though, it might have been that older gcc/linker versions either created a unnecessary ctor/dtor or they had additional 0-padding right after the ctor/dtor sections.
    Either way I suspect that the NULL-check is wrong either way (since I don’t think the list needs to be 0-terminated), I really think it should instead use the __CTOR_LIST__ and __CTOR_END__ symbols.

  2. SvdB Says:

    It looks like a hack to get at the constructors and destructors.
    ctor_list and dtor_list don’t actually point to the list of constructors and destructors; they are newly declared variables which are declared to reside in the .ctor and .dtor sections!
    The arch_ctors() and arch_dtors() functions look at the memory right behind those variables (hence the +1).
    The assumption is that right after those variables, the real constructors and destructors live. When that assumption is false, this code breaks.

  3. Multimedia Mike Says:

    Thanks for the analyses of this problem. When I comment out the functions that process these lists, the .ctors and .dtors sections don’t appear in the compiled ELF files. So it sounds like their needed in the first place.

  4. Tobi Says:

    hi mike,

    have you had any luck with this ? I tried to get a working dreamcast toolchain using the script that comes with KOS – Having some issues with that. The fact that i’m building on OSX does not make things any better.

    regards,
    Tobi

  5. Multimedia Mike Says:

    @Tobi: Yeah, just comment out arch_ctors() and arch_dtors() in the main kernel module and KOS should work.

  6. Tobi Says:

    @mike well at the moment i don’t even get the toolchain to compile :-} have to look at it again this evening. Got too late yesterday..