This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

LX51 overlapping code with no warnings/errors

Hi there,

I am currently seeing a relatively strange occurrence with the Keil compiler and linker overlapping two pieces of code into the same code area without showing any warnings or errors to the user.

Here are the details:

* Compiler/Linker versions:

CX51 COMPILER V9.02 - SN: K1NCC-YA1WVE
COPYRIGHT KEIL ELEKTRONIK GmbH 1987 - 2010

LX51 LINKER/LOCATER V4.47a - SN: K1NCC-YA1WVE COPYRIGHT KEIL ELEKTRONIK GmbH 1995 - 2010

* Attached map file (linker_overlap.map), lines 651 - 653:

00CD88H 00CD93H 00000CH BYTE UNIT CODE_TIMER/B3 ?PR?_TIMER_INIT?TIMER
*** OVERLAP ***
00CD89H 00CD95H 00000DH BYTE UNIT CODE/B3 ?L?COM000A

The linker is clearly overlapping two functions (timer_init and a linker optimized code block) in the same code space, causing timer_init to contain invalid code. It clearly detects it during linking, since it prints and *** OVERLAP *** message in the .map file, but it shows no errors or warnings.

* Additional details:

- Multiple libraries linked in with a mix of linker code packing enabled and disabled per library.
- Warning level set to 2 in the .lin file (WARNINGLEVEL(2))
- Optimization level set to 9 (Common Block Subroutines) in all libraries

Thanks in advance!

Carles

  • "Attached map file"

    This forum does not support attachments - so you cannot possibly have attached a map file!

    Is this copied and pasted from another forum?

    "The linker is clearly overlapping two functions (timer_init and a linker optimized code block) in the same code space, causing timer_init to contain invalid code"

    How do you know it's invalid?
    Couldn't it be a perfectly valid optimisation?

    One of the main extensions of the LX51 linker over BL51 is the additional link-time optimisations - which include reorganising code.
    Maybe this reorganisation just happens to look like an overlap - which could be why you get no errors?

    As you're using LX51, I think this must mean that you have a fully-licensed version?
    If so, you should contact Keil support direct...


  • This forum does not support attachments - so you cannot possibly have attached a map file!
    Is this copied and pasted from another forum?

    No it is not, it is copy and pasted from an internal email.


    How do you know it's invalid?
    Couldn't it be a perfectly valid optimisation?

    I know it's invalid because when the code executes I trace it in the debugger (and I checked the resulting .hex file too) and the function timer_init() is invoked correctly, but apart from its first byte, the rest actually contains code from COM000A, which makes the execution fail. The linker is effectively overwriting our function with a common optimization, but the fact that makes me think there's a problem with the linker is twofold:

    1) The .map file shows *** OVERLAP ***. I have checked other map files and never seen this before, even with all optimizations on.
    2) The first thing executed in timer_init() is a call to UIDIV. As you can see from the .map lines I posted, the overlap begins one byte after the beginning of timer_init(). What we see is an LCALL followed by and address which makes no sense at all (it is in the middle of another function, completely unrelated and in another module), and that is because COM000A has overwritten the implementation of timer_init() thus placing common optimization code where the LCALL address should be.


    Maybe this reorganisation just happens to look like an overlap - which could be why you get no errors?

    We have been using Linker Code Packing for a while, and until now we have seen no issues.


    As you're using LX51, I think this must mean that you have a fully-licensed version?
    If so, you should contact Keil support direct...
    <i/>

    We do have a fully-licensed version and I have contacted support already. Posting it in the forum goes to the benefit of other LX51 users that may encounter the same problem.

    Thanks!

    --
    Carles

  • "What we see is an LCALL followed by and address which makes no sense at all (it is in the middle of another function, completely unrelated and in another module)"

    That doesn't prove that it's actually invalid.

    The Linker doesn't care where the code comes from - if it spots some common stuff anywhere in the project, it can combine them...

    It's not the addresses that need to make sense - it's the sequence of instructions that actually gets executed.

    Yes, it can be very hard indeed to make sense of highly-optimised code like this!


  • The Linker doesn't care where the code comes from - if it spots some common stuff anywhere in the project, it can combine them...

    It's not the addresses that need to make sense - it's the sequence of instructions that actually gets executed.

    I agree with you, and we have examined the disassembly and noticed how the linker reuses assembly segments to reduce code size, however in this particular instance the linker does not seem to be doing that at all, here are some additional details that may come in useful:

    a) We are linking against an .omf file which is a precompiled ROM
    b) We make use of bank switching in both the ROM code and the final project

    This is the sequence of events when the problem does not occur:

    1) main() invokes an internal function timer_init() which is in bank #3. That function starts by performing an integer division, which gets translated into a call to C?UIDIV. This particular function, C?UIDIV, was compiled into the ROM (common area) and it is linked publicly through the .omf file
    2) The first assembly instruction in timer_init() is an LCALL to C?UIDIV to perform the division. We can confirm that this LCALL is to the correct address for C?UIDIV by examining the precompiled ROM map file. We can also confirm when we step through the code that the division is indeed done and then the flow returns back to timer_init()

    This is the sequence of events when the problem does occur:

    1) Same as above
    2) The first assembly instruction in timer_init() is also an LCALL but this time the address that follows it causes the flow to jump into the middle of function_x() in the common area. As per your suggestion, we just verified whether that could be an optimization trick used by the linker, but when we step through this code segment we see that function_x() completes as if it was called. This is not an integer division and the control flow never returns back to timer_init(), as it should if this was indeed an optimization.

    Thanks,

    Carles

  • a) We are linking against an .omf file which is a precompiled ROM
    b) We make use of bank switching in both the ROM code and the final project

    And you honestly expected that kind of contraption to work? Wow, you must be one brave coder.


  • And you honestly expected that kind of contraption to work? Wow, you must be one brave coder.

    Honestly, we kinda were expecting it to work :) and in fact it did until very recently when we spotted this problem.

  • You still haven't said that there is any actual problem in the operation of the system

  • You still haven't said that there is any actual problem in the operation of the system

    From my previous post:


    2) The first assembly instruction in timer_init() is also an LCALL but this time the address that follows it causes the flow to jump into the middle of function_x() in the common area. As per your suggestion, we just verified whether that could be an optimization trick used by the linker, but when we step through this code segment we see that function_x() completes as if it was called. This is not an integer division and the control flow never returns back to timer_init(), as it should if this was indeed an optimization.

    When the problem occurs, the flow never returns to timer_init() and the code basically vectors into space. This particular function, timer_init() is invoked from our main() but since it never returns from it, the software effectively hangs completely, not being able to proceed into the actual useful code that is run after the initialization functions (timer_init() being one of them).

    Thanks!

    Carles

  • I see.

    I think Keil support is your only hope here; it's just too deep for people on a forum - with no sight of your system or your source code - to make meaningful, detailed analysis or comments.

    Or maybe you need to get in an indepenent reviewer?

    A forum can only really give general observations, like the fact that you are making this really, really, really complicated - and that is just asking for trouble!

    When you get to a stage like this where you're having to pull every single trick in the book to the max to fight the limitations of the 8051 architecture, you really have to ask yourself whether it's worth the effort - or would it be less trouble to switch to another architecture that just doesn't have the limitations that you're fighting...

    Sorry - no easy answer there!


  • I think Keil support is your only hope here; it's just too deep for people on a forum - with no sight of your system or your source code - to make meaningful, detailed analysis or comments.

    Yep, as I think I mentioned earlier, we have already contacted Keil's support for this. However I thought I might as well give it a shot in the forums, although I am perfectly aware that not all information required has been made available.


    When you get to a stage like this where you're having to pull every single trick in the book to the max to fight the limitations of the 8051 architecture, you really have to ask yourself whether it's worth the effort - or would it be less trouble to switch to another architecture that just doesn't have the limitations that you're fighting...

    Yes, indeed I agree with you.

    Thanks for your help!

    Carles

  • a) We are linking against an .omf file which is a precompiled ROM
    1) no .omf can exist in a ROM
    2) if you have a .omf, you should have the source

    Erik


  • 1) no .omf can exist in a ROM

    The .omf is not part of the ROM. When we build the ROM we generate an .omf file against which we link the full firmware release.


    2) if you have a .omf, you should have the source

    Yes, indeed, this is our own ROM. That code that is ROMed is frozen, and we continue developing the rest while linking with the ROM.
    The .omf is used to be able to link against the ROM without having to build its source again. The .omf contains the public symbols defined by the ROM and their location in code space.

    Carles

  • ... it generates them.

    What am I missing?

    Erik

  • Oh yes it does!

    The Compiler generates its objects in OMF;

    The Linker accepts OMF files as input, and can generate an OMF file as its output.